UNPKG

@ethereumjs/block

Version:
139 lines 6.22 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CLIQUE_EXTRA_SEAL = exports.CLIQUE_EXTRA_VANITY = void 0; exports.requireClique = requireClique; exports.cliqueSigHash = cliqueSigHash; exports.cliqueIsEpochTransition = cliqueIsEpochTransition; exports.cliqueExtraVanity = cliqueExtraVanity; exports.cliqueExtraSeal = cliqueExtraSeal; exports.cliqueEpochTransitionSigners = cliqueEpochTransitionSigners; exports.cliqueSigner = cliqueSigner; exports.cliqueVerifySignature = cliqueVerifySignature; exports.generateCliqueBlockExtraData = generateCliqueBlockExtraData; const common_1 = require("@ethereumjs/common"); const rlp_1 = require("@ethereumjs/rlp"); const util_1 = require("@ethereumjs/util"); const secp256k1_js_1 = require("ethereum-cryptography/secp256k1.js"); // Fixed number of extra-data prefix bytes reserved for signer vanity exports.CLIQUE_EXTRA_VANITY = 32; // Fixed number of extra-data suffix bytes reserved for signer seal exports.CLIQUE_EXTRA_SEAL = 65; // This function is not exported in the index file to keep it internal function requireClique(header, name) { if (header.common.consensusAlgorithm() !== common_1.ConsensusAlgorithm.Clique) { const msg = header['_errorMsg'](`BlockHeader.${name}() call only supported for clique PoA networks`); throw (0, util_1.EthereumJSErrorWithoutCode)(msg); } } /** * PoA clique signature hash without the seal. */ function cliqueSigHash(header) { requireClique(header, 'cliqueSigHash'); const raw = header.raw(); raw[12] = header.extraData.subarray(0, header.extraData.length - exports.CLIQUE_EXTRA_SEAL); return header['keccakFunction'](rlp_1.RLP.encode(raw)); } /** * Checks if the block header is an epoch transition * header (only clique PoA, throws otherwise) */ function cliqueIsEpochTransition(header) { requireClique(header, 'cliqueIsEpochTransition'); const epoch = BigInt(header.common.consensusConfig().epoch); // Epoch transition block if the block number has no // remainder on the division by the epoch length return header.number % epoch === util_1.BIGINT_0; } /** * Returns extra vanity data * (only clique PoA, throws otherwise) */ function cliqueExtraVanity(header) { requireClique(header, 'cliqueExtraVanity'); return header.extraData.subarray(0, exports.CLIQUE_EXTRA_VANITY); } /** * Returns extra seal data * (only clique PoA, throws otherwise) */ function cliqueExtraSeal(header) { requireClique(header, 'cliqueExtraSeal'); return header.extraData.subarray(-exports.CLIQUE_EXTRA_SEAL); } /** * Returns a list of signers * (only clique PoA, throws otherwise) * * This function throws if not called on an epoch * transition block and should therefore be used * in conjunction with {@link BlockHeader.cliqueIsEpochTransition} */ function cliqueEpochTransitionSigners(header) { requireClique(header, 'cliqueEpochTransitionSigners'); if (!cliqueIsEpochTransition(header)) { const msg = header['_errorMsg']('Signers are only included in epoch transition blocks (clique)'); throw (0, util_1.EthereumJSErrorWithoutCode)(msg); } const start = exports.CLIQUE_EXTRA_VANITY; const end = header.extraData.length - exports.CLIQUE_EXTRA_SEAL; const signerBytes = header.extraData.subarray(start, end); const signerList = []; const signerLength = 20; for (let start = 0; start <= signerBytes.length - signerLength; start += signerLength) { signerList.push(signerBytes.subarray(start, start + signerLength)); } return signerList.map((buf) => new util_1.Address(buf)); } /** * Returns the signer address */ function cliqueSigner(header) { requireClique(header, 'cliqueSigner'); const extraSeal = cliqueExtraSeal(header); // Reasonable default for default blocks if (extraSeal.length === 0 || (0, util_1.equalsBytes)(extraSeal, new Uint8Array(65))) { return (0, util_1.createZeroAddress)(); } const r = extraSeal.subarray(0, 32); const s = extraSeal.subarray(32, 64); const v = (0, util_1.bytesToBigInt)(extraSeal.subarray(64, 65)) + util_1.BIGINT_27; const pubKey = (0, util_1.ecrecover)(cliqueSigHash(header), v, r, s); return (0, util_1.createAddressFromPublicKey)(pubKey); } /** * Verifies the signature of the block (last 65 bytes of extraData field) * (only clique PoA, throws otherwise) * * Method throws if signature is invalid */ function cliqueVerifySignature(header, signerList) { requireClique(header, 'cliqueVerifySignature'); const signerAddress = cliqueSigner(header); const signerFound = signerList.find((signer) => { return signer.equals(signerAddress); }); return !!signerFound; } /** * Generates the extraData from a sealed block header * @param header block header from which to retrieve extraData * @param cliqueSigner clique signer key used for creating sealed block * @returns clique seal (i.e. extradata) for the block */ function generateCliqueBlockExtraData(header, cliqueSigner) { // Ensure extraData is at least length CLIQUE_EXTRA_VANITY + CLIQUE_EXTRA_SEAL const minExtraDataLength = exports.CLIQUE_EXTRA_VANITY + exports.CLIQUE_EXTRA_SEAL; if (header.extraData.length < minExtraDataLength) { const remainingLength = minExtraDataLength - header.extraData.length; header.extraData = (0, util_1.concatBytes)(header.extraData, new Uint8Array(remainingLength)); } requireClique(header, 'generateCliqueBlockExtraData'); const ecSignFunction = header.common.customCrypto?.ecsign ?? secp256k1_js_1.secp256k1.sign; const signature = ecSignFunction(cliqueSigHash(header), cliqueSigner); const signatureB = (0, util_1.concatBytes)((0, util_1.setLengthLeft)((0, util_1.bigIntToUnpaddedBytes)(signature.r), 32), (0, util_1.setLengthLeft)((0, util_1.bigIntToUnpaddedBytes)(signature.s), 32), (0, util_1.bigIntToBytes)(BigInt(signature.recovery))); const extraDataWithoutSeal = header.extraData.subarray(0, header.extraData.length - exports.CLIQUE_EXTRA_SEAL); const extraData = (0, util_1.concatBytes)(extraDataWithoutSeal, signatureB); return extraData; } //# sourceMappingURL=clique.js.map