Home Reference Source Test

src/main/generic/consensus/nano/NanoMempool.js

/**
 * @deprecated
 */
class NanoMempool extends Observable {
    /**
     * @param {IBlockchain} blockchain
     */
    constructor(blockchain) {
        super();

        /** @type {IBlockchain} */
        this._blockchain = blockchain;

        // Our pool of transactions.
        /** @type {HashMap.<Hash, Transaction>} */
        this._transactionsByHash = new HashMap();
        /** @type {HashMap.<Address, MempoolTransactionSet>} */
        this._transactionSetBySender = new HashMap();
        /** @type {HashMap.<Address, HashSet.<Transaction>>} */
        this._transactionSetByRecipient = new HashMap();
    }

    /**
     * @param {Transaction} transaction
     * @fires Mempool#transaction-added
     * @returns {Promise.<Mempool.ReturnCode>}
     */
    async pushTransaction(transaction) {
        // Check if we already know this transaction.
        const hash = transaction.hash();
        if (this._transactionsByHash.contains(hash)) {
            Log.v(Mempool, () => `Ignoring known transaction ${hash.toBase64()}`);
            return Mempool.ReturnCode.KNOWN;
        }

        // Check validity based on startHeight.
        if (this._blockchain.height >= transaction.validityStartHeight + Policy.TRANSACTION_VALIDITY_WINDOW) {
            Log.v(Mempool, () => `Ignoring expired transaction ${hash.toBase64()}`);
            return Mempool.ReturnCode.EXPIRED;
        }

        // Verify transaction.
        if (!transaction.verify()) {
            return Mempool.ReturnCode.INVALID;
        }

        // Transaction is valid, add it to the mempool.
        this._transactionsByHash.put(hash, transaction);
        const set = this._transactionSetBySender.get(transaction.sender) || new MempoolTransactionSet();
        set.add(transaction);
        this._transactionSetBySender.put(transaction.sender, set);
        const recs = this._transactionSetByRecipient.get(transaction.recipient) || new HashSet();
        recs.add(transaction);
        this._transactionSetByRecipient.put(transaction.recipient, recs);

        // Tell listeners about the new transaction we received.
        this.fire('transaction-added', transaction);

        return Mempool.ReturnCode.ACCEPTED;
    }

    /**
     * @param {Hash} hash
     * @returns {Transaction}
     */
    getTransaction(hash) {
        return this._transactionsByHash.get(hash);
    }

    /**
     * @param {number} maxCount
     * @returns {Array.<Transaction>}
     */
    getTransactions(maxCount = 5000) {
        return this._transactionsByHash.values().sort((a, b) => a.compare(b)).slice(0, maxCount);
    }

    /**
     * @param {Address} address
     * @return {Array.<Transaction>}
     */
    getPendingTransactions(address) {
        return this.getTransactionsBySender(address);
    }

    /**
     * @param {Address} address
     * @return {Array.<Transaction>}
     */
    getTransactionsBySender(address) {
        /** @type {MempoolTransactionSet} */
        const set = this._transactionSetBySender.get(address);
        return set ? set.transactions : [];
    }

    /**
     * @param {Address} address
     * @return {Array.<Transaction>}
     */
    getTransactionsByRecipient(address) {
        /** @type {HashSet.<Transaction>} */
        const set = this._transactionSetByRecipient.get(address);
        if (!set) {
            return [];
        }

        return set.values();
    }

    /**
     * @param {Array.<Address>} addresses
     * @param {number} [maxTransactions]
     * @return {Array.<Transaction>}
     */
    getTransactionsByAddresses(addresses, maxTransactions = Infinity) {
        const transactions = [];
        for (const address of addresses) {
            // Fetch transactions by sender first
            /** @type {Array.<Transaction>} */
            const bySender = this.getTransactionsBySender(address);
            for (const tx of bySender) {
                if (transactions.length >= maxTransactions) return transactions;
                transactions.push(tx);
            }

            // Fetch transactions by recipient second
            /** @type {Array.<Transaction>} */
            const byRecipient = this.getTransactionsByRecipient(address);
            for (const tx of byRecipient) {
                if (transactions.length >= maxTransactions) return transactions;
                transactions.push(tx);
            }
        }
        return transactions;
    }

    /**
     * @param {Block} block
     * @param {Array.<Transaction>} transactions
     */
    async changeHead(block, transactions) {
        await this._evictTransactions(block, transactions);
    }

    /**
     * @param {Transaction} transaction
     */
    removeTransaction(transaction) {
        this._transactionsByHash.remove(transaction.hash());

        /** @type {MempoolTransactionSet} */
        const set = this._transactionSetBySender.get(transaction.sender);
        set.remove(transaction);

        if (set.length === 0) {
            this._transactionSetBySender.remove(transaction.sender);
        }

        const recs = this._transactionSetByRecipient.get(transaction.recipient);
        recs.remove(transaction);

        if (recs.length === 0) {
            this._transactionSetByRecipient.remove(transaction.recipient);
        }

        this.fire('transaction-removed', transaction);
    }

    /**
     * @param {Array.<Address>} addresses
     */
    evictExceptAddresses(addresses) {
        const addressSet = new HashSet();
        addressSet.addAll(addresses);
        for (const /** @type {Transaction} */ tx of this._transactionsByHash.values()) {
            if (!addressSet.contains(tx.sender) && !addressSet.contains(tx.recipient)) {
                this.removeTransaction(tx);
            }
        }
    }

    /**
     * @param {Block} block
     * @param {Array.<Transaction>} transactions
     * @private
     */
    async _evictTransactions(block, transactions) {
        // Remove expired transactions.
        for (const /** @type {Transaction} */ tx of this._transactionsByHash.values()) {
            if (block.height >= tx.validityStartHeight + Policy.TRANSACTION_VALIDITY_WINDOW) {
                this.removeTransaction(tx);

                this.fire('transaction-expired', tx);
            }
        }

        // Remove mined transactions.
        for (const /** @type {Transaction} */ tx of transactions) {
            const txHash = tx.hash();
            if (this._transactionsByHash.contains(txHash)) {
                this.removeTransaction(tx);

                this.fire('transaction-mined', tx, block);
            }
        }
    }

    /** @type {number} */
    get length() {
        return this._transactionsByHash.length;
    }
}
Class.register(NanoMempool);