UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

333 lines • 13.7 kB
import { BitArray, deserializeUint8ArrayBitListFromBytes } from "@chainsafe/ssz"; import { BYTES_PER_FIELD_ELEMENT, FIELD_ELEMENTS_PER_BLOB, ForkSeq, MAX_COMMITTEES_PER_SLOT, isForkPostElectra, } from "@lodestar/params"; // 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; 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); } /** * 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; } //# sourceMappingURL=sszBytes.js.map