lisk-framework
Version:
Lisk blockchain application platform
196 lines • 9.87 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LegacyChainHandler = void 0;
const lisk_codec_1 = require("@liskhq/lisk-codec");
const lisk_db_1 = require("@liskhq/lisk-db");
const schema_1 = require("../consensus/schema");
const storage_1 = require("./storage");
const codec_1 = require("./codec");
const errors_1 = require("./errors");
const validate_1 = require("./validate");
const constants_1 = require("./constants");
const schemas_1 = require("./schemas");
const constants_2 = require("../consensus/constants");
const wait = async (duration) => new Promise(resolve => {
const timeout = setTimeout(() => {
resolve(timeout);
}, duration);
});
class LegacyChainHandler {
constructor(args) {
this._syncedBrackets = [];
this._legacyConfig = args.legacyConfig;
this._network = args.network;
this._logger = args.logger;
}
async init(args) {
this._storage = new storage_1.Storage(args.db);
for (const bracketInfo of this._legacyConfig.brackets) {
try {
const bracketStorageKey = Buffer.from(bracketInfo.snapshotBlockID, 'hex');
const bracketExists = await this._storage.hasBracketInfo(bracketStorageKey);
if (!bracketExists) {
await this._storage.setBracketInfo(bracketStorageKey, {
startHeight: bracketInfo.startHeight,
snapshotBlockHeight: bracketInfo.snapshotHeight,
lastBlockHeight: bracketInfo.snapshotHeight,
});
continue;
}
const storedBracketInfo = await this._storage.getBracketInfo(bracketStorageKey);
const startBlock = await this._storage.getBlockByHeight(bracketInfo.startHeight);
await this._storage.setBracketInfo(bracketStorageKey, {
...storedBracketInfo,
startHeight: bracketInfo.startHeight,
snapshotBlockHeight: bracketInfo.snapshotHeight,
lastBlockHeight: startBlock ? bracketInfo.startHeight : bracketInfo.snapshotHeight,
});
}
catch (error) {
if (!(error instanceof lisk_db_1.NotFoundError)) {
throw error;
}
}
}
}
stop() {
clearTimeout(this._syncTimeout);
}
async sync() {
for (const bracket of this._legacyConfig.brackets) {
const bracketInfo = await this._storage.getBracketInfo(Buffer.from(bracket.snapshotBlockID, 'hex'));
if (bracket.startHeight === bracketInfo.lastBlockHeight) {
this._syncedBrackets.push(Buffer.from(bracket.snapshotBlockID, 'hex'));
this._network.applyNodeInfo({
legacy: [...this._syncedBrackets],
});
continue;
}
let lastBlockID;
try {
const lastBlock = (0, codec_1.decodeBlock)(await this._storage.getBlockByHeight(bracketInfo.lastBlockHeight)).block;
lastBlockID = lastBlock.header.id;
}
catch (error) {
if (!(error instanceof lisk_db_1.NotFoundError)) {
throw error;
}
lastBlockID = Buffer.from(bracket.snapshotBlockID, 'hex');
}
this._logger.info(constants_1.LOG_OBJECT_ENGINE_LEGACY_MODULE, `Started syncing legacy blocks for bracket with snapshotBlockID ${bracket.snapshotBlockID}`);
this._trySyncBlocks(bracket, lastBlockID).catch((err) => this._logger.error({ err }, 'Failed to sync block with error'));
}
}
async _trySyncBlocks(bracket, lastBlockID, syncRetryCounter = 0) {
try {
await this._syncBlocks(bracket, lastBlockID, syncRetryCounter);
}
catch (error) {
if (error instanceof errors_1.FailSyncError) {
this._logger.debug(constants_1.LOG_OBJECT_ENGINE_LEGACY_MODULE, `Retrying syncing legacy blocks for bracket with snapshotBlockID ${bracket.snapshotBlockID}`);
clearTimeout(this._syncTimeout);
this._syncTimeout = await wait(constants_1.FAILED_SYNC_RETRY_TIMEOUT);
}
else {
this._logger.debug({ ...constants_1.LOG_OBJECT_ENGINE_LEGACY_MODULE, error: error.message }, `Retrying syncing legacy blocks for bracket with snapshotBlockID ${bracket.snapshotBlockID}`);
}
await this._trySyncBlocks(bracket, lastBlockID);
}
}
async _syncBlocks(bracket, lastBlockID, failedAttempts = 0) {
const connectedPeers = this._network.getConnectedPeers();
const peersWithLegacyInfo = connectedPeers.filter(peer => !!peer.options.legacy.find(snapshotBlockID => snapshotBlockID === bracket.snapshotBlockID));
if (peersWithLegacyInfo.length === 0) {
const errorMessage = 'No peer found with legacy info.';
this._logger.warn({ ...constants_1.LOG_OBJECT_ENGINE_LEGACY_MODULE, method: 'syncBlocks' }, errorMessage);
throw new errors_1.FailSyncError(errorMessage);
}
const randomPeerIndex = Math.trunc(Math.random() * peersWithLegacyInfo.length - 1);
const { peerId } = peersWithLegacyInfo[randomPeerIndex];
const requestData = lisk_codec_1.codec.encode(schemas_1.getLegacyBlocksFromIdRequestSchema, {
blockID: lastBlockID,
snapshotBlockID: Buffer.from(bracket.snapshotBlockID, 'hex'),
});
const p2PRequestPacket = {
procedure: constants_2.NETWORK_LEGACY_GET_BLOCKS_FROM_ID,
data: requestData,
};
let syncRetryCounter = failedAttempts;
let response;
try {
response = await this._network.requestFromPeer({ ...p2PRequestPacket, peerId });
syncRetryCounter = 0;
}
catch (error) {
syncRetryCounter += 1;
if (syncRetryCounter > constants_1.MAX_FAILED_ATTEMPTS) {
const errorMessage = `Failed ${constants_1.MAX_FAILED_ATTEMPTS} times to request from peer.`;
this._logger.warn({ ...constants_1.LOG_OBJECT_ENGINE_LEGACY_MODULE, peerId, method: 'requestFromPeer' }, errorMessage);
throw new errors_1.FailSyncError(errorMessage);
}
return this._trySyncBlocks(bracket, lastBlockID, syncRetryCounter);
}
const { data } = response;
let legacyBlocks;
try {
const { blocks } = lisk_codec_1.codec.decode(schema_1.getBlocksFromIdResponseSchema, data);
if (blocks.length === 0) {
this.applyPenaltyOnSyncFailure('Received empty response', peerId);
return this._trySyncBlocks(bracket, lastBlockID, syncRetryCounter);
}
this._applyValidation(blocks);
legacyBlocks = blocks.map(block => (0, codec_1.decodeBlock)(block).block);
}
catch (err) {
this.applyPenaltyOnSyncFailure(err.message, peerId);
return this._trySyncBlocks(bracket, lastBlockID, syncRetryCounter);
}
for (const block of legacyBlocks) {
if (block.header.height >= bracket.startHeight) {
const payload = block.payload.length ? block.payload : [];
await this._storage.saveBlock(block.header.id, block.header.height, (0, codec_1.encodeBlockHeader)(block.header), payload);
}
}
const lastBlock = legacyBlocks[legacyBlocks.length - 1];
if (lastBlock && lastBlock.header.height > bracket.startHeight) {
this._logger.debug(constants_1.LOG_OBJECT_ENGINE_LEGACY_MODULE, `Saved blocks from ${legacyBlocks[0].header.height} to ${lastBlock.header.height}`);
await this._updateBracketInfo(lastBlock, bracket);
clearTimeout(this._syncTimeout);
this._syncTimeout = await wait(constants_1.SUCCESS_SYNC_RETRY_TIMEOUT);
await this._trySyncBlocks(bracket, lastBlock.header.id, syncRetryCounter);
}
else {
this._logger.info(constants_1.LOG_OBJECT_ENGINE_LEGACY_MODULE, `Finished syncing legacy blocks for bracket with snapshotBlockID ${bracket.snapshotBlockID}`);
this._syncedBrackets.push(Buffer.from(bracket.snapshotBlockID, 'hex'));
this._network.applyNodeInfo({
legacy: [...this._syncedBrackets],
});
}
return this._updateBracketInfo(lastBlock, bracket);
}
async _updateBracketInfo(lastBlock, bracket) {
await this._storage.setBracketInfo(Buffer.from(bracket.snapshotBlockID, 'hex'), {
startHeight: bracket.startHeight,
lastBlockHeight: lastBlock === null || lastBlock === void 0 ? void 0 : lastBlock.header.height,
snapshotBlockHeight: bracket.snapshotHeight,
});
}
applyPenaltyOnSyncFailure(msg, peerId) {
this._logger.warn({ ...constants_1.LOG_OBJECT_ENGINE_LEGACY_MODULE, peerId }, `${msg}: Applying a penalty to the peer`);
this._network.applyPenaltyOnPeer({ peerId, penalty: 100 });
}
_applyValidation(blocks) {
const sortedBlocks = [];
for (let i = blocks.length - 1; i >= 0; i -= 1) {
sortedBlocks.push(blocks[i]);
}
const sortedLegacyBlocks = sortedBlocks.map(block => (0, codec_1.decodeBlock)(block).block);
sortedBlocks.forEach((block, index) => {
if (index < sortedBlocks.length - 1) {
(0, validate_1.validateLegacyBlock)(block, sortedLegacyBlocks[index + 1]);
}
});
}
}
exports.LegacyChainHandler = LegacyChainHandler;
//# sourceMappingURL=legacy_chain_handler.js.map