diff --git a/packages/js-drive/lib/abci/handlers/extendVoteHandlerFactory.js b/packages/js-drive/lib/abci/handlers/extendVoteHandlerFactory.js index c57f6b11799..76f178b3f3a 100644 --- a/packages/js-drive/lib/abci/handlers/extendVoteHandlerFactory.js +++ b/packages/js-drive/lib/abci/handlers/extendVoteHandlerFactory.js @@ -17,11 +17,16 @@ const { function extendVoteHandlerFactory(proposalBlockExecutionContext) { /** * @typedef extendVoteHandler - * @param {Object} request - * @param {number} request.round * @return {Promise} */ async function extendVoteHandler() { + const consensusLogger = proposalBlockExecutionContext.getConsensusLogger() + .child({ + abciMethod: 'extendVote', + }); + + consensusLogger.debug('ExtendVote ABCI method requested'); + const unsignedWithdrawalTransactionsMap = proposalBlockExecutionContext .getWithdrawalTransactionsMap(); @@ -32,6 +37,25 @@ function extendVoteHandlerFactory(proposalBlockExecutionContext) { extension: Buffer.from(txHashHex, 'hex'), })); + const voteExtensionTypeName = { + [VoteExtensionType.DEFAULT]: 'default', + [VoteExtensionType.THRESHOLD_RECOVER]: 'threshold recovery', + }; + + voteExtensions.forEach(({ extension, type }) => { + const extensionString = extension.toString('hex'); + + const extensionTruncatedString = extensionString.substring( + 0, + Math.min(30, extensionString.length), + ); + + consensusLogger.debug({ + type, + extension: extensionString, + }, `Vote extended to obtain ${voteExtensionTypeName} signature for ${extensionTruncatedString}... payload`); + }); + return new ResponseExtendVote({ voteExtensions, }); diff --git a/packages/js-drive/lib/abci/handlers/finalizeBlockHandlerFactory.js b/packages/js-drive/lib/abci/handlers/finalizeBlockHandlerFactory.js index 09b6afd46fc..c7173db3127 100644 --- a/packages/js-drive/lib/abci/handlers/finalizeBlockHandlerFactory.js +++ b/packages/js-drive/lib/abci/handlers/finalizeBlockHandlerFactory.js @@ -2,6 +2,7 @@ const { tendermint: { abci: { ResponseFinalizeBlock, + RequestProcessProposal, }, }, } = require('@dashevo/abci/types'); @@ -11,13 +12,12 @@ const { * @return {finalizeBlockHandler} * @param {GroveDBStore} groveDBStore * @param {BlockExecutionContextRepository} blockExecutionContextRepository - * @param {LRUCache} dataContractCache * @param {CoreRpcClient} coreRpcClient * @param {BaseLogger} logger * @param {ExecutionTimer} executionTimer * @param {BlockExecutionContext} latestBlockExecutionContext * @param {BlockExecutionContext} proposalBlockExecutionContext - * @param {processProposalHandler} processProposalHandler + * @param {processProposal} processProposal */ function finalizeBlockHandlerFactory( groveDBStore, @@ -27,7 +27,7 @@ function finalizeBlockHandlerFactory( executionTimer, latestBlockExecutionContext, proposalBlockExecutionContext, - processProposalHandler, + processProposal, ) { /** * @typedef finalizeBlockHandler @@ -44,16 +44,20 @@ function finalizeBlockHandlerFactory( const consensusLogger = logger.child({ height: height.toString(), + round, abciMethod: 'finalizeBlock', }); consensusLogger.debug('FinalizeBlock ABCI method requested'); consensusLogger.trace({ abciRequest: request }); - if (proposalBlockExecutionContext.getRound() !== round) { - consensusLogger.warn( - `Finalizing previously executed round ${round} instead of the last known ${proposalBlockExecutionContext.getRound()}`, - ); + const lastProcessedRound = proposalBlockExecutionContext.getRound(); + + if (lastProcessedRound !== round) { + consensusLogger.warn({ + lastProcessedRound, + round, + }, `Finalizing previously executed round ${round} instead of the last known ${lastProcessedRound}`); const { block: { @@ -69,7 +73,7 @@ function finalizeBlockHandlerFactory( }, } = request; - await processProposalHandler({ + const processProposalRequest = new RequestProcessProposal({ height, txs, coreChainLockedHeight, @@ -79,11 +83,16 @@ function finalizeBlockHandlerFactory( proposerProTxHash, round, }); + + await processProposal(processProposalRequest, consensusLogger); + + // Revert consensus logger + proposalBlockExecutionContext.setConsensusLogger(consensusLogger); } proposalBlockExecutionContext.setLastCommitInfo(commitInfo); - // Store block execution context + // Store proposal block execution context await blockExecutionContextRepository.store( proposalBlockExecutionContext, { @@ -91,9 +100,10 @@ function finalizeBlockHandlerFactory( }, ); - // Commit the current block db transactions + // Commit the current block db transactions into storage await groveDBStore.commitTransaction(); + // Update last block execution context with proposal data latestBlockExecutionContext.populate(proposalBlockExecutionContext); // Send withdrawal transactions to Core @@ -124,11 +134,11 @@ function finalizeBlockHandlerFactory( const blockExecutionTimings = executionTimer.stopTimer('blockExecution'); - consensusLogger.trace( + consensusLogger.info( { timings: blockExecutionTimings, }, - `Block #${height} execution took ${blockExecutionTimings} seconds`, + `Block #${height} finalized in ${round + 1} round(s) and ${blockExecutionTimings} seconds`, ); return new ResponseFinalizeBlock(); diff --git a/packages/js-drive/lib/abci/handlers/prepareProposalHandlerFactory.js b/packages/js-drive/lib/abci/handlers/prepareProposalHandlerFactory.js index b90a89101a6..730522c1095 100644 --- a/packages/js-drive/lib/abci/handlers/prepareProposalHandlerFactory.js +++ b/packages/js-drive/lib/abci/handlers/prepareProposalHandlerFactory.js @@ -22,6 +22,7 @@ const txAction = { * @param {beginBlock} beginBlock * @param {endBlock} endBlock * @param {createCoreChainLockUpdate} createCoreChainLockUpdate + * @param {ExecutionTimer} executionTimer * @return {prepareProposalHandler} */ function prepareProposalHandlerFactory( @@ -31,6 +32,7 @@ function prepareProposalHandlerFactory( beginBlock, endBlock, createCoreChainLockUpdate, + executionTimer, ) { /** * @typedef prepareProposalHandler @@ -49,20 +51,18 @@ function prepareProposalHandlerFactory( proposerProTxHash, round, } = request; + const consensusLogger = logger.child({ height: height.toString(), + round, abciMethod: 'prepareProposal', }); - consensusLogger.info( - { - height, - }, - `Prepare proposal #${height}`, - ); consensusLogger.debug('PrepareProposal ABCI method requested'); consensusLogger.trace({ abciRequest: request }); + consensusLogger.info(`Preparing a block proposal for height #${height} round #${round}`); + await beginBlock( { lastCommitInfo, @@ -119,6 +119,7 @@ function prepareProposalHandlerFactory( txResults.push(txResult); } + // Revert consensus logger after deliverTx proposalBlockExecutionContext.setConsensusLogger(consensusLogger); const coreChainLockUpdate = await createCoreChainLockUpdate(round, consensusLogger); @@ -134,13 +135,19 @@ function prepareProposalHandlerFactory( coreChainLockedHeight, }, consensusLogger); + const roundExecutionTime = executionTimer.getTimer('roundExecution', true); + + const mempoolTxCount = txs.length - validTxCount - invalidTxCount; + consensusLogger.info( { + roundExecutionTime, validTxCount, invalidTxCount, + mempoolTxCount, }, - `Prepare proposal #${height} with appHash ${appHash.toString('hex').toUpperCase()}` - + ` (valid txs = ${validTxCount}, invalid txs = ${invalidTxCount})`, + `Prepared block proposal for height #${height} with appHash ${appHash.toString('hex').toUpperCase()}` + + ` in ${roundExecutionTime} seconds (valid txs = ${validTxCount}, invalid txs = ${invalidTxCount}, mempool txs = ${mempoolTxCount})`, ); proposalBlockExecutionContext.setPrepareProposalResult({ diff --git a/packages/js-drive/lib/abci/handlers/processProposalHandlerFactory.js b/packages/js-drive/lib/abci/handlers/processProposalHandlerFactory.js index a65523b2477..f66412d21d3 100644 --- a/packages/js-drive/lib/abci/handlers/processProposalHandlerFactory.js +++ b/packages/js-drive/lib/abci/handlers/processProposalHandlerFactory.js @@ -6,68 +6,49 @@ const { }, } = require('@dashevo/abci/types'); -const aggregateFees = require('./proposal/fees/aggregateFees'); - -const proposalStatus = { - UNKNOWN: 0, // Unknown status. Returning this from the application is always an error. - ACCEPT: 1, // Status that signals that the application finds the proposal valid. - REJECT: 2, // Status that signals that the application finds the proposal invalid. -}; +const statuses = require('./proposal/statuses'); /** - * @param {deliverTx} wrappedDeliverTx * @param {BaseLogger} logger - * @param {BlockExecutionContext} proposalBlockExecutionContext - * @param {beginBlock} beginBlock - * @param {endBlock} endBlock * @param {verifyChainLock} verifyChainLock + * @param {processProposal} processProposal + * @param {BlockExecutionContext} proposalBlockExecutionContext * @return {processProposalHandler} */ function processProposalHandlerFactory( - wrappedDeliverTx, logger, - proposalBlockExecutionContext, - beginBlock, - endBlock, verifyChainLock, + processProposal, + proposalBlockExecutionContext, ) { /** * @typedef processProposalHandler + * @param {abci.RequestProcessProposal} request * @return {Promise} */ async function processProposalHandler(request) { const { height, - txs, - coreChainLockedHeight, - version, - proposedLastCommit: lastCommitInfo, - time, - proposerProTxHash, coreChainLockUpdate, round, } = request; const consensusLogger = logger.child({ height: height.toString(), + round, abciMethod: 'processProposal', }); - consensusLogger.info( - { - height, - }, - `Process proposal #${height}`, - ); consensusLogger.debug('ProcessProposal ABCI method requested'); consensusLogger.trace({ abciRequest: request }); + // Skip process proposal if it was already prepared for this height and round const prepareProposalResult = proposalBlockExecutionContext.getPrepareProposalResult(); if (prepareProposalResult && proposalBlockExecutionContext.getHeight().toNumber() === height.toNumber() && proposalBlockExecutionContext.getRound() === round) { - consensusLogger.debug('Returning cached result'); + consensusLogger.debug('Skip processing proposal and return prepared result'); const { appHash, @@ -77,7 +58,7 @@ function processProposalHandlerFactory( } = prepareProposalResult; return new ResponseProcessProposal({ - status: proposalStatus.ACCEPT, + status: statuses.ACCEPT, appHash, txResults, consensusParamUpdates, @@ -89,83 +70,21 @@ function processProposalHandlerFactory( const chainLockIsValid = await verifyChainLock(coreChainLockUpdate); if (!chainLockIsValid) { + consensusLogger.warn({ + coreChainLockUpdate, + }, `Block proposal #${height} round #${round} rejected due to invalid core chain locked height update`); + return new ResponseProcessProposal({ - status: proposalStatus.REJECT, + status: statuses.REJECT, }); } - } - - await beginBlock( - { - lastCommitInfo, - height, - coreChainLockedHeight, - version, - time, - proposerProTxHash: Buffer.from(proposerProTxHash), - round, - }, - consensusLogger, - ); - - const txResults = []; - const feeResults = []; - let validTxCount = 0; - let invalidTxCount = 0; - - for (const tx of txs) { - const { - code, - info, - fees, - } = await wrappedDeliverTx(tx, round, consensusLogger); - - if (code === 0) { - validTxCount += 1; - // TODO We probably should calculate fees for invalid transitions as well - feeResults.push(fees); - } else { - invalidTxCount += 1; - } - - const txResult = { code }; - - if (info) { - txResult.info = info; - } - txResults.push(txResult); + logger.debug({ + coreChainLockUpdate, + }, `ChainLock is valid for height ${coreChainLockUpdate.coreBlockHeight}`); } - proposalBlockExecutionContext.setConsensusLogger(consensusLogger); - - const { - consensusParamUpdates, - validatorSetUpdate, - appHash, - } = await endBlock({ - height, - round, - fees: aggregateFees(feeResults), - coreChainLockedHeight, - }, consensusLogger); - - consensusLogger.info( - { - validTxCount, - invalidTxCount, - }, - `Process proposal #${height} with appHash ${appHash.toString('hex').toUpperCase()}` - + ` (valid txs = ${validTxCount}, invalid txs = ${invalidTxCount})`, - ); - - return new ResponseProcessProposal({ - status: proposalStatus.ACCEPT, - appHash, - txResults, - consensusParamUpdates, - validatorSetUpdate, - }); + return processProposal(request, consensusLogger); } return processProposalHandler; diff --git a/packages/js-drive/lib/abci/handlers/proposal/beginBlockFactory.js b/packages/js-drive/lib/abci/handlers/proposal/beginBlockFactory.js index 3f79eaaf249..d5fc50c9a58 100644 --- a/packages/js-drive/lib/abci/handlers/proposal/beginBlockFactory.js +++ b/packages/js-drive/lib/abci/handlers/proposal/beginBlockFactory.js @@ -39,12 +39,12 @@ function beginBlockFactory( /** * @typedef beginBlock * @param {Object} request - * @param {ILastCommitInfo} [request.lastCommitInfo] - * @param {Long} [request.height] - * @param {number} [request.coreChainLockedHeight] - * @param {IConsensus} [request.version] - * @param {ITimestamp} [request.time] - * @param {Buffer} [request.proposerProTxHash] + * @param {ILastCommitInfo} request.lastCommitInfo + * @param {Long} request.height + * @param {number} request.coreChainLockedHeight + * @param {IConsensus} request.version + * @param {ITimestamp} request.time + * @param {Buffer} request.proposerProTxHash * @param {BaseLogger} consensusLogger * * @return {Promise} @@ -86,27 +86,25 @@ function beginBlockFactory( await waitForChainLockedHeight(coreChainLockedHeight); // Reset block execution context - proposalBlockExecutionContext.reset(); - // Set block execution context params proposalBlockExecutionContext.setConsensusLogger(consensusLogger); proposalBlockExecutionContext.setHeight(height); proposalBlockExecutionContext.setVersion(version); + proposalBlockExecutionContext.setRound(round); proposalBlockExecutionContext.setTimeMs(protoTimestampToMillis(time)); proposalBlockExecutionContext.setCoreChainLockedHeight(coreChainLockedHeight); proposalBlockExecutionContext.setLastCommitInfo(lastCommitInfo); - proposalBlockExecutionContext.setRound(round); // Set protocol version to DPP dpp.setProtocolVersion(version.app.toNumber()); transactionalDpp.setProtocolVersion(version.app.toNumber()); + // Restart transaction if already started if (await groveDBStore.isTransactionStarted()) { await groveDBStore.abortTransaction(); } - // Start db transaction for the block await groveDBStore.startTransaction(); // Call RS ABCI @@ -155,7 +153,7 @@ function beginBlockFactory( const blockTimeFormatted = new Date(proposalBlockExecutionContext.getTimeMs()).toUTCString(); - consensusLogger.debug(debugData, `Fee epoch #${currentEpochIndex} started on block #${height} at ${blockTimeFormatted}`); + consensusLogger.info(debugData, `Epoch #${currentEpochIndex} started on block #${height} at ${blockTimeFormatted}`); } // Update SML @@ -193,8 +191,6 @@ function beginBlockFactory( ); } } - - consensusLogger.info(`Block begin #${height}`); } return beginBlock; diff --git a/packages/js-drive/lib/abci/handlers/proposal/createCoreChainLockUpdateFactory.js b/packages/js-drive/lib/abci/handlers/proposal/createCoreChainLockUpdateFactory.js index 206a795e957..2c9856c449f 100644 --- a/packages/js-drive/lib/abci/handlers/proposal/createCoreChainLockUpdateFactory.js +++ b/packages/js-drive/lib/abci/handlers/proposal/createCoreChainLockUpdateFactory.js @@ -18,7 +18,7 @@ function createCoreChainLockUpdateFactory( /** * @typedef createCoreChainLockUpdate * @param {number} round - * @param {BaseLogger} logger + * @param {BaseLogger} consensusLogger * @return {Promise} */ async function createCoreChainLockUpdate(round, consensusLogger) { @@ -34,7 +34,7 @@ function createCoreChainLockUpdateFactory( signature: coreChainLock.signature, }); - consensusLogger.trace( + consensusLogger.debug( { nextCoreChainLockHeight: coreChainLock.height, }, diff --git a/packages/js-drive/lib/abci/handlers/proposal/deliverTxFactory.js b/packages/js-drive/lib/abci/handlers/proposal/deliverTxFactory.js index 861211c37ba..4fa28bf1f9b 100644 --- a/packages/js-drive/lib/abci/handlers/proposal/deliverTxFactory.js +++ b/packages/js-drive/lib/abci/handlers/proposal/deliverTxFactory.js @@ -66,14 +66,18 @@ function deliverTxFactory( .toString('hex') .toUpperCase(); - proposalBlockExecutionContext.setConsensusLogger(consensusLogger); + const txConsensusLogger = consensusLogger.child({ + txId: stHash, + }); - consensusLogger.info(`Deliver state transition ${stHash} from block #${blockHeight}`); + proposalBlockExecutionContext.setConsensusLogger(txConsensusLogger); + + txConsensusLogger.info(`Deliver state transition ${stHash} from block #${blockHeight}`); const stateTransition = await transactionalUnserializeStateTransition( stateTransitionByteArray, { - logger: consensusLogger, + logger: txConsensusLogger, executionTimer, }, ); @@ -94,8 +98,8 @@ function deliverTxFactory( const consensusError = result.getFirstError(); const message = 'State transition is invalid against the state'; - consensusLogger.info(message); - consensusLogger.debug({ + txConsensusLogger.info(message); + txConsensusLogger.debug({ consensusError, }); @@ -151,7 +155,7 @@ function deliverTxFactory( const description = DATA_CONTRACT_ACTION_DESCRIPTIONS[stateTransition.getType()]; - consensusLogger.info( + txConsensusLogger.info( { dataContractId: dataContract.getId().toString(), }, @@ -163,7 +167,7 @@ function deliverTxFactory( case stateTransitionTypes.IDENTITY_CREATE: { const identityId = stateTransition.getIdentityId(); - consensusLogger.info( + txConsensusLogger.info( { identityId: identityId.toString(), }, @@ -175,7 +179,7 @@ function deliverTxFactory( case stateTransitionTypes.IDENTITY_TOP_UP: { const identityId = stateTransition.getIdentityId(); - consensusLogger.info( + txConsensusLogger.info( { identityId: identityId.toString(), }, @@ -187,7 +191,7 @@ function deliverTxFactory( case stateTransitionTypes.IDENTITY_UPDATE: { const identityId = stateTransition.getIdentityId(); - consensusLogger.info( + txConsensusLogger.info( { identityId: identityId.toString(), }, @@ -199,7 +203,7 @@ function deliverTxFactory( stateTransition.getTransitions().forEach((transition) => { const description = DOCUMENT_ACTION_DESCRIPTIONS[transition.getAction()]; - consensusLogger.info( + txConsensusLogger.info( { documentId: transition.getId().toString(), }, @@ -227,7 +231,7 @@ function deliverTxFactory( processingFee: predictedProcessingFee, } = calculateOperationFees(predictedStateTransitionOperations); - consensusLogger.trace( + txConsensusLogger.trace( { timings: { overall: deliverTxTiming, diff --git a/packages/js-drive/lib/abci/handlers/proposal/endBlockFactory.js b/packages/js-drive/lib/abci/handlers/proposal/endBlockFactory.js index 2b662067c3d..4eb35d17e10 100644 --- a/packages/js-drive/lib/abci/handlers/proposal/endBlockFactory.js +++ b/packages/js-drive/lib/abci/handlers/proposal/endBlockFactory.js @@ -28,10 +28,10 @@ function endBlockFactory( * @typedef endBlock * * @param {Object} request - * @param {number} [request.height] - * @param {number} [request.round] - * @param {FeeResult} [request.fees] - * @param {number} [request.coreChainLockedHeight] + * @param {number} request.height + * @param {number} request.round + * @param {FeeResult} request.fees + * @param {number} request.coreChainLockedHeight * @param {BaseLogger} consensusLogger * @return {Promise<{ * consensusParamUpdates: ConsensusParams, @@ -50,8 +50,6 @@ function endBlockFactory( coreChainLockedHeight, } = request; - consensusLogger.debug('EndBlock ABCI method requested'); - // Call RS ABCI const rsRequest = { @@ -88,19 +86,17 @@ function endBlockFactory( } const consensusParamUpdates = await createConsensusParamUpdate(height, round, consensusLogger); + const validatorSetUpdate = await rotateAndCreateValidatorSetUpdate( height, coreChainLockedHeight, round, consensusLogger, ); - const appHash = await groveDBStore.getRootHash({ useTransaction: true }); - const prepareProposalTimings = executionTimer.stopTimer('roundExecution'); + const appHash = await groveDBStore.getRootHash({ useTransaction: true }); - consensusLogger.info( - `Round execution took ${prepareProposalTimings} seconds`, - ); + executionTimer.stopTimer('roundExecution', true); return { consensusParamUpdates, diff --git a/packages/js-drive/lib/abci/handlers/proposal/processProposalFactory.js b/packages/js-drive/lib/abci/handlers/proposal/processProposalFactory.js new file mode 100644 index 00000000000..2ede0ba2b88 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/proposal/processProposalFactory.js @@ -0,0 +1,131 @@ +const { + tendermint: { + abci: { + ResponseProcessProposal, + }, + }, +} = require('@dashevo/abci/types'); + +const statuses = require('./statuses'); + +const aggregateFees = require('./fees/aggregateFees'); + +/** + * + * @param {deliverTx} wrappedDeliverTx + * @param {BlockExecutionContext} proposalBlockExecutionContext + * @param {beginBlock} beginBlock + * @param {endBlock} endBlock + * @param {ExecutionTimer} executionTimer + * + * @return {processProposal} + */ +function processProposalFactory( + wrappedDeliverTx, + proposalBlockExecutionContext, + beginBlock, + endBlock, + executionTimer, +) { + /** + * @param {abci.RequestProcessProposal} request + * @param {BaseLogger} consensusLogger + * + * @typedef processProposal + */ + async function processProposal(request, consensusLogger) { + const { + height, + txs, + coreChainLockedHeight, + version, + proposedLastCommit: lastCommitInfo, + time, + proposerProTxHash, + round, + } = request; + + consensusLogger.info(`Processing a block proposal for height #${height} round #${round}`); + + await beginBlock( + { + lastCommitInfo, + height, + coreChainLockedHeight, + version, + time, + proposerProTxHash: Buffer.from(proposerProTxHash), + round, + }, + consensusLogger, + ); + + const txResults = []; + const feeResults = []; + + let validTxCount = 0; + let invalidTxCount = 0; + + for (const tx of txs) { + const { + code, + info, + fees, + } = await wrappedDeliverTx(tx, round, consensusLogger); + + if (code === 0) { + validTxCount += 1; + // TODO We probably should calculate fees for invalid transitions as well + feeResults.push(fees); + } else { + invalidTxCount += 1; + } + + const txResult = { code }; + + if (info) { + txResult.info = info; + } + + txResults.push(txResult); + } + + // Revert consensus logger after deliverTx + proposalBlockExecutionContext.setConsensusLogger(consensusLogger); + + const { + consensusParamUpdates, + validatorSetUpdate, + appHash, + } = await endBlock({ + height, + round, + fees: aggregateFees(feeResults), + coreChainLockedHeight, + }, consensusLogger); + + const roundExecutionTime = executionTimer.getTimer('roundExecution', true); + + consensusLogger.info( + { + roundExecutionTime, + validTxCount, + invalidTxCount, + }, + `Processed proposal #${height} with appHash ${appHash.toString('hex').toUpperCase()}` + + ` in ${roundExecutionTime} seconds (valid txs = ${validTxCount}, invalid txs = ${invalidTxCount})`, + ); + + return new ResponseProcessProposal({ + status: statuses.ACCEPT, + appHash, + txResults, + consensusParamUpdates, + validatorSetUpdate, + }); + } + + return processProposal; +} + +module.exports = processProposalFactory; diff --git a/packages/js-drive/lib/abci/handlers/proposal/statuses.js b/packages/js-drive/lib/abci/handlers/proposal/statuses.js new file mode 100644 index 00000000000..adb8785b6c5 --- /dev/null +++ b/packages/js-drive/lib/abci/handlers/proposal/statuses.js @@ -0,0 +1,5 @@ +module.exports = { + UNKNOWN: 0, // Unknown status. Returning this from the application is always an error. + ACCEPT: 1, // Status that signals that the application finds the proposal valid. + REJECT: 2, // Status that signals that the application finds the proposal invalid. +}; diff --git a/packages/js-drive/lib/abci/handlers/proposal/verifyChainLockFactory.js b/packages/js-drive/lib/abci/handlers/proposal/verifyChainLockFactory.js index 295d24956c8..3fb6100b860 100644 --- a/packages/js-drive/lib/abci/handlers/proposal/verifyChainLockFactory.js +++ b/packages/js-drive/lib/abci/handlers/proposal/verifyChainLockFactory.js @@ -60,12 +60,6 @@ function verifyChainLockFactory( throw e; } - if (!isVerified) { - logger.debug(`Invalid chainLock for height ${coreChainLock.coreBlockHeight}`); - } else { - logger.debug(`ChainLock is valid for height ${coreChainLock.coreBlockHeight}`); - } - return isVerified; } diff --git a/packages/js-drive/lib/abci/handlers/verifyVoteExtensionHandlerFactory.js b/packages/js-drive/lib/abci/handlers/verifyVoteExtensionHandlerFactory.js index 1674152a0a3..1c5a56479b9 100644 --- a/packages/js-drive/lib/abci/handlers/verifyVoteExtensionHandlerFactory.js +++ b/packages/js-drive/lib/abci/handlers/verifyVoteExtensionHandlerFactory.js @@ -13,15 +13,24 @@ const verifyStatus = { }; /** - * + * @param {BlockExecutionContext} proposalBlockExecutionContext * @return {verifyVoteExtensionHandler} */ -function verifyVoteExtensionHandlerFactory() { +function verifyVoteExtensionHandlerFactory(proposalBlockExecutionContext) { /** * @typedef verifyVoteExtensionHandler * @return {Promise} */ async function verifyVoteExtensionHandler() { + const consensusLogger = proposalBlockExecutionContext.getConsensusLogger() + .child({ + abciMethod: 'verifyVoteExtension', + }); + + consensusLogger.debug('VerifyVote ABCI method requested'); + + // TODO Verify withdrawal vote extensions and add logs + return new ResponseVerifyVoteExtension({ status: verifyStatus.ACCEPT, }); diff --git a/packages/js-drive/lib/createDIContainer.js b/packages/js-drive/lib/createDIContainer.js index ccd13cb658c..5e30e59a235 100644 --- a/packages/js-drive/lib/createDIContainer.js +++ b/packages/js-drive/lib/createDIContainer.js @@ -137,6 +137,7 @@ const noopLoggerInstance = require('./util/noopLogger'); const fetchTransactionFactory = require('./core/fetchTransactionFactory'); const LastSyncedCoreHeightRepository = require('./identity/masternode/LastSyncedCoreHeightRepository'); const fetchSimplifiedMNListFactory = require('./core/fetchSimplifiedMNListFactory'); +const processProposalFactory = require('./abci/handlers/proposal/processProposalFactory'); /** * @@ -721,56 +722,49 @@ function createDIContainer(options) { return router; }).singleton(), - infoHandler: asFunction(infoHandlerFactory).singleton(), - checkTxHandler: asFunction(checkTxHandlerFactory).singleton(), + beginBlock: asFunction(beginBlockFactory).singleton(), + + processProposal: asFunction(processProposalFactory), + + deliverTx: asFunction(deliverTxFactory).singleton(), - beginBlockHandler: asFunction(beginBlockFactory).singleton(), - beginBlock: asFunction(( - enrichErrorWithConsensusError, - beginBlockHandler, - ) => enrichErrorWithConsensusError(beginBlockHandler)).singleton(), - deliverTxHandler: asFunction(deliverTxFactory).singleton(), wrappedDeliverTx: asFunction(( wrapInErrorHandler, enrichErrorWithConsensusError, - deliverTxHandler, + deliverTx, ) => wrapInErrorHandler( - enrichErrorWithConsensusError(deliverTxHandler), + enrichErrorWithConsensusError(deliverTx), { respondWithInternalError: true }, )).singleton(), - endBlockHandler: asFunction(endBlockFactory).singleton(), - endBlock: asFunction(( - enrichErrorWithConsensusError, - endBlockHandler, - ) => enrichErrorWithConsensusError(endBlockHandler)).singleton(), - verifyChainLockHandler: asFunction(verifyChainLockFactory).singleton(), - verifyChainLock: asFunction(( - enrichErrorWithConsensusError, - verifyChainLockHandler, - ) => enrichErrorWithConsensusError(verifyChainLockHandler)).singleton(), - rotateAndCreateValidatorSetUpdateHandler: asFunction( + + endBlock: asFunction(endBlockFactory).singleton(), + + verifyChainLock: asFunction(verifyChainLockFactory).singleton(), + + rotateAndCreateValidatorSetUpdate: asFunction( rotateAndCreateValidatorSetUpdateFactory, ).singleton(), - rotateAndCreateValidatorSetUpdate: asFunction(( - enrichErrorWithConsensusError, - rotateAndCreateValidatorSetUpdateHandler, - ) => enrichErrorWithConsensusError(rotateAndCreateValidatorSetUpdateHandler)).singleton(), - createConsensusParamUpdateHandler: asFunction(createConsensusParamUpdateFactory).singleton(), - createConsensusParamUpdate: asFunction(( - enrichErrorWithConsensusError, - createConsensusParamUpdateHandler, - ) => enrichErrorWithConsensusError(createConsensusParamUpdateHandler)).singleton(), - createCoreChainLockUpdateHandler: asFunction(createCoreChainLockUpdateFactory).singleton(), - createCoreChainLockUpdate: asFunction(( - enrichErrorWithConsensusError, - createCoreChainLockUpdateHandler, - ) => enrichErrorWithConsensusError(createCoreChainLockUpdateHandler)).singleton(), + + createConsensusParamUpdate: asFunction(createConsensusParamUpdateFactory).singleton(), + + createCoreChainLockUpdate: asFunction(createCoreChainLockUpdateFactory).singleton(), + + infoHandler: asFunction(infoHandlerFactory).singleton(), + + checkTxHandler: asFunction(checkTxHandlerFactory).singleton(), + initChainHandler: asFunction(initChainHandlerFactory).singleton(), + queryHandler: asFunction(queryHandlerFactory).singleton(), + extendVoteHandler: asFunction(extendVoteHandlerFactory).singleton(), + finalizeBlockHandler: asFunction(finalizeBlockHandlerFactory).singleton(), + prepareProposalHandler: asFunction(prepareProposalHandlerFactory).singleton(), + processProposalHandler: asFunction(processProposalHandlerFactory).singleton(), + verifyVoteExtensionHandler: asFunction(verifyVoteExtensionHandlerFactory).singleton(), wrapInErrorHandler: asFunction(wrapInErrorHandlerFactory).singleton(), diff --git a/packages/js-drive/lib/identity/masternode/synchronizeMasternodeIdentitiesFactory.js b/packages/js-drive/lib/identity/masternode/synchronizeMasternodeIdentitiesFactory.js index 42abf93d4af..14003bdcda6 100644 --- a/packages/js-drive/lib/identity/masternode/synchronizeMasternodeIdentitiesFactory.js +++ b/packages/js-drive/lib/identity/masternode/synchronizeMasternodeIdentitiesFactory.js @@ -46,7 +46,6 @@ function synchronizeMasternodeIdentitiesFactory( * }>} */ async function synchronizeMasternodeIdentities(coreHeight, blockInfo) { - // TODO: We should either pass block info and transaction or just use state repository (?) if (!lastSyncedCoreHeight) { const lastSyncedHeightResult = await lastSyncedCoreHeightRepository.fetch({ useTransaction: true, diff --git a/packages/js-drive/test/unit/abci/handlers/extendVoteHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/extendVoteHandlerFactory.spec.js index 956b4507a47..5c313aefbe0 100644 --- a/packages/js-drive/test/unit/abci/handlers/extendVoteHandlerFactory.spec.js +++ b/packages/js-drive/test/unit/abci/handlers/extendVoteHandlerFactory.spec.js @@ -11,6 +11,7 @@ const { hash } = require('@dashevo/dpp/lib/util/hash'); const extendVoteHandlerFactory = require('../../../../lib/abci/handlers/extendVoteHandlerFactory'); const BlockExecutionContextMock = require('../../../../lib/test/mock/BlockExecutionContextMock'); +const LoggerMock = require('../../../../lib/test/mock/LoggerMock'); describe('extendVoteHandlerFactory', () => { let extendVoteHandler; @@ -19,6 +20,10 @@ describe('extendVoteHandlerFactory', () => { beforeEach(function beforeEach() { blockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + const loggerMock = new LoggerMock(this.sinon); + + blockExecutionContextMock.getConsensusLogger.returns(loggerMock); + blockExecutionContextMock.getWithdrawalTransactionsMap.returns({}); extendVoteHandler = extendVoteHandlerFactory(blockExecutionContextMock); diff --git a/packages/js-drive/test/unit/abci/handlers/finalizeBlockHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/finalizeBlockHandlerFactory.spec.js index 1325caf0053..919fe252f06 100644 --- a/packages/js-drive/test/unit/abci/handlers/finalizeBlockHandlerFactory.spec.js +++ b/packages/js-drive/test/unit/abci/handlers/finalizeBlockHandlerFactory.spec.js @@ -2,6 +2,7 @@ const { tendermint: { abci: { ResponseFinalizeBlock, + RequestProcessProposal, }, }, } = require('@dashevo/abci/types'); @@ -30,7 +31,7 @@ describe('finalizeBlockHandlerFactory', () => { let proposalBlockExecutionContextMock; let round; let block; - let processProposalHandlerMock; + let processProposalMock; beforeEach(function beforeEach() { round = 0; @@ -95,7 +96,7 @@ describe('finalizeBlockHandlerFactory', () => { this.sinon, ); - processProposalHandlerMock = this.sinon.stub(); + processProposalMock = this.sinon.stub(); finalizeBlockHandler = finalizeBlockHandlerFactory( groveDBStoreMock, @@ -105,7 +106,7 @@ describe('finalizeBlockHandlerFactory', () => { executionTimerMock, latestBlockExecutionContextMock, proposalBlockExecutionContextMock, - processProposalHandlerMock, + processProposalMock, ); }); @@ -128,7 +129,7 @@ describe('finalizeBlockHandlerFactory', () => { expect(groveDBStoreMock.commitTransaction).to.be.calledOnceWithExactly(); expect(latestBlockExecutionContextMock.populate).to.be.calledOnce(); - expect(processProposalHandlerMock).to.be.not.called(); + expect(processProposalMock).to.be.not.called(); }); it('should send withdrawal transaction if vote extensions are present', async () => { @@ -158,16 +159,17 @@ describe('finalizeBlockHandlerFactory', () => { await finalizeBlockHandler(requestMock); expect(coreRpcClientMock.sendRawTransaction).to.have.been.calledTwice(); - expect(processProposalHandlerMock).to.be.not.called(); + expect(processProposalMock).to.be.not.called(); }); - it('should call processProposalHandler if round is not equal to execution context', async () => { + it('should call processProposal if round is not equal to execution context', async () => { proposalBlockExecutionContextMock.getRound.returns(round + 1); const result = await finalizeBlockHandler(requestMock); expect(result).to.be.an.instanceOf(ResponseFinalizeBlock); - expect(processProposalHandlerMock).to.be.calledOnceWithExactly({ + + const processProposalRequest = new RequestProcessProposal({ height: requestMock.height, txs: block.data.txs, coreChainLockedHeight: block.header.coreChainLockedHeight, @@ -177,5 +179,7 @@ describe('finalizeBlockHandlerFactory', () => { proposerProTxHash: block.header.proposerProTxHash, round, }); + + expect(processProposalMock).to.be.calledOnceWithExactly(processProposalRequest, loggerMock); }); }); diff --git a/packages/js-drive/test/unit/abci/handlers/prepareProposalHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/prepareProposalHandlerFactory.spec.js index 6631ea60c05..b2a3a3e977f 100644 --- a/packages/js-drive/test/unit/abci/handlers/prepareProposalHandlerFactory.spec.js +++ b/packages/js-drive/test/unit/abci/handlers/prepareProposalHandlerFactory.spec.js @@ -34,6 +34,7 @@ describe('prepareProposalHandlerFactory', () => { let endBlockResult; let proposalBlockExecutionContextMock; let round; + let executionTimerMock; beforeEach(function beforeEach() { round = 1; @@ -58,6 +59,7 @@ describe('prepareProposalHandlerFactory', () => { appVersion: 1, }, }); + validatorSetUpdate = new ValidatorSetUpdate(); proposalBlockExecutionContextMock = new BlockExecutionContextMock(this.sinon); @@ -83,6 +85,10 @@ describe('prepareProposalHandlerFactory', () => { updateCoreChainLockMock = this.sinon.stub().resolves(coreChainLockUpdate); + executionTimerMock = { + getTimer: this.sinon.stub().returns(0.1), + }; + prepareProposalHandler = prepareProposalHandlerFactory( deliverTxMock, loggerMock, @@ -90,6 +96,7 @@ describe('prepareProposalHandlerFactory', () => { beginBlockMock, endBlockMock, updateCoreChainLockMock, + executionTimerMock, ); const maxTxBytes = 42; diff --git a/packages/js-drive/test/unit/abci/handlers/processProposalHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/processProposalHandlerFactory.spec.js index 4adcd9f7ed5..647901fc7e9 100644 --- a/packages/js-drive/test/unit/abci/handlers/processProposalHandlerFactory.spec.js +++ b/packages/js-drive/test/unit/abci/handlers/processProposalHandlerFactory.spec.js @@ -12,8 +12,6 @@ const { const Long = require('long'); -const FeeResult = require('@dashevo/rs-drive/FeeResult'); - const processProposalHandlerFactory = require('../../../../lib/abci/handlers/processProposalHandlerFactory'); const LoggerMock = require('../../../../lib/test/mock/LoggerMock'); const BlockExecutionContextMock = require('../../../../lib/test/mock/BlockExecutionContextMock'); @@ -22,25 +20,22 @@ describe('processProposalHandlerFactory', () => { let processProposalHandler; let request; let loggerMock; - let beginBlockMock; - let endBlockMock; let verifyChainLockMock; - let deliverTxMock; - let appHash; - let validatorSetUpdate; - let consensusParamUpdates; let coreChainLockUpdate; - let proposalBlockExecutionContextMock; + let processProposalMock; let round; + let appHash; + let proposalBlockExecutionContextMock; + let consensusParamUpdates; + let validatorSetUpdate; beforeEach(function beforeEach() { round = 0; + appHash = Buffer.alloc(1, 1); proposalBlockExecutionContextMock = new BlockExecutionContextMock(this.sinon); - loggerMock = new LoggerMock(this.sinon); - consensusParamUpdates = new ConsensusParams({ block: { maxBytes: 1, @@ -57,26 +52,17 @@ describe('processProposalHandlerFactory', () => { }); validatorSetUpdate = new ValidatorSetUpdate(); - beginBlockMock = this.sinon.stub(); - endBlockMock = this.sinon.stub().resolves({ - consensusParamUpdates, - appHash, - validatorSetUpdate, - }); - deliverTxMock = this.sinon.stub().resolves({ - code: 0, - fees: FeeResult.create(1, 2), - }); - beginBlockMock = this.sinon.stub(); + loggerMock = new LoggerMock(this.sinon); + verifyChainLockMock = this.sinon.stub().resolves(true); + processProposalMock = this.sinon.stub().resolves(new ResponseProcessProposal({ status: 1 })); + processProposalHandler = processProposalHandlerFactory( - deliverTxMock, loggerMock, - proposalBlockExecutionContextMock, - beginBlockMock, - endBlockMock, verifyChainLockMock, + processProposalMock, + proposalBlockExecutionContextMock, ); const txs = new Array(3).fill(Buffer.alloc(5, 0)); @@ -116,43 +102,14 @@ describe('processProposalHandlerFactory', () => { const result = await processProposalHandler(request); expect(result).to.be.an.instanceOf(ResponseProcessProposal); - expect(result.status).to.equal(1); - expect(result.appHash).to.equal(appHash); - expect(result.txResults).to.be.deep.equal(new Array(3).fill({ code: 0 })); - expect(result.consensusParamUpdates).to.be.equal(consensusParamUpdates); - expect(result.validatorSetUpdate).to.be.equal(validatorSetUpdate); - expect(beginBlockMock).to.be.calledOnceWithExactly( - { - lastCommitInfo: request.proposedLastCommit, - height: request.height, - coreChainLockedHeight: request.coreChainLockedHeight, - version: request.version, - time: request.time, - proposerProTxHash: Buffer.from(request.proposerProTxHash), - round, - }, - loggerMock, - ); + expect(result.status).to.equal(1); - expect(deliverTxMock).to.be.calledThrice(); + expect(processProposalMock).to.be.calledOnceWithExactly(request, loggerMock); expect(verifyChainLockMock).to.be.calledOnceWithExactly( coreChainLockUpdate, ); - - expect(endBlockMock).to.be.calledOnceWithExactly({ - height: request.height, - round, - fees: FeeResult.create(), - coreChainLockedHeight: request.coreChainLockedHeight, - }, - loggerMock); - - const { fees } = endBlockMock.getCall(0).args[0]; - - expect(fees.storageFee).to.equal(3); - expect(fees.processingFee).to.equal(6); }); it('should return rejected ResponseProcessProposal if chainlock can\'t be verified', async () => { @@ -162,9 +119,11 @@ describe('processProposalHandlerFactory', () => { expect(result).to.be.an.instanceOf(ResponseProcessProposal); expect(result.status).to.equal(2); + + expect(processProposalMock).to.not.be.called(); }); - it('should return prepareProposalResult from execution context', async () => { + it('should return already prepared result for this height and round', async () => { proposalBlockExecutionContextMock.getHeight.returns(request.height); proposalBlockExecutionContextMock.getRound.returns(request.round); @@ -186,12 +145,8 @@ describe('processProposalHandlerFactory', () => { expect(result.consensusParamUpdates).to.be.equal(consensusParamUpdates); expect(result.validatorSetUpdate).to.be.equal(validatorSetUpdate); - expect(beginBlockMock).to.not.be.called(); - - expect(deliverTxMock).to.not.be.called(); + expect(processProposalMock).to.not.be.called(); expect(verifyChainLockMock).to.not.be.called(); - - expect(endBlockMock).to.not.be.called(); }); }); diff --git a/packages/js-drive/test/unit/abci/handlers/proposal/endBlockFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/proposal/endBlockFactory.spec.js index e470c850e4f..5137f715499 100644 --- a/packages/js-drive/test/unit/abci/handlers/proposal/endBlockFactory.spec.js +++ b/packages/js-drive/test/unit/abci/handlers/proposal/endBlockFactory.spec.js @@ -129,6 +129,6 @@ describe('endBlockFactory', () => { expect(actualFees.storageFee).to.equal(1); expect(actualFees.processingFee).to.equal(2); - expect(executionTimerMock.stopTimer).to.be.calledOnceWithExactly('roundExecution'); + expect(executionTimerMock.stopTimer).to.be.calledOnceWithExactly('roundExecution', true); }); }); diff --git a/packages/js-drive/test/unit/abci/handlers/proposal/processProposalFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/proposal/processProposalFactory.spec.js new file mode 100644 index 00000000000..2f83c429b3d --- /dev/null +++ b/packages/js-drive/test/unit/abci/handlers/proposal/processProposalFactory.spec.js @@ -0,0 +1,155 @@ +const { + tendermint: { + abci: { + ResponseProcessProposal, + ValidatorSetUpdate, + }, + types: { + ConsensusParams, + }, + }, +} = require('@dashevo/abci/types'); + +const Long = require('long'); + +const FeeResult = require('@dashevo/rs-drive/FeeResult'); + +const processProposalHandlerFactory = require('../../../../../lib/abci/handlers/proposal/processProposalFactory'); +const LoggerMock = require('../../../../../lib/test/mock/LoggerMock'); +const BlockExecutionContextMock = require('../../../../../lib/test/mock/BlockExecutionContextMock'); + +describe('processProposalFactory', () => { + let processProposalHandler; + let request; + let loggerMock; + let beginBlockMock; + let endBlockMock; + let deliverTxMock; + let appHash; + let validatorSetUpdate; + let consensusParamUpdates; + let coreChainLockUpdate; + let proposalBlockExecutionContextMock; + let round; + let executionTimerMock; + + beforeEach(function beforeEach() { + round = 0; + appHash = Buffer.alloc(1, 1); + + proposalBlockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + + loggerMock = new LoggerMock(this.sinon); + + consensusParamUpdates = new ConsensusParams({ + block: { + maxBytes: 1, + maxGas: 2, + }, + evidence: { + maxAgeDuration: null, + maxAgeNumBlocks: 1, + maxBytes: 2, + }, + version: { + appVersion: 1, + }, + }); + validatorSetUpdate = new ValidatorSetUpdate(); + + beginBlockMock = this.sinon.stub(); + endBlockMock = this.sinon.stub().resolves({ + consensusParamUpdates, + appHash, + validatorSetUpdate, + }); + deliverTxMock = this.sinon.stub().resolves({ + code: 0, + fees: FeeResult.create(1, 2), + }); + beginBlockMock = this.sinon.stub(); + + executionTimerMock = { + getTimer: this.sinon.stub().returns(0.1), + }; + + processProposalHandler = processProposalHandlerFactory( + deliverTxMock, + proposalBlockExecutionContextMock, + beginBlockMock, + endBlockMock, + executionTimerMock, + ); + + const txs = new Array(3).fill(Buffer.alloc(5, 0)); + + const height = new Long(42); + + const time = { + seconds: Math.ceil(new Date().getTime() / 1000), + }; + const version = { + app: Long.fromInt(1), + }; + const proposerProTxHash = Uint8Array.from([1, 2, 3, 4]); + const coreChainLockedHeight = 10; + const proposedLastCommit = {}; + + coreChainLockUpdate = { + coreBlockHeight: 42, + coreBlockHash: '1528e523f4c20fa84ba70dd96372d34e00ce260f357d53ad1a8bc892ebf20e2d', + signature: '1897ce8f54d2070f44ca5c29983b68b391e8137c25e44f67416e579f3e3bdfef7b4fd22db7818399147e52907998857b0fbc8edfdc40a64f2c7df0e88544d31d12ca8c15e73d50dda25ca23f754ed3f789ed4bcb392161995f464017c10df404', + }; + + request = { + round, + height, + txs, + coreChainLockedHeight, + version, + proposedLastCommit, + time, + proposerProTxHash, + coreChainLockUpdate, + }; + }); + + it('should return ResponseProcessProposal', async () => { + const result = await processProposalHandler(request, loggerMock); + + expect(result).to.be.an.instanceOf(ResponseProcessProposal); + expect(result.status).to.equal(1); + expect(result.appHash).to.equal(appHash); + expect(result.txResults).to.be.deep.equal(new Array(3).fill({ code: 0 })); + expect(result.consensusParamUpdates).to.be.equal(consensusParamUpdates); + expect(result.validatorSetUpdate).to.be.equal(validatorSetUpdate); + + expect(beginBlockMock).to.be.calledOnceWithExactly( + { + lastCommitInfo: request.proposedLastCommit, + height: request.height, + coreChainLockedHeight: request.coreChainLockedHeight, + version: request.version, + time: request.time, + proposerProTxHash: Buffer.from(request.proposerProTxHash), + round, + }, + loggerMock, + ); + + expect(deliverTxMock).to.be.calledThrice(); + + expect(endBlockMock).to.be.calledOnceWithExactly({ + height: request.height, + round, + fees: FeeResult.create(), + coreChainLockedHeight: request.coreChainLockedHeight, + }, + loggerMock); + + const { fees } = endBlockMock.getCall(0).args[0]; + + expect(fees.storageFee).to.equal(3); + expect(fees.processingFee).to.equal(6); + }); +}); diff --git a/packages/js-drive/test/unit/abci/handlers/verifyVoteExtensionHandlerFactory.spec.js b/packages/js-drive/test/unit/abci/handlers/verifyVoteExtensionHandlerFactory.spec.js index a83ed8f15ce..d1821dc96de 100644 --- a/packages/js-drive/test/unit/abci/handlers/verifyVoteExtensionHandlerFactory.spec.js +++ b/packages/js-drive/test/unit/abci/handlers/verifyVoteExtensionHandlerFactory.spec.js @@ -6,12 +6,22 @@ const { }, } = require('@dashevo/abci/types'); const verifyVoteExtensionHandlerFactory = require('../../../../lib/abci/handlers/verifyVoteExtensionHandlerFactory'); +const BlockExecutionContextMock = require('../../../../lib/test/mock/BlockExecutionContextMock'); +const LoggerMock = require('../../../../lib/test/mock/LoggerMock'); describe('verifyVoteExtensionHandlerFactory', () => { let verifyVoteExtensionHandler; + let proposalBlockExecutionContextMock; - beforeEach(() => { - verifyVoteExtensionHandler = verifyVoteExtensionHandlerFactory(); + beforeEach(function beforeEach() { + proposalBlockExecutionContextMock = new BlockExecutionContextMock(this.sinon); + + const loggerMock = new LoggerMock(this.sinon); + proposalBlockExecutionContextMock.getConsensusLogger.returns(loggerMock); + + verifyVoteExtensionHandler = verifyVoteExtensionHandlerFactory( + proposalBlockExecutionContextMock, + ); }); it('should return ResponseVerifyVoteExtension', async () => {