UNPKG

@lodestar/config

Version:

Chain configuration required for lodestar

142 lines 7.1 kB
import { digest } from "@chainsafe/as-sha256"; import { DOMAIN_VOLUNTARY_EXIT, ForkName, ForkSeq, SLOTS_PER_EPOCH } from "@lodestar/params"; import { ssz } from "@lodestar/types"; import { intToBytes, strip0xPrefix, toHex, xor } from "@lodestar/utils"; export function createCachedGenesis(chainForkConfig, genesisValidatorsRoot) { const domainCache = new Map(); const forkDigestByEpoch = new Map(); const forkDigestHexByEpoch = new Map(); /** Map of ForkDigest in hex format without prefix: `0011aabb` */ const epochByForkDigest = new Map(); const { forkBoundariesAscendingEpochOrder } = chainForkConfig; for (let i = 0; i < forkBoundariesAscendingEpochOrder.length; i++) { const currentForkBoundary = forkBoundariesAscendingEpochOrder[i]; const nextForkBoundary = forkBoundariesAscendingEpochOrder[i + 1]; const currentEpoch = currentForkBoundary.epoch; const nextEpoch = nextForkBoundary !== undefined ? nextForkBoundary.epoch : Infinity; // Edge case: If multiple fork boundaries start at the same epoch, only consider the latest one if (currentEpoch === nextEpoch) { continue; } const forkDigest = computeForkDigest(chainForkConfig, genesisValidatorsRoot, currentEpoch); const forkDigestHex = toHexStringNoPrefix(forkDigest); epochByForkDigest.set(forkDigestHex, currentEpoch); forkDigestByEpoch.set(currentEpoch, forkDigest); forkDigestHexByEpoch.set(currentEpoch, forkDigestHex); } return { genesisValidatorsRoot, getDomain(stateSlot, domainType, messageSlot) { // ```py // def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch=None) -> Domain: // """ // Return the signature domain (fork version concatenated with domain type) of a message. // """ // epoch = get_current_epoch(state) if epoch is None else epoch // fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version // return compute_domain(domain_type, fork_version, state.genesis_validators_root) // ``` const epoch = Math.floor((messageSlot ?? stateSlot) / SLOTS_PER_EPOCH); // Get pre-computed fork schedule, which _should_ match the one in the state const stateForkInfo = chainForkConfig.getForkInfo(stateSlot); // Only allow to select either current or previous fork respective of the fork schedule at stateSlot const forkName = epoch < stateForkInfo.epoch ? stateForkInfo.prevForkName : stateForkInfo.name; const forkInfo = chainForkConfig.forks[forkName]; let domainByType = domainCache.get(forkInfo.name); if (!domainByType) { domainByType = new Map(); domainCache.set(forkInfo.name, domainByType); } let domain = domainByType.get(domainType); if (!domain) { domain = computeDomain(domainType, forkInfo.version, genesisValidatorsRoot); domainByType.set(domainType, domain); } return domain; }, getDomainAtFork(forkName, domainType) { // For some of the messages, irrespective of which slot they are signed // they need to use a fixed fork version even if other forks are scheduled // at the same fork. // // For e.g. BLSToExecutionChange has to be signed using GENESIS_FORK_VERSION // corresponding to phase0 const forkInfo = chainForkConfig.forks[forkName]; let domainByType = domainCache.get(forkInfo.name); if (!domainByType) { domainByType = new Map(); domainCache.set(forkInfo.name, domainByType); } let domain = domainByType.get(domainType); if (!domain) { domain = computeDomain(domainType, forkInfo.version, genesisValidatorsRoot); domainByType.set(domainType, domain); } return domain; }, getDomainForVoluntaryExit(stateSlot, messageSlot) { // Deneb onwards the signature domain fork is fixed to capella const domain = stateSlot < chainForkConfig.DENEB_FORK_EPOCH * SLOTS_PER_EPOCH ? this.getDomain(stateSlot, DOMAIN_VOLUNTARY_EXIT, messageSlot) : this.getDomainAtFork(ForkName.capella, DOMAIN_VOLUNTARY_EXIT); return domain; }, forkDigest2ForkBoundary(forkDigest) { const forkDigestHex = toHexStringNoPrefix(forkDigest); const epoch = epochByForkDigest.get(forkDigestHex); if (epoch == null) { throw Error(`Unknown forkDigest ${forkDigestHex}`); } return chainForkConfig.getForkBoundaryAtEpoch(epoch); }, forkDigest2ForkBoundaryOption(forkDigest) { const forkDigestHex = toHexStringNoPrefix(forkDigest); const epoch = epochByForkDigest.get(forkDigestHex); if (epoch == null) { return null; } return chainForkConfig.getForkBoundaryAtEpoch(epoch); }, forkBoundary2ForkDigest(boundary) { const forkDigest = forkDigestByEpoch.get(boundary.epoch); if (!forkDigest) { throw Error(`No precomputed forkDigest for ${boundary.epoch}`); } return forkDigest; }, forkBoundary2ForkDigestHex(boundary) { const forkDigestHex = forkDigestHexByEpoch.get(boundary.epoch); if (!forkDigestHex) { throw Error(`No precomputed forkDigest for ${boundary.epoch}`); } return toHexStringNoPrefix(forkDigestHex); }, }; } function computeDomain(domainType, forkVersion, genesisValidatorRoot) { const forkDataRoot = computeForkDataRoot(forkVersion, genesisValidatorRoot); const domain = new Uint8Array(32); domain.set(domainType, 0); domain.set(forkDataRoot.slice(0, 28), 4); return domain; } function computeForkDataRoot(currentVersion, genesisValidatorsRoot) { const forkData = { currentVersion, genesisValidatorsRoot, }; return ssz.phase0.ForkData.hashTreeRoot(forkData); } function toHexStringNoPrefix(hex) { return strip0xPrefix(typeof hex === "string" ? hex : toHex(hex)); } export function computeForkDigest(config, genesisValidatorsRoot, epoch) { const currentFork = config.getForkInfoAtEpoch(epoch); const baseDigest = computeForkDataRoot(currentFork.version, genesisValidatorsRoot); if (currentFork.seq < ForkSeq.fulu) { return baseDigest.slice(0, 4); } const blobParameters = config.getBlobParameters(epoch); return xor(baseDigest, digest(Buffer.concat([intToBytes(blobParameters.epoch, 8, "le"), intToBytes(blobParameters.maxBlobsPerBlock, 8, "le")]))).slice(0, 4); } //# sourceMappingURL=index.js.map