import { BitArray, deserializeUint8ArrayBitListFromBytes } from "@chainsafe/ssz";
import { BYTES_PER_FIELD_ELEMENT, FIELD_ELEMENTS_PER_BLOB, ForkSeq, MAX_COMMITTEES_PER_SLOT, isForkPostElectra, } from "@lodestar/params";
import { ssz } from "@lodestar/types";
// pre-electra
// class Attestation(Container):
//   aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] - offset 4
//   data: AttestationData - target data - 128
//   signature: BLSSignature - 96
// electra
// class Attestation(Container):
//   aggregation_bits: BitList[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] - offset 4
//   data: AttestationData - target data - 128
//   signature: BLSSignature - 96
//   committee_bits: BitVector[MAX_COMMITTEES_PER_SLOT]
// electra
// class SingleAttestation(Container):
//   committeeIndex: CommitteeIndex - data 8
//   attesterIndex: ValidatorIndex - data 8
//   data: AttestationData - data 128
//   signature: BLSSignature - data 96
//
// for all forks
// class AttestationData(Container): 128 bytes fixed size
//   slot: Slot                - data 8
//   index: CommitteeIndex     - data 8
//   beacon_block_root: Root   - data 32
//   source: Checkpoint        - data 40
//   target: Checkpoint        - data 40
const VARIABLE_FIELD_OFFSET = 4;
const ATTESTATION_BEACON_BLOCK_ROOT_OFFSET = VARIABLE_FIELD_OFFSET + 8 + 8;
export const ROOT_SIZE = 32;
const SLOT_SIZE = 8;
const COMMITTEE_INDEX_SIZE = 8;
const ATTESTATION_DATA_SIZE = 128;
// MAX_COMMITTEES_PER_SLOT is in bit, need to convert to byte
const COMMITTEE_BITS_SIZE = Math.max(Math.ceil(MAX_COMMITTEES_PER_SLOT / 8), 1);
const SIGNATURE_SIZE = 96;
const SINGLE_ATTESTATION_ATTDATA_OFFSET = 8 + 8;
const SINGLE_ATTESTATION_SLOT_OFFSET = SINGLE_ATTESTATION_ATTDATA_OFFSET;
const SINGLE_ATTESTATION_COMMITTEE_INDEX_OFFSET = 0;
const SINGLE_ATTESTATION_ATTESTER_INDEX_OFFSET = 8;
const SINGLE_ATTESTATION_BEACON_BLOCK_ROOT_OFFSET = SINGLE_ATTESTATION_ATTDATA_OFFSET + 8 + 8;
const SINGLE_ATTESTATION_SIGNATURE_OFFSET = SINGLE_ATTESTATION_ATTDATA_OFFSET + ATTESTATION_DATA_SIZE;
const SINGLE_ATTESTATION_SIZE = SINGLE_ATTESTATION_SIGNATURE_OFFSET + SIGNATURE_SIZE;
// shared Buffers to convert bytes to hex/base64
const blockRootBuf = Buffer.alloc(ROOT_SIZE);
const attDataBuf = Buffer.alloc(ATTESTATION_DATA_SIZE);
const committeeBitsDataBuf = Buffer.alloc(COMMITTEE_BITS_SIZE);
/**
 * Extract slot from attestation serialized bytes.
 * Return null if data is not long enough to extract slot.
 */
export function getSlotFromAttestationSerialized(data) {
    if (data.length < VARIABLE_FIELD_OFFSET + SLOT_SIZE) {
        return null;
    }
    return getSlotFromOffset(data, VARIABLE_FIELD_OFFSET);
}
/**
 * Extract block root from attestation serialized bytes.
 * Return null if data is not long enough to extract block root.
 */
export function getBlockRootFromAttestationSerialized(data) {
    if (data.length < ATTESTATION_BEACON_BLOCK_ROOT_OFFSET + ROOT_SIZE) {
        return null;
    }
    blockRootBuf.set(data.subarray(ATTESTATION_BEACON_BLOCK_ROOT_OFFSET, ATTESTATION_BEACON_BLOCK_ROOT_OFFSET + ROOT_SIZE));
    return "0x" + blockRootBuf.toString("hex");
}
/**
 * Extract attestation data base64 from all forks' attestation serialized bytes.
 * Return null if data is not long enough to extract attestation data.
 */
