import { Transaction } from '@uniqys/blockchain' ; import { Hash } from '@uniqys/signature' ; import debug from 'debug' ; const logger = debug ( 'chain-core:tx-pool' ) ; export interface TransactionPoolOptions { maxPooledTransactions : number ; } export namespace TransactionPoolOptions { export const defaults = { maxPooledTransactions : 1000 } ; } export class TransactionPool { private readonly options : TransactionPoolOptions ; private pool = new Map < string , Transaction > ( ) ; constructor ( private readonly validator , private readonly selector , private readonly propagator , options ? ) { this . options = Object . assign ( { } , TransactionPoolOptions . defaults , options ) ; } public has ( hash ) { return this . pool . has ( hash . toHexString ( ) ) ; } public async add ( tx ) { const key = tx . hash . toHexString ( ) ; if ( ! this . canAdd ( key ) ) return ; if ( await this . validator ( tx ) ) { if ( ! this . canAdd ( key ) ) return ; this . pool . set ( key , tx ) ; this . propagator ( tx ) ; } } public async update ( executed ) { executed . forEach ( tx => this . pool . delete ( tx . hash . toHexString ( ) ) ) ; const pooled = Array . from ( this . pool . values ( ) ) ; await Promise . all ( pooled . map ( tx => this . validator ( tx ) . then ( ok => { if ( ! ok ) { this . pool . delete ( tx . hash . toHexString ( ) ) ; } } ) ) ) ; } public selectTransactions ( ) { return this . pool . size === 0 ? Promise . resolve ( [ ] ) : this . selector ( Array . from ( this . pool . values ( ) ) ) ; } private canAdd ( key ) { if ( this . pool . has ( key ) ) return false ; if ( this . pool . size >= this . options . maxPooledTransactions ) { logger ( 'transaction pool is full' ) ; return false ; } return true ; } }