lisk-framework
Version:
Lisk blockchain application platform
187 lines • 8.9 kB
JavaScript
"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