From fa6d79ac6de08faf8f3ce88d644f5b95782af6de Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Mon, 18 May 2020 20:08:18 -0400 Subject: [PATCH 01/26] Added additional aa field to tx_list to be used in tx_pool for limiting transactions. Added necessary changes to the transaction struct with associated methods. --- core/tx_list.go | 4 +++- core/tx_list_test.go | 2 +- core/tx_pool.go | 4 ++-- core/types/transaction.go | 48 ++++++++++++++++++++++++++++++--------- 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/core/tx_list.go b/core/tx_list.go index 6b22cbbebe4a..4a64f9ecd4e3 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -225,15 +225,17 @@ type txList struct { costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit) + isAA bool } // newTxList create a new transaction list for maintaining nonce-indexable fast, // gapped, sortable transaction lists. -func newTxList(strict bool) *txList { +func newTxList(strict bool, isAA bool) *txList { return &txList{ strict: strict, txs: newTxSortedMap(), costcap: new(big.Int), + isAA: isAA, } } diff --git a/core/tx_list_test.go b/core/tx_list_test.go index d579f501afa8..294ee711f356 100644 --- a/core/tx_list_test.go +++ b/core/tx_list_test.go @@ -35,7 +35,7 @@ func TestStrictTxListAdd(t *testing.T) { txs[i] = transaction(uint64(i), 0, key) } // Insert the transactions in a random order - list := newTxList(true) + list := newTxList(true, false) for _, v := range rand.Perm(len(txs)) { list.Add(txs[v], DefaultTxPoolConfig.PriceBump) } diff --git a/core/tx_pool.go b/core/tx_pool.go index d4a6100c55a7..a9c3e66de99b 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -644,7 +644,7 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, er // Try to insert the transaction into the future queue from, _ := types.Sender(pool.signer, tx) // already validated if pool.queue[from] == nil { - pool.queue[from] = newTxList(false) + pool.queue[from] = newTxList(false, false) } inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump) if !inserted { @@ -687,7 +687,7 @@ func (pool *TxPool) journalTx(from common.Address, tx *types.Transaction) { func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool { // Try to insert the transaction into the pending queue if pool.pending[addr] == nil { - pool.pending[addr] = newTxList(true) + pool.pending[addr] = newTxList(true, false) } list := pool.pending[addr] diff --git a/core/types/transaction.go b/core/types/transaction.go index 3eb8df0ac7c7..1bc68af0ab9b 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -38,9 +38,10 @@ var ( type Transaction struct { data txdata // caches - hash atomic.Value - size atomic.Value - from atomic.Value + hash atomic.Value + size atomic.Value + from atomic.Value + price atomic.Value } type txdata struct { @@ -72,11 +73,11 @@ type txdataMarshaling struct { } func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { - return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data) + return newTransaction(nonce, &to, amount, 5, gasPrice, data) } func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { - return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data) + return newTransaction(nonce, nil, amount, 5, gasPrice, data) } func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { @@ -104,6 +105,10 @@ func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit return &Transaction{data: d} } +func (tx *Transaction) isAA() bool { + return (tx.data.R == common.Big0 && tx.data.S == common.Big0) +} + // ChainId returns which chain id this transaction was signed for (if at all) func (tx *Transaction) ChainId() *big.Int { return deriveChainId(tx.data.V) @@ -172,12 +177,33 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { return nil } -func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } -func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit } -func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } -func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } -func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce } -func (tx *Transaction) CheckNonce() bool { return true } +func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } +func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit } +func (tx *Transaction) GasPrice() *big.Int { + if tx.isAA() { + if price := tx.price.Load(); price != nil { + var current_price big.Int = price.(big.Int) + return ¤t_price + } + + return nil + } + + return new(big.Int).Set(tx.data.Price) +} + +func (tx *Transaction) Validate() uint64 { return 200000 } + +func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } +func (tx *Transaction) Nonce() uint64 { + if tx.isAA() { + return 0 + } + + return tx.data.AccountNonce +} + +func (tx *Transaction) CheckNonce() bool { return true } // To returns the recipient address of the transaction. // It returns nil if the transaction is a contract creation. From 5ed9330cab86af51192207f543142ad1febc77e5 Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Wed, 20 May 2020 18:59:23 -0400 Subject: [PATCH 02/26] Primitive implementation in place. One item in the queue, one item in pending. --- core/tx_list.go | 6 +-- core/tx_pool.go | 70 ++++++++++++++++++++++++++----- core/types/transaction.go | 11 +++-- core/types/transaction_signing.go | 5 +++ 4 files changed, 75 insertions(+), 17 deletions(-) diff --git a/core/tx_list.go b/core/tx_list.go index 4a64f9ecd4e3..e57b06fe5fde 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -225,17 +225,17 @@ type txList struct { costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit) - isAA bool + IsAA bool } // newTxList create a new transaction list for maintaining nonce-indexable fast, // gapped, sortable transaction lists. -func newTxList(strict bool, isAA bool) *txList { +func newTxList(strict bool, IsAA bool) *txList { return &txList{ strict: strict, txs: newTxSortedMap(), costcap: new(big.Int), - isAA: isAA, + IsAA: IsAA, } } diff --git a/core/tx_pool.go b/core/tx_pool.go index a9c3e66de99b..e5286da7a2d9 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -56,6 +56,9 @@ var ( // within the pool. ErrAlreadyKnown = errors.New("already known") + // Queue for AA transactions is already filled + ErrAACapacity = errors.New("aa txs at capacity") + // ErrInvalidSender is returned if the transaction contains an invalid signature. ErrInvalidSender = errors.New("invalid sender") @@ -527,11 +530,22 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if pool.currentMaxGas < tx.Gas() { return ErrGasLimit } - // Make sure the transaction is signed properly - from, err := types.Sender(pool.signer, tx) - if err != nil { - return ErrInvalidSender + + var from common.Address + + if tx.IsAA() { + tx.Validate() + from = *tx.To() + // manage error logic here + } else { + // Make sure the transaction is signed properly + var err error + from, err = types.Sender(pool.signer, tx) + if err != nil { + return ErrInvalidSender + } } + // Drop non-local transactions under our own minimal accepted gas price local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 { @@ -572,6 +586,14 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e knownTxMeter.Mark(1) return false, ErrAlreadyKnown } + + if tx.IsAA() { + if pool.pending[*tx.To()] != nil { + return false, ErrAACapacity + } + + } + // If the transaction fails basic validation, discard it if err := pool.validateTx(tx, local); err != nil { log.Trace("Discarding invalid transaction", "hash", hash, "err", err) @@ -643,13 +665,16 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, error) { // Try to insert the transaction into the future queue from, _ := types.Sender(pool.signer, tx) // already validated + if pool.queue[from] == nil { - pool.queue[from] = newTxList(false, false) + pool.queue[from] = newTxList(false, tx.IsAA()) } + + // AA transactions all have the same nonce, so to replace its queue position + // it needs to meet the price bump inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump) if !inserted { // An older transaction was better, discard this - queuedDiscardMeter.Mark(1) return false, ErrReplaceUnderpriced } // Discard any previous transaction and mark this @@ -687,7 +712,7 @@ func (pool *TxPool) journalTx(from common.Address, tx *types.Transaction) { func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool { // Try to insert the transaction into the pending queue if pool.pending[addr] == nil { - pool.pending[addr] = newTxList(true, false) + pool.pending[addr] = newTxList(true, tx.IsAA()) } list := pool.pending[addr] @@ -1060,7 +1085,10 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt // Update all accounts to the latest known pending nonce for addr, list := range pool.pending { txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway - pool.pendingNonces.set(addr, txs[len(txs)-1].Nonce()+1) + + if !list.IsAA { + pool.pendingNonces.set(addr, txs[len(txs)-1].Nonce()+1) + } } pool.mu.Unlock() @@ -1078,7 +1106,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt // of the transaction pool is valid with regard to the chain state. func (pool *TxPool) reset(oldHead, newHead *types.Header) { // If we're reorging an old state, reinject all dropped transactions - var reinject types.Transactions + var reinject, included types.Transactions if oldHead != nil && oldHead.Hash() != newHead.ParentHash { // If the reorg is too deep, avoid doing it (will happen during fast sync) @@ -1089,7 +1117,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { log.Debug("Skipping deep transaction reorg", "depth", depth) } else { // Reorg seems shallow enough to pull in all transactions into memory - var discarded, included types.Transactions + var discarded types.Transactions var ( rem = pool.chain.GetBlock(oldHead.Hash(), oldHead.Number.Uint64()) add = pool.chain.GetBlock(newHead.Hash(), newHead.Number.Uint64()) @@ -1139,6 +1167,28 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { reinject = types.TxDifference(discarded, included) } } + + // for AA we need to re-validate accounts that were included + // split into separate function + for _, tx := range included { + var account = tx.To() + if account != nil && tx.IsAA() { + if pool.Has(tx.Hash()) { + pool.removeTx(tx.Hash(), true) + } else { + if pending := pool.pending[*account]; pending != nil { + pending_tx := pending.txs.Flatten()[0] + pending_tx.Validate() + } + + if queued := pool.queue[*account]; queued != nil { + queued_tx := queued.txs.Flatten()[0] + queued_tx.Validate() + } + } + } + } + // Initialize the internal state to the current head if newHead == nil { newHead = pool.chain.CurrentBlock().Header() // Special case during testing diff --git a/core/types/transaction.go b/core/types/transaction.go index 1bc68af0ab9b..86e1eeebc1d5 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -105,7 +105,7 @@ func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit return &Transaction{data: d} } -func (tx *Transaction) isAA() bool { +func (tx *Transaction) IsAA() bool { return (tx.data.R == common.Big0 && tx.data.S == common.Big0) } @@ -180,7 +180,7 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit } func (tx *Transaction) GasPrice() *big.Int { - if tx.isAA() { + if tx.IsAA() { if price := tx.price.Load(); price != nil { var current_price big.Int = price.(big.Int) return ¤t_price @@ -192,11 +192,14 @@ func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } -func (tx *Transaction) Validate() uint64 { return 200000 } +func (tx *Transaction) Validate() { + tx.price.Store(big.NewInt(1200)) + // return error here +} func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } func (tx *Transaction) Nonce() uint64 { - if tx.isAA() { + if tx.IsAA() { return 0 } diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 842fedbd03d6..892be9862086 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -131,6 +131,11 @@ func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) { if tx.ChainId().Cmp(s.chainId) != 0 { return common.Address{}, ErrInvalidChainId } + + if tx.IsAA() { + return common.HexToAddress("0x"), nil + } + V := new(big.Int).Sub(tx.data.V, s.chainIdMul) V.Sub(V, big8) return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true) From 0c13218cdb265daf6681cc5cfc0000a9c173f77b Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Thu, 21 May 2020 13:03:14 -0400 Subject: [PATCH 03/26] Cost and Signing returns correct values for AA transactions --- core/tx_pool.go | 24 ++++++++++++++---------- core/types/transaction.go | 8 +++++++- core/types/transaction_signing.go | 2 +- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index e5286da7a2d9..37054e77e34b 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -59,6 +59,9 @@ var ( // Queue for AA transactions is already filled ErrAACapacity = errors.New("aa txs at capacity") + // When aa validation fails + ErrInvalidAA = errors.New("aa tx invalidated") + // ErrInvalidSender is returned if the transaction contains an invalid signature. ErrInvalidSender = errors.New("invalid sender") @@ -531,18 +534,16 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrGasLimit } - var from common.Address + // Make sure the transaction is signed properly + from, err := types.Sender(pool.signer, tx) + if err != nil { + return ErrInvalidSender + } if tx.IsAA() { - tx.Validate() - from = *tx.To() - // manage error logic here - } else { - // Make sure the transaction is signed properly - var err error - from, err = types.Sender(pool.signer, tx) + _, err := tx.Validate() if err != nil { - return ErrInvalidSender + return ErrInvalidAA } } @@ -551,15 +552,18 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 { return ErrUnderpriced } + // Ensure the transaction adheres to nonce ordering - if pool.currentState.GetNonce(from) > tx.Nonce() { + if !tx.IsAA() && (pool.currentState.GetNonce(from) > tx.Nonce()) { return ErrNonceTooLow } + // Transactor should have enough funds to cover the costs // cost == V + GP * GL if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 { return ErrInsufficientFunds } + // Ensure the transaction has more gas than the basic tx fee. intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, true, pool.istanbul) if err != nil { diff --git a/core/types/transaction.go b/core/types/transaction.go index 86e1eeebc1d5..9cf562aa0905 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -192,9 +192,11 @@ func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } -func (tx *Transaction) Validate() { +func (tx *Transaction) Validate() (big.Int, error) { + var limit = big.NewInt(1200) tx.price.Store(big.NewInt(1200)) // return error here + return *limit, nil } func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } @@ -276,6 +278,10 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e // Cost returns amount + gasprice * gaslimit. func (tx *Transaction) Cost() *big.Int { + if tx.IsAA() { + return new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.data.GasLimit)) + } + total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit)) total.Add(total, tx.data.Amount) return total diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 892be9862086..9c1accb16f4b 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -133,7 +133,7 @@ func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) { } if tx.IsAA() { - return common.HexToAddress("0x"), nil + return *tx.To(), nil } V := new(big.Int).Sub(tx.data.V, s.chainIdMul) From e6eb1a2f4ad908a180992a961c02ac2b8080216a Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Thu, 21 May 2020 14:04:51 -0400 Subject: [PATCH 04/26] Adjusted logic to allow for only one in pending/queued. Also remove transaction on reset then reinject --- core/tx_pool.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 37054e77e34b..cda414cdcf42 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -592,7 +592,10 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } if tx.IsAA() { - if pool.pending[*tx.To()] != nil { + current_pending = pool.pending[*tx.To()] + current_queue = pool.queue[*tx.To()] + + if current_pending != nil && current_queue != nil { return false, ErrAACapacity } @@ -1174,6 +1177,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { // for AA we need to re-validate accounts that were included // split into separate function + // This introduces some basic race conditions, so this needs to be refactored for _, tx := range included { var account = tx.To() if account != nil && tx.IsAA() { @@ -1182,12 +1186,14 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { } else { if pending := pool.pending[*account]; pending != nil { pending_tx := pending.txs.Flatten()[0] - pending_tx.Validate() + pool.removeTx(pending_tx.Hash(), true) + pool.add(pending_tx) } if queued := pool.queue[*account]; queued != nil { queued_tx := queued.txs.Flatten()[0] - queued_tx.Validate() + pool.removeTx(queued_tx.Hash()) + pool.add(queued_tx) } } } From 3a82c2e79902ddd3374883849f1752fbbcdecbbc Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Thu, 21 May 2020 14:22:59 -0400 Subject: [PATCH 05/26] Only one tx allowed per AA account --- core/tx_pool.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index cda414cdcf42..e4f6d8608b23 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -592,10 +592,12 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } if tx.IsAA() { - current_pending = pool.pending[*tx.To()] - current_queue = pool.queue[*tx.To()] + current_pending := pool.pending[*tx.To()] + current_queue := pool.queue[*tx.To()] - if current_pending != nil && current_queue != nil { + // If pending or queue have a transaction, then we are at capacity + // Expand soon to have one transaction waiting in the queue + if current_pending != nil || current_queue != nil { return false, ErrAACapacity } @@ -1187,13 +1189,15 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { if pending := pool.pending[*account]; pending != nil { pending_tx := pending.txs.Flatten()[0] pool.removeTx(pending_tx.Hash(), true) - pool.add(pending_tx) + // ignore locals for now + pool.add(pending_tx, false) } if queued := pool.queue[*account]; queued != nil { queued_tx := queued.txs.Flatten()[0] - pool.removeTx(queued_tx.Hash()) - pool.add(queued_tx) + pool.removeTx(queued_tx.Hash(), true) + // ignore locals for now + pool.add(queued_tx, false) } } } From f1946b9ca6205a9a2b81e4edce3055f55b91c31b Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Fri, 22 May 2020 11:08:18 -0400 Subject: [PATCH 06/26] Support new AA transaction for now. Minor bugs around gas price, limit --- core/types/transaction.go | 38 ++++++++++++++++++++++++++----- core/types/transaction_signing.go | 9 ++++---- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index 9cf562aa0905..6d641e7b2ec6 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -73,11 +73,11 @@ type txdataMarshaling struct { } func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { - return newTransaction(nonce, &to, amount, 5, gasPrice, data) + return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data) } func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { - return newTransaction(nonce, nil, amount, 5, gasPrice, data) + return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data) } func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { @@ -105,8 +105,33 @@ func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit return &Transaction{data: d} } +func NewAATransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { + if len(data) > 0 { + data = common.CopyBytes(data) + } + d := txdata{ + AccountNonce: nonce, + Recipient: to, + Payload: data, + Amount: new(big.Int), + GasLimit: gasLimit, + Price: new(big.Int), + V: big.NewInt(128 + 35), + R: new(big.Int), + S: new(big.Int), + } + if amount != nil { + d.Amount.Set(amount) + } + if gasPrice != nil { + d.Price.Set(gasPrice) + } + + return &Transaction{data: d} +} + func (tx *Transaction) IsAA() bool { - return (tx.data.R == common.Big0 && tx.data.S == common.Big0) + return (tx.data.R.Cmp(common.Big0) == 0 && tx.data.S.Cmp(common.Big0) == 0) } // ChainId returns which chain id this transaction was signed for (if at all) @@ -193,8 +218,8 @@ func (tx *Transaction) GasPrice() *big.Int { } func (tx *Transaction) Validate() (big.Int, error) { - var limit = big.NewInt(1200) - tx.price.Store(big.NewInt(1200)) + var limit = big.NewInt(40000) + tx.price.Store(*big.NewInt(10)) // return error here return *limit, nil } @@ -279,7 +304,8 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e // Cost returns amount + gasprice * gaslimit. func (tx *Transaction) Cost() *big.Int { if tx.IsAA() { - return new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.data.GasLimit)) + var price = tx.GasPrice() + return new(big.Int).Mul(price, new(big.Int).SetUint64(tx.data.GasLimit)) } total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit)) diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 9c1accb16f4b..b60fbfc65a72 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -125,17 +125,18 @@ func (s EIP155Signer) Equal(s2 Signer) bool { var big8 = big.NewInt(8) func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) { + if tx.IsAA() { + return *tx.To(), nil + } + if !tx.Protected() { return HomesteadSigner{}.Sender(tx) } + if tx.ChainId().Cmp(s.chainId) != 0 { return common.Address{}, ErrInvalidChainId } - if tx.IsAA() { - return *tx.To(), nil - } - V := new(big.Int).Sub(tx.data.V, s.chainIdMul) V.Sub(V, big8) return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true) From eecdb02ac154baf76e2c56f9c8b8cf18702596de Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Fri, 22 May 2020 11:09:21 -0400 Subject: [PATCH 07/26] Include initial set of passing tests to make sure queue and pool are limited to only one tx --- core/tx_pool_test.go | 65 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 78290862e03a..9594ba9b0ca4 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -72,6 +72,10 @@ func transaction(nonce uint64, gaslimit uint64, key *ecdsa.PrivateKey) *types.Tr return pricedTransaction(nonce, gaslimit, big.NewInt(1), key) } +func aaTransaction(gaslimit uint64, to common.Address) *types.Transaction { + return types.NewAATransaction(0, &to, big.NewInt(0), gaslimit, big.NewInt(0), nil) +} + func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction { tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, gasprice, nil), types.HomesteadSigner{}, key) return tx @@ -323,6 +327,67 @@ func TestTransactionQueue2(t *testing.T) { } } +func TestAAQueue2(t *testing.T) { + t.Parallel() + + pool, _ := setupTxPool() + defer pool.Stop() + key1, _ := crypto.GenerateKey() + address1 := crypto.PubkeyToAddress(key1.PublicKey) + + tx1 := aaTransaction(100, address1) + tx2 := aaTransaction(100, address1) + tx3 := aaTransaction(100, address1) + + tx1.Validate() + tx2.Validate() + tx3.Validate() + + pool.currentState.AddBalance(address1, big.NewInt(3000)) + pool.reset(nil, nil) + + pool.enqueueTx(tx1.Hash(), tx1) + pool.enqueueTx(tx2.Hash(), tx2) + pool.enqueueTx(tx3.Hash(), tx3) + + if pool.queue[address1].Len() != 1 { + t.Error("expected len(queue) == 1, got", pool.queue[address1].Len()) + } +} + +func TestAAPending2(t *testing.T) { + t.Parallel() + + pool, _ := setupTxPool() + defer pool.Stop() + key1, _ := crypto.GenerateKey() + address1 := crypto.PubkeyToAddress(key1.PublicKey) + + tx1 := aaTransaction(100, address1) + tx2 := aaTransaction(100, address1) + tx3 := aaTransaction(100, address1) + + tx1.Validate() + tx2.Validate() + tx3.Validate() + + pool.currentState.AddBalance(address1, big.NewInt(3000)) + pool.reset(nil, nil) + + pool.enqueueTx(tx1.Hash(), tx1) + pool.enqueueTx(tx2.Hash(), tx2) + pool.enqueueTx(tx3.Hash(), tx3) + pool.promoteExecutables([]common.Address{address1}) + + if pool.queue[address1] != nil { + t.Error("expected len(queue) == 0, got", pool.queue[address1].Len()) + } + + if len(pool.pending) != 1 { + t.Error("expected len(pending) == 1, got", len(pool.pending)) + } +} + func TestTransactionNegativeValue(t *testing.T) { t.Parallel() From 160921926262a0d67e229635e03552af1a653835 Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Thu, 28 May 2020 16:21:58 -0400 Subject: [PATCH 08/26] Update tx pool to validate incoming transactions then replace queue/pending if price is higher --- core/tx_pool.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index e4f6d8608b23..7e44bdc607b1 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -569,6 +569,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if err != nil { return err } + if tx.Gas() < intrGas { return ErrIntrinsicGas } @@ -597,7 +598,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e // If pending or queue have a transaction, then we are at capacity // Expand soon to have one transaction waiting in the queue - if current_pending != nil || current_queue != nil { + if current_pending != nil && current_queue != nil { return false, ErrAACapacity } From 108daa69a38efd448e1f6e409649282c66b7a9d2 Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Thu, 28 May 2020 16:25:44 -0400 Subject: [PATCH 09/26] Expand tests to check for queue, pending replace. 1 tx limit for AA. --- core/tx_pool_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 9594ba9b0ca4..bf2741ffc24a 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -327,6 +327,47 @@ func TestTransactionQueue2(t *testing.T) { } } +func TestAAQueue(t *testing.T) { + // should test that more expensive takes place in queue + // should make sure only one goes to pending + // need to test validation error + // need to test intrinsic gas + // need to test not enough gas + t.Parallel() + + pool, _ := setupTxPool() + defer pool.Stop() + key1, _ := crypto.GenerateKey() + address1 := crypto.PubkeyToAddress(key1.PublicKey) + + tx1 := aaTransaction(22000, address1) + + pool.currentState.AddBalance(address1, big.NewInt(1000000)) + <-pool.requestReset(nil, nil) + + pool.add(tx1, false) + + <-pool.requestPromoteExecutables(newAccountSet(pool.signer, address1)) + if len(pool.pending) != 1 { + t.Error("expected valid txs to be 1 is", len(pool.pending)) + } + + tx2 := aaTransaction(22000, address1) + pool.add(tx2, false) + + <-pool.requestPromoteExecutables(newAccountSet(pool.signer, address1)) + if len(pool.queue) > 0 { + t.Error("expected transaction queue to be empty. is", len(pool.queue)) + } + if len(pool.pending) > 1 { + t.Error("expected transaction queue to be empty. is", len(pool.queue)) + } +} + +func TestAAPending(t *testing.T) { + +} + func TestAAQueue2(t *testing.T) { t.Parallel() From c798f81b729c08bcaef5603a72042babfc3fa004 Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Fri, 29 May 2020 09:27:48 -0400 Subject: [PATCH 10/26] For now remove reliance on Sender() function for aa from/addr field --- core/tx_pool.go | 85 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 7e44bdc607b1..0a87b19277ca 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -535,7 +535,16 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { } // Make sure the transaction is signed properly - from, err := types.Sender(pool.signer, tx) + var from common.Address + var err error + + if tx.IsAA() { + from = *tx.To() + err = nil + } else { + from, err = types.Sender(pool.signer, tx) // already validated + } + if err != nil { return ErrInvalidSender } @@ -627,7 +636,13 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } } // Try to replace an existing transaction in the pending pool - from, _ := types.Sender(pool.signer, tx) // already validated + var from common.Address + if tx.IsAA() { + from = *tx.To() + } else { + from, _ = types.Sender(pool.signer, tx) // already validated + } + if list := pool.pending[from]; list != nil && list.Overlaps(tx) { // Nonce already pending, check if required price bump is met inserted, old := list.Add(tx, pool.config.PriceBump) @@ -674,7 +689,12 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e // Note, this method assumes the pool lock is held! func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, error) { // Try to insert the transaction into the future queue - from, _ := types.Sender(pool.signer, tx) // already validated + var from common.Address + if tx.IsAA() { + from = *tx.To() + } else { + from, _ = types.Sender(pool.signer, tx) // already validated + } if pool.queue[from] == nil { pool.queue[from] = newTxList(false, tx.IsAA()) @@ -871,7 +891,14 @@ func (pool *TxPool) Status(hashes []common.Hash) []TxStatus { if tx == nil { continue } - from, _ := types.Sender(pool.signer, tx) // already validated + + var from common.Address + if tx.IsAA() { + from = *tx.To() + } else { + from, _ = types.Sender(pool.signer, tx) // already validated + } + pool.mu.RLock() if txList := pool.pending[from]; txList != nil && txList.txs.items[tx.Nonce()] != nil { status[i] = TxStatusPending @@ -904,7 +931,13 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) { if tx == nil { return } - addr, _ := types.Sender(pool.signer, tx) // already validated during insertion + + var addr common.Address + if tx.IsAA() { + addr = *tx.To() + } else { + addr, _ = types.Sender(pool.signer, tx) // already validated + } // Remove it from the list of known transactions pool.all.Remove(hash) @@ -1027,7 +1060,14 @@ func (pool *TxPool) scheduleReorgLoop() { case tx := <-pool.queueTxEventCh: // Queue up the event, but don't schedule a reorg. It's up to the caller to // request one later if they want the events sent. - addr, _ := types.Sender(pool.signer, tx) + var addr common.Address + + if tx.IsAA() { + addr = *tx.To() + } else { + addr, _ = types.Sender(pool.signer, tx) // already validated + } + if _, ok := queuedEvents[addr]; !ok { queuedEvents[addr] = newTxSortedMap() } @@ -1076,7 +1116,15 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt // Check for pending transactions for every account that sent new ones promoted := pool.promoteExecutables(promoteAddrs) for _, tx := range promoted { - addr, _ := types.Sender(pool.signer, tx) + + var addr common.Address + + if tx.IsAA() { + addr = *tx.To() + } else { + addr, _ = types.Sender(pool.signer, tx) // already validated + } + if _, ok := events[addr]; !ok { events[addr] = newTxSortedMap() } @@ -1518,9 +1566,19 @@ func (as *accountSet) contains(addr common.Address) bool { // containsTx checks if the sender of a given tx is within the set. If the sender // cannot be derived, this method returns false. func (as *accountSet) containsTx(tx *types.Transaction) bool { - if addr, err := types.Sender(as.signer, tx); err == nil { + var addr common.Address + var err error + + if tx.IsAA() { + addr = *tx.To() + } else { + addr, err = types.Sender(as.signer, tx) // already validated + } + + if err == nil { return as.contains(addr) } + return false } @@ -1532,7 +1590,16 @@ func (as *accountSet) add(addr common.Address) { // addTx adds the sender of tx into the set. func (as *accountSet) addTx(tx *types.Transaction) { - if addr, err := types.Sender(as.signer, tx); err == nil { + var addr common.Address + var err error + + if tx.IsAA() { + addr = *tx.To() + } else { + addr, err = types.Sender(as.signer, tx) + } + + if err == nil { as.add(addr) } } From ce81904b59d19143aec267ad7332c98ac1d557fc Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Fri, 29 May 2020 12:56:26 -0400 Subject: [PATCH 11/26] Add some direction for making sure the right tx is swapped with 1 tx constraint. --- core/tx_pool_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index bf2741ffc24a..6249ea1ff451 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -384,7 +384,7 @@ func TestAAQueue2(t *testing.T) { tx2.Validate() tx3.Validate() - pool.currentState.AddBalance(address1, big.NewInt(3000)) + pool.currentState.AddBalance(address1, big.NewInt(1000000)) pool.reset(nil, nil) pool.enqueueTx(tx1.Hash(), tx1) @@ -394,9 +394,14 @@ func TestAAQueue2(t *testing.T) { if pool.queue[address1].Len() != 1 { t.Error("expected len(queue) == 1, got", pool.queue[address1].Len()) } + + // Need to add tests here to make sure higher gas aa transaction replace low priced + // if pool.queue[address1].txs.Flatten()[0].Hash() != tx1.Hash() { + // t.Error("expected higher gas price to take priority, expected hash", tx2.Hash()) + // } } -func TestAAPending2(t *testing.T) { +func TestAAPending(t *testing.T) { t.Parallel() pool, _ := setupTxPool() From 6ac99c31ce4f4fece6d41ac3a63c41f6ad5aba14 Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Mon, 1 Jun 2020 14:08:11 -0400 Subject: [PATCH 12/26] Remove logic that restricts new transactions from being validated. --- core/tx_pool.go | 137 +++++++++++++----------------------------------- 1 file changed, 36 insertions(+), 101 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 0a87b19277ca..91b6753eb605 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -534,16 +534,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrGasLimit } - // Make sure the transaction is signed properly - var from common.Address - var err error - - if tx.IsAA() { - from = *tx.To() - err = nil - } else { - from, err = types.Sender(pool.signer, tx) // already validated - } + addr, err := txSponsor(pool.signer, tx) if err != nil { return ErrInvalidSender @@ -557,19 +548,19 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { } // Drop non-local transactions under our own minimal accepted gas price - local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network + local = local || pool.locals.contains(addr) // account may be local even if the transaction arrived from the network if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 { return ErrUnderpriced } // Ensure the transaction adheres to nonce ordering - if !tx.IsAA() && (pool.currentState.GetNonce(from) > tx.Nonce()) { + if !tx.IsAA() && (pool.currentState.GetNonce(addr) > tx.Nonce()) { return ErrNonceTooLow } // Transactor should have enough funds to cover the costs // cost == V + GP * GL - if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 { + if pool.currentState.GetBalance(addr).Cmp(tx.Cost()) < 0 { return ErrInsufficientFunds } @@ -601,18 +592,6 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e return false, ErrAlreadyKnown } - if tx.IsAA() { - current_pending := pool.pending[*tx.To()] - current_queue := pool.queue[*tx.To()] - - // If pending or queue have a transaction, then we are at capacity - // Expand soon to have one transaction waiting in the queue - if current_pending != nil && current_queue != nil { - return false, ErrAACapacity - } - - } - // If the transaction fails basic validation, discard it if err := pool.validateTx(tx, local); err != nil { log.Trace("Discarding invalid transaction", "hash", hash, "err", err) @@ -636,14 +615,9 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } } // Try to replace an existing transaction in the pending pool - var from common.Address - if tx.IsAA() { - from = *tx.To() - } else { - from, _ = types.Sender(pool.signer, tx) // already validated - } + addr, _ := txSponsor(pool.signer, tx) - if list := pool.pending[from]; list != nil && list.Overlaps(tx) { + if list := pool.pending[addr]; list != nil && list.Overlaps(tx) { // Nonce already pending, check if required price bump is met inserted, old := list.Add(tx, pool.config.PriceBump) if !inserted { @@ -658,9 +632,9 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } pool.all.Add(tx) pool.priced.Put(tx) - pool.journalTx(from, tx) + pool.journalTx(addr, tx) pool.queueTxEvent(tx) - log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To()) + log.Trace("Pooled new executable transaction", "hash", hash, "sponsor", addr, "to", tx.To()) return old != nil, nil } // New transaction isn't replacing a pending one, push into queue @@ -670,17 +644,17 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } // Mark local addresses and journal local transactions if local { - if !pool.locals.contains(from) { - log.Info("Setting new local account", "address", from) - pool.locals.add(from) + if !pool.locals.contains(addr) { + log.Info("Setting new local account", "address", addr) + pool.locals.add(addr) } } - if local || pool.locals.contains(from) { + if local || pool.locals.contains(addr) { localGauge.Inc(1) } - pool.journalTx(from, tx) + pool.journalTx(addr, tx) - log.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To()) + log.Trace("Pooled new future transaction", "hash", hash, "from", addr, "to", tx.To()) return replaced, nil } @@ -688,21 +662,15 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e // // Note, this method assumes the pool lock is held! func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, error) { - // Try to insert the transaction into the future queue - var from common.Address - if tx.IsAA() { - from = *tx.To() - } else { - from, _ = types.Sender(pool.signer, tx) // already validated - } + addr, _ := txSponsor(pool.signer, tx) - if pool.queue[from] == nil { - pool.queue[from] = newTxList(false, tx.IsAA()) + if pool.queue[addr] == nil { + pool.queue[addr] = newTxList(false, tx.IsAA()) } // AA transactions all have the same nonce, so to replace its queue position // it needs to meet the price bump - inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump) + inserted, old := pool.queue[addr].Add(tx, pool.config.PriceBump) if !inserted { // An older transaction was better, discard this return false, ErrReplaceUnderpriced @@ -725,9 +693,9 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, er // journalTx adds the specified transaction to the local disk journal if it is // deemed to have been sent from a local account. -func (pool *TxPool) journalTx(from common.Address, tx *types.Transaction) { +func (pool *TxPool) journalTx(addr common.Address, tx *types.Transaction) { // Only journal if it's enabled and the transaction is local - if pool.journal == nil || !pool.locals.contains(from) { + if pool.journal == nil || !pool.locals.contains(addr) { return } if err := pool.journal.insert(tx); err != nil { @@ -892,17 +860,12 @@ func (pool *TxPool) Status(hashes []common.Hash) []TxStatus { continue } - var from common.Address - if tx.IsAA() { - from = *tx.To() - } else { - from, _ = types.Sender(pool.signer, tx) // already validated - } + addr, _ := txSponsor(pool.signer, tx) // already validated pool.mu.RLock() - if txList := pool.pending[from]; txList != nil && txList.txs.items[tx.Nonce()] != nil { + if txList := pool.pending[addr]; txList != nil && txList.txs.items[tx.Nonce()] != nil { status[i] = TxStatusPending - } else if txList := pool.queue[from]; txList != nil && txList.txs.items[tx.Nonce()] != nil { + } else if txList := pool.queue[addr]; txList != nil && txList.txs.items[tx.Nonce()] != nil { status[i] = TxStatusQueued } // implicit else: the tx may have been included into a block between @@ -932,12 +895,7 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) { return } - var addr common.Address - if tx.IsAA() { - addr = *tx.To() - } else { - addr, _ = types.Sender(pool.signer, tx) // already validated - } + addr, _ := txSponsor(pool.signer, tx) // Remove it from the list of known transactions pool.all.Remove(hash) @@ -1060,13 +1018,7 @@ func (pool *TxPool) scheduleReorgLoop() { case tx := <-pool.queueTxEventCh: // Queue up the event, but don't schedule a reorg. It's up to the caller to // request one later if they want the events sent. - var addr common.Address - - if tx.IsAA() { - addr = *tx.To() - } else { - addr, _ = types.Sender(pool.signer, tx) // already validated - } + addr, _ := txSponsor(pool.signer, tx) if _, ok := queuedEvents[addr]; !ok { queuedEvents[addr] = newTxSortedMap() @@ -1117,13 +1069,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt promoted := pool.promoteExecutables(promoteAddrs) for _, tx := range promoted { - var addr common.Address - - if tx.IsAA() { - addr = *tx.To() - } else { - addr, _ = types.Sender(pool.signer, tx) // already validated - } + addr, _ := txSponsor(pool.signer, tx) if _, ok := events[addr]; !ok { events[addr] = newTxSortedMap() @@ -1566,19 +1512,9 @@ func (as *accountSet) contains(addr common.Address) bool { // containsTx checks if the sender of a given tx is within the set. If the sender // cannot be derived, this method returns false. func (as *accountSet) containsTx(tx *types.Transaction) bool { - var addr common.Address - var err error - - if tx.IsAA() { - addr = *tx.To() - } else { - addr, err = types.Sender(as.signer, tx) // already validated - } - - if err == nil { + if addr, err := txSponsor(as.signer, tx); err == nil { return as.contains(addr) } - return false } @@ -1590,16 +1526,7 @@ func (as *accountSet) add(addr common.Address) { // addTx adds the sender of tx into the set. func (as *accountSet) addTx(tx *types.Transaction) { - var addr common.Address - var err error - - if tx.IsAA() { - addr = *tx.To() - } else { - addr, err = types.Sender(as.signer, tx) - } - - if err == nil { + if addr, err := txSponsor(as.signer, tx); err == nil { as.add(addr) } } @@ -1709,3 +1636,11 @@ func (t *txLookup) Remove(hash common.Hash) { func numSlots(tx *types.Transaction) int { return int((tx.Size() + txSlotSize - 1) / txSlotSize) } + +func txSponsor(s types.Signer, tx *types.Transaction) (common.Address, error) { + if tx.IsAA() { + return *tx.To(), nil + } + + return types.Sender(s, tx) +} From 5aad5a594cc539debbdd95c7753be31748dbcbc9 Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Mon, 1 Jun 2020 14:13:33 -0400 Subject: [PATCH 13/26] Remove ErrAACapacity. This isn't needed anymore. --- core/tx_pool.go | 3 --- core/tx_pool_test.go | 4 ---- 2 files changed, 7 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 91b6753eb605..164ffd981b0e 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -56,9 +56,6 @@ var ( // within the pool. ErrAlreadyKnown = errors.New("already known") - // Queue for AA transactions is already filled - ErrAACapacity = errors.New("aa txs at capacity") - // When aa validation fails ErrInvalidAA = errors.New("aa tx invalidated") diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 6249ea1ff451..f975965dea40 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -364,10 +364,6 @@ func TestAAQueue(t *testing.T) { } } -func TestAAPending(t *testing.T) { - -} - func TestAAQueue2(t *testing.T) { t.Parallel() From 9e8c99fb97baabcb47fbe1ae3a31a4b65c69922b Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Tue, 2 Jun 2020 13:50:18 -0400 Subject: [PATCH 14/26] Deal with edge case where a txlist is AA, but tx is not AA (or vice versa) --- core/tx_list.go | 6 +++--- core/tx_pool.go | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/core/tx_list.go b/core/tx_list.go index e57b06fe5fde..4a64f9ecd4e3 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -225,17 +225,17 @@ type txList struct { costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit) - IsAA bool + isAA bool } // newTxList create a new transaction list for maintaining nonce-indexable fast, // gapped, sortable transaction lists. -func newTxList(strict bool, IsAA bool) *txList { +func newTxList(strict bool, isAA bool) *txList { return &txList{ strict: strict, txs: newTxSortedMap(), costcap: new(big.Int), - IsAA: IsAA, + isAA: isAA, } } diff --git a/core/tx_pool.go b/core/tx_pool.go index 164ffd981b0e..6d1c3f32bce2 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -59,6 +59,9 @@ var ( // When aa validation fails ErrInvalidAA = errors.New("aa tx invalidated") + // When incoming tx is not AA for an AA account + ErrNotAA = errors.New("tx is not AA") + // ErrInvalidSender is returned if the transaction contains an invalid signature. ErrInvalidSender = errors.New("invalid sender") @@ -595,6 +598,32 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e invalidTxMeter.Mark(1) return false, err } + + addr, _ := txSponsor(pool.signer, tx) + txIsAA := tx.IsAA() + pending := pool.pending[addr] + queue := pool.queue[addr] + + pendingIsAA := pending != nil && pending.isAA + queueIsAA := queue != nil && queue.isAA + + if pendingIsAA || queueIsAA { + return false, ErrNotAA + } + + // If the txlist was not marked as AA, then we remove its current transactions + if pending != nil && !pendingIsAA && txIsAA { + for _, tx := range pending.Flatten() { + pool.removeTx(tx.Hash(), true) + } + } + + if queue != nil && !queueIsAA && txIsAA { + for _, tx := range queue.Flatten() { + pool.removeTx(tx.Hash(), true) + } + } + // If the transaction pool is full, discard underpriced transactions if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue { // If the new transaction is underpriced, don't accept it @@ -611,9 +640,8 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e pool.removeTx(tx.Hash(), false) } } - // Try to replace an existing transaction in the pending pool - addr, _ := txSponsor(pool.signer, tx) + // Try to replace an existing transaction in the pending pool if list := pool.pending[addr]; list != nil && list.Overlaps(tx) { // Nonce already pending, check if required price bump is met inserted, old := list.Add(tx, pool.config.PriceBump) @@ -1087,7 +1115,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt for addr, list := range pool.pending { txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway - if !list.IsAA { + if !list.isAA { pool.pendingNonces.set(addr, txs[len(txs)-1].Nonce()+1) } } From 4bbb669da156305743b224babe294f413490b2cd Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Tue, 9 Jun 2020 15:37:16 -0400 Subject: [PATCH 15/26] Remove checks for a conflict betweeen possible incoming AA/nonAA transactions. This should not be possible statistically. --- core/tx_pool.go | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index c0d6bc0bcd7b..d50dab35ea73 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -623,31 +623,6 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e return false, err } - addr, _ := txSponsor(pool.signer, tx) - txIsAA := tx.IsAA() - pending := pool.pending[addr] - queue := pool.queue[addr] - - pendingIsAA := pending != nil && pending.isAA - queueIsAA := queue != nil && queue.isAA - - if pendingIsAA || queueIsAA { - return false, ErrNotAA - } - - // If the txlist was not marked as AA, then we remove its current transactions - if pending != nil && !pendingIsAA && txIsAA { - for _, tx := range pending.Flatten() { - pool.removeTx(tx.Hash(), true) - } - } - - if queue != nil && !queueIsAA && txIsAA { - for _, tx := range queue.Flatten() { - pool.removeTx(tx.Hash(), true) - } - } - // If the transaction pool is full, discard underpriced transactions if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue { // If the new transaction is underpriced, don't accept it @@ -665,6 +640,8 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } } + addr, _ := txSponsor(pool.signer, tx) + // Try to replace an existing transaction in the pending pool if list := pool.pending[addr]; list != nil && list.Overlaps(tx) { // Nonce already pending, check if required price bump is met From 46cc2fcbb87d5c6f3c84ac0f8f969d32f1832ca1 Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Tue, 9 Jun 2020 15:40:36 -0400 Subject: [PATCH 16/26] Move validation timers to a more appropriate space. --- core/tx_pool.go | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index d50dab35ea73..84bd973cd54a 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -530,29 +530,24 @@ func (pool *TxPool) local() map[common.Address]types.Transactions { // validateTx checks whether a transaction is valid according to the consensus // rules and adheres to some heuristic limits of the local node (price and size). func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { - var start = time.Now() validationMeter.Mark(1) // Reject transactions over defined size to prevent DOS attacks if uint64(tx.Size()) > txMaxSize { - validationTimer.UpdateSince(start) return ErrOversizedData } // Transactions can't be negative. This may never happen using RLP decoded // transactions but may occur if you create a transaction using the RPC. if tx.Value().Sign() < 0 { - validationTimer.UpdateSince(start) return ErrNegativeValue } // Ensure the transaction doesn't exceed the current block limit gas. if pool.currentMaxGas < tx.Gas() { - validationTimer.UpdateSince(start) return ErrGasLimit } addr, err := txSponsor(pool.signer, tx) if err != nil { - validationTimer.UpdateSince(start) return ErrInvalidSender } @@ -566,37 +561,30 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { // Drop non-local transactions under our own minimal accepted gas price local = local || pool.locals.contains(addr) // account may be local even if the transaction arrived from the network if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 { - validationTimer.UpdateSince(start) return ErrUnderpriced } // Ensure the transaction adheres to nonce ordering if !tx.IsAA() && (pool.currentState.GetNonce(addr) > tx.Nonce()) { - validationTimer.UpdateSince(start) return ErrNonceTooLow } // Transactor should have enough funds to cover the costs // cost == V + GP * GL if pool.currentState.GetBalance(addr).Cmp(tx.Cost()) < 0 { - validationTimer.UpdateSince(start) return ErrInsufficientFunds } // Ensure the transaction has more gas than the basic tx fee. intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, true, pool.istanbul) if err != nil { - validationTimer.UpdateSince(start) return err } if tx.Gas() < intrGas { - validationTimer.UpdateSince(start) return ErrIntrinsicGas } - validationTimer.UpdateSince(start) - successValidationTimer.UpdateSince(start) return nil } @@ -617,11 +605,15 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } // If the transaction fails basic validation, discard it + var start = time.Now() if err := pool.validateTx(tx, local); err != nil { + validationTimer.UpdateSince(start) log.Trace("Discarding invalid transaction", "hash", hash, "err", err) invalidTxMeter.Mark(1) return false, err } + validationTimer.UpdateSince(start) + successValidationTimer.UpdateSince(start) // If the transaction pool is full, discard underpriced transactions if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue { From 0e7626bc616fc4c5a171efbf4896f00f8d64044f Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Tue, 9 Jun 2020 15:48:44 -0400 Subject: [PATCH 17/26] Removed unnecessary workarounds with Ansgar's submitted work --- core/aa_transaction_test.go | 2 +- core/transaction_validator.go | 7 ++++--- core/types/transaction.go | 4 ---- core/types/transaction_signing.go | 6 ------ 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/core/aa_transaction_test.go b/core/aa_transaction_test.go index b97d64aa6951..586fae4ee373 100644 --- a/core/aa_transaction_test.go +++ b/core/aa_transaction_test.go @@ -159,7 +159,7 @@ func TestMalformedTransaction(t *testing.T) { key, _ = crypto.GenerateKey() tx = pricedTransaction(0, 100000, big.NewInt(0), key) err = Validate(tx, types.HomesteadSigner{}, vmenv, 400000) - expectedErr = ErrMalformedTransaction + expectedErr = ErrMalformedAATransaction ) if err != expectedErr { t.Error("\n\texpected:", expectedErr, "\n\tgot:", err) diff --git a/core/transaction_validator.go b/core/transaction_validator.go index fc82d39a2dad..21eb58b91224 100644 --- a/core/transaction_validator.go +++ b/core/transaction_validator.go @@ -18,13 +18,14 @@ package core import ( "errors" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" ) var ( - ErrIncorrectAAConfig = errors.New("incorrect AA config for EVM") - ErrMalformedTransaction = errors.New("AA transaction malformed") + ErrIncorrectAAConfig = errors.New("incorrect AA config for EVM") + ErrMalformedAATransaction = errors.New("AA transaction malformed") ) func Validate(tx *types.Transaction, s types.Signer, evm *vm.EVM, gasLimit uint64) error { @@ -39,7 +40,7 @@ func Validate(tx *types.Transaction, s types.Signer, evm *vm.EVM, gasLimit uint6 if err != nil { return err } else if !msg.IsAA() { - return ErrMalformedTransaction + return ErrMalformedAATransaction } msg.SetGas(gasLimit) gp := new(GasPool).AddGas(gasLimit) diff --git a/core/types/transaction.go b/core/types/transaction.go index 81bdb87113b4..c5351cf5b1fa 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -130,10 +130,6 @@ func NewAATransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimi return &Transaction{data: d} } -func (tx *Transaction) IsAA() bool { - return (tx.data.R.Cmp(common.Big0) == 0 && tx.data.S.Cmp(common.Big0) == 0) -} - // ChainId returns which chain id this transaction was signed for (if at all) func (tx *Transaction) ChainId() *big.Int { return deriveChainId(tx.data.V) diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index bf060b0e2f77..7946d5cea3fe 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -125,18 +125,12 @@ func (s EIP155Signer) Equal(s2 Signer) bool { var big8 = big.NewInt(8) func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) { - if tx.IsAA() { - return *tx.To(), nil - } - if !tx.Protected() { return HomesteadSigner{}.Sender(tx) } - if tx.ChainId().Cmp(s.chainId) != 0 { return common.Address{}, ErrInvalidChainId } - V := new(big.Int).Sub(tx.data.V, s.chainIdMul) if tx.data.R.Sign() == 0 && tx.data.S.Sign() == 0 && V.BitLen() <= 8 && V.Uint64() == 35 { return common.NewEntryPointAddress(), nil From e17614a2e33f3653d6d67f5c12908f8dde62458b Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Tue, 9 Jun 2020 16:32:32 -0400 Subject: [PATCH 18/26] Removed nonce exception for aa transactions, since we read it from the submitted field. --- core/types/transaction.go | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index c5351cf5b1fa..374fab42adbb 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -105,31 +105,6 @@ func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit return &Transaction{data: d} } -func NewAATransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { - if len(data) > 0 { - data = common.CopyBytes(data) - } - d := txdata{ - AccountNonce: nonce, - Recipient: to, - Payload: data, - Amount: new(big.Int), - GasLimit: gasLimit, - Price: new(big.Int), - V: big.NewInt(128 + 35), - R: new(big.Int), - S: new(big.Int), - } - if amount != nil { - d.Amount.Set(amount) - } - if gasPrice != nil { - d.Price.Set(gasPrice) - } - - return &Transaction{data: d} -} - // ChainId returns which chain id this transaction was signed for (if at all) func (tx *Transaction) ChainId() *big.Int { return deriveChainId(tx.data.V) @@ -221,13 +196,7 @@ func (tx *Transaction) Validate() (big.Int, error) { } func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } -func (tx *Transaction) Nonce() uint64 { - if tx.IsAA() { - return 0 - } - - return tx.data.AccountNonce -} +func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce } func (tx *Transaction) CheckNonce() bool { return true } From 0c897bc3247d93f138c47dcc90e0c397e4a50b1a Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Wed, 10 Jun 2020 11:05:03 -0400 Subject: [PATCH 19/26] Add proper aa validation, also expand the blockchain interface to support. --- core/tx_pool.go | 28 ++++++++++++++++++++++++---- core/types/transaction.go | 2 ++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 84bd973cd54a..d6643614edb6 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -26,8 +26,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/prque" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -59,8 +61,8 @@ var ( // When aa validation fails ErrInvalidAA = errors.New("aa tx invalidated") - // When incoming tx is not AA for an AA account - ErrNotAA = errors.New("tx is not AA") + // AA values are not zeroed out + ErrInvalidAAData = errors.New("aa values not zeroed out") // ErrInvalidSender is returned if the transaction contains an invalid signature. ErrInvalidSender = errors.New("invalid sender") @@ -140,10 +142,12 @@ const ( // blockChain provides the state of blockchain and current gas limit to do // some pre checks in tx pool and event subscribers. type blockChain interface { + Config() *params.ChainConfig CurrentBlock() *types.Block + GetHeader(common.Hash, uint64) *types.Header GetBlock(hash common.Hash, number uint64) *types.Block StateAt(root common.Hash) (*state.StateDB, error) - + Engine() consensus.Engine SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription } @@ -545,6 +549,20 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrGasLimit } + if tx.IsAA() { + if tx.Value().Sign() > 0 { + return ErrInvalidAAData + } + + if tx.RawGasPrice().Sign() != 0 { + return ErrInvalidAAData + } + + if tx.Nonce() != 0 { + return ErrInvalidAAData + } + } + addr, err := txSponsor(pool.signer, tx) if err != nil { @@ -552,7 +570,9 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { } if tx.IsAA() { - _, err := tx.Validate() + context := NewEVMContext(types.AADummyMessage, pool.chain.CurrentBlock().Header(), pool.chain, &common.Address{}) + vm := vm.NewEVM(context, pool.currentState, pool.chain.Config(), vm.Config{PaygasMode: vm.PaygasHalt}) + err := Validate(tx, pool.signer, vm, 400000) if err != nil { return ErrInvalidAA } diff --git a/core/types/transaction.go b/core/types/transaction.go index 374fab42adbb..11208c2aa2a5 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -188,6 +188,8 @@ func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } +func (tx *Transaction) RawGasPrice() *big.Int { return tx.data.Price } + func (tx *Transaction) Validate() (big.Int, error) { var limit = big.NewInt(40000) tx.price.Store(*big.NewInt(10)) From a3ce459ed8c7de1d27b8a359889f6e0cb95d29e4 Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Wed, 10 Jun 2020 11:28:33 -0400 Subject: [PATCH 20/26] Add aa validation timer & update dummy message to entry message --- core/aa_transaction_test.go | 6 +++--- core/tx_pool.go | 5 ++++- core/types/transaction.go | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/core/aa_transaction_test.go b/core/aa_transaction_test.go index 586fae4ee373..eb34c3aba6ca 100644 --- a/core/aa_transaction_test.go +++ b/core/aa_transaction_test.go @@ -65,7 +65,7 @@ func setupBlockchain(blockGasLimit uint64) *BlockChain { func testValidate(blockchain *BlockChain, statedb *state.StateDB, transaction *types.Transaction, validationGasLimit uint64, expectedErr error, t *testing.T) { var ( snapshotRevisionId = statedb.Snapshot() - context = NewEVMContext(types.AADummyMessage, blockchain.CurrentHeader(), blockchain, &common.Address{}) + context = NewEVMContext(types.AAEntryMessage, blockchain.CurrentHeader(), blockchain, &common.Address{}) vmenv = vm.NewEVM(context, statedb, blockchain.Config(), vm.Config{PaygasMode: vm.PaygasHalt}) err = Validate(transaction, types.HomesteadSigner{}, vmenv, validationGasLimit) ) @@ -136,7 +136,7 @@ func TestInvalidEVMConfig(t *testing.T) { blockchain = setupBlockchain(10000000) statedb, _ = blockchain.State() - context = NewEVMContext(types.AADummyMessage, blockchain.CurrentHeader(), blockchain, &common.Address{}) + context = NewEVMContext(types.AAEntryMessage, blockchain.CurrentHeader(), blockchain, &common.Address{}) vmenv = vm.NewEVM(context, statedb, blockchain.Config(), vm.Config{}) tx = &types.Transaction{} @@ -153,7 +153,7 @@ func TestMalformedTransaction(t *testing.T) { blockchain = setupBlockchain(10000000) statedb, _ = blockchain.State() - context = NewEVMContext(types.AADummyMessage, blockchain.CurrentHeader(), blockchain, &common.Address{}) + context = NewEVMContext(types.AAEntryMessage, blockchain.CurrentHeader(), blockchain, &common.Address{}) vmenv = vm.NewEVM(context, statedb, blockchain.Config(), vm.Config{PaygasMode: vm.PaygasHalt}) key, _ = crypto.GenerateKey() diff --git a/core/tx_pool.go b/core/tx_pool.go index d6643614edb6..c6cee0e8150e 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -127,6 +127,7 @@ var ( validationMeter = metrics.NewRegisteredMeter("txpool/validation", nil) validationTimer = metrics.NewRegisteredTimer("txpool/validation/execution", nil) successValidationTimer = metrics.NewRegisteredTimer("txpool/validation_success/execution", nil) + aaValidationTimer = metrics.NewRegisteredTimer("txpool/aa_validation/execution", nil) ) // TxStatus is the current status of a transaction as seen by the pool. @@ -570,9 +571,11 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { } if tx.IsAA() { - context := NewEVMContext(types.AADummyMessage, pool.chain.CurrentBlock().Header(), pool.chain, &common.Address{}) + now := time.Now() + context := NewEVMContext(types.AAEntryMessage, pool.chain.CurrentBlock().Header(), pool.chain, &common.Address{}) vm := vm.NewEVM(context, pool.currentState, pool.chain.Config(), vm.Config{PaygasMode: vm.PaygasHalt}) err := Validate(tx, pool.signer, vm, 400000) + aaValidationTimer.UpdateSince(now) if err != nil { return ErrInvalidAA } diff --git a/core/types/transaction.go b/core/types/transaction.go index 11208c2aa2a5..bbe0f2bf8811 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -441,7 +441,7 @@ type Message struct { isAA bool } -var AADummyMessage = Message{from: common.NewEntryPointAddress(), gasPrice: big.NewInt(0)} +var AAEntryMessage = Message{from: common.NewEntryPointAddress(), gasPrice: big.NewInt(0)} func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool) Message { isAA := from.IsEntryPoint() From 6a78ca161d5faba18c8afd111ae89f04f4d46d05 Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Wed, 10 Jun 2020 15:43:22 -0400 Subject: [PATCH 21/26] Remove mock validate and return proper type for GasPrice() --- core/types/transaction.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index bbe0f2bf8811..64cf7d747a48 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -178,8 +178,8 @@ func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit } func (tx *Transaction) GasPrice() *big.Int { if tx.IsAA() { if price := tx.price.Load(); price != nil { - var current_price big.Int = price.(big.Int) - return ¤t_price + var current_price *big.Int = price.(*big.Int) + return current_price } return nil @@ -190,13 +190,6 @@ func (tx *Transaction) GasPrice() *big.Int { func (tx *Transaction) RawGasPrice() *big.Int { return tx.data.Price } -func (tx *Transaction) Validate() (big.Int, error) { - var limit = big.NewInt(40000) - tx.price.Store(*big.NewInt(10)) - // return error here - return *limit, nil -} - func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce } From 31005ffa720cda6364df5486a10cb2339c02d11c Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Wed, 10 Jun 2020 15:44:10 -0400 Subject: [PATCH 22/26] Add expansive testing on pending, queue for aa transactions --- core/tx_pool_test.go | 186 ++++++++++++++++++++++++++++++++----------- 1 file changed, 138 insertions(+), 48 deletions(-) diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index f975965dea40..06d467fe8e5a 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -27,6 +27,8 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -56,10 +58,18 @@ func (bc *testBlockChain) CurrentBlock() *types.Block { }, nil, nil, nil) } +func (bc *testBlockChain) Config() *params.ChainConfig { return params.AllEthashProtocolChanges } + +func (bc *testBlockChain) Engine() consensus.Engine { return ethash.NewFaker() } + func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { return bc.CurrentBlock() } +func (bc *testBlockChain) GetHeader(hash common.Hash, number uint64) *types.Header { + return bc.CurrentBlock().Header() +} + func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { return bc.statedb, nil } @@ -72,10 +82,6 @@ func transaction(nonce uint64, gaslimit uint64, key *ecdsa.PrivateKey) *types.Tr return pricedTransaction(nonce, gaslimit, big.NewInt(1), key) } -func aaTransaction(gaslimit uint64, to common.Address) *types.Transaction { - return types.NewAATransaction(0, &to, big.NewInt(0), gaslimit, big.NewInt(0), nil) -} - func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction { tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, gasprice, nil), types.HomesteadSigner{}, key) return tx @@ -328,39 +334,45 @@ func TestTransactionQueue2(t *testing.T) { } func TestAAQueue(t *testing.T) { - // should test that more expensive takes place in queue - // should make sure only one goes to pending - // need to test validation error - // need to test intrinsic gas - // need to test not enough gas t.Parallel() pool, _ := setupTxPool() defer pool.Stop() key1, _ := crypto.GenerateKey() + address1 := crypto.PubkeyToAddress(key1.PublicKey) - tx1 := aaTransaction(22000, address1) + tx1 := aaTransaction(address1, 50000, 1, false, big.NewInt(1)) + tx2 := aaTransaction(address1, 50000, 1, true, big.NewInt(1)) + tx3 := aaTransaction(address1, 50000, 1, true, big.NewInt(1)) + pool.currentState.SetCode(address1, common.FromHex(contractCode)) pool.currentState.AddBalance(address1, big.NewInt(1000000)) - <-pool.requestReset(nil, nil) pool.add(tx1, false) + <-pool.requestReset(nil, nil) - <-pool.requestPromoteExecutables(newAccountSet(pool.signer, address1)) - if len(pool.pending) != 1 { - t.Error("expected valid txs to be 1 is", len(pool.pending)) + // any aa validation error (in this case, does not call paygas) + // should not progress the tx + if len(pool.queue) != 0 { + t.Error("expected valid txs to be 0 is", len(pool.queue)) } - tx2 := aaTransaction(22000, address1) pool.add(tx2, false) + if len(pool.queue) != 1 { + t.Error("expected valid txs to be 1 is", len(pool.queue)) + } + <-pool.requestPromoteExecutables(newAccountSet(pool.signer, address1)) + + pool.add(tx3, false) + if len(pool.queue) > 0 { t.Error("expected transaction queue to be empty. is", len(pool.queue)) } if len(pool.pending) > 1 { - t.Error("expected transaction queue to be empty. is", len(pool.queue)) + t.Error("expected pending to have a transaction", len(pool.pending)) } } @@ -370,31 +382,29 @@ func TestAAQueue2(t *testing.T) { pool, _ := setupTxPool() defer pool.Stop() key1, _ := crypto.GenerateKey() - address1 := crypto.PubkeyToAddress(key1.PublicKey) - tx1 := aaTransaction(100, address1) - tx2 := aaTransaction(100, address1) - tx3 := aaTransaction(100, address1) + address1 := crypto.PubkeyToAddress(key1.PublicKey) - tx1.Validate() - tx2.Validate() - tx3.Validate() + tx1 := aaTransaction(address1, 50000, 1, true, big.NewInt(1)) + tx2 := aaTransaction(address1, 50000, 1, true, big.NewInt(2)) + tx3 := aaTransaction(address1, 50000, 1, true, big.NewInt(2)) + pool.currentState.SetCode(address1, common.FromHex(contractCode)) pool.currentState.AddBalance(address1, big.NewInt(1000000)) - pool.reset(nil, nil) - pool.enqueueTx(tx1.Hash(), tx1) - pool.enqueueTx(tx2.Hash(), tx2) - pool.enqueueTx(tx3.Hash(), tx3) + pool.reset(nil, nil) + pool.add(tx1, false) + pool.add(tx2, false) + pool.add(tx3, false) - if pool.queue[address1].Len() != 1 { - t.Error("expected len(queue) == 1, got", pool.queue[address1].Len()) + if len(pool.queue) != 1 { + t.Error("expected valid txs to be 1 is", len(pool.queue)) } - // Need to add tests here to make sure higher gas aa transaction replace low priced - // if pool.queue[address1].txs.Flatten()[0].Hash() != tx1.Hash() { - // t.Error("expected higher gas price to take priority, expected hash", tx2.Hash()) - // } + // higher priced transaction should replace the lower priced + if pool.queue[address1].txs.Flatten()[0].Hash() != tx2.Hash() { + t.Error("expected higher gas price transaction to be accepted") + } } func TestAAPending(t *testing.T) { @@ -403,33 +413,113 @@ func TestAAPending(t *testing.T) { pool, _ := setupTxPool() defer pool.Stop() key1, _ := crypto.GenerateKey() + address1 := crypto.PubkeyToAddress(key1.PublicKey) - tx1 := aaTransaction(100, address1) - tx2 := aaTransaction(100, address1) - tx3 := aaTransaction(100, address1) + tx1 := aaTransaction(address1, 50000, 1, true, big.NewInt(1)) + tx2 := aaTransaction(address1, 50000, 1, true, big.NewInt(2)) + tx3 := aaTransaction(address1, 50000, 1, true, big.NewInt(2)) - tx1.Validate() - tx2.Validate() - tx3.Validate() + pool.currentState.SetCode(address1, common.FromHex(contractCode)) + pool.currentState.AddBalance(address1, big.NewInt(1000000)) - pool.currentState.AddBalance(address1, big.NewInt(3000)) - pool.reset(nil, nil) + <-pool.requestReset(nil, nil) - pool.enqueueTx(tx1.Hash(), tx1) - pool.enqueueTx(tx2.Hash(), tx2) - pool.enqueueTx(tx3.Hash(), tx3) - pool.promoteExecutables([]common.Address{address1}) + pool.add(tx1, false) + <-pool.requestPromoteExecutables(newAccountSet(pool.signer, address1)) - if pool.queue[address1] != nil { - t.Error("expected len(queue) == 0, got", pool.queue[address1].Len()) + if len(pool.queue) > 0 { + t.Error("expected transaction queue to be empty. is", len(pool.queue)) + } + if len(pool.pending) != 1 { + t.Error("expected pending to have a transaction", len(pool.pending)) } + pool.add(tx2, false) + pool.add(tx3, false) + + // Should replace the current pending directly + if len(pool.queue) > 0 { + t.Error("expected transaction queue to be empty. is", len(pool.queue)) + } if len(pool.pending) != 1 { - t.Error("expected len(pending) == 1, got", len(pool.pending)) + t.Error("expected pending to have a transaction", len(pool.pending)) + } + if pool.pending[address1].txs.Flatten()[0].Hash() != tx2.Hash() { + t.Error("expected higher gas price transaction to be accepted") } } +// func TestAAQueue2(t *testing.T) { +// t.Parallel() + +// pool, _ := setupTxPool() +// defer pool.Stop() +// key1, _ := crypto.GenerateKey() +// address1 := crypto.PubkeyToAddress(key1.PublicKey) + +// tx1 := aaTransaction(address1, 10000, 1, true, big.NewInt(1)) +// tx2 := aaTransaction(address1, 10000, 1, true, big.NewInt(1)) +// tx3 := aaTransaction(address1, 10000, 1, true, big.NewInt(1)) + +// tx1.Validate() +// tx2.Validate() +// tx3.Validate() + +// pool.currentState.AddBalance(address1, big.NewInt(1000000)) +// pool.currentState.SetCode(address1, common.FromHex(contractCode)) + +// pool.reset(nil, nil) + +// pool.enqueueTx(tx1.Hash(), tx1) +// pool.enqueueTx(tx2.Hash(), tx2) +// pool.enqueueTx(tx3.Hash(), tx3) + +// if pool.queue[address1].Len() != 1 { +// t.Error("expected len(queue) == 1, got", pool.queue[address1].Len()) +// } + +// Need to add tests here to make sure higher gas aa transaction replace low priced +// if pool.queue[address1].txs.Flatten()[0].Hash() != tx1.Hash() { +// t.Error("expected higher gas price to take priority, expected hash", tx2.Hash()) +// } +// } + +// func TestAAPending(t *testing.T) { +// t.Parallel() + +// pool, _ := setupTxPool() +// defer pool.Stop() +// key1, _ := crypto.GenerateKey() +// address1 := crypto.PubkeyToAddress(key1.PublicKey) + +// tx1 := aaTransaction(address1, 10000, 1, true, big.NewInt(1)) +// tx2 := aaTransaction(address1, 10000, 1, true, big.NewInt(1)) +// tx3 := aaTransaction(address1, 10000, 1, true, big.NewInt(1)) + +// tx1.Validate() +// tx2.Validate() +// tx3.Validate() + +// pool.currentState.AddBalance(address1, big.NewInt(3000)) +// pool.currentState.SetCode(address1, common.FromHex(contractCode)) + +// pool.reset(nil, nil) + +// pool.enqueueTx(tx1.Hash(), tx1) +// pool.enqueueTx(tx2.Hash(), tx2) +// pool.enqueueTx(tx3.Hash(), tx3) +// pool.promoteExecutables([]common.Address{address1}) + +// if pool.queue[address1] != nil { +// t.Error("expected len(queue) == 0, got", pool.queue[address1].Len()) +// } + +// if len(pool.pending) != 1 { +// t.Error("expected len(pending) == 1, got", len(pool.pending)) +// } +// } + func TestTransactionNegativeValue(t *testing.T) { t.Parallel() From 253b6845acb10c6078dfef7c1d3be340a9a3dd93 Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Wed, 10 Jun 2020 15:56:01 -0400 Subject: [PATCH 23/26] Set config for AA gas limit allowed --- core/tx_pool.go | 8 +++++--- core/tx_pool_test.go | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index c6cee0e8150e..45026f7e8b92 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -167,7 +167,8 @@ type TxPoolConfig struct { AccountQueue uint64 // Maximum number of non-executable transaction slots permitted per account GlobalQueue uint64 // Maximum number of non-executable transaction slots for all accounts - Lifetime time.Duration // Maximum amount of time non-executable transaction are queued + Lifetime time.Duration // Maximum amount of time non-executable transaction are queued + AAGasLimit uint64 // Maximum gas used for AA validation } // DefaultTxPoolConfig contains the default configurations for the transaction @@ -184,7 +185,8 @@ var DefaultTxPoolConfig = TxPoolConfig{ AccountQueue: 64, GlobalQueue: 1024, - Lifetime: 3 * time.Hour, + Lifetime: 3 * time.Hour, + AAGasLimit: 400000, } // sanitize checks the provided user configurations and changes anything that's @@ -574,7 +576,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { now := time.Now() context := NewEVMContext(types.AAEntryMessage, pool.chain.CurrentBlock().Header(), pool.chain, &common.Address{}) vm := vm.NewEVM(context, pool.currentState, pool.chain.Config(), vm.Config{PaygasMode: vm.PaygasHalt}) - err := Validate(tx, pool.signer, vm, 400000) + err := Validate(tx, pool.signer, vm, pool.config.AAGasLimit) aaValidationTimer.UpdateSince(now) if err != nil { return ErrInvalidAA diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 06d467fe8e5a..ae134b555969 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -339,26 +339,32 @@ func TestAAQueue(t *testing.T) { pool, _ := setupTxPool() defer pool.Stop() key1, _ := crypto.GenerateKey() + config := testTxPoolConfig + beforeLimit := config.AAGasLimit + config.AAGasLimit = 400000 address1 := crypto.PubkeyToAddress(key1.PublicKey) tx1 := aaTransaction(address1, 50000, 1, false, big.NewInt(1)) - tx2 := aaTransaction(address1, 50000, 1, true, big.NewInt(1)) + tx2 := aaTransaction(address1, 400000, 16000, true, big.NewInt(1)) tx3 := aaTransaction(address1, 50000, 1, true, big.NewInt(1)) + tx4 := aaTransaction(address1, 50000, 1, true, big.NewInt(2)) pool.currentState.SetCode(address1, common.FromHex(contractCode)) pool.currentState.AddBalance(address1, big.NewInt(1000000)) - pool.add(tx1, false) <-pool.requestReset(nil, nil) + pool.add(tx1, false) + pool.add(tx2, false) // any aa validation error (in this case, does not call paygas) // should not progress the tx + // Also if gas required is greater than the config max if len(pool.queue) != 0 { t.Error("expected valid txs to be 0 is", len(pool.queue)) } - pool.add(tx2, false) + pool.add(tx3, false) if len(pool.queue) != 1 { t.Error("expected valid txs to be 1 is", len(pool.queue)) @@ -366,7 +372,7 @@ func TestAAQueue(t *testing.T) { <-pool.requestPromoteExecutables(newAccountSet(pool.signer, address1)) - pool.add(tx3, false) + pool.add(tx4, false) if len(pool.queue) > 0 { t.Error("expected transaction queue to be empty. is", len(pool.queue)) @@ -374,6 +380,8 @@ func TestAAQueue(t *testing.T) { if len(pool.pending) > 1 { t.Error("expected pending to have a transaction", len(pool.pending)) } + + config.AAGasLimit = beforeLimit } func TestAAQueue2(t *testing.T) { From 82a54d0bda810a8c26ebe72a49f3c8782ac19131 Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Wed, 10 Jun 2020 15:58:25 -0400 Subject: [PATCH 24/26] Remove stale tests --- core/tx_pool_test.go | 70 -------------------------------------------- 1 file changed, 70 deletions(-) diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index ae134b555969..d9869afcd937 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -458,76 +458,6 @@ func TestAAPending(t *testing.T) { } } -// func TestAAQueue2(t *testing.T) { -// t.Parallel() - -// pool, _ := setupTxPool() -// defer pool.Stop() -// key1, _ := crypto.GenerateKey() -// address1 := crypto.PubkeyToAddress(key1.PublicKey) - -// tx1 := aaTransaction(address1, 10000, 1, true, big.NewInt(1)) -// tx2 := aaTransaction(address1, 10000, 1, true, big.NewInt(1)) -// tx3 := aaTransaction(address1, 10000, 1, true, big.NewInt(1)) - -// tx1.Validate() -// tx2.Validate() -// tx3.Validate() - -// pool.currentState.AddBalance(address1, big.NewInt(1000000)) -// pool.currentState.SetCode(address1, common.FromHex(contractCode)) - -// pool.reset(nil, nil) - -// pool.enqueueTx(tx1.Hash(), tx1) -// pool.enqueueTx(tx2.Hash(), tx2) -// pool.enqueueTx(tx3.Hash(), tx3) - -// if pool.queue[address1].Len() != 1 { -// t.Error("expected len(queue) == 1, got", pool.queue[address1].Len()) -// } - -// Need to add tests here to make sure higher gas aa transaction replace low priced -// if pool.queue[address1].txs.Flatten()[0].Hash() != tx1.Hash() { -// t.Error("expected higher gas price to take priority, expected hash", tx2.Hash()) -// } -// } - -// func TestAAPending(t *testing.T) { -// t.Parallel() - -// pool, _ := setupTxPool() -// defer pool.Stop() -// key1, _ := crypto.GenerateKey() -// address1 := crypto.PubkeyToAddress(key1.PublicKey) - -// tx1 := aaTransaction(address1, 10000, 1, true, big.NewInt(1)) -// tx2 := aaTransaction(address1, 10000, 1, true, big.NewInt(1)) -// tx3 := aaTransaction(address1, 10000, 1, true, big.NewInt(1)) - -// tx1.Validate() -// tx2.Validate() -// tx3.Validate() - -// pool.currentState.AddBalance(address1, big.NewInt(3000)) -// pool.currentState.SetCode(address1, common.FromHex(contractCode)) - -// pool.reset(nil, nil) - -// pool.enqueueTx(tx1.Hash(), tx1) -// pool.enqueueTx(tx2.Hash(), tx2) -// pool.enqueueTx(tx3.Hash(), tx3) -// pool.promoteExecutables([]common.Address{address1}) - -// if pool.queue[address1] != nil { -// t.Error("expected len(queue) == 0, got", pool.queue[address1].Len()) -// } - -// if len(pool.pending) != 1 { -// t.Error("expected len(pending) == 1, got", len(pool.pending)) -// } -// } - func TestTransactionNegativeValue(t *testing.T) { t.Parallel() From fb1cbf99421fb3352b8999e191d901eecc590cc1 Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Thu, 11 Jun 2020 15:29:48 -0400 Subject: [PATCH 25/26] General cleanup, use contract address vs. EOA, equality checks. --- core/tx_pool.go | 2 +- core/tx_pool_test.go | 11 +++++++---- core/types/transaction.go | 5 ++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/core/tx_pool.go b/core/tx_pool.go index 45026f7e8b92..3bec400e1c16 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -553,7 +553,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { } if tx.IsAA() { - if tx.Value().Sign() > 0 { + if tx.Value().Sign() != 0 { return ErrInvalidAAData } diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index d9869afcd937..1c46ac0e2986 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -343,7 +343,8 @@ func TestAAQueue(t *testing.T) { beforeLimit := config.AAGasLimit config.AAGasLimit = 400000 - address1 := crypto.PubkeyToAddress(key1.PublicKey) + eoa := crypto.PubkeyToAddress(key1.PublicKey) + address1 := crypto.CreateAddress(eoa, 0) tx1 := aaTransaction(address1, 50000, 1, false, big.NewInt(1)) tx2 := aaTransaction(address1, 400000, 16000, true, big.NewInt(1)) @@ -377,7 +378,7 @@ func TestAAQueue(t *testing.T) { if len(pool.queue) > 0 { t.Error("expected transaction queue to be empty. is", len(pool.queue)) } - if len(pool.pending) > 1 { + if len(pool.pending) != 1 { t.Error("expected pending to have a transaction", len(pool.pending)) } @@ -391,7 +392,8 @@ func TestAAQueue2(t *testing.T) { defer pool.Stop() key1, _ := crypto.GenerateKey() - address1 := crypto.PubkeyToAddress(key1.PublicKey) + eoa := crypto.PubkeyToAddress(key1.PublicKey) + address1 := crypto.CreateAddress(eoa, 0) tx1 := aaTransaction(address1, 50000, 1, true, big.NewInt(1)) tx2 := aaTransaction(address1, 50000, 1, true, big.NewInt(2)) @@ -422,7 +424,8 @@ func TestAAPending(t *testing.T) { defer pool.Stop() key1, _ := crypto.GenerateKey() - address1 := crypto.PubkeyToAddress(key1.PublicKey) + eoa := crypto.PubkeyToAddress(key1.PublicKey) + address1 := crypto.CreateAddress(eoa, 0) tx1 := aaTransaction(address1, 50000, 1, true, big.NewInt(1)) tx2 := aaTransaction(address1, 50000, 1, true, big.NewInt(2)) diff --git a/core/types/transaction.go b/core/types/transaction.go index 64cf7d747a48..22b8b9f64819 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -275,12 +275,11 @@ func (tx *Transaction) WithAASignature() *Transaction { // Cost returns amount + gasprice * gaslimit. func (tx *Transaction) Cost() *big.Int { + total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.data.GasLimit)) if tx.IsAA() { - var price = tx.GasPrice() - return new(big.Int).Mul(price, new(big.Int).SetUint64(tx.data.GasLimit)) + return total } - total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit)) total.Add(total, tx.data.Amount) return total } From ea087fff0a56e49191077e36258664af1313de1a Mon Sep 17 00:00:00 2001 From: villanuevawill Date: Thu, 11 Jun 2020 15:42:58 -0400 Subject: [PATCH 26/26] Include state snaphshot reversion on validation --- core/tx_pool.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/tx_pool.go b/core/tx_pool.go index 3bec400e1c16..44eeab2d1adf 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -576,7 +576,10 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { now := time.Now() context := NewEVMContext(types.AAEntryMessage, pool.chain.CurrentBlock().Header(), pool.chain, &common.Address{}) vm := vm.NewEVM(context, pool.currentState, pool.chain.Config(), vm.Config{PaygasMode: vm.PaygasHalt}) + snapshotRevisionId := pool.currentState.Snapshot() err := Validate(tx, pool.signer, vm, pool.config.AAGasLimit) + // validate updates the current state, we need to revert + pool.currentState.RevertToSnapshot(snapshotRevisionId) aaValidationTimer.UpdateSince(now) if err != nil { return ErrInvalidAA