diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 54c0ddebb..456de3cb6 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -169,6 +169,36 @@ func (pool *TxPool) Stats() (pending int, queued int) { return } +// Content retrieves the data content of the transaction pool, returning all the +// pending as well as queued transactions, grouped by account and nonce. +func (pool *TxPool) Content() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) { + pool.mu.RLock() + defer pool.mu.RUnlock() + + // Retrieve all the pending transactions and sort by account and by nonce + pending := make(map[common.Address]map[uint64][]*types.Transaction) + for _, tx := range pool.pending { + account, _ := tx.From() + + owned, ok := pending[account] + if !ok { + owned = make(map[uint64][]*types.Transaction) + pending[account] = owned + } + owned[tx.Nonce()] = append(owned[tx.Nonce()], tx) + } + // Retrieve all the queued transactions and sort by account and by nonce + queued := make(map[common.Address]map[uint64][]*types.Transaction) + for account, txs := range pool.queue { + owned := make(map[uint64][]*types.Transaction) + for _, tx := range txs { + owned[tx.Nonce()] = append(owned[tx.Nonce()], tx) + } + queued[account] = owned + } + return pending, queued +} + // SetLocal marks a transaction as local, skipping gas price // check against local miner minimum in the future func (pool *TxPool) SetLocal(tx *types.Transaction) { diff --git a/eth/api.go b/eth/api.go index 08b103a4c..0f55c60a9 100644 --- a/eth/api.go +++ b/eth/api.go @@ -227,6 +227,39 @@ func NewPublicTxPoolAPI(e *Ethereum) *PublicTxPoolAPI { return &PublicTxPoolAPI{e} } +// Content returns the transactions contained within the transaction pool. +func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string][]*RPCTransaction { + content := map[string]map[string]map[string][]*RPCTransaction{ + "pending": make(map[string]map[string][]*RPCTransaction), + "queued": make(map[string]map[string][]*RPCTransaction), + } + pending, queue := s.e.TxPool().Content() + + // Flatten the pending transactions + for account, batches := range pending { + dump := make(map[string][]*RPCTransaction) + for nonce, txs := range batches { + nonce := fmt.Sprintf("%d", nonce) + for _, tx := range txs { + dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx)) + } + } + content["pending"][account.Hex()] = dump + } + // Flatten the queued transactions + for account, batches := range queue { + dump := make(map[string][]*RPCTransaction) + for nonce, txs := range batches { + nonce := fmt.Sprintf("%d", nonce) + for _, tx := range txs { + dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx)) + } + } + content["queued"][account.Hex()] = dump + } + return content +} + // Status returns the number of pending and queued transaction in the pool. func (s *PublicTxPoolAPI) Status() map[string]*rpc.HexNumber { pending, queue := s.e.TxPool().Stats() @@ -236,6 +269,47 @@ func (s *PublicTxPoolAPI) Status() map[string]*rpc.HexNumber { } } +// Inspect retrieves the content of the transaction pool and flattens it into an +// easily inspectable list. +func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string][]string { + content := map[string]map[string]map[string][]string{ + "pending": make(map[string]map[string][]string), + "queued": make(map[string]map[string][]string), + } + pending, queue := s.e.TxPool().Content() + + // Define a formatter to flatten a transaction into a string + var format = func(tx *types.Transaction) string { + if to := tx.To(); to != nil { + return fmt.Sprintf("%s: %v wei + %v × %v gas", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice()) + } + return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice()) + } + // Flatten the pending transactions + for account, batches := range pending { + dump := make(map[string][]string) + for nonce, txs := range batches { + nonce := fmt.Sprintf("%d", nonce) + for _, tx := range txs { + dump[nonce] = append(dump[nonce], format(tx)) + } + } + content["pending"][account.Hex()] = dump + } + // Flatten the queued transactions + for account, batches := range queue { + dump := make(map[string][]string) + for nonce, txs := range batches { + nonce := fmt.Sprintf("%d", nonce) + for _, tx := range txs { + dump[nonce] = append(dump[nonce], format(tx)) + } + } + content["queued"][account.Hex()] = dump + } + return content +} + // PublicAccountAPI provides an API to access accounts managed by this node. // It offers only methods that can retrieve accounts. type PublicAccountAPI struct { diff --git a/rpc/javascript.go b/rpc/javascript.go index 9e3b2218b..c145163e5 100644 --- a/rpc/javascript.go +++ b/rpc/javascript.go @@ -70,9 +70,17 @@ web3._extend({ ], properties: [ + new web3._extend.Property({ + name: 'content', + getter: 'txpool_content' + }), + new web3._extend.Property({ + name: 'inspect', + getter: 'txpool_inspect' + }), new web3._extend.Property({ name: 'status', - getter: 'txpool_status' + getter: 'txpool_status', outputFormatter: function(status) { status.pending = web3._extend.utils.toDecimal(status.pending); status.queued = web3._extend.utils.toDecimal(status.queued);