@ethereumjs/block
Version:
Provides Block serialization and help functions
139 lines • 6.22 kB
JavaScript
;
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