UNPKG

lisk-framework

Version:

Lisk blockchain application platform

196 lines 9.87 kB
"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