mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
core/txpool/legacypool: reject gapped tx from delegated account
This commit is contained in:
parent
7d99f7df00
commit
bdc3e5d72f
2 changed files with 39 additions and 23 deletions
|
|
@ -66,6 +66,10 @@ var (
|
||||||
// transactions is reached for specific accounts.
|
// transactions is reached for specific accounts.
|
||||||
ErrInflightTxLimitReached = errors.New("in-flight transaction limit reached for delegated accounts")
|
ErrInflightTxLimitReached = errors.New("in-flight transaction limit reached for delegated accounts")
|
||||||
|
|
||||||
|
// ErrOutOfOrderTxFromDelegated is returned when the transaction with gapped
|
||||||
|
// nonce received from the accounts with delegation or pending delegation.
|
||||||
|
ErrOutOfOrderTxFromDelegated = errors.New("gapped-nonce tx from delegated accounts")
|
||||||
|
|
||||||
// ErrAuthorityReserved is returned if a transaction has an authorization
|
// ErrAuthorityReserved is returned if a transaction has an authorization
|
||||||
// signed by an address which already has in-flight transactions known to the
|
// signed by an address which already has in-flight transactions known to the
|
||||||
// pool.
|
// pool.
|
||||||
|
|
@ -605,33 +609,40 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction) error {
|
||||||
return pool.validateAuth(tx)
|
return pool.validateAuth(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// limitTxFromDelegated ensures the account with either delegation or pending
|
||||||
|
// delegation can at most send one inflight **executable** transaction.
|
||||||
|
func (pool *LegacyPool) limitTxFromDelegated(tx *types.Transaction) error {
|
||||||
|
from, _ := types.Sender(pool.signer, tx) // validated
|
||||||
|
|
||||||
|
// Short circuit if the sender has neither delegation nor pending delegation.
|
||||||
|
if pool.currentState.GetCodeHash(from) == types.EmptyCodeHash && len(pool.all.auths[from]) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pending := pool.pending[from]
|
||||||
|
if pending == nil {
|
||||||
|
// Transaction with gapped nonce is not supported for delegated accounts
|
||||||
|
if pool.pendingNonces.get(from) != tx.Nonce() {
|
||||||
|
return ErrOutOfOrderTxFromDelegated
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Transaction replacement is supported
|
||||||
|
if pending.Contains(tx.Nonce()) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if pending.Len() >= 1 {
|
||||||
|
return ErrInflightTxLimitReached
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// validateAuth verifies that the transaction complies with code authorization
|
// validateAuth verifies that the transaction complies with code authorization
|
||||||
// restrictions brought by SetCode transaction type.
|
// restrictions brought by SetCode transaction type.
|
||||||
func (pool *LegacyPool) validateAuth(tx *types.Transaction) error {
|
func (pool *LegacyPool) validateAuth(tx *types.Transaction) error {
|
||||||
from, _ := types.Sender(pool.signer, tx) // validated
|
|
||||||
|
|
||||||
// Allow at most one in-flight tx for delegated accounts or those with a
|
// Allow at most one in-flight tx for delegated accounts or those with a
|
||||||
// pending authorization.
|
// pending authorization.
|
||||||
if pool.currentState.GetCodeHash(from) != types.EmptyCodeHash || len(pool.all.auths[from]) != 0 {
|
if err := pool.limitTxFromDelegated(tx); err != nil {
|
||||||
var (
|
return err
|
||||||
count int
|
|
||||||
exists bool
|
|
||||||
)
|
|
||||||
pending := pool.pending[from]
|
|
||||||
if pending != nil {
|
|
||||||
count += pending.Len()
|
|
||||||
exists = pending.Contains(tx.Nonce())
|
|
||||||
}
|
|
||||||
queue := pool.queue[from]
|
|
||||||
if queue != nil {
|
|
||||||
count += queue.Len()
|
|
||||||
exists = exists || queue.Contains(tx.Nonce())
|
|
||||||
}
|
|
||||||
// Replace the existing in-flight transaction for delegated accounts
|
|
||||||
// are still supported
|
|
||||||
if count >= 1 && !exists {
|
|
||||||
return ErrInflightTxLimitReached
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Authorities cannot conflict with any pending or queued transactions.
|
// Authorities cannot conflict with any pending or queued transactions.
|
||||||
if auths := tx.SetCodeAuthorities(); len(auths) > 0 {
|
if auths := tx.SetCodeAuthorities(); len(auths) > 0 {
|
||||||
|
|
|
||||||
|
|
@ -2262,6 +2262,11 @@ func TestSetCodeTransactions(t *testing.T) {
|
||||||
aa := common.Address{0xaa, 0xaa}
|
aa := common.Address{0xaa, 0xaa}
|
||||||
statedb.SetCode(addrA, append(types.DelegationPrefix, aa.Bytes()...))
|
statedb.SetCode(addrA, append(types.DelegationPrefix, aa.Bytes()...))
|
||||||
statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)})
|
statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)})
|
||||||
|
|
||||||
|
// Send gapped transaction, it should be rejected
|
||||||
|
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrOutOfOrderTxFromDelegated) {
|
||||||
|
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrOutOfOrderTxFromDelegated, err)
|
||||||
|
}
|
||||||
// Send transactions. First is accepted, second is rejected.
|
// Send transactions. First is accepted, second is rejected.
|
||||||
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyA)); err != nil {
|
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyA)); err != nil {
|
||||||
t.Fatalf("%s: failed to add remote transaction: %v", name, err)
|
t.Fatalf("%s: failed to add remote transaction: %v", name, err)
|
||||||
|
|
@ -2269,7 +2274,7 @@ func TestSetCodeTransactions(t *testing.T) {
|
||||||
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
|
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
|
||||||
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
|
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
|
||||||
}
|
}
|
||||||
// Also check gapped transaction.
|
// Also check gapped transaction again
|
||||||
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
|
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
|
||||||
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
|
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue