diff --git a/core/txpool/localpool/blockchain_test.go b/core/txpool/localpool/blockchain_test.go new file mode 100644 index 0000000000..9ce3126345 --- /dev/null +++ b/core/txpool/localpool/blockchain_test.go @@ -0,0 +1,39 @@ +package localpool + +import ( + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" +) + +type MockBC struct { + currentBlock *types.Header + dbs map[common.Hash]*state.StateDB +} + +func (m *MockBC) Config() *params.ChainConfig { + return params.AllDevChainProtocolChanges +} + +func (m *MockBC) CurrentBlock() *types.Header { + return m.currentBlock +} + +func (m *MockBC) GetBlock(hash common.Hash, number uint64) *types.Block { + return nil +} + +func (m *MockBC) StateAt(root common.Hash) (*state.StateDB, error) { + state, ok := m.dbs[root] + if !ok { + return nil, errors.New("not found") + } + return state, nil +} + +func (m *MockBC) SetState(root common.Hash, db *state.StateDB) { + m.dbs[root] = db +} diff --git a/core/txpool/localpool/localpool.go b/core/txpool/localpool/localpool.go index 73e71dee77..b7772292a8 100644 --- a/core/txpool/localpool/localpool.go +++ b/core/txpool/localpool/localpool.go @@ -87,7 +87,7 @@ func (l *LocalPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Add l.reserver = reserve // TODO load transactions.rlp l.Reset(nil, head) - return errors.New("not implemented") + return nil } func (l *LocalPool) Close() error { @@ -149,7 +149,7 @@ func (l *LocalPool) Add(txs []*types.Transaction, local bool, sync bool) []error } // notify all listeners about successfully added txs l.txFeed.Send(core.NewTxsEvent{Txs: successfulTxs}) - return nil + return errs } func (l *LocalPool) add(tx *types.Transaction) error { diff --git a/core/txpool/localpool/localpool_test.go b/core/txpool/localpool/localpool_test.go index 2bacb0f0f6..804d788805 100644 --- a/core/txpool/localpool/localpool_test.go +++ b/core/txpool/localpool/localpool_test.go @@ -1,6 +1,17 @@ package localpool -import "errors" +import ( + "errors" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" +) func (l *LocalPool) verifyConsistency() error { for _, list := range l.allAccounts { @@ -25,3 +36,112 @@ func (l *LocalPool) verifyConsistency() error { } return nil } + +func TestReset(t *testing.T) { + // Generate a faucet + key, err := crypto.GenerateKey() + if err != nil { + t.Fatal(err) + } + addr := crypto.PubkeyToAddress(key.PublicKey) + // Setup a mock blockchain + bc := &MockBC{ + currentBlock: &types.Header{Root: common.Hash{}}, + dbs: make(map[common.Hash]*state.StateDB), + } + initialDB, err := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + if err != nil { + t.Fatal(err) + } + initialDB.CreateAccount(addr) + initialDB.SetBalance(addr, big.NewInt(1_000_000_000_000_000_000)) + bc.SetState(common.Hash{}, initialDB) + // Setup the txpool + pool, err := NewLocalPool(bc, types.LatestSigner(params.AllDevChainProtocolChanges)) + if err != nil { + t.Fatal(err) + } + if err := pool.Init(nil, &types.Header{Root: common.Hash{}, GasLimit: 200_000}, func(addr common.Address, reserve bool) error { return nil }); err != nil { + t.Fatal(err) + } + // Queue transactions + // TODO there might be an off-by-one error here + for i := 1; i < 100; i++ { + tx := types.NewTransaction(uint64(i), common.Address{}, new(big.Int), 100000, big.NewInt(1234), nil) + signer := types.LatestSigner(params.AllDevChainProtocolChanges) + signed, err := types.SignTx(tx, signer, key) + if err != nil { + t.Fatal(err) + } + if errs := pool.Add([]*types.Transaction{signed}, true, false); errs[0] != nil { + t.Fatal(errs[0]) + } + } + // Verify integrity + if err := pool.verifyConsistency(); err != nil { + t.Fatal(err) + } + pending, queued := pool.ContentFrom(addr) + if len(pending) != 99 || len(queued) != 0 { + t.Fatal(len(pending), len(queued)) + } + // Reset the pool + newDB := initialDB.Copy() + newDB.SetNonce(addr, uint64(100+1)) + bc.SetState(common.Hash{1}, newDB) + pool.Reset(nil, &types.Header{Root: common.Hash{1}}) + // Verify post state + if err := pool.verifyConsistency(); err != nil { + t.Fatal(err) + } + pending, queued = pool.ContentFrom(addr) + if len(pending) != 0 || len(queued) != 0 { + t.Fatal(pending, queued) + } +} + +func BenchmarkReorg(b *testing.B) { + // Generate a faucet + key, err := crypto.GenerateKey() + if err != nil { + b.Fatal(err) + } + addr := crypto.PubkeyToAddress(key.PublicKey) + // Setup a mock blockchain + bc := &MockBC{ + currentBlock: &types.Header{Root: common.Hash{}}, + dbs: make(map[common.Hash]*state.StateDB), + } + initialDB, err := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + if err != nil { + b.Fatal(err) + } + initialDB.CreateAccount(addr) + initialDB.SetBalance(addr, big.NewInt(1_000_000_000_000_000_000)) + bc.SetState(common.Hash{}, initialDB) + // Setup the txpool + pool, err := NewLocalPool(bc, types.LatestSigner(params.AllDevChainProtocolChanges)) + if err != nil { + b.Fatal(err) + } + if err := pool.Init(nil, &types.Header{Root: common.Hash{}, GasLimit: 200_000}, func(addr common.Address, reserve bool) error { return nil }); err != nil { + b.Fatal(err) + } + // Queue transactions + for i := 0; i < b.N; i++ { + tx := types.NewTransaction(uint64(i), common.Address{}, new(big.Int), 100000, big.NewInt(1234), nil) + signer := types.LatestSigner(params.AllDevChainProtocolChanges) + signed, err := types.SignTx(tx, signer, key) + if err != nil { + b.Fatal(err) + } + if errs := pool.Add([]*types.Transaction{signed}, true, false); errs[0] != nil { + b.Fatal(errs[0]) + } + } + newDB := initialDB.Copy() + newDB.SetNonce(addr, uint64(b.N+1)) + bc.SetState(common.Hash{1}, newDB) + b.ResetTimer() + pool.Reset(nil, &types.Header{Root: common.Hash{1}}) +}