From 8aaad1e97bdae678167fcca68fd7b89198952ec7 Mon Sep 17 00:00:00 2001 From: "Owain G. Ainsworth" Date: Wed, 11 Dec 2013 17:32:16 +0000 Subject: [PATCH] Add support for verbose in getrawmempool. Closes #55. --- mempool.go | 53 +++++++++++++++++++++++++++++++++++-------- rpcserver.go | 47 ++++++++++++++++++++++++++++++++++---- util/btcctl/btcctl.go | 8 +++++-- 3 files changed, 92 insertions(+), 16 deletions(-) diff --git a/mempool.go b/mempool.go index 201b30fb..5709fba1 100644 --- a/mempool.go +++ b/mempool.go @@ -78,13 +78,22 @@ const ( blockPrioritySize = 27000 ) +// TxDesc is a descriptor containing a transaction in the mempool and the +// metadata we store about it. +type TxDesc struct { + Tx *btcutil.Tx // Transaction. + Added time.Time // Time when added to pool. + Height int64 // Blockheight when added to pool. + Fee int64 // Transaction fees. +} + // txMemPool is used as a source of transactions that need to be mined into // blocks and relayed to other peers. It is safe for concurrent access from // multiple peers. type txMemPool struct { sync.RWMutex server *server - pool map[btcwire.ShaHash]*btcutil.Tx + pool map[btcwire.ShaHash]*TxDesc orphans map[btcwire.ShaHash]*btcutil.Tx orphansByPrev map[btcwire.ShaHash]*list.List outpoints map[btcwire.OutPoint]*btcutil.Tx @@ -581,8 +590,8 @@ func (mp *txMemPool) removeTransaction(tx *btcutil.Tx) { // Remove the transaction and mark the referenced outpoints as unspent // by the pool. - if tx, exists := mp.pool[*txHash]; exists { - for _, txIn := range tx.MsgTx().TxIn { + if txDesc, exists := mp.pool[*txHash]; exists { + for _, txIn := range txDesc.Tx.MsgTx().TxIn { delete(mp.outpoints, txIn.PreviousOutpoint) } delete(mp.pool, *txHash) @@ -627,10 +636,15 @@ func (mp *txMemPool) RemoveDoubleSpends(tx *btcutil.Tx) { // helper for maybeAcceptTransaction. // // This function MUST be called with the mempool lock held (for writes). -func (mp *txMemPool) addTransaction(tx *btcutil.Tx) { +func (mp *txMemPool) addTransaction(tx *btcutil.Tx, height, fee int64) { // Add the transaction to the pool and mark the referenced outpoints // as spent by the pool. - mp.pool[*tx.Sha()] = tx + mp.pool[*tx.Sha()] = &TxDesc{ + Tx: tx, + Added: time.Now(), + Height: height, + Fee: fee, + } for _, txIn := range tx.MsgTx().TxIn { mp.outpoints[txIn.PreviousOutpoint] = tx } @@ -668,7 +682,8 @@ func (mp *txMemPool) fetchInputTransactions(tx *btcutil.Tx) (btcchain.TxStore, e // Attempt to populate any missing inputs from the transaction pool. for _, txD := range txStore { if txD.Err == btcdb.TxShaMissing || txD.Tx == nil { - if poolTx, exists := mp.pool[*txD.Hash]; exists { + if poolTxDesc, exists := mp.pool[*txD.Hash]; exists { + poolTx := poolTxDesc.Tx txD.Tx = poolTx txD.BlockHeight = mempoolHeight txD.Spent = make([]bool, len(poolTx.MsgTx().TxOut)) @@ -690,8 +705,8 @@ func (mp *txMemPool) FetchTransaction(txHash *btcwire.ShaHash) (*btcutil.Tx, err mp.RLock() defer mp.RUnlock() - if tx, exists := mp.pool[*txHash]; exists { - return tx, nil + if txDesc, exists := mp.pool[*txHash]; exists { + return txDesc.Tx, nil } return nil, fmt.Errorf("transaction is not in the pool") @@ -856,7 +871,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool) erro } // Add to transaction pool. - mp.addTransaction(tx) + mp.addTransaction(tx, nextBlockHeight-1, txFee) txmpLog.Debugf("Accepted transaction %v (pool size: %v)", txHash, len(mp.pool)) @@ -1005,12 +1020,30 @@ func (mp *txMemPool) TxShas() []*btcwire.ShaHash { return hashes } +// TxDescs returns a slice of descriptors for all the transactions in the pool. +// The descriptors are to be treated as read only. +// +// This function is safe for concurrent access. +func (mp *txMemPool) TxDescs() []*TxDesc { + mp.RLock() + defer mp.RUnlock() + + descs := make([]*TxDesc, len(mp.pool)) + i := 0 + for _, desc := range mp.pool { + descs[i] = desc + i++ + } + + return descs +} + // newTxMemPool returns a new memory pool for validating and storing standalone // transactions until they are mined into a block. func newTxMemPool(server *server) *txMemPool { return &txMemPool{ server: server, - pool: make(map[btcwire.ShaHash]*btcutil.Tx), + pool: make(map[btcwire.ShaHash]*TxDesc), orphans: make(map[btcwire.ShaHash]*btcutil.Tx), orphansByPrev: make(map[btcwire.ShaHash]*list.List), outpoints: make(map[btcwire.OutPoint]*btcutil.Tx), diff --git a/rpcserver.go b/rpcserver.go index 1b19315a..92643290 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -870,13 +870,52 @@ func handleGetPeerInfo(s *rpcServer, cmd btcjson.Cmd, walletNotification chan [] return s.server.PeerInfo(), nil } +type mempoolDescriptor struct { + Size int `json:"size"` + Fee int64 `json:"fee"` + Time int64 `json:"time"` + Height int64 `json:"height"` + StartingPriority int `json:"startingpriority"` + CurrentPriority int `json:"currentpriority"` + Depends []string `json:"depends"` + +} + // handleGetRawMempool implements the getrawmempool command. func handleGetRawMempool(s *rpcServer, cmd btcjson.Cmd, walletNotification chan []byte) (interface{}, error) { - hashes := s.server.txMemPool.TxShas() - hashStrings := make([]string, len(hashes)) - for i := 0; i < len(hashes); i++ { - hashStrings[i] = hashes[i].String() + c := cmd.(*btcjson.GetRawMempoolCmd) + descs := s.server.txMemPool.TxDescs() + + if c.Verbose { + result := make(map[string]*mempoolDescriptor, len(descs)) + for _, desc := range descs { + mpd := &mempoolDescriptor{ + Size: desc.Tx.MsgTx().SerializeSize(), + Fee: desc.Fee, + Time: desc.Added.Unix(), + Height: desc.Height, + StartingPriority: 0, // We don't mine. + CurrentPriority: 0, // We don't mine. + } + for _, txIn := range desc.Tx.MsgTx().TxIn { + hash := &txIn.PreviousOutpoint.Hash + if s.server.txMemPool.HaveTransaction(hash) { + mpd.Depends = append(mpd.Depends, + hash.String()) + } + } + + result[desc.Tx.Sha().String()] = mpd + } + + return result, nil } + hashStrings := make([]string, len(descs)) + for i := range(hashStrings) { + hashStrings[i] = descs[i].Tx.Sha().String() + } + + return hashStrings, nil } diff --git a/util/btcctl/btcctl.go b/util/btcctl/btcctl.go index 7d9cad93..19cd1e88 100644 --- a/util/btcctl/btcctl.go +++ b/util/btcctl/btcctl.go @@ -56,7 +56,7 @@ var commandHandlers = map[string]*handlerData{ "getdifficulty": &handlerData{0, 0, displayFloat64, nil, makeGetDifficulty, ""}, "getgenerate": &handlerData{0, 0, displayGeneric, nil, makeGetGenerate, ""}, "getpeerinfo": &handlerData{0, 0, displaySpewDump, nil, makeGetPeerInfo, ""}, - "getrawmempool": &handlerData{0, 0, displaySpewDump, nil, makeGetRawMempool, ""}, + "getrawmempool": &handlerData{0, 1, displaySpewDump, []conversionHandler{toBool}, makeGetRawMempool, "[verbose=false]"}, "getrawtransaction": &handlerData{1, 1, displaySpewDump, []conversionHandler{nil, toInt}, makeGetRawTransaction, " [verbose=0]"}, "importprivkey": &handlerData{1, 2, displayGeneric, []conversionHandler{nil, nil, toBool}, makeImportPrivKey, " [label] [rescan=true]"}, "listtransactions": &handlerData{0, 3, displaySpewDump, []conversionHandler{nil, toInt, toInt}, makeListTransactions, "[account] [count=10] [from=0]"}, @@ -209,7 +209,11 @@ func makeGetPeerInfo(args []interface{}) (btcjson.Cmd, error) { // makeRawMempool generates the cmd structure for // getrawmempool comands. func makeGetRawMempool(args []interface{}) (btcjson.Cmd, error) { - return btcjson.NewGetRawMempoolCmd("btcctl") + opt := make([]bool, 0, 1) + if len(args) > 0 { + opt = append(opt, args[0].(bool)) + } + return btcjson.NewGetRawMempoolCmd("btcctl", opt...) } // makeRawTransaction generates the cmd structure for