Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Update block schema to include transactionRoot - Closes #5210 #5359

Merged
merged 7 commits into from
May 26, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions commander/src/commands/network-identifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { flags as commonFlags } from '../utils/flags';

export default class NetworkIdentifierCommand extends BaseCommand {
static description = `
Creates Network identifier for the given genesis payload hash and community identifier.
Creates Network identifier for the given genesis block transaction root and community identifier.
`;

static examples = [
Expand All @@ -30,8 +30,8 @@ export default class NetworkIdentifierCommand extends BaseCommand {

static args = [
{
name: 'genesisPayloadHash',
description: 'Payload hash of genesis block from the network.',
name: 'genesisBlockTransactionRoot',
description: 'Transaction root of genesis block from the network.',
required: true,
},
];
Expand All @@ -49,10 +49,10 @@ export default class NetworkIdentifierCommand extends BaseCommand {
const {
flags: { 'community-identifier': communityIdentifier },
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
args: { genesisPayloadHash },
args: { genesisBlockTransactionRoot },
} = this.parse(NetworkIdentifierCommand);
const networkIdentifier = getNetworkIdentifier(
genesisPayloadHash as string,
genesisBlockTransactionRoot as string,
communityIdentifier,
);
this.print({ networkIdentifier });
Expand Down
2 changes: 1 addition & 1 deletion commander/src/commands/transaction/create/pom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ interface RawHeader {
readonly generatorPublicKey: string;
readonly numberOfTransactions: number;
readonly payloadLength: number;
readonly payloadHash: string;
readonly transactionRoot: string;
readonly totalAmount: string;
readonly totalFee: string;
readonly reward: string;
Expand Down
4 changes: 2 additions & 2 deletions commander/src/utils/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ export const getAPIClient = ({
nodes,
network,
}: APIClientOptions): APIClient => {
const genesisBlockPayloadHash = NETHASHES[network] || network;
const genesisBlockTransactionRoot = NETHASHES[network] || network;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const clientNodes = nodes && nodes.length > 0 ? nodes : seedNodes[network];

return new APIClient(clientNodes, { genesisBlockPayloadHash });
return new APIClient(clientNodes, { genesisBlockTransactionRoot });
};
2 changes: 1 addition & 1 deletion commander/test/commands/config/set.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ describe('config:set', () => {
.stdout()
.command(['config:set', 'api.network', validNethash])
.it(
'should set api.network to the custom genesisBlockPayloadHash',
'should set api.network to the custom genesisBlockTransactionRoot',
() => {
const newConfig = {
...defaultConfig,
Expand Down
4 changes: 2 additions & 2 deletions commander/test/commands/transaction/create/pom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('transaction:create:pom', () => {
totalFee: '10000000000',
reward: '10000000000',
payloadLength: 0,
payloadHash:
transactionRoot:
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
generatorPublicKey:
'addb0e15a44b0fdc6ff291be28d8c98f5551d0cd9218d749e30ddb87c6e31ca9',
Expand All @@ -54,7 +54,7 @@ describe('transaction:create:pom', () => {
totalFee: '10000000000',
reward: '10000000000',
payloadLength: 0,
payloadHash:
transactionRoot:
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
generatorPublicKey:
'addb0e15a44b0fdc6ff291be28d8c98f5551d0cd9218d749e30ddb87c6e31ca9',
Expand Down
8 changes: 4 additions & 4 deletions elements/lisk-api-client/src/api_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,14 @@ export class APIClient {

public static createMainnetAPIClient(options?: InitOptions): APIClient {
return new APIClient(constants.MAINNET_NODES, {
genesisBlockPayloadHash: constants.MAINNET_NETHASH,
genesisBlockTransactionRoot: constants.MAINNET_NETHASH,
...options,
});
}

public static createTestnetAPIClient(options?: InitOptions): APIClient {
return new APIClient(constants.TESTNET_NODES, {
genesisBlockPayloadHash: constants.TESTNET_NETHASH,
genesisBlockTransactionRoot: constants.TESTNET_NETHASH,
...options,
});
}
Expand Down Expand Up @@ -170,8 +170,8 @@ export class APIClient {

this.headers = {
...commonHeaders,
...(options.genesisBlockPayloadHash
? { nethash: options.genesisBlockPayloadHash }
...(options.genesisBlockTransactionRoot
? { nethash: options.genesisBlockTransactionRoot }
: {}),
...(options.client ? getClientHeaders(options.client) : {}),
};
Expand Down
2 changes: 1 addition & 1 deletion elements/lisk-api-client/src/api_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export interface HashMap {
export interface InitOptions {
readonly bannedNodes?: ReadonlyArray<string>;
readonly client?: object;
readonly genesisBlockPayloadHash?: string;
readonly genesisBlockTransactionRoot?: string;
readonly node?: string;
readonly randomizeNodes?: boolean;
}
Expand Down
6 changes: 3 additions & 3 deletions elements/lisk-api-client/test/api_client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ describe('APIClient module', () => {

it('should call initialize with the nodes and provided options', () => {
const providedOptions = {
genesisBlockPayloadHash:
genesisBlockTransactionRoot:
'0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
};
apiClient = new APIClient(defaultNodes, providedOptions);
Expand Down Expand Up @@ -185,7 +185,7 @@ describe('APIClient module', () => {

it('should set custom headers with supplied options', () => {
apiClient = new APIClient(defaultNodes, {
genesisBlockPayloadHash: testnetHash,
genesisBlockTransactionRoot: testnetHash,
client: {
name: 'LiskHub',
version: '5.0',
Expand All @@ -197,7 +197,7 @@ describe('APIClient module', () => {

it('should not set User-Agent header when client options were not given', () => {
apiClient = new APIClient(defaultNodes, {
genesisBlockPayloadHash: testnetHash,
genesisBlockTransactionRoot: testnetHash,
});
return expect(apiClient.headers).not.toHaveProperty('User-Agent');
});
Expand Down
2 changes: 1 addition & 1 deletion elements/lisk-bft/test/fixtures/blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const BlockHeader = stampit.compose({
'56d63b563e00332ec31451376f5f2665fcf7e118d45e68f8db0b00db5963b56bc6776a42d520978c1522c39545c9aff62a7d5bdcf851bf65904b2c2158870f00',
generatorPublicKey: '',
numberOfTransactions: 2,
payloadHash:
transactionRoot:
'be0df321b1653c203226add63ac0d13b3411c2f4caf0a213566cbd39edb7ce3b',
payloadLength: 494,
height: 489,
Expand Down
1 change: 1 addition & 0 deletions elements/lisk-chain/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@liskhq/lisk-cryptography": "2.5.0-alpha.0",
"@liskhq/lisk-db": "0.1.0",
"@liskhq/lisk-transactions": "4.0.0-alpha.0",
"@liskhq/lisk-tree": "0.1.0",
"@types/node": "12.12.11",
"debug": "4.1.1",
"lodash.clonedeep": "4.5.0",
Expand Down
4 changes: 2 additions & 2 deletions elements/lisk-chain/src/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import {
import * as blocksUtils from './utils';
import {
validateBlockSlot,
validatePayload,
validateTransactionRoot,
validatePreviousBlockProperty,
validateReward,
validateSignature,
Expand Down Expand Up @@ -347,7 +347,7 @@ export class Chain {
throw invalidTransactionResponse.errors;
}

validatePayload(block, this.constants.maxPayloadLength);
validateTransactionRoot(block, this.constants.maxPayloadLength);
ManuGowda marked this conversation as resolved.
Show resolved Hide resolved

// Update id
// eslint-disable-next-line no-param-reassign
Expand Down
4 changes: 2 additions & 2 deletions elements/lisk-chain/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const baseBlockSchema = {
numberOfTransactions: {
type: 'integer',
},
payloadHash: {
transactionRoot: {
type: 'string',
format: 'hex',
},
Expand Down Expand Up @@ -76,7 +76,7 @@ export const baseBlockSchema = {
'blockSignature',
'generatorPublicKey',
'numberOfTransactions',
'payloadHash',
'transactionRoot',
'payloadLength',
'timestamp',
'totalAmount',
Expand Down
2 changes: 1 addition & 1 deletion elements/lisk-chain/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export interface BlockHeaderJSON {
generatorPublicKey: string;
numberOfTransactions: number;
payloadLength: number;
payloadHash: string;
transactionRoot: string;
totalAmount: string;
totalFee: string;
reward: string;
Expand Down
32 changes: 19 additions & 13 deletions elements/lisk-chain/src/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@
* Removal or modification of this copyright notice is prohibited.
*/

import { hash, verifyData } from '@liskhq/lisk-cryptography';
import {
verifyData,
stringToBuffer,
bufferToHex,
} from '@liskhq/lisk-cryptography';
import { BaseTransaction } from '@liskhq/lisk-transactions';
import { MerkleTree } from '@liskhq/lisk-tree';

import { Slots } from './slots';
import { BlockInstance, GenesisBlock } from './types';
Expand Down Expand Up @@ -74,7 +79,14 @@ export const validateReward = (
}
};

export const validatePayload = (
export const getTransactionRoot = (ids: ReadonlyArray<string>): string => {
const idsAsBuffers = ids.map(id => stringToBuffer(id));
const tree = new MerkleTree(idsAsBuffers);

return bufferToHex(tree.root);
};

export const validateTransactionRoot = (
block: BlockInstance,
maxPayloadLength: number,
): void => {
Expand All @@ -84,32 +96,26 @@ export const validatePayload = (

let totalAmount = BigInt(0);
let totalFee = BigInt(0);
const transactionsBytesArray: Buffer[] = [];
const transactionIds: string[] = [];
const appliedTransactions: { [id: string]: BaseTransaction } = {};

block.transactions.forEach(transaction => {
const transactionBytes = transaction.getBytes();

// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (appliedTransactions[transaction.id]) {
throw new Error(`Encountered duplicate transaction: ${transaction.id}`);
}

transactionIds.push(transaction.id);
appliedTransactions[transaction.id] = transaction;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (transactionBytes) {
transactionsBytesArray.push(transactionBytes);
}
// eslint-disable-next-line
totalAmount += BigInt((transaction.asset as any).amount || 0);
totalFee += BigInt(transaction.fee);
});

const transactionsBuffer = Buffer.concat(transactionsBytesArray);
const payloadHash = hash(transactionsBuffer).toString('hex');
const transactionRoot = getTransactionRoot(transactionIds);

if (payloadHash !== block.payloadHash) {
throw new Error('Invalid payload hash');
if (transactionRoot !== block.transactionRoot) {
throw new Error('Invalid transaction root');
}

if (totalAmount !== BigInt(block.totalAmount)) {
Expand Down
2 changes: 1 addition & 1 deletion elements/lisk-chain/src/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export class BlocksVerify {
public matchGenesisBlock(block: BlockHeader): boolean {
return (
block.id === this.genesisBlock.id &&
block.payloadHash === this.genesisBlock.payloadHash &&
block.transactionRoot === this.genesisBlock.transactionRoot &&
block.blockSignature === this.genesisBlock.blockSignature
);
}
Expand Down
2 changes: 1 addition & 1 deletion elements/lisk-chain/test/fixtures/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const Block = stampit.compose({
'56d63b563e00332ec31451376f5f2665fcf7e118d45e68f8db0b00db5963b56bc6776a42d520978c1522c39545c9aff62a7d5bdcf851bf65904b2c2158870f00',
generatorPublicKey: '',
numberOfTransactions: 2,
payloadHash:
transactionRoot:
'be0df321b1653c203226add63ac0d13b3411c2f4caf0a213566cbd39edb7ce3b',
payloadLength: 494,
height: 489,
Expand Down
2 changes: 1 addition & 1 deletion elements/lisk-chain/test/fixtures/genesis_block.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"totalFee": "0",
"communityIdentifier": "Lisk",
"generatorPublicKey": "e925106c5b0f276dfb0a3d60c4ed6068ec0181a70dab680199d65369fb69b9f8",
"payloadHash": "19074b69c97e6f6b86969bb62d4f15b888898b499777bda56a3a2ee642a7f20a",
"transactionRoot": "19074b69c97e6f6b86969bb62d4f15b888898b499777bda56a3a2ee642a7f20a",
"payloadLength": 39677,
"totalAmount": "10000000000000000",
"transactions": [
Expand Down
8 changes: 4 additions & 4 deletions elements/lisk-chain/test/unit/chain.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jest.mock('events');
jest.mock('@liskhq/lisk-db');

const networkIdentifier = getNetworkIdentifier(
genesisBlock.payloadHash,
genesisBlock.transactionRoot,
'Lisk',
);

Expand Down Expand Up @@ -136,12 +136,12 @@ describe('chain', () => {
await expect(chainInstance.init()).rejects.toEqual(error);
});

it('should throw an error if the genesis block payloadHash is different', async () => {
it('should throw an error if the genesis block transactionRoot is different', async () => {
// Arrange
const error = new Error('Genesis block does not match');
const mutatedGenesisBlock = {
...genesisBlock,
payloadHash: genesisBlock.payloadHash.replace('0', '1'),
transactionRoot: genesisBlock.transactionRoot.replace('0', '1'),
};
when(db.get)
.calledWith(`blocks:height:${formatInt(1)}`)
Expand Down Expand Up @@ -290,7 +290,7 @@ describe('chain', () => {
const blockJSON = {
totalFee: '10000000',
totalAmount: '1',
payloadHash:
transactionRoot:
'564352bc451aca0e2aeca2aebf7a3d7af18dbac73eaa31623971bfc63d20339c',
payloadLength: 117,
numberOfTransactions: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ describe('data_access', () => {
const blockJSON = {
totalFee: '10000000',
totalAmount: '1',
payloadHash:
transactionRoot:
'564352bc451aca0e2aeca2aebf7a3d7af18dbac73eaa31623971bfc63d20339c',
payloadLength: 117,
numberOfTransactions: 1,
Expand Down
8 changes: 4 additions & 4 deletions elements/lisk-chain/test/unit/process.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ describe('blocks/header', () => {
epochTime: new Date(Date.UTC(2016, 4, 24, 17, 0, 0, 0)).toISOString(),
};
const networkIdentifier = getNetworkIdentifier(
genesisBlock.payloadHash,
genesisBlock.transactionRoot,
genesisBlock.communityIdentifier,
);

Expand Down Expand Up @@ -170,7 +170,7 @@ describe('blocks/header', () => {
});
});

describe('when payload hash is incorrect', () => {
describe('when transaction root is incorrect', () => {
it('should throw error', () => {
// Arrange
const txs = new Array(20).fill(0).map(() =>
Expand All @@ -187,12 +187,12 @@ describe('blocks/header', () => {
}) as TransactionJSON,
),
);
block = newBlock({ transactions: txs, payloadHash: '1234567890' });
block = newBlock({ transactions: txs, transactionRoot: '1234567890' });
blockBytes = getBytes(block);
// Act & assert
expect(() =>
chainInstance.validateBlockHeader(block, blockBytes),
).toThrow('Invalid payload hash');
).toThrow('Invalid transaction root');
});
});

Expand Down
2 changes: 1 addition & 1 deletion elements/lisk-chain/test/unit/transactions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('blocks/transactions', () => {
epochTime: new Date(Date.UTC(2016, 4, 24, 17, 0, 0, 0)).toISOString(),
};
const networkIdentifier = getNetworkIdentifier(
genesisBlock.payloadHash,
genesisBlock.transactionRoot,
genesisBlock.communityIdentifier,
);

Expand Down
Loading