@ -51,6 +51,80 @@ func deriveSender(tx *types.Transaction) (common.Address, error) {
return types . Sender ( types . HomesteadSigner { } , tx )
}
// This test simulates a scenario where a new block is imported during a
// state reset and tests whether the pending state is in sync with the
// block head event that initiated the resetState().
func TestStateChangeDuringPoolReset ( t * testing . T ) {
var (
db , _ = ethdb . NewMemDatabase ( )
key , _ = crypto . GenerateKey ( )
address = crypto . PubkeyToAddress ( key . PublicKey )
mux = new ( event . TypeMux )
statedb , _ = state . New ( common . Hash { } , db )
trigger = false
)
// setup pool with 2 transaction in it
statedb . SetBalance ( address , new ( big . Int ) . Mul ( common . Big1 , common . Ether ) )
tx0 := transaction ( 0 , big . NewInt ( 100000 ) , key )
tx1 := transaction ( 1 , big . NewInt ( 100000 ) , key )
// stateFunc is used multiple times to reset the pending state.
// when simulate is true it will create a state that indicates
// that tx0 and tx1 are included in the chain.
stateFunc := func ( ) ( * state . StateDB , error ) {
// delay "state change" by one. The tx pool fetches the
// state multiple times and by delaying it a bit we simulate
// a state change between those fetches.
stdb := statedb
if trigger {
statedb , _ = state . New ( common . Hash { } , db )
// simulate that the new head block included tx0 and tx1
statedb . SetNonce ( address , 2 )
statedb . SetBalance ( address , new ( big . Int ) . Mul ( common . Big1 , common . Ether ) )
trigger = false
}
return stdb , nil
}
gasLimitFunc := func ( ) * big . Int { return big . NewInt ( 1000000000 ) }
txpool := NewTxPool ( testChainConfig ( ) , mux , stateFunc , gasLimitFunc )
txpool . resetState ( )
nonce := txpool . State ( ) . GetNonce ( address )
if nonce != 0 {
t . Fatalf ( "Invalid nonce, want 0, got %d" , nonce )
}
txpool . AddBatch ( types . Transactions { tx0 , tx1 } )
nonce = txpool . State ( ) . GetNonce ( address )
if nonce != 2 {
t . Fatalf ( "Invalid nonce, want 2, got %d" , nonce )
}
// trigger state change in the background
trigger = true
txpool . resetState ( )
pendingTx , err := txpool . Pending ( )
if err != nil {
t . Fatalf ( "Could not fetch pending transactions: %v" , err )
}
for addr , txs := range pendingTx {
t . Logf ( "%0x: %d\n" , addr , len ( txs ) )
}
nonce = txpool . State ( ) . GetNonce ( address )
if nonce != 2 {
t . Fatalf ( "Invalid nonce, want 2, got %d" , nonce )
}
}
func TestInvalidTransactions ( t * testing . T ) {
pool , key := setupTxPool ( )
@ -97,9 +171,10 @@ func TestTransactionQueue(t *testing.T) {
from , _ := deriveSender ( tx )
currentState , _ := pool . currentState ( )
currentState . AddBalance ( from , big . NewInt ( 1000 ) )
pool . resetState ( )
pool . enqueueTx ( tx . Hash ( ) , tx )
pool . promoteExecutables ( )
pool . promoteExecutables ( currentState )
if len ( pool . pending ) != 1 {
t . Error ( "expected valid txs to be 1 is" , len ( pool . pending ) )
}
@ -108,7 +183,7 @@ func TestTransactionQueue(t *testing.T) {
from , _ = deriveSender ( tx )
currentState . SetNonce ( from , 2 )
pool . enqueueTx ( tx . Hash ( ) , tx )
pool . promoteExecutables ( )
pool . promoteExecutables ( currentState )
if _ , ok := pool . pending [ from ] . txs . items [ tx . Nonce ( ) ] ; ok {
t . Error ( "expected transaction to be in tx pool" )
}
@ -124,11 +199,13 @@ func TestTransactionQueue(t *testing.T) {
from , _ = deriveSender ( tx1 )
currentState , _ = pool . currentState ( )
currentState . AddBalance ( from , big . NewInt ( 1000 ) )
pool . resetState ( )
pool . enqueueTx ( tx1 . Hash ( ) , tx1 )
pool . enqueueTx ( tx2 . Hash ( ) , tx2 )
pool . enqueueTx ( tx3 . Hash ( ) , tx3 )
pool . promoteExecutables ( )
pool . promoteExecutables ( currentState )
if len ( pool . pending ) != 1 {
t . Error ( "expected tx pool to be 1, got" , len ( pool . pending ) )
@ -225,7 +302,8 @@ func TestTransactionDoubleNonce(t *testing.T) {
if err := pool . add ( tx2 ) ; err != nil {
t . Error ( "didn't expect error" , err )
}
pool . promoteExecutables ( )
state , _ := pool . currentState ( )
pool . promoteExecutables ( state )
if pool . pending [ addr ] . Len ( ) != 1 {
t . Error ( "expected 1 pending transactions, got" , pool . pending [ addr ] . Len ( ) )
}
@ -236,7 +314,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
if err := pool . add ( tx3 ) ; err != nil {
t . Error ( "didn't expect error" , err )
}
pool . promoteExecutables ( )
pool . promoteExecutables ( state )
if pool . pending [ addr ] . Len ( ) != 1 {
t . Error ( "expected 1 pending transactions, got" , pool . pending [ addr ] . Len ( ) )
}
@ -295,6 +373,7 @@ func TestRemovedTxEvent(t *testing.T) {
from , _ := deriveSender ( tx )
currentState , _ := pool . currentState ( )
currentState . AddBalance ( from , big . NewInt ( 1000000000000 ) )
pool . resetState ( )
pool . eventMux . Post ( RemovedTransactionEvent { types . Transactions { tx } } )
pool . eventMux . Post ( ChainHeadEvent { nil } )
if pool . pending [ from ] . Len ( ) != 1 {
@ -452,6 +531,7 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
state , _ := pool . currentState ( )
state . AddBalance ( account , big . NewInt ( 1000000 ) )
pool . resetState ( )
// Keep queuing up transactions and make sure all above a limit are dropped
for i := uint64 ( 1 ) ; i <= maxQueuedPerAccount + 5 ; i ++ {
@ -564,6 +644,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
state , _ := pool . currentState ( )
state . AddBalance ( account , big . NewInt ( 1000000 ) )
pool . resetState ( )
// Keep queuing up transactions and make sure all above a limit are dropped
for i := uint64 ( 0 ) ; i < maxQueuedPerAccount + 5 ; i ++ {
@ -733,7 +814,7 @@ func benchmarkPendingDemotion(b *testing.B, size int) {
// Benchmark the speed of pool validation
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
pool . demoteUnexecutables ( )
pool . demoteUnexecutables ( state )
}
}
@ -757,7 +838,7 @@ func benchmarkFuturePromotion(b *testing.B, size int) {
// Benchmark the speed of pool validation
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
pool . promoteExecutables ( )
pool . promoteExecutables ( state )
}
}