Skip to content

Commit

Permalink
Merge feature/7136-support-legacy-blocks-and-transactions to developm…
Browse files Browse the repository at this point in the history
…ent (#7680)

Closes #7136

🌱 Introduces support legacy blocks and transactions
  • Loading branch information
ishantiw committed Oct 24, 2022
2 parents 3f5c702 + 05a3907 commit aaa4bf0
Show file tree
Hide file tree
Showing 31 changed files with 2,114 additions and 63 deletions.
13 changes: 13 additions & 0 deletions framework/src/engine/consensus/consensus.ts
Expand Up @@ -37,6 +37,7 @@ import { AbortError, ApplyPenaltyAndRestartError, RestartError } from './synchro
import { BlockExecutor } from './synchronizer/type';
import { Network } from '../network';
import { NetworkEndpoint, EndpointArgs } from './network_endpoint';
import { LegacyNetworkEndpoint } from '../legacy/network_endpoint';
import { EventPostBlockData, postBlockEventSchema } from './schema';
import {
CONSENSUS_EVENT_BLOCK_BROADCAST,
Expand All @@ -52,6 +53,7 @@ import {
NETWORK_RPC_GET_BLOCKS_FROM_ID,
NETWORK_RPC_GET_HIGHEST_COMMON_BLOCK,
NETWORK_RPC_GET_LAST_BLOCK,
NETWORK_LEGACY_GET_BLOCKS_FROM_ID,
} from './constants';
import { GenesisConfig } from '../../types';
import { AggregateCommit } from './types';
Expand All @@ -74,6 +76,7 @@ interface InitArgs {
logger: Logger;
genesisBlock: Block;
db: Database;
legacyDB: Database;
}

interface ExecuteOptions {
Expand Down Expand Up @@ -109,6 +112,7 @@ export class Consensus {
private _db!: Database;
private _commitPool!: CommitPool;
private _endpoint!: NetworkEndpoint;
private _legacyEndpoint!: LegacyNetworkEndpoint;
private _synchronizer!: Synchronizer;
private _blockSlot!: Slots;

Expand Down Expand Up @@ -140,6 +144,11 @@ export class Consensus {
network: this._network,
db: this._db,
} as EndpointArgs); // TODO: Remove casting in issue where commitPool is added here
this._legacyEndpoint = new LegacyNetworkEndpoint({
logger: this._logger,
network: this._network,
db: args.legacyDB,
});
const blockExecutor = this._createBlockExecutor();
const blockSyncMechanism = new BlockSynchronizationMechanism({
chain: this._chain,
Expand All @@ -164,6 +173,9 @@ export class Consensus {
interval: this._genesisConfig.blockTime,
});

this._network.registerEndpoint(NETWORK_LEGACY_GET_BLOCKS_FROM_ID, async ({ data, peerId }) =>
this._legacyEndpoint.handleRPCGetLegacyBlocksFromID(data, peerId),
);
this._network.registerEndpoint(NETWORK_RPC_GET_LAST_BLOCK, ({ peerId }) =>
this._endpoint.handleRPCGetLastBlock(peerId),
);
Expand Down Expand Up @@ -550,6 +562,7 @@ export class Consensus {
});
await this._executeValidated(block);

// Since legacy property is optional we don't need to send it here
this._network.applyNodeInfo({
height: block.header.height,
lastBlockID: block.header.id,
Expand Down
2 changes: 2 additions & 0 deletions framework/src/engine/consensus/constants.ts
Expand Up @@ -29,4 +29,6 @@ export const NETWORK_RPC_GET_BLOCKS_FROM_ID = 'getBlocksFromId';
export const NETWORK_RPC_GET_HIGHEST_COMMON_BLOCK = 'getHighestCommonBlock';
export const NETWORK_RPC_GET_SINGLE_COMMIT_FROM_ID = 'getSingleCommit';

export const NETWORK_LEGACY_GET_BLOCKS_FROM_ID = 'getLegacyBlocksFromId';

export const EMPTY_HASH = utils.hash(Buffer.alloc(0));
27 changes: 27 additions & 0 deletions framework/src/engine/engine.ts
Expand Up @@ -45,6 +45,8 @@ import { GENERATOR_EVENT_NEW_TRANSACTION_ANNOUNCEMENT } from './generator/consta
import { ConsensusEndpoint } from './endpoint/consensus';
import { EngineConfig } from '../types';
import { readGenesisBlock } from '../utils/genesis_block';
import { LegacyChainHandler } from './legacy/legacy_chain_handler';
import { LegacyEndpoint } from './legacy/endpoint';

const isEmpty = (value: unknown): boolean => {
switch (typeof value) {
Expand Down Expand Up @@ -82,11 +84,13 @@ export class Engine {
private _network!: Network;
private _chain!: Chain;
private _bftModule!: BFTModule;
private _legacyChainHandler!: LegacyChainHandler;
private _rpcServer!: RPCServer;
private _logger!: Logger;
private _nodeDB!: Database;
private _generatorDB!: Database;
private _blockchainDB!: Database;
private _legacyDB!: Database;
private _chainID!: Buffer;

public constructor(abi: ABI, config: EngineConfig) {
Expand All @@ -101,6 +105,11 @@ export class Engine {
public async start() {
await this._init();
await this._network.start();
if (this._config.legacy.sync) {
this._logger.info('Legacy block sync started');
await this._legacyChainHandler.sync();
this._logger.info('Legacy block sync completed');
}
await this._generator.start();
await this._consensus.start();
await this._rpcServer.start();
Expand Down Expand Up @@ -153,6 +162,10 @@ export class Engine {
config: this._config,
network: this._network,
});
this._legacyChainHandler = new LegacyChainHandler({
legacyConfig: this._config.legacy,
network: this._network,
});
this._rpcServer = new RPCServer(this._config.system.dataPath, this._config.rpc);

const genesis = readGenesisBlock(this._config, this._logger);
Expand All @@ -163,6 +176,7 @@ export class Engine {
this._generatorDB = new Database(
path.join(this._config.system.dataPath, 'data', 'generator.db'),
);
this._legacyDB = new Database(path.join(this._config.system.dataPath, 'data', 'legacy.db'));
this._nodeDB = new Database(path.join(this._config.system.dataPath, 'data', 'node.db'));

this._chainID = Buffer.from(this._config.genesis.chainID, 'hex');
Expand All @@ -181,19 +195,28 @@ export class Engine {
db: this._blockchainDB,
genesisBlock: genesis,
logger: this._logger,
legacyDB: this._legacyDB,
});
await this._generator.init({
blockchainDB: this._blockchainDB,
generatorDB: this._generatorDB,
logger: this._logger,
});
await this._legacyChainHandler.init({
db: this._legacyDB,
});

this._registerEventListeners();

this._rpcServer.init({
logger: this._logger,
chainID: this._chain.chainID,
});

const legacyEndpoint = new LegacyEndpoint({
db: this._legacyDB,
});

const chainEndpoint = new ChainEndpoint({
chain: this._chain,
genesisBlockTimestamp: genesis.header.timestamp,
Expand All @@ -220,6 +243,9 @@ export class Engine {
blockchainDB: this._blockchainDB,
});

for (const [name, handler] of Object.entries(getEndpointHandlers(legacyEndpoint))) {
this._rpcServer.registerEndpoint('legacy', name, handler);
}
for (const [name, handler] of Object.entries(getEndpointHandlers(chainEndpoint))) {
this._rpcServer.registerEndpoint('chain', name, handler);
}
Expand Down Expand Up @@ -311,6 +337,7 @@ export class Engine {
private _closeDB(): void {
this._blockchainDB.close();
this._generatorDB.close();
this._legacyDB.close();
this._nodeDB.close();
}
}
94 changes: 94 additions & 0 deletions framework/src/engine/legacy/codec.ts
@@ -0,0 +1,94 @@
/*
* Copyright © 2022 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*/

import { codec, Schema } from '@liskhq/lisk-codec';
import { utils } from '@liskhq/lisk-cryptography';
import { blockHeaderSchemaV2, blockSchemaV2, legacyChainBracketInfoSchema } from './schemas';
import {
LegacyBlock,
LegacyBlockJSON,
LegacyChainBracketInfo,
RawLegacyBlock,
LegacyBlockWithID,
LegacyBlockHeaderWithID,
} from './types';

interface LegacyBlockSchema {
header: Schema;
block: Schema;
}

export const blockSchemaMap: Record<number, LegacyBlockSchema> = {
2: {
block: blockSchemaV2,
header: blockHeaderSchemaV2,
},
};

// Implement read version logic when adding more versions
const readVersion = (): number => 2;

export const decodeBlock = (
data: Buffer,
): { block: LegacyBlockWithID; schema: LegacyBlockSchema } => {
const version = readVersion();
const blockSchema = blockSchemaMap[version];
if (!blockSchema) {
throw new Error(`Legacy block version ${version} is not registered.`);
}
const rawBlock = codec.decode<RawLegacyBlock>(blockSchema.block, data);
const id = utils.hash(rawBlock.header);
return {
block: {
...rawBlock,
header: {
...codec.decode<LegacyBlockHeaderWithID>(blockSchema.header, rawBlock.header),
id,
},
},
schema: blockSchema,
};
};

export const decodeBlockJSON = (
data: Buffer,
): { block: LegacyBlockJSON; schema: LegacyBlockSchema } => {
const { block, schema } = decodeBlock(data);
return {
block: {
header: {
...codec.toJSON(schema.header, block.header),
id: block.header.id.toString('hex'),
},
payload: block.payload.map(tx => tx.toString('hex')),
},
schema,
};
};

export const encodeBlock = (data: LegacyBlock): Buffer => {
const blockSchema = blockSchemaMap[data.header.version];
if (!blockSchema) {
throw new Error(`Legacy block version ${data.header.version} is not registered.`);
}
const headerBytes = codec.encode(blockSchema.header, data.header);

return codec.encode(blockSchema.block, {
header: headerBytes,
payload: data.payload,
});
};

export const encodeLegacyChainBracketInfo = (data: LegacyChainBracketInfo): Buffer =>
codec.encode(legacyChainBracketInfoSchema, data);
17 changes: 17 additions & 0 deletions framework/src/engine/legacy/constants.ts
@@ -0,0 +1,17 @@
/*
* Copyright © 2022 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*/

export const DB_KEY_BLOCK_ID = Buffer.from([0]);
export const DB_KEY_BLOCK_HEIGHT = Buffer.from([1]);
export const DB_KEY_LEGACY_BRACKET = Buffer.from([2]);
51 changes: 51 additions & 0 deletions framework/src/engine/legacy/endpoint.ts
@@ -0,0 +1,51 @@
/*
* Copyright © 2022 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*/

import { Database } from '@liskhq/lisk-db';
import { isHexString } from '@liskhq/lisk-validator';
import { RequestContext } from '../rpc/rpc_server';
import { LegacyBlockJSON } from './types';
import { Storage } from './storage';
import { decodeBlockJSON } from './codec';

interface EndpointArgs {
db: Database;
}

export class LegacyEndpoint {
[key: string]: unknown;
public readonly storage: Storage;

public constructor(args: EndpointArgs) {
this.storage = new Storage(args.db);
}

public async getBlockByID(context: RequestContext): Promise<LegacyBlockJSON> {
const { id } = context.params;
if (!isHexString(id)) {
throw new Error('Invalid parameters. `id` must be a valid hex string.');
}

return decodeBlockJSON(await this.storage.getBlockByID(Buffer.from(id as string, 'hex'))).block;
}

public async getBlockByHeight(context: RequestContext): Promise<LegacyBlockJSON> {
const { height } = context.params;
if (typeof height !== 'number' || height < 0) {
throw new Error('Invalid parameters. `height` must be zero or a positive number.');
}

return decodeBlockJSON(await this.storage.getBlockByHeight(height)).block;
}
}
20 changes: 20 additions & 0 deletions framework/src/engine/legacy/errors.ts
@@ -0,0 +1,20 @@
/*
* Copyright © 2022 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*/

export class PeerNotFoundWithLegacyInfo extends Error {
public constructor(message: string) {
super(message);
this.name = this.constructor.name;
}
}
13 changes: 13 additions & 0 deletions framework/src/engine/legacy/index.ts
@@ -0,0 +1,13 @@
/*
* Copyright © 2022 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*/

0 comments on commit aaa4bf0

Please sign in to comment.