lisk-framework
Version:
Lisk blockchain application platform
258 lines • 12.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBlockProcessingEnv = void 0;
const fs = require("fs-extra");
const path = require("path");
const os = require("os");
const lisk_chain_1 = require("@liskhq/lisk-chain");
const lisk_db_1 = require("@liskhq/lisk-db");
const lisk_utils_1 = require("@liskhq/lisk-utils");
const lisk_codec_1 = require("@liskhq/lisk-codec");
const mocks_1 = require("./mocks");
const fixtures_1 = require("./fixtures");
const utils_1 = require("./utils");
const state_machine_1 = require("../state_machine");
const engine_1 = require("../engine");
const method_context_1 = require("../state_machine/method_context");
const genesis_asset_1 = require("./fixtures/genesis-asset");
const validators_1 = require("../modules/validators");
const token_1 = require("../modules/token");
const auth_1 = require("../modules/auth");
const fee_1 = require("../modules/fee");
const random_1 = require("../modules/random");
const pos_1 = require("../modules/pos");
const abi_handler_1 = require("../abi_handler/abi_handler");
const genesis_block_1 = require("../genesis_block");
const system_dirs_1 = require("../system_dirs");
const prefixed_state_read_writer_1 = require("../state_machine/prefixed_state_read_writer");
const logger_1 = require("../logger");
const interoperability_1 = require("../modules/interoperability");
const dynamic_reward_1 = require("../modules/dynamic_reward");
const getAppConfig = (genesisConfig, moduleConfig) => {
const mergedConfig = lisk_utils_1.objects.mergeDeep({}, {
...fixtures_1.defaultConfig,
system: {
...fixtures_1.defaultConfig.system,
dataPath: path.join(os.tmpdir(), `lisk-framework-test-${Date.now().toString()}`),
},
genesis: {
...fixtures_1.defaultConfig.genesis,
...(genesisConfig !== null && genesisConfig !== void 0 ? genesisConfig : {}),
},
modules: {
...(moduleConfig !== null && moduleConfig !== void 0 ? moduleConfig : {}),
},
});
return mergedConfig;
};
const getNextTimestamp = (engine, previousBlock, distance = 1) => {
const previousSlotNumber = engine['_consensus']['_bft'].method.getSlotNumber(previousBlock.timestamp);
return engine['_consensus']['_bft'].method.getSlotTime(previousSlotNumber + distance);
};
const createProcessableBlock = async (engine, transactions, timestamp) => {
engine['_logger'].info({ numberOfTransactions: transactions.length }, 'Start generating block');
const stateStore = new lisk_chain_1.StateStore(engine['_blockchainDB']);
const previousBlockHeader = engine['_chain'].lastBlock.header;
const nextTimestamp = timestamp !== null && timestamp !== void 0 ? timestamp : getNextTimestamp(engine, previousBlockHeader);
const generator = await engine['_consensus']['_bft'].method.getGeneratorAtTimestamp(stateStore, previousBlockHeader.height + 1, nextTimestamp);
const generatorPrivateKey = (0, fixtures_1.getGeneratorPrivateKeyFromDefaultConfig)(generator.address);
for (const tx of transactions) {
await engine['_generator']['_pool'].add(tx);
}
const block = await engine.generateBlock({
generatorAddress: generator.address,
height: previousBlockHeader.height + 1,
privateKey: generatorPrivateKey,
timestamp: nextTimestamp,
transactions,
});
engine['_logger'].info({ height: block.header.height, numberOfTransactions: block.transactions.length }, 'Generated block');
return block;
};
const getBlockProcessingEnv = async (params) => {
var _a, _b, _c, _d;
const chainID = Buffer.from('00000000', 'hex');
const appConfig = getAppConfig(((_a = params.options) === null || _a === void 0 ? void 0 : _a.genesis)
? {
...params.options.genesis,
chainID: chainID.toString('hex'),
}
: { chainID: chainID.toString('hex') }, (_c = (_b = params.options) === null || _b === void 0 ? void 0 : _b.modules) !== null && _c !== void 0 ? _c : {});
const systemDir = (0, system_dirs_1.systemDirs)(appConfig.system.dataPath);
(0, utils_1.removeDB)(systemDir.data);
const moduleDB = new lisk_db_1.Database(path.join(systemDir.data, 'module.db'));
const stateDB = new lisk_db_1.StateDB(path.join(systemDir.data, 'state.db'));
const validatorsModule = new validators_1.ValidatorsModule();
const authModule = new auth_1.AuthModule();
const tokenModule = new token_1.TokenModule();
const feeModule = new fee_1.FeeModule();
const rewardModule = new dynamic_reward_1.DynamicRewardModule();
const randomModule = new random_1.RandomModule();
const posModule = new pos_1.PoSModule();
const interopModule = new interoperability_1.MainchainInteroperabilityModule();
const modules = [
validatorsModule,
authModule,
tokenModule,
feeModule,
rewardModule,
randomModule,
posModule,
];
const stateMachine = new state_machine_1.StateMachine();
const logger = (0, logger_1.createLogger)({ name: 'blockProcessingEnv', logLevel: (_d = params.logLevel) !== null && _d !== void 0 ? _d : 'none' });
tokenModule.addDependencies(interopModule.method, feeModule.method);
feeModule.addDependencies(tokenModule.method, interopModule.method);
rewardModule.addDependencies(tokenModule.method, randomModule.method, validatorsModule.method, posModule.method);
posModule.addDependencies(randomModule.method, validatorsModule.method, tokenModule.method, feeModule.method);
stateMachine.registerModule(feeModule);
stateMachine.registerModule(authModule);
stateMachine.registerModule(validatorsModule);
stateMachine.registerModule(tokenModule);
stateMachine.registerModule(rewardModule);
stateMachine.registerModule(randomModule);
stateMachine.registerModule(posModule);
const blockAssets = genesis_asset_1.blockAssetsJSON.map(asset => ({
...asset,
data: lisk_codec_1.codec.fromJSON(asset.schema, asset.data),
}));
await stateMachine.init(logger, appConfig.genesis, appConfig.modules);
const genesisBlock = await (0, genesis_block_1.generateGenesisBlock)(stateMachine, logger, {
timestamp: Math.floor(Date.now() / 1000) - 60 * 60 * 12,
assets: blockAssets,
chainID,
});
const abiHandler = new abi_handler_1.ABIHandler({
channel: mocks_1.channelMock,
config: appConfig,
logger,
stateDB,
moduleDB,
modules,
stateMachine,
chainID,
});
appConfig.genesis.block.blob = genesisBlock.getBytes().toString('hex');
appConfig.generator.keys.fromFile = path.join(__dirname, './fixtures/keys_fixture.json');
const engine = new engine_1.Engine(abiHandler, appConfig);
await engine['_init']();
engine['_logger'] = logger;
engine['_consensus']['_logger'] = logger;
await abiHandler.init({
chainID,
lastBlockHeight: engine['_chain'].lastBlock.header.height,
lastStateRoot: engine['_chain'].lastBlock.header.stateRoot,
});
return {
createBlock: async (transactions = [], timestamp) => createProcessableBlock(engine, transactions, timestamp),
getGenesisBlock: () => genesisBlock,
getChain: () => engine['_chain'],
getConsensus: () => engine['_consensus'],
getConsensusStore: () => new lisk_chain_1.StateStore(engine['_blockchainDB']),
getGenerator: () => engine['_generator'],
getMethodContext: () => (0, method_context_1.createNewMethodContext)(stateDB.newReadWriter()),
getBlockchainDB: () => engine['_blockchainDB'],
process: async (block) => {
logger.debug({ height: block.header.height, numberOfTransactions: block.transactions.length }, 'Start executing block');
await engine['_consensus']['_execute'](block, 'peer-id');
logger.debug({ height: block.header.height, numberOfTransactions: block.transactions.length }, 'Executed block');
},
processUntilHeight: async (height) => {
while (engine['_chain'].lastBlock.header.height < height) {
const nextBlock = await createProcessableBlock(engine, []);
await engine['_consensus'].execute(nextBlock);
}
},
getLastBlock: () => engine['_chain'].lastBlock,
getNextValidatorKeys: async (previousBlockHeader, distance = 1) => {
const stateStore = new lisk_chain_1.StateStore(engine['_blockchainDB']);
const nextTimestamp = getNextTimestamp(engine, previousBlockHeader, distance);
const validator = await engine['_consensus']['_bft'].method.getGeneratorAtTimestamp(stateStore, previousBlockHeader.height + distance, nextTimestamp);
const keys = (0, fixtures_1.getKeysFromDefaultConfig)(validator.address);
return keys;
},
getEvents: async (height, module, name) => {
const handler = engine['_rpcServer']['_getHandler']('chain', 'getEvents');
if (!handler) {
throw new Error('invalid');
}
const resp = (await handler({
logger,
chainID: engine['_chain'].chainID,
params: { height },
}));
const eventMetadata = stateMachine['_modules']
.map(m => ({ module: m.name, ...m.metadata().events }))
.flat();
const results = [];
for (const event of resp) {
if (module && event.module !== module) {
continue;
}
if (name && event.name !== name) {
continue;
}
if (event.name === 'commandExecutionResult') {
results.push({
...event,
decodedData: lisk_codec_1.codec.decodeJSON(lisk_chain_1.standardEventDataSchema, Buffer.from(event.data, 'hex')),
});
continue;
}
const metadata = eventMetadata.find(e => e.module === event.module && e.name === event.name);
results.push({
...event,
decodedData: metadata
? lisk_codec_1.codec.decodeJSON(metadata.schema, Buffer.from(event.data, 'hex'))
: {},
});
}
return results;
},
async invoke(func, input = {}) {
const [namespace, method] = func.split('_');
const handler = engine['_rpcServer']['_getHandler'](namespace, method);
if (handler) {
const resp = (await handler({
logger,
chainID: engine['_chain'].chainID,
params: input,
}));
return resp;
}
const moduleIndex = modules.findIndex(mod => mod.name === namespace);
if (moduleIndex < 0) {
throw new Error(`namespace ${namespace} is not registered`);
}
const moduleHandler = modules[moduleIndex].endpoint[method];
if (!moduleHandler) {
throw new Error(`Method ${method} in namespace ${namespace} is not registered`);
}
const bindedHandler = moduleHandler.bind(modules[moduleIndex].endpoint);
const stateStore = new prefixed_state_read_writer_1.PrefixedStateReadWriter(stateDB.newReadWriter());
try {
const result = await bindedHandler({
getStore: (moduleID, storePrefix) => stateStore.getStore(moduleID, storePrefix),
getImmutableMethodContext: () => (0, method_context_1.createImmutableMethodContext)(stateStore),
logger: engine['_logger'],
chainID: engine['_chain'].chainID,
params: input,
});
return result;
}
finally {
stateStore.inner.close();
}
},
getChainID: () => chainID,
getDataAccess: () => engine['_chain'].dataAccess,
cleanup: (_val) => {
engine['_closeDB']();
moduleDB.close();
stateDB.close();
fs.removeSync(systemDir.data);
},
};
};
exports.getBlockProcessingEnv = getBlockProcessingEnv;
//# sourceMappingURL=block_processing_env.js.map