export function getAttDataFromAttestationSerialized(data) {
    if (data.length < VARIABLE_FIELD_OFFSET + ATTESTATION_DATA_SIZE) {
        return null;
    }
    // base64 is a bit efficient than hex
    attDataBuf.set(data.subarray(VARIABLE_FIELD_OFFSET, VARIABLE_FIELD_OFFSET + ATTESTATION_DATA_SIZE));
    return attDataBuf.toString("base64");
}
/**
 * Extract AttDataBase64 from `beacon_attestation` gossip message serialized bytes.
 * This is used for GossipQueue.
 */
export function getBeaconAttestationGossipIndex(fork, data) {
    return ForkSeq[fork] >= ForkSeq.electra
        ? getAttDataFromSingleAttestationSerialized(data)
        : getAttDataFromAttestationSerialized(data);
}
/**
 * Extract slot from `beacon_attestation` gossip message serialized bytes.
 */
export function getSlotFromBeaconAttestationSerialized(fork, data) {
    return ForkSeq[fork] >= ForkSeq.electra
        ? getSlotFromSingleAttestationSerialized(data)
        : getSlotFromAttestationSerialized(data);
}
/**
 * Extract block root from `beacon_attestation` gossip message serialized bytes.
 */
export function getBlockRootFromBeaconAttestationSerialized(fork, data) {
    return ForkSeq[fork] >= ForkSeq.electra
        ? getBlockRootFromSingleAttestationSerialized(data)
        : getBlockRootFromAttestationSerialized(data);
}
/**
 * Extract aggregation bits from attestation serialized bytes.
 * Return null if data is not long enough to extract aggregation bits.
 * Pre-electra attestation only
 */
export function getAggregationBitsFromAttestationSerialized(data) {
    const aggregationBitsStartIndex = VARIABLE_FIELD_OFFSET + ATTESTATION_DATA_SIZE + SIGNATURE_SIZE;
    if (data.length < aggregationBitsStartIndex) {
        return null;
    }
    const { uint8Array, bitLen } = deserializeUint8ArrayBitListFromBytes(data, aggregationBitsStartIndex, data.length);
    return new BitArray(uint8Array, bitLen);
}
/**
 * Extract signature from attestation serialized bytes.
 * Return null if data is not long enough to extract signature.
 */
export function getSignatureFromAttestationSerialized(data) {
    const signatureStartIndex = VARIABLE_FIELD_OFFSET + ATTESTATION_DATA_SIZE;
    if (data.length < signatureStartIndex + SIGNATURE_SIZE) {
        return null;
    }
    return data.subarray(signatureStartIndex, signatureStartIndex + SIGNATURE_SIZE);
}
/**
 * Extract slot from SingleAttestation serialized bytes.
 * Return null if data is not long enough to extract slot.
 */
export function getSlotFromSingleAttestationSerialized(data) {
    if (data.length !== SINGLE_ATTESTATION_SIZE) {
        return null;
    }
    return getSlotFromOffset(data, SINGLE_ATTESTATION_SLOT_OFFSET);
}
/**
 * Extract committee index from SingleAttestation serialized bytes.
 * Return null if data is not long enough to extract slot.
 */
export function getCommitteeIndexFromSingleAttestationSerialized(fork, data) {
    if (isForkPostElectra(fork)) {
        if (data.length !== SINGLE_ATTESTATION_SIZE) {
            return null;
        }
        return getIndexFromOffset(data, SINGLE_ATTESTATION_COMMITTEE_INDEX_OFFSET);
    }
    if (data.length < VARIABLE_FIELD_OFFSET + SLOT_SIZE + COMMITTEE_INDEX_SIZE) {
        return null;
    }
    return getIndexFromOffset(data, VARIABLE_FIELD_OFFSET + SLOT_SIZE);
}
/**
 * Extract attester index from SingleAttestation serialized bytes.
 * Return null if data is not long enough to extract index.
 */
export function getAttesterIndexFromSingleAttestationSerialized(data) {
    if (data.length !== SINGLE_ATTESTATION_SIZE) {
        return null;
    }
    return getIndexFromOffset(data, SINGLE_ATTESTATION_ATTESTER_INDEX_OFFSET);
}
/**
 * Extract block root from SingleAttestation serialized bytes.
 * Return null if data is not long enough to extract block root.
 */
export function getBlockRootFromSingleAttestationSerialized(data) {
    if (data.length !== SINGLE_ATTESTATION_SIZE) {
        return null;
    }
    blockRootBuf.set(data.subarray(SINGLE_ATTESTATION_BEACON_BLOCK_ROOT_OFFSET, SINGLE_ATTESTATION_BEACON_BLOCK_ROOT_OFFSET + ROOT_SIZE));
    return `0x${blockRootBuf.toString("hex")}`;
}
/**
 * Extract attestation data base64 from SingleAttestation serialized bytes.
 * Return null if data is not long enough to extract attestation data.
 */
export function getAttDataFromSingleAttestationSerialized(data) {
    if (data.length !== SINGLE_ATTESTATION_SIZE) {
        return null;
    }
    // base64 is a bit efficient than hex
    attDataBuf.set(data.subarray(SINGLE_ATTESTATION_ATTDATA_OFFSET, SINGLE_ATTESTATION_ATTDATA_OFFSET + ATTESTATION_DATA_SIZE));
    return attDataBuf.toString("base64");
}
/**
 * Extract signature from SingleAttestation serialized bytes.
 * Return null if data is not long enough to extract signature.
 */
export function getSignatureFromSingleAttestationSerialized(data) {
    if (data.length !== SINGLE_ATTESTATION_SIZE) {
        return null;
    }
    return data.subarray(SINGLE_ATTESTATION_SIGNATURE_OFFSET, SINGLE_ATTESTATION_SIGNATURE_OFFSET + SIGNATURE_SIZE);
}
//
// class SignedAggregateAndProof(Container):
//    message: AggregateAndProof - offset 4
//    signature: BLSSignature    - data 96
// class AggregateAndProof(Container)
//    aggregatorIndex: ValidatorIndex - data 8
//    aggregate: Attestation          - offset 4
//    selectionProof: BLSSignature    - data 96
const AGGREGATE_AND_PROOF_OFFSET = 4 + 96;
const AGGREGATE_OFFSET = AGGREGATE_AND_PROOF_OFFSET + 8 + 4 + 96;
const SIGNED_AGGREGATE_AND_PROOF_SLOT_OFFSET = AGGREGATE_OFFSET + VARIABLE_FIELD_OFFSET;
const SIGNED_AGGREGATE_AND_PROOF_BLOCK_ROOT_OFFSET = SIGNED_AGGREGATE_AND_PROOF_SLOT_OFFSET + 8 + 8;
/**
 * Extract slot from signed aggregate and proof serialized bytes
 * Return null if data is not long enough to extract slot
 * This works for both phase + electra
 */
export function getSlotFromSignedAggregateAndProofSerialized(data) {
    if (data.length < SIGNED_AGGREGATE_AND_PROOF_SLOT_OFFSET + SLOT_SIZE) {
        return null;
    }
    return getSlotFromOffset(data, SIGNED_AGGREGATE_AND_PROOF_SLOT_OFFSET);
}
/**
 * Extract block root from signed aggregate and proof serialized bytes
 * Return null if data is not long enough to extract block root
 * This works for both phase + electra
 */
export function getBlockRootFromSignedAggregateAndProofSerialized(data) {
    if (data.length < SIGNED_AGGREGATE_AND_PROOF_BLOCK_ROOT_OFFSET + ROOT_SIZE) {
        return null;
    }
    blockRootBuf.set(data.subarray(SIGNED_AGGREGATE_AND_PROOF_BLOCK_ROOT_OFFSET, SIGNED_AGGREGATE_AND_PROOF_BLOCK_ROOT_OFFSET + ROOT_SIZE));
    return "0x" + blockRootBuf.toString("hex");
}
/**
 * Extract AttestationData base64 from SignedAggregateAndProof for electra
 * Return null if data is not long enough
 */
export function getAttDataFromSignedAggregateAndProofElectra(data) {
    const startIndex = SIGNED_AGGREGATE_AND_PROOF_SLOT_OFFSET;
    const endIndex = startIndex + ATTESTATION_DATA_SIZE;
    if (data.length < endIndex + SIGNATURE_SIZE + COMMITTEE_BITS_SIZE) {
        return null;
    }
    attDataBuf.set(data.subarray(startIndex, endIndex));
    return attDataBuf.toString("base64");
}
/**
 * Extract CommitteeBits base64 from SignedAggregateAndProof for electra
 * Return null if data is not long enough
 */
export function getCommitteeBitsFromSignedAggregateAndProofElectra(data) {
    const startIndex = SIGNED_AGGREGATE_AND_PROOF_SLOT_OFFSET + ATTESTATION_DATA_SIZE + SIGNATURE_SIZE;
    const endIndex = startIndex + COMMITTEE_BITS_SIZE;
    if (data.length < endIndex) {
        return null;
    }
    committeeBitsDataBuf.set(data.subarray(startIndex, endIndex));
    return committeeBitsDataBuf.toString("base64");
}
/**
 * Extract attestation data base64 from signed aggregate and proof serialized bytes.
 * Return null if data is not long enough to extract attestation data.
 */
export function getAttDataFromSignedAggregateAndProofPhase0(data) {
    if (data.length < SIGNED_AGGREGATE_AND_PROOF_SLOT_OFFSET + ATTESTATION_DATA_SIZE) {
        return null;
    }
    // base64 is a bit efficient than hex
    attDataBuf.set(data.subarray(SIGNED_AGGREGATE_AND_PROOF_SLOT_OFFSET, SIGNED_AGGREGATE_AND_PROOF_SLOT_OFFSET + ATTESTATION_DATA_SIZE));
    return attDataBuf.toString("base64");
}
/**
 * 4 + 96 = 100
 * ```
 * class SignedBeaconBlock(Container):
 *   message: BeaconBlock [offset - 4 bytes]
 *   signature: BLSSignature [fixed - 96 bytes]
 *
 * class BeaconBlock(Container):
 *   slot: Slot [fixed - 8 bytes]
 *   proposer_index: ValidatorIndex
 *   parent_root: Root
 *   state_root: Root
 *   body: BeaconBlockBody
 * ```
 */
const SLOT_BYTES_POSITION_IN_SIGNED_BEACON_BLOCK = VARIABLE_FIELD_OFFSET + SIGNATURE_SIZE;
export function getSlotFromSignedBeaconBlockSerialized(data) {
    if (data.length < SLOT_BYTES_POSITION_IN_SIGNED_BEACON_BLOCK + SLOT_SIZE) {
        return null;
    }
    return getSlotFromOffset(data, SLOT_BYTES_POSITION_IN_SIGNED_BEACON_BLOCK);
}
/**
 * class BlobSidecar(Container):
 *  index: BlobIndex [fixed - 8 bytes ],
 *  blob: Blob, BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB
 *  kzgCommitment: Bytes48,
 *  kzgProof: Bytes48,
 *  signedBlockHeader:
 *    slot: 8 bytes
 */
const SLOT_BYTES_POSITION_IN_SIGNED_BLOB_SIDECAR = 8 + BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB + 48 + 48;
export function getSlotFromBlobSidecarSerialized(data) {
    if (data.length < SLOT_BYTES_POSITION_IN_SIGNED_BLOB_SIDECAR + SLOT_SIZE) {
        return null;
    }
    return getSlotFromOffset(data, SLOT_BYTES_POSITION_IN_SIGNED_BLOB_SIDECAR);
}
/**
 * {
    index: ColumnIndex [ fixed - 8 bytes],
    column: DataColumn BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_CELL * <some non fixed length>,
    kzgCommitments: denebSsz.BlobKzgCommitments,
    kzgProofs: denebSsz.KZGProofs,
    signedBlockHeader: phase0Ssz.SignedBeaconBlockHeader,
    kzgCommitmentsInclusionProof: KzgCommitmentsInclusionProof,
  }
 */
const SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR = 20;
export function getSlotFromDataColumnSidecarSerialized(data) {
    if (data.length < SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR + SLOT_SIZE) {
        return null;
    }
    return getSlotFromOffset(data, SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR);
}
/**
 * BeaconState of all forks (up until Electra, check with new forks)
 * class BeaconState(Container):
 *   genesis_time: uint64                    - 8 bytes
 *   genesis_validators_root: Root           - 32 bytes
 *   slot: Slot                              - 8 bytes
 *   fork: Fork                              - 16 bytes
 *   latest_block_header: BeaconBlockHeader  - fixed size
 *     slot: Slot                            - 8 bytes
 *
 */
const BLOCK_HEADER_SLOT_BYTES_POSITION_IN_BEACON_STATE = 8 + 32 + 8 + 16;
export function getLastProcessedSlotFromBeaconStateSerialized(data) {
    if (data.length < BLOCK_HEADER_SLOT_BYTES_POSITION_IN_BEACON_STATE + SLOT_SIZE) {
        return null;
    }
    return getSlotFromOffset(data, BLOCK_HEADER_SLOT_BYTES_POSITION_IN_BEACON_STATE);
}
const SLOT_BYTES_POSITION_IN_BEACON_STATE = 8 + 32;
export function getSlotFromBeaconStateSerialized(data) {
    if (data.length < SLOT_BYTES_POSITION_IN_BEACON_STATE) {
        return null;
    }
    return getSlotFromOffset(data, SLOT_BYTES_POSITION_IN_BEACON_STATE);
}
/**
 * Read only the first 4 bytes of Slot, max value is 4,294,967,295 will be reached 1634 years after genesis
 *
 * If the high bytes are not zero, return null
 */
function getSlotFromOffset(data, offset) {
    return checkSlotHighBytes(data, offset) ? getSlotFromOffsetTrusted(data, offset) : null;
}
/**
 * Alias of `getSlotFromOffset` for readability
 */
function getIndexFromOffset(data, offset) {
    return getSlotFromOffset(data, offset);
}
/**
 * Read only the first 4 bytes of Slot, max value is 4,294,967,295 will be reached 1634 years after genesis
 */
function getSlotFromOffsetTrusted(data, offset) {
    return (data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24)) >>> 0;
}
function checkSlotHighBytes(data, offset) {
    return (data[offset + 4] | data[offset + 5] | data[offset + 6] | data[offset + 7]) === 0;
}
export function getBlobKzgCommitmentsCountFromSignedBeaconBlockSerialized(config, blockBytes) {
    const slot = getSlotFromSignedBeaconBlockSerialized(blockBytes);
    if (slot === null)
        throw new Error("Can not parse the slot from block bytes");
    if (config.getForkSeq(slot) < ForkSeq.deneb)
        return 0;
    const { SignedBeaconBlock, BeaconBlock, BeaconBlockBody, KZGCommitment } = ssz[config.getForkName(slot)];
    const view = new DataView(blockBytes.buffer, blockBytes.byteOffset, blockBytes.byteLength);
    const singedBlockFieldRanges = SignedBeaconBlock.getFieldRanges(view, 0, blockBytes.length);
    const messageIndex = Object.keys(SignedBeaconBlock.fields).indexOf("message");
    const messageRange = singedBlockFieldRanges[messageIndex];
    const blockFieldRanges = BeaconBlock.getFieldRanges(view, messageRange.start, messageRange.end);
    const bodyIndex = Object.keys(BeaconBlock.fields).indexOf("body");
    const bodyRange = blockFieldRanges[bodyIndex];
    const bodyFieldRanges = BeaconBlockBody.getFieldRanges(view, messageRange.start + bodyRange.start, messageRange.end + bodyRange.end);
    const kzgCommitmentsIndex = Object.keys(BeaconBlockBody.fields).indexOf("blobKzgCommitments");
    const kzgCommitmentsRange = bodyFieldRanges[kzgCommitmentsIndex];
    const commitmentSize = KZGCommitment.fixedSize;
    const end = messageRange.end + bodyRange.end + kzgCommitmentsRange.end;
    const start = messageRange.start + bodyRange.start + kzgCommitmentsRange.start;
    return Math.round(((end > blockBytes.byteLength ? blockBytes.byteLength : end) - start) / commitmentSize);
}
//# sourceMappingURL=sszBytes.js.map