UNPKG

lisk-framework

Version:

Lisk blockchain application platform

185 lines 9.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BFTMethod = void 0; const lisk_cryptography_1 = require("@liskhq/lisk-cryptography"); const lisk_utils_1 = require("@liskhq/lisk-utils"); const utils_1 = require("./utils"); const bft_params_1 = require("./bft_params"); const constants_1 = require("./constants"); const schemas_1 = require("./schemas"); const errors_1 = require("./errors"); class BFTMethod { blockTime() { return this._blockTime; } init(batchSize, blockTime) { this._batchSize = batchSize; this._blockTime = blockTime; } areHeadersContradicting(bftHeader1, bftHeader2) { if (bftHeader1.id.equals(bftHeader2.id)) { return false; } return (0, utils_1.areDistinctHeadersContradicting)(bftHeader1, bftHeader2); } async isHeaderContradictingChain(stateStore, header) { const votesStore = stateStore.getStore(constants_1.MODULE_STORE_PREFIX_BFT, constants_1.STORE_PREFIX_BFT_VOTES); const bftVotes = await votesStore.getWithSchema(constants_1.EMPTY_KEY, schemas_1.bftVotesSchema); for (const bftBlock of bftVotes.blockBFTInfos) { if (bftBlock.generatorAddress.equals(header.generatorAddress)) { return (0, utils_1.areDistinctHeadersContradicting)(bftBlock, header); } } return false; } async existBFTParameters(stateStore, height) { const paramsStore = stateStore.getStore(constants_1.MODULE_STORE_PREFIX_BFT, constants_1.STORE_PREFIX_BFT_PARAMETERS); return paramsStore.has(lisk_cryptography_1.utils.intToBuffer(height, 4)); } async getBFTParameters(stateStore, height) { const paramsStore = stateStore.getStore(constants_1.MODULE_STORE_PREFIX_BFT, constants_1.STORE_PREFIX_BFT_PARAMETERS); return (0, bft_params_1.getBFTParameters)(paramsStore, height); } async getBFTParametersActiveValidators(stateStore, height) { const bftParams = await this.getBFTParameters(stateStore, height); return { ...bftParams, validators: bftParams.validators.filter(v => v.bftWeight > BigInt(0)), }; } async getBFTHeights(stateStore) { const votesStore = stateStore.getStore(constants_1.MODULE_STORE_PREFIX_BFT, constants_1.STORE_PREFIX_BFT_VOTES); const bftVotes = await votesStore.getWithSchema(constants_1.EMPTY_KEY, schemas_1.bftVotesSchema); return { maxHeightPrevoted: bftVotes.maxHeightPrevoted, maxHeightPrecommitted: bftVotes.maxHeightPrecommitted, maxHeightCertified: bftVotes.maxHeightCertified, }; } async impliesMaximalPrevotes(stateStore, header) { const votesStore = stateStore.getStore(constants_1.MODULE_STORE_PREFIX_BFT, constants_1.STORE_PREFIX_BFT_VOTES); const bftVotes = await votesStore.getWithSchema(constants_1.EMPTY_KEY, schemas_1.bftVotesSchema); if (bftVotes.blockBFTInfos.length === 0) { return header.height > header.maxHeightGenerated; } const [currentTip] = bftVotes.blockBFTInfos; if (currentTip.height + 1 !== header.height) { throw new Error(`Input header with height ${header.height} is invalid. It must be ${currentTip.height + 1}.`); } const previousHeight = header.maxHeightGenerated; if (previousHeight >= header.height) { return false; } const offset = currentTip.height - previousHeight; if (offset >= bftVotes.blockBFTInfos.length) { return true; } if (!bftVotes.blockBFTInfos[offset].generatorAddress.equals(header.generatorAddress)) { return false; } return true; } async getNextHeightBFTParameters(stateStore, height) { const paramsStore = stateStore.getStore(constants_1.MODULE_STORE_PREFIX_BFT, constants_1.STORE_PREFIX_BFT_PARAMETERS); const start = lisk_cryptography_1.utils.intToBuffer(height + 1, 4); const end = lisk_cryptography_1.utils.intToBuffer(constants_1.MAX_UINT32, 4); const results = await paramsStore.iterate({ limit: 1, gte: start, lte: end, }); if (results.length !== 1) { throw new errors_1.BFTParameterNotFoundError(); } const [result] = results; return result.key.readUInt32BE(0); } async setBFTParameters(stateStore, precommitThreshold, certificateThreshold, validators) { if (validators.length > this._batchSize) { throw new Error(`Invalid validators size. The number of validators can be at most the batch size ${this._batchSize}.`); } const validatorAddresses = []; const validatorValidBLSKeys = []; for (const validator of validators) { validatorAddresses.push(validator.address); if (!validator.blsKey.equals(constants_1.EMPTY_BLS_KEY)) { validatorValidBLSKeys.push(validator.blsKey); } } if (!lisk_utils_1.objects.bufferArrayUniqueItems(validatorAddresses)) { throw new Error('Provided validator addresses are not unique.'); } if (!lisk_utils_1.objects.bufferArrayUniqueItems(validatorValidBLSKeys)) { throw new Error('Provided validator BLS keys are not unique.'); } let aggregateBFTWeight = BigInt(0); for (const validator of validators) { if (validator.bftWeight < 0) { throw new Error('BFT Weight must be 0 or greater.'); } aggregateBFTWeight += validator.bftWeight; } if (aggregateBFTWeight / BigInt(3) + BigInt(1) > precommitThreshold || precommitThreshold > aggregateBFTWeight) { throw new Error('Invalid precommitThreshold input.'); } if (aggregateBFTWeight / BigInt(3) + BigInt(1) > certificateThreshold || certificateThreshold > aggregateBFTWeight) { throw new Error('Invalid certificateThreshold input.'); } const validatorsWithBFTWeight = validators .filter(validator => validator.bftWeight > BigInt(0)) .map(validator => ({ bftWeight: validator.bftWeight, blsKey: validator.blsKey })); (0, utils_1.sortValidatorsByBLSKey)(validatorsWithBFTWeight); const validatorsHash = (0, utils_1.computeValidatorsHash)(validatorsWithBFTWeight, certificateThreshold); const bftParams = { prevoteThreshold: (BigInt(2) * aggregateBFTWeight) / BigInt(3) + BigInt(1), precommitThreshold, certificateThreshold, validators, validatorsHash, }; const votesStore = stateStore.getStore(constants_1.MODULE_STORE_PREFIX_BFT, constants_1.STORE_PREFIX_BFT_VOTES); const bftVotes = await votesStore.getWithSchema(constants_1.EMPTY_KEY, schemas_1.bftVotesSchema); const nextHeight = (bftVotes.blockBFTInfos.length > 0 ? bftVotes.blockBFTInfos[0].height : bftVotes.maxHeightPrevoted) + 1; const paramsStore = stateStore.getStore(constants_1.MODULE_STORE_PREFIX_BFT, constants_1.STORE_PREFIX_BFT_PARAMETERS); const nextHeightBytes = lisk_cryptography_1.utils.intToBuffer(nextHeight, 4); await paramsStore.setWithSchema(nextHeightBytes, bftParams, schemas_1.bftParametersSchema); const nextActiveValidators = []; for (const validator of validators) { const existingValidator = bftVotes.activeValidatorsVoteInfo.find(v => v.address.equals(validator.address)); if (existingValidator) { nextActiveValidators.push(existingValidator); continue; } nextActiveValidators.push({ address: validator.address, minActiveHeight: nextHeight, largestHeightPrecommit: nextHeight - 1, }); } (0, utils_1.sortValidatorsByAddress)(nextActiveValidators); bftVotes.activeValidatorsVoteInfo = nextActiveValidators; await votesStore.setWithSchema(constants_1.EMPTY_KEY, bftVotes, schemas_1.bftVotesSchema); } async getGeneratorAtTimestamp(stateStore, height, timestamp) { const paramsStore = stateStore.getStore(constants_1.MODULE_STORE_PREFIX_BFT, constants_1.STORE_PREFIX_BFT_PARAMETERS); const bftParams = await (0, bft_params_1.getBFTParameters)(paramsStore, height); const currentSlot = this.getSlotNumber(timestamp); const generator = bftParams.validators[currentSlot % bftParams.validators.length]; return generator; } getSlotNumber(timestamp) { return Math.floor(timestamp / this._blockTime); } getSlotTime(slot) { return slot * this._blockTime; } isWithinTimeslot(slot, timestamp) { return this.getSlotNumber(timestamp) === slot; } } exports.BFTMethod = BFTMethod; //# sourceMappingURL=method.js.map