@lodestar/config
Version:
Chain configuration required for lodestar
142 lines • 7.1 kB
JavaScript
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