OSDN Git Service

Mempool: add no btm input tx filter (#1605)
authoryahtoo <yahtoo.ma@gmail.com>
Fri, 1 Mar 2019 14:35:50 +0000 (22:35 +0800)
committerPaladz <yzhu101@uottawa.ca>
Fri, 1 Mar 2019 14:35:50 +0000 (22:35 +0800)
* Mempool: add no btm input tx filter

* Fix review error

* Fix review error

* Fix review error

* Fix review error

* Fix review error

* Add test case

netsync/handle.go
protocol/tx.go
protocol/txpool.go
protocol/txpool_test.go

index b412bee..643ae0a 100644 (file)
@@ -340,7 +340,7 @@ func (sm *SyncManager) handleTransactionMsg(peer *peer, msg *TransactionMessage)
                return
        }
 
-       if isOrphan, err := sm.chain.ValidateTx(tx); err != nil && !isOrphan {
+       if isOrphan, err := sm.chain.ValidateTx(tx); err != nil && err != core.ErrDustTx && !isOrphan {
                sm.peers.addBanScore(peer.ID(), 10, 0, "fail on validate tx transaction")
        }
 }
index 4797c1f..c5efb1f 100644 (file)
@@ -31,10 +31,14 @@ func (c *Chain) ValidateTx(tx *types.Tx) (bool, error) {
                return false, c.txPool.GetErrCache(&tx.ID)
        }
 
+       if c.txPool.IsDust(tx) {
+               c.txPool.AddErrCache(&tx.ID, ErrDustTx)
+               return false, ErrDustTx
+       }
+
        bh := c.BestBlockHeader()
-       block := types.MapBlock(&types.Block{BlockHeader: *bh})
-       gasStatus, err := validation.ValidateTx(tx.Tx, block)
-       if gasStatus.GasValid == false {
+       gasStatus, err := validation.ValidateTx(tx.Tx, types.MapBlock(&types.Block{BlockHeader: *bh}))
+       if !gasStatus.GasValid {
                c.txPool.AddErrCache(&tx.ID, err)
                return false, err
        }
@@ -43,5 +47,5 @@ func (c *Chain) ValidateTx(tx *types.Tx) (bool, error) {
                log.WithFields(log.Fields{"module": logModule, "tx_id": tx.Tx.ID.String(), "error": err}).Info("transaction status fail")
        }
 
-       return c.txPool.ProcessTransaction(tx, err != nil, block.BlockHeader.Height, gasStatus.BTMValue)
+       return c.txPool.ProcessTransaction(tx, err != nil, bh.Height, gasStatus.BTMValue)
 }
index edb6af0..10fbbc5 100644 (file)
@@ -36,6 +36,8 @@ var (
        ErrTransactionNotExist = errors.New("transaction are not existed in the mempool")
        // ErrPoolIsFull indicates the pool is full
        ErrPoolIsFull = errors.New("transaction pool reach the max number")
+       // ErrDustTx indicates transaction is dust tx
+       ErrDustTx = errors.New("transaction is dust tx")
 )
 
 type TxMsgEvent struct{ TxMsg *TxPoolMsg }
@@ -190,8 +192,20 @@ func (tp *TxPool) HaveTransaction(txHash *bc.Hash) bool {
        return tp.IsTransactionInPool(txHash) || tp.IsTransactionInErrCache(txHash)
 }
 
-// ProcessTransaction is the main entry for txpool handle new tx
-func (tp *TxPool) ProcessTransaction(tx *types.Tx, statusFail bool, height, fee uint64) (bool, error) {
+func isTransactionNoBtmInput(tx *types.Tx) bool {
+       for _, input := range tx.TxData.Inputs {
+               if input.AssetID() == *consensus.BTMAssetID {
+                       return false
+               }
+       }
+       return true
+}
+
+func (tp *TxPool) IsDust(tx *types.Tx) bool {
+       return isTransactionNoBtmInput(tx)
+}
+
+func (tp *TxPool) processTransaction(tx *types.Tx, statusFail bool, height, fee uint64) (bool, error) {
        tp.mtx.Lock()
        defer tp.mtx.Unlock()
 
@@ -219,6 +233,15 @@ func (tp *TxPool) ProcessTransaction(tx *types.Tx, statusFail bool, height, fee
        return false, nil
 }
 
+// ProcessTransaction is the main entry for txpool handle new tx, ignore dust tx.
+func (tp *TxPool) ProcessTransaction(tx *types.Tx, statusFail bool, height, fee uint64) (bool, error) {
+       if tp.IsDust(tx) {
+               log.WithFields(log.Fields{"module": logModule, "tx_id": tx.ID.String()}).Warn("dust tx")
+               return false, nil
+       }
+       return tp.processTransaction(tx, statusFail, height, fee)
+}
+
 func (tp *TxPool) addOrphan(txD *TxDesc, requireParents []*bc.Hash) error {
        if len(tp.orphans) >= maxOrphanNum {
                return ErrPoolIsFull
index 34986ac..7d87648 100644 (file)
@@ -4,6 +4,8 @@ import (
        "testing"
        "time"
 
+       "github.com/davecgh/go-spew/spew"
+
        "github.com/bytom/consensus"
        "github.com/bytom/database/storage"
        "github.com/bytom/event"
@@ -14,6 +16,7 @@ import (
 )
 
 var testTxs = []*types.Tx{
+       //tx0
        types.NewTx(types.TxData{
                SerializedSize: 100,
                Inputs: []*types.TxInput{
@@ -23,6 +26,7 @@ var testTxs = []*types.Tx{
                        types.NewTxOutput(*consensus.BTMAssetID, 1, []byte{0x6a}),
                },
        }),
+       //tx1
        types.NewTx(types.TxData{
                SerializedSize: 100,
                Inputs: []*types.TxInput{
@@ -32,6 +36,7 @@ var testTxs = []*types.Tx{
                        types.NewTxOutput(*consensus.BTMAssetID, 1, []byte{0x6b}),
                },
        }),
+       //tx2
        types.NewTx(types.TxData{
                SerializedSize: 150,
                TimeRange:      0,
@@ -44,6 +49,7 @@ var testTxs = []*types.Tx{
                        types.NewTxOutput(bc.NewAssetID([32]byte{0xa1}), 4, []byte{0x61}),
                },
        }),
+       //tx3
        types.NewTx(types.TxData{
                SerializedSize: 100,
                Inputs: []*types.TxInput{
@@ -54,6 +60,7 @@ var testTxs = []*types.Tx{
                        types.NewTxOutput(bc.NewAssetID([32]byte{0xa1}), 1, []byte{0x63}),
                },
        }),
+       //tx4
        types.NewTx(types.TxData{
                SerializedSize: 100,
                Inputs: []*types.TxInput{
@@ -559,3 +566,88 @@ func TestRemoveOrphan(t *testing.T) {
                }
        }
 }
+
+type mockStore1 struct{}
+
+func (s *mockStore1) BlockExist(hash *bc.Hash) bool                                { return false }
+func (s *mockStore1) GetBlock(*bc.Hash) (*types.Block, error)                      { return nil, nil }
+func (s *mockStore1) GetStoreStatus() *BlockStoreState                             { return nil }
+func (s *mockStore1) GetTransactionStatus(*bc.Hash) (*bc.TransactionStatus, error) { return nil, nil }
+func (s *mockStore1) GetTransactionsUtxo(utxoView *state.UtxoViewpoint, tx []*bc.Tx) error {
+       for _, hash := range testTxs[2].SpentOutputIDs {
+               utxoView.Entries[hash] = &storage.UtxoEntry{IsCoinBase: false, Spent: false}
+       }
+       return nil
+}
+func (s *mockStore1) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error)                 { return nil, nil }
+func (s *mockStore1) LoadBlockIndex(uint64) (*state.BlockIndex, error)             { return nil, nil }
+func (s *mockStore1) SaveBlock(*types.Block, *bc.TransactionStatus) error          { return nil }
+func (s *mockStore1) SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint) error { return nil }
+
+func TestProcessTransaction(t *testing.T) {
+       txPool := &TxPool{
+               pool:            make(map[bc.Hash]*TxDesc),
+               utxo:            make(map[bc.Hash]*types.Tx),
+               orphans:         make(map[bc.Hash]*orphanTx),
+               orphansByPrev:   make(map[bc.Hash]map[bc.Hash]*orphanTx),
+               store:           &mockStore1{},
+               eventDispatcher: event.NewDispatcher(),
+       }
+       cases := []struct {
+               want  *TxPool
+               addTx *TxDesc
+       }{
+               //Dust tx
+               {
+                       want: &TxPool{},
+                       addTx: &TxDesc{
+                               Tx:         testTxs[3],
+                               StatusFail: false,
+                       },
+               },
+               //normal tx
+               {
+                       want: &TxPool{
+                               pool: map[bc.Hash]*TxDesc{
+                                       testTxs[2].ID: {
+                                               Tx:         testTxs[2],
+                                               StatusFail: false,
+                                               Weight:     150,
+                                       },
+                               },
+                               utxo: map[bc.Hash]*types.Tx{
+                                       *testTxs[2].ResultIds[0]: testTxs[2],
+                                       *testTxs[2].ResultIds[1]: testTxs[2],
+                               },
+                       },
+                       addTx: &TxDesc{
+                               Tx:         testTxs[2],
+                               StatusFail: false,
+                       },
+               },
+       }
+
+       for i, c := range cases {
+               txPool.ProcessTransaction(c.addTx.Tx, c.addTx.StatusFail, 0, 0)
+               for _, txD := range txPool.pool {
+                       txD.Added = time.Time{}
+               }
+               for _, txD := range txPool.orphans {
+                       txD.Added = time.Time{}
+                       txD.expiration = time.Time{}
+               }
+
+               if !testutil.DeepEqual(txPool.pool, c.want.pool) {
+                       t.Errorf("case %d: test ProcessTransaction pool mismatch got %s want %s", i, spew.Sdump(txPool.pool), spew.Sdump(c.want.pool))
+               }
+               if !testutil.DeepEqual(txPool.utxo, c.want.utxo) {
+                       t.Errorf("case %d: test ProcessTransaction utxo mismatch got %s want %s", i, spew.Sdump(txPool.utxo), spew.Sdump(c.want.utxo))
+               }
+               if !testutil.DeepEqual(txPool.orphans, c.want.orphans) {
+                       t.Errorf("case %d: test ProcessTransaction orphans mismatch got %s want %s", i, spew.Sdump(txPool.orphans), spew.Sdump(c.want.orphans))
+               }
+               if !testutil.DeepEqual(txPool.orphansByPrev, c.want.orphansByPrev) {
+                       t.Errorf("case %d: test ProcessTransaction orphansByPrev mismatch got %s want %s", i, spew.Sdump(txPool.orphansByPrev), spew.Sdump(c.want.orphansByPrev))
+               }
+       }
+}