Skip to content

Commit 8d21bc0

Browse files
authored
Merge pull request #1204 from ainblockchain/release/v1.1.2
Release/v1.1.2
2 parents 29e8b4b + 8cb3d83 commit 8d21bc0

File tree

14 files changed

+222
-52
lines changed

14 files changed

+222
-52
lines changed

blockchain/index.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ class Blockchain {
9696
const blockPath = FileUtil.getBlockPath(this.blockchainPath,
9797
FileUtil.readH2nFile(this.blockchainPath, hash));
9898
if (!blockPath) {
99-
return this.chain.find((block) => block.hash === hash);
99+
const block = this.chain.find((block) => block.hash === hash);
100+
// NOTE(platfowner): See https://github.com/ainblockchain/ain-blockchain/issues/1200 .
101+
return block ? JSON.parse(JSON.stringify(block)) : block;
100102
} else {
101103
return Block.parse(FileUtil.readCompressedJsonSync(blockPath));
102104
}
@@ -115,7 +117,9 @@ class Blockchain {
115117
if (blockNumber === 0) return this.genesisBlock;
116118
const blockPath = FileUtil.getBlockPath(this.blockchainPath, blockNumber);
117119
if (!blockPath || blockNumber > this.lastBlockNumber() - NodeConfigs.ON_MEMORY_CHAIN_LENGTH) {
118-
return this.chain.find((block) => block.number === blockNumber);
120+
const block = this.chain.find((block) => block.number === blockNumber);
121+
// NOTE(platfowner): See https://github.com/ainblockchain/ain-blockchain/issues/1200 .
122+
return block ? JSON.parse(JSON.stringify(block)) : block;
119123
} else {
120124
return Block.parse(FileUtil.readCompressedJsonSync(blockPath));
121125
}
@@ -177,7 +181,8 @@ class Blockchain {
177181
addBlockToChain(block) {
178182
const LOG_HEADER = 'addBlockToChain';
179183

180-
this.chain.push(block);
184+
// NOTE(platfowner): See https://github.com/ainblockchain/ain-blockchain/issues/1200 .
185+
this.chain.push(JSON.parse(JSON.stringify(block)));
181186
logger.info(`[${LOG_HEADER}] Successfully added block ${block.number} to chain.`);
182187

183188
// Keep up to latest ON_MEMORY_CHAIN_LENGTH blocks

client/protocol_versions.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,5 +128,8 @@
128128
},
129129
"1.1.1": {
130130
"min": "1.0.0"
131+
},
132+
"1.1.2": {
133+
"min": "1.0.0"
131134
}
132135
}

common/constants.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -574,15 +574,17 @@ const WriteDbOperations = {
574574
*/
575575
const TransactionStates = {
576576
FINALIZED: 'FINALIZED',
577-
REVERTED: 'REVERTED', // Reverted means it's failed but included in a block
577+
REVERTED: 'REVERTED', // Failed but included in a block
578578
EXECUTED: 'EXECUTED',
579-
FAILED: 'FAILED', // Failed means it's failed and is NOT included in a block
579+
FAILED: 'FAILED', // Failed and is NOT included in a block
580+
IN_BLOCK: 'IN_BLOCK', // Included in a block, NOT reverted nor finalized.
580581
PENDING: 'PENDING',
581582
TIMED_OUT: 'TIMED_OUT',
582583
};
583584

584585
function isTxInBlock(state) {
585-
return state === TransactionStates.FINALIZED || state === TransactionStates.REVERTED;
586+
return state === TransactionStates.FINALIZED || state === TransactionStates.IN_BLOCK
587+
|| state === TransactionStates.REVERTED;
586588
}
587589

588590
function isEndState(state) {

consensus/index.js

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const {
1717
PredefinedDbPaths,
1818
StateVersions,
1919
ValueChangedEventSources,
20+
TransactionStates,
2021
} = require('../common/constants');
2122
const { ConsensusErrorCode } = require('../common/result-code');
2223
const {
@@ -600,7 +601,8 @@ class Consensus {
600601

601602
static validateAndExecuteLastVotes(lastVotes, lastHash, number, blockTime, db, blockPool, eventSource) {
602603
if (number === 0) return;
603-
if (!db.executeTransactionList(lastVotes, true, false, number, blockTime, eventSource)) {
604+
const txsRes = db.executeTransactionList(lastVotes, true, false, number, blockTime, eventSource);
605+
if (!txsRes) {
604606
throw new ConsensusError({
605607
code: ConsensusErrorCode.EXECUTING_LAST_VOTES_FAILURE,
606608
message: `Failed to execute last votes`,
@@ -628,6 +630,7 @@ class Consensus {
628630
level: 'error'
629631
});
630632
}
633+
return txsRes;
631634
}
632635

633636
// Cross-check the offenses in proposalTx & the evidence in proposalBlock
@@ -726,6 +729,7 @@ class Consensus {
726729
level: 'error'
727730
});
728731
}
732+
return txsRes;
729733
}
730734

731735
static validateStateProofHash(expectedStateProofHash, block, db, node, takeSnapshot) {
@@ -774,16 +778,17 @@ class Consensus {
774778
});
775779
}
776780
// Try executing the proposal tx on the proposal block's db state
777-
const proposalTxExecRes = tempDb.executeTransaction(executableTx, true, false, number, blockTime);
781+
const txRes = tempDb.executeTransaction(executableTx, true, false, number, blockTime);
778782
tempDb.destroyDb();
779-
if (CommonUtil.isFailedTx(proposalTxExecRes)) {
783+
if (CommonUtil.isFailedTx(txRes)) {
780784
throw new ConsensusError({
781785
code: ConsensusErrorCode.EXECUTING_PROPOSAL_FAILURE,
782-
message: `Failed to execute the proposal tx: ${JSON.stringify(proposalTxExecRes)}`,
786+
message: `Failed to execute the proposal tx: ${JSON.stringify(txRes)}`,
783787
level: 'error'
784788
});
785789
}
786790
node.tp.addTransaction(executableTx);
791+
return txRes;
787792
}
788793

789794
static addBlockToBlockPool(block, proposalTx, db, blockPool) {
@@ -797,7 +802,54 @@ class Consensus {
797802
blockPool.addToHashToDbMap(block.hash, db);
798803
}
799804

800-
static validateAndExecuteBlockOnDb(rawBlock, node, stateVersionPrefix, proposalTx = null, takeSnapshot = false) {
805+
static updateTransactionInfoForBlock(node, voteTxs, proposalTx, transactions, voteTxsRes, proposalTxRes, txsRes) {
806+
const trackedAt = Date.now();
807+
for (let i = 0; i < voteTxs.length; i++) {
808+
const voteTx = voteTxs[i];
809+
const voteResult = voteTxsRes[i];
810+
const tracked = node.tp.transactionTracker.get(voteTx.hash) || {};
811+
const beforeState = _.get(tracked, 'state', null);
812+
node.tp.updateTransactionInfo(voteTx.hash, {
813+
state: TransactionStates.IN_BLOCK,
814+
exec_result: voteResult,
815+
tracked_at: trackedAt,
816+
});
817+
if (node.eh) {
818+
node.eh.emitTxStateChanged(voteTx, beforeState, TransactionStates.IN_BLOCK);
819+
}
820+
}
821+
// NOTE(platfowner): Genesis block has no porposal transaction.
822+
if (proposalTx) {
823+
const trackedProposalTx = node.tp.transactionTracker.get(proposalTx.hash) || {};
824+
const beforeState = _.get(trackedProposalTx, 'state', null);
825+
node.tp.updateTransactionInfo(proposalTx.hash, {
826+
state: TransactionStates.IN_BLOCK,
827+
exec_result: proposalTxRes,
828+
tracked_at: trackedAt,
829+
});
830+
if (node.eh) {
831+
node.eh.emitTxStateChanged(proposalTx, beforeState, TransactionStates.IN_BLOCK);
832+
}
833+
}
834+
for (let i = 0; i < transactions.length; i++) {
835+
const tx = transactions[i];
836+
const txResult = txsRes[i];
837+
const tracked = node.tp.transactionTracker.get(tx.hash) || {};
838+
const beforeState = _.get(tracked, 'state', null);
839+
const txState =
840+
CommonUtil.isFailedTx(txResult) ? TransactionStates.REVERTED : TransactionStates.IN_BLOCK;
841+
node.tp.updateTransactionInfo(tx.hash, {
842+
state: txState,
843+
exec_result: txResult,
844+
tracked_at: trackedAt,
845+
});
846+
if (node.eh) {
847+
node.eh.emitTxStateChanged(tx, beforeState, txState);
848+
}
849+
}
850+
}
851+
852+
static validateAndExecuteBlockOnDb(rawBlock, node, stateVersionPrefix, proposalTx, updateTxInfo, takeSnapshot = false) {
801853
const block = Block.parse(rawBlock);
802854
if (!block) {
803855
throw new ConsensusError({
@@ -822,23 +874,29 @@ class Consensus {
822874
node.bc.lastBlockNumber(), node.stateManager.getFinalVersion(), node.bp);
823875
const db = Consensus.getNewDbForProposal(number, baseVersion, stateVersionPrefix, node);
824876

877+
let voteTxsRes = null;
878+
let proposalTxRes = null;
879+
let txsRes = null;
825880
try {
826881
Consensus.validateBlockNumberAndHashes(block, prevBlock, node.bc.genesisBlockHash);
827882
Consensus.validateValidators(validators, number, baseVersion, node);
828883
Consensus.validateProposer(number, prevBlockLastVotesHash, epoch, validators, proposer);
829-
Consensus.validateAndExecuteLastVotes(last_votes, last_hash, number, timestamp, db, node.bp, ValueChangedEventSources.BLOCK);
884+
voteTxsRes = Consensus.validateAndExecuteLastVotes(last_votes, last_hash, number, timestamp, db, node.bp, ValueChangedEventSources.BLOCK);
830885
Consensus.validateAndExecuteOffensesAndEvidence(
831886
evidence, validators, prevBlockMajority, number, timestamp, proposalTx, db, ValueChangedEventSources.BLOCK);
832-
Consensus.validateAndExecuteTransactions(
887+
txsRes = Consensus.validateAndExecuteTransactions(
833888
transactions, receipts, number, timestamp, gas_amount_total, gas_cost_total, db, node, ValueChangedEventSources.BLOCK);
834889
db.applyBandagesForBlockNumber(number);
835890
Consensus.validateStateProofHash(state_proof_hash, block, db, node, takeSnapshot);
836-
Consensus.executeProposalTx(proposalTx, number, timestamp, db, node);
891+
proposalTxRes = Consensus.executeProposalTx(proposalTx, number, timestamp, db, node);
837892
Consensus.addBlockToBlockPool(block, proposalTx, db, node.bp);
838893
} catch (e) {
839894
db.destroyDb();
840895
throw e;
841896
}
897+
if (updateTxInfo) {
898+
Consensus.updateTransactionInfoForBlock(node, last_votes, proposalTx, transactions, voteTxsRes, proposalTxRes, txsRes);
899+
}
842900
}
843901

844902
/**
@@ -854,7 +912,7 @@ class Consensus {
854912
`${proposalBlock.number} / ${proposalBlock.epoch} / ${proposalBlock.hash} / ${proposalBlock.proposer}\n` +
855913
`${proposalTx.hash} / ${proposalTx.address}`);
856914

857-
Consensus.validateAndExecuteBlockOnDb(proposalBlock, this.node, StateVersions.POOL, proposalTx);
915+
Consensus.validateAndExecuteBlockOnDb(proposalBlock, this.node, StateVersions.POOL, proposalTx, true);
858916

859917
if (proposalBlock.number > 1 && !this.node.bp.longestNotarizedChainTips.includes(proposalBlock.last_hash)) {
860918
throw new ConsensusError({
@@ -1144,7 +1202,7 @@ class Consensus {
11441202
proposalTx = i < chain.length - 1 ? ConsensusUtil.filterProposalFromVotes(chain[i + 1].last_votes) : null;
11451203
logger.debug(`[${LOG_HEADER}] applying block ${JSON.stringify(block)}`);
11461204
try {
1147-
Consensus.validateAndExecuteBlockOnDb(block, this.node, StateVersions.SNAP, proposalTx);
1205+
Consensus.validateAndExecuteBlockOnDb(block, this.node, StateVersions.SNAP, proposalTx, false);
11481206
} catch (e) {
11491207
logger.error(`[${LOG_HEADER}] Failed to validate and execute block ${block.number}: ${e}`);
11501208
return null;

node/index.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -806,11 +806,11 @@ class BlockchainNode {
806806
// will not be included in the tx pool, as they can't be included in a block
807807
// anyway, and may be used for attacks on blockchain nodes.
808808
if (!isDryrun && !CommonUtil.txPrecheckFailed(result)) {
809-
this.tp.addTransaction(executableTx);
809+
this.tp.addTransaction(executableTx, result);
810810
}
811811
} else {
812812
if (!isDryrun) {
813-
this.tp.addTransaction(executableTx, true);
813+
this.tp.addTransaction(executableTx, result, true);
814814
}
815815
}
816816

@@ -851,7 +851,7 @@ class BlockchainNode {
851851
const latestDb = this.createTempDb(latestSnapshotStateVersion, `${StateVersions.LOAD}:${number}`, number);
852852
this.bp.addToHashToDbMap(block.hash, latestDb);
853853
} else {
854-
Consensus.validateAndExecuteBlockOnDb(block, this, StateVersions.LOAD, proposalTx, true);
854+
Consensus.validateAndExecuteBlockOnDb(block, this, StateVersions.LOAD, proposalTx, true, true);
855855
if (number === 0) {
856856
this.bc.addBlockToChainAndWriteToDisk(block, false);
857857
this.cloneAndFinalizeVersion(this.bp.hashToDb.get(block.hash).stateVersion, 0);
@@ -905,7 +905,7 @@ class BlockchainNode {
905905
const proposalTx = i < validBlocks.length - 1 ?
906906
ConsensusUtil.filterProposalFromVotes(validBlocks[i + 1].last_votes) : null;
907907
try {
908-
Consensus.validateAndExecuteBlockOnDb(block, this, StateVersions.SEGMENT, proposalTx, true);
908+
Consensus.validateAndExecuteBlockOnDb(block, this, StateVersions.SEGMENT, proposalTx, true, true);
909909
this.tryFinalizeChain();
910910
} catch (e) {
911911
logger.info(`[${LOG_HEADER}] Failed to add new block (${block.number} / ${block.hash}) to chain: ${e.stack}`);
@@ -1044,7 +1044,7 @@ class BlockchainNode {
10441044
lastFinalizedBlock = blockToFinalize;
10451045
logger.debug(`[${LOG_HEADER}] Finalized a block of number ${blockToFinalize.number} and ` +
10461046
`hash ${blockToFinalize.hash}`);
1047-
this.tp.cleanUpForNewBlock(blockToFinalize);
1047+
this.tp.cleanUpForFinalizedBlock(blockToFinalize);
10481048
if (!CommonUtil.isEmpty(blockToFinalize.evidence)) {
10491049
Object.values(blockToFinalize.evidence).forEach((evidenceList) => {
10501050
evidenceList.forEach((val) => {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "ain-blockchain",
33
"description": "AI Network Blockchain",
4-
"version": "1.1.1",
4+
"version": "1.1.2",
55
"private": true,
66
"license": "MIT",
77
"author": "[email protected]",

test/integration/event_handler.test.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -371,12 +371,15 @@ describe('Event Handler Test', function() {
371371
if (eventTriggeredCnt === 0) {
372372
expect(txState.before).to.equal(null);
373373
expect(txState.after).to.equal(TransactionStates.EXECUTED);
374-
eventTriggeredCnt++;
375-
} else {
374+
} else if (eventTriggeredCnt === 1) {
376375
expect(txState.before).to.equal(TransactionStates.EXECUTED);
376+
expect(txState.after).to.equal(TransactionStates.IN_BLOCK);
377+
} else if (eventTriggeredCnt === 2) {
378+
expect(txState.before).to.equal(TransactionStates.IN_BLOCK);
377379
expect(txState.after).to.equal(TransactionStates.FINALIZED);
378380
done();
379381
}
382+
eventTriggeredCnt++;
380383
}
381384
});
382385
const txResult = setValue(serverList[EVENT_HANDLER_NODE_INDEX], targetPath, 'change')
@@ -402,15 +405,17 @@ describe('Event Handler Test', function() {
402405
if (eventTriggeredCnt < 2) {
403406
expect(txState.before).to.equal(null);
404407
expect(txState.after).to.equal(TransactionStates.EXECUTED);
405-
eventTriggeredCnt++;
406-
} else {
408+
} else if (eventTriggeredCnt < 4) {
407409
expect(txState.before).to.equal(TransactionStates.EXECUTED);
410+
expect(txState.after).to.equal(TransactionStates.IN_BLOCK);
411+
} else if (eventTriggeredCnt < 6) {
412+
expect(txState.before).to.equal(TransactionStates.IN_BLOCK);
408413
expect(txState.after).to.equal(TransactionStates.FINALIZED);
409-
eventTriggeredCnt++;
410-
if (eventTriggeredCnt === 4) {
414+
if (eventTriggeredCnt === 5) {
411415
done();
412416
}
413417
}
418+
eventTriggeredCnt++;
414419
}
415420
});
416421
const txResult = setValue(serverList[EVENT_HANDLER_NODE_INDEX], targetPath, 'change')
@@ -443,12 +448,12 @@ describe('Event Handler Test', function() {
443448
if (eventTriggeredCnt === 0) {
444449
expect(txState.before).to.equal(null);
445450
expect(txState.after).to.equal(TransactionStates.PENDING);
446-
eventTriggeredCnt++;
447-
} else {
451+
} else if (eventTriggeredCnt === 1) {
448452
expect(txState.before).to.equal(TransactionStates.PENDING);
449453
expect(txState.after).to.equal(TransactionStates.REVERTED);
450454
done();
451455
}
456+
eventTriggeredCnt++;
452457
}
453458
});
454459
registerFilter(wsClient, filterId, BlockchainEventTypes.TX_STATE_CHANGED, config);

test/unit/tx-pool.test.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,13 @@ describe('TransactionPool', () => {
6464
});
6565

6666
it('add an executed transaction', () => {
67-
node.tp.addTransaction(txToAdd, true);
67+
const execResult = { code: 0 };
68+
node.tp.addTransaction(txToAdd, execResult, true);
6869
const addedTx = node.tp.transactions.get(node.account.address).find((t) => t.hash === txToAdd.hash);
6970
assert.deepEqual(eraseTxCreatedAt(addedTx), eraseTxCreatedAt(txToAdd));
7071
const txInfo = node.getTransactionByHash(txToAdd.hash);
7172
expect(txInfo.state).to.equal(TransactionStates.EXECUTED);
73+
assert.deepEqual(txInfo.exec_result, execResult);
7274
});
7375
});
7476

@@ -764,7 +766,7 @@ describe('TransactionPool', () => {
764766
nodes = [node2, node3, node4];
765767
});
766768

767-
it('cleanUpForNewBlock()', () => {
769+
it('cleanUpForFinalizedBlock()', () => {
768770
const number = 1;
769771
const lastBlock = node.bc.genesisBlock;
770772
const transactions = node.tp.getValidTransactions();
@@ -787,7 +789,7 @@ describe('TransactionPool', () => {
787789
}));
788790
node.tp.addTransaction(newTransactions[node.account.address][i]);
789791
}
790-
node.tp.cleanUpForNewBlock(block);
792+
node.tp.cleanUpForFinalizedBlock(block);
791793
assert.deepEqual(newTransactions, Object.fromEntries(node.tp.transactions));
792794
});
793795

tools/block/getBlockByNumber.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// A tool to get block by number
2+
const ainUtil = require('@ainblockchain/ain-util');
3+
const { sendGetBlockByNumberRequest } = require('../util');
4+
5+
async function getBlockByNumber(endpointUrl, blockNumber) {
6+
console.log(`\n*** Send request with blockNumber: ${blockNumber}`);
7+
const result = await sendGetBlockByNumberRequest(endpointUrl, blockNumber);
8+
console.log(`\nResult: ${JSON.stringify(result, null, 2)}`);
9+
}
10+
11+
async function processArguments() {
12+
if (process.argv.length !== 4) {
13+
usage();
14+
}
15+
const endpointUrl = process.argv[2];
16+
const blockNumber = process.argv[3];
17+
await getBlockByNumber(endpointUrl, blockNumber);
18+
}
19+
20+
function usage() {
21+
console.log('\nUsage: node getBlockByNumber.js <Endpoint Url> <Block Number>');
22+
console.log('Example: node getBlockByNumber.js http://localhost:8081 2519120');
23+
console.log('Example: node getBlockByNumber.js https://staging-api.ainetwork.ai 2519120');
24+
console.log('Example: node getBlockByNumber.js https://testnet-api.ainetwork.ai 2519120');
25+
console.log('Example: node getBlockByNumber.js https://mainnet-api.ainetwork.ai 2519120');
26+
console.log('');
27+
process.exit(0)
28+
}
29+
30+
processArguments();

0 commit comments

Comments
 (0)