UNPKG

lisk-framework

Version:

Lisk blockchain application platform

187 lines 8.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NetworkEndpoint = void 0; const lisk_codec_1 = require("@liskhq/lisk-codec"); const lisk_validator_1 = require("@liskhq/lisk-validator"); const lisk_utils_1 = require("@liskhq/lisk-utils"); const lisk_chain_1 = require("@liskhq/lisk-chain"); const events_1 = require("events"); const base_network_endpoint_1 = require("../network/base_network_endpoint"); const constants_1 = require("./constants"); const schemas_1 = require("./schemas"); const errors_1 = require("./errors"); const abi_1 = require("../../abi"); class NetworkEndpoint extends base_network_endpoint_1.BaseNetworkEndpoint { constructor(args) { super(args.network); this.event = new events_1.EventEmitter(); this._pool = args.pool; this._chain = args.chain; this._abi = args.abi; this._broadcaster = args.broadcaster; } init(args) { this._logger = args.logger; } async handleRPCGetTransactions(data, peerId) { this.addRateLimit(constants_1.NETWORK_RPC_GET_TRANSACTIONS, peerId, constants_1.DEFAULT_RATE_LIMIT_FREQUENCY); let decodedData = { transactionIds: [] }; if (Buffer.isBuffer(data)) { decodedData = lisk_codec_1.codec.decode(schemas_1.getTransactionRequestSchema, data); try { lisk_validator_1.validator.validate(schemas_1.getTransactionRequestSchema, decodedData); } catch (error) { this._logger.warn({ err: error, peerId }, 'Received invalid getTransactions body. Applying a penalty to the peer'); this.network.applyPenaltyOnPeer({ peerId, penalty: 100, }); throw error; } if (!lisk_utils_1.objects.bufferArrayUniqueItems(decodedData.transactionIds)) { this._logger.warn({ peerId }, 'Received invalid getTransactions body. Applying a penalty to the peer'); this.network.applyPenaltyOnPeer({ peerId, penalty: 100, }); throw new Error('Received invalid getTransactions body'); } } const { transactionIds } = decodedData; if (!(transactionIds === null || transactionIds === void 0 ? void 0 : transactionIds.length)) { const transactionsBySender = this._pool.getProcessableTransactions(); const transactions = transactionsBySender .values() .flat() .map(tx => tx.getBytes()); transactions.splice(constants_1.DEFAULT_RELEASE_LIMIT); return lisk_codec_1.codec.encode(schemas_1.getTransactionsResponseSchema, { transactions, }); } if (transactionIds.length > constants_1.DEFAULT_RELEASE_LIMIT) { const error = new Error(`Requested number of transactions ${transactionIds.length} exceeds maximum allowed.`); this._logger.warn({ err: error, peerId }, 'Received invalid request. Applying a penalty to the peer'); this.network.applyPenaltyOnPeer({ peerId, penalty: 100, }); throw error; } const transactionsFromQueues = []; const idsNotInPool = []; for (const id of transactionIds) { const transaction = this._pool.get(id); if (transaction) { transactionsFromQueues.push(transaction.getBytes()); } else { idsNotInPool.push(id); } } if (idsNotInPool.length) { const transactionsFromDatabase = await this._chain.dataAccess.getTransactionsByIDs(idsNotInPool); return lisk_codec_1.codec.encode(schemas_1.getTransactionsResponseSchema, { transactions: transactionsFromQueues.concat(transactionsFromDatabase.map(t => t.getBytes())), }); } return lisk_codec_1.codec.encode(schemas_1.getTransactionsResponseSchema, { transactions: transactionsFromQueues, }); } async handleEventPostTransactionsAnnouncement(data, peerId) { this.addRateLimit(constants_1.NETWORK_EVENT_POST_TRANSACTIONS_ANNOUNCEMENT, peerId, constants_1.DEFAULT_RATE_LIMIT_FREQUENCY); if (!Buffer.isBuffer(data)) { const errorMessage = 'Received invalid transaction announcement data. Applying a penalty to the peer'; this._logger.warn({ peerId }, errorMessage); this.network.applyPenaltyOnPeer({ peerId, penalty: 100, }); throw new Error(errorMessage); } const decodedData = lisk_codec_1.codec.decode(schemas_1.postTransactionsAnnouncementSchema, data); try { lisk_validator_1.validator.validate(schemas_1.postTransactionsAnnouncementSchema, decodedData); } catch (error) { this._logger.warn({ err: error, peerId }, 'Received invalid transactions body. Applying a penalty to the peer'); this.network.applyPenaltyOnPeer({ peerId, penalty: 100, }); throw error; } this.event.emit(constants_1.GENERATOR_EVENT_NEW_TRANSACTION_ANNOUNCEMENT, decodedData); const unknownTransactionIDs = await this._obtainUnknownTransactionIDs(decodedData.transactionIds); if (unknownTransactionIDs.length > 0) { const transactionIdsBuffer = lisk_codec_1.codec.encode(schemas_1.getTransactionRequestSchema, { transactionIds: unknownTransactionIDs, }); const encodedData = (await this.network.requestFromPeer({ procedure: constants_1.NETWORK_RPC_GET_TRANSACTIONS, data: transactionIdsBuffer, peerId, })); const transactionsData = lisk_codec_1.codec.decode(schemas_1.getTransactionsResponseSchema, encodedData.data); try { for (const transactionBytes of transactionsData.transactions) { const transaction = lisk_chain_1.Transaction.fromBytes(transactionBytes); transaction.validate(); await this._receiveTransaction(transaction); } } catch (err) { if (err instanceof errors_1.InvalidTransactionError) { this._logger.debug({ err, peerId }, 'Received invalid transactions.'); return; } this._logger.warn({ err, peerId }, 'Received invalid transactions. Applying a penalty to the peer'); this.network.applyPenaltyOnPeer({ peerId, penalty: 100, }); } } } async _receiveTransaction(transaction) { var _a; const { result } = await this._abi.verifyTransaction({ contextID: constants_1.EMPTY_BUFFER, transaction: transaction.toObject(), header: this._chain.lastBlock.header.toObject(), onlyCommand: false, }); if (result === abi_1.TransactionVerifyResult.INVALID) { throw new errors_1.InvalidTransactionError('Transaction verification failed.', transaction.id); } if (this._pool.contains(transaction.id)) { return; } this._broadcaster.enqueueTransactionId(transaction.id); const { error } = await this._pool.add(transaction); if (!error) { this.event.emit(constants_1.GENERATOR_EVENT_NEW_TRANSACTION, { transaction: transaction.toJSON() }); this._logger.info({ id: transaction.id, nonce: transaction.nonce.toString(), senderPublicKey: transaction.senderPublicKey, }, 'Added transaction to pool'); return; } this._logger.error({ err: error }, 'Failed to add transaction to pool.'); throw new errors_1.InvalidTransactionError((_a = error.message) !== null && _a !== void 0 ? _a : 'Transaction verification failed.', transaction.id); } async _obtainUnknownTransactionIDs(ids) { const unknownTransactionsIDs = ids.filter(id => !this._pool.contains(id)); if (unknownTransactionsIDs.length) { const existingTransactions = await this._chain.dataAccess.getTransactionsByIDs(unknownTransactionsIDs); return unknownTransactionsIDs.filter(id => existingTransactions.find(existingTransaction => existingTransaction.id.equals(id)) === undefined); } return unknownTransactionsIDs; } } exports.NetworkEndpoint = NetworkEndpoint; //# sourceMappingURL=network_endpoint.js.map