From 3bb0c90585ffb92485cc546da69bf98f246b3e8c Mon Sep 17 00:00:00 2001 From: mmsqe Date: Fri, 22 Aug 2025 09:14:10 +0800 Subject: [PATCH 1/5] fix: align temporary reject check for included tx from txpool doc --- CHANGELOG.md | 1 + mempool/txpool/legacypool/legacypool.go | 33 +++++--------------- mempool/txpool/legacypool/legacypool_test.go | 13 ++++---- rpc/backend/call_tx.go | 13 +++----- rpc/backend/sign_tx.go | 12 +++---- rpc/backend/utils.go | 21 +++++++++++++ 6 files changed, 44 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e3ad482b..155e52adf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [\#471](https://github.com/cosmos/evm/pull/471) Notify new block for mempool in time. - [\#492](https://github.com/cosmos/evm/pull/492) Duplicate case switch to avoid empty execution block +- [\#494](https://github.com/cosmos/evm/pull/494) Align temporary reject check for included tx from txpool. ### IMPROVEMENTS diff --git a/mempool/txpool/legacypool/legacypool.go b/mempool/txpool/legacypool/legacypool.go index 0e2cc0cb6..e6a4853a2 100644 --- a/mempool/txpool/legacypool/legacypool.go +++ b/mempool/txpool/legacypool/legacypool.go @@ -18,7 +18,6 @@ package legacypool import ( - "errors" "maps" "math/big" "slices" @@ -31,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto/kzg4844" @@ -58,25 +58,6 @@ const ( txMaxSize = 4 * txSlotSize // 128KB ) -var ( - // ErrTxPoolOverflow is returned if the transaction pool is full and can't accept - // another remote transaction. - ErrTxPoolOverflow = errors.New("txpool is full") - - // 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 - // signed by an address which already has in-flight transactions known to the - // pool. - ErrAuthorityReserved = errors.New("authority already reserved") - - // ErrFutureReplacePending is returned if a future transaction replaces a pending - // one. Future transactions should only be able to replace other future transactions. - ErrFutureReplacePending = errors.New("future transaction tries to replace pending") -) - var ( evictionInterval = time.Minute // Time interval to check for evictable transactions statsReportInterval = 8 * time.Second // Time interval to report transaction pool stats @@ -623,7 +604,7 @@ func (pool *LegacyPool) checkDelegationLimit(tx *types.Transaction) error { if pending == nil { // Transaction with gapped nonce is not supported for delegated accounts if pool.pendingNonces.get(from) != tx.Nonce() { - return ErrOutOfOrderTxFromDelegated + return legacypool.ErrOutOfOrderTxFromDelegated } return nil } @@ -654,7 +635,7 @@ func (pool *LegacyPool) validateAuth(tx *types.Transaction) error { count += queue.Len() } if count > 1 { - return ErrAuthorityReserved + return legacypool.ErrAuthorityReserved } // Because there is no exclusive lock held between different subpools // when processing transactions, the SetCode transaction may be accepted @@ -665,7 +646,7 @@ func (pool *LegacyPool) validateAuth(tx *types.Transaction) error { // that attackers cannot easily stack a SetCode transaction when the sender // is reserved by other pools. if pool.reserver.Has(auth) { - return ErrAuthorityReserved + return legacypool.ErrAuthorityReserved } } } @@ -730,7 +711,7 @@ func (pool *LegacyPool) add(tx *types.Transaction) (replaced bool, err error) { // replacements to 25% of the slots if pool.changesSinceReorg > int(pool.config.GlobalSlots/4) { throttleTxMeter.Mark(1) - return false, ErrTxPoolOverflow + return false, legacypool.ErrTxPoolOverflow } // New transaction is better than our worse ones, make room for it. @@ -741,7 +722,7 @@ func (pool *LegacyPool) add(tx *types.Transaction) (replaced bool, err error) { if !success { log.Trace("Discarding overflown transaction", "hash", hash) overflowedTxMeter.Mark(1) - return false, ErrTxPoolOverflow + return false, legacypool.ErrTxPoolOverflow } // If the new transaction is a future transaction it should never churn pending transactions @@ -760,7 +741,7 @@ func (pool *LegacyPool) add(tx *types.Transaction) (replaced bool, err error) { pool.priced.Put(dropTx) } log.Trace("Discarding future transaction replacing pending tx", "hash", hash) - return false, ErrFutureReplacePending + return false, legacypool.ErrFutureReplacePending } } diff --git a/mempool/txpool/legacypool/legacypool_test.go b/mempool/txpool/legacypool/legacypool_test.go index 465d9f186..65186b187 100644 --- a/mempool/txpool/legacypool/legacypool_test.go +++ b/mempool/txpool/legacypool/legacypool_test.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -1674,8 +1675,8 @@ func TestUnderpricing(t *testing.T) { t.Fatalf("failed to add well priced transaction: %v", err) } // Ensure that replacing a pending transaction with a future transaction fails - if err := pool.addRemoteSync(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); !errors.Is(err, ErrFutureReplacePending) { - t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, ErrFutureReplacePending) + if err := pool.addRemoteSync(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); !errors.Is(err, legacypool.ErrFutureReplacePending) { + t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, legacypool.ErrFutureReplacePending) } pending, queued = pool.Stats() if pending != 4 { @@ -2275,8 +2276,8 @@ func TestSetCodeTransactions(t *testing.T) { 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) + if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, legacypool.ErrOutOfOrderTxFromDelegated) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, legacypool.ErrOutOfOrderTxFromDelegated, err) } // Send transactions. First is accepted, second is rejected. if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyA)); err != nil { @@ -2355,8 +2356,8 @@ func TestSetCodeTransactions(t *testing.T) { t.Fatalf("%s: failed to add with pending delegation: %v", name, err) } // Delegation rejected since two txs are already in-flight. - if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{0, keyB}})); !errors.Is(err, ErrAuthorityReserved) { - t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrAuthorityReserved, err) + if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{0, keyB}})); !errors.Is(err, legacypool.ErrAuthorityReserved) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, legacypool.ErrAuthorityReserved, err) } }, }, diff --git a/rpc/backend/call_tx.go b/rpc/backend/call_tx.go index 0c54f45c9..f65684ff7 100644 --- a/rpc/backend/call_tx.go +++ b/rpc/backend/call_tx.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "math/big" - "strings" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -16,7 +15,6 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/cosmos/evm/mempool" rpctypes "github.com/cosmos/evm/rpc/types" evmtypes "github.com/cosmos/evm/x/vm/types" @@ -147,19 +145,16 @@ func (b *Backend) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) { } txHash := ethereumTx.AsTransaction().Hash() - syncCtx := b.ClientCtx.WithBroadcastMode(flags.BroadcastSync) rsp, err := syncCtx.BroadcastTx(txBytes) if rsp != nil && rsp.Code != 0 { + if HandleBroadcastRawLog(rsp.RawLog, txHash) { + b.Logger.Debug("transaction temporarily rejected or queued", "hash", txHash.Hex()) + return txHash, nil + } err = errorsmod.ABCIError(rsp.Codespace, rsp.Code, rsp.RawLog) } if err != nil { - // Check if this is a nonce gap error that was successfully queued - if strings.Contains(err.Error(), mempool.ErrNonceGap.Error()) { - // Transaction was successfully queued due to nonce gap, return success to client - b.Logger.Debug("transaction queued due to nonce gap", "hash", txHash.Hex()) - return txHash, nil - } b.Logger.Error("failed to broadcast tx", "error", err.Error()) return txHash, fmt.Errorf("failed to broadcast transaction: %w", err) } diff --git a/rpc/backend/sign_tx.go b/rpc/backend/sign_tx.go index af1f60341..5e125299b 100644 --- a/rpc/backend/sign_tx.go +++ b/rpc/backend/sign_tx.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "math/big" - "strings" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" @@ -13,7 +12,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/signer/core/apitypes" - "github.com/cosmos/evm/mempool" evmtypes "github.com/cosmos/evm/x/vm/types" errorsmod "cosmossdk.io/errors" @@ -106,15 +104,13 @@ func (b *Backend) SendTransaction(args evmtypes.TransactionArgs) (common.Hash, e syncCtx := b.ClientCtx.WithBroadcastMode(flags.BroadcastSync) rsp, err := syncCtx.BroadcastTx(txBytes) if rsp != nil && rsp.Code != 0 { + if HandleBroadcastRawLog(rsp.RawLog, txHash) { + b.Logger.Debug("transaction temporarily rejected or queued", "hash", txHash.Hex()) + return txHash, nil + } err = errorsmod.ABCIError(rsp.Codespace, rsp.Code, rsp.RawLog) } if err != nil { - // Check if this is a nonce gap error that was successfully queued - if strings.Contains(err.Error(), mempool.ErrNonceGap.Error()) { - // Transaction was successfully queued due to nonce gap, return success to client - b.Logger.Debug("transaction queued due to nonce gap", "hash", txHash.Hex()) - return txHash, nil - } b.Logger.Error("failed to broadcast tx", "error", err.Error()) return txHash, err } diff --git a/rpc/backend/utils.go b/rpc/backend/utils.go index 9e7b28248..afb3c4672 100644 --- a/rpc/backend/utils.go +++ b/rpc/backend/utils.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/txpool/legacypool" ethtypes "github.com/ethereum/go-ethereum/core/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -17,6 +18,8 @@ import ( "github.com/cometbft/cometbft/proto/tendermint/crypto" cmtrpctypes "github.com/cometbft/cometbft/rpc/core/types" + "github.com/cosmos/evm/mempool" + "github.com/cosmos/evm/mempool/txpool" "github.com/cosmos/evm/rpc/types" cosmosevmtypes "github.com/cosmos/evm/types" "github.com/cosmos/evm/utils" @@ -291,3 +294,21 @@ func GetHexProofs(proof *crypto.ProofOps) []string { } return proofs } + +// HandleBroadcastRawLog checks the RawLog for known temporary rejection or nonce gap error. +// NOTE: make sure it sync with the latest go-ethereum logic when upgrade: +// https://github.com/ethereum/go-ethereum/blob/master/core/txpool/locals/errors.go#L29 +func HandleBroadcastRawLog(rawLog string, txHash common.Hash) bool { + switch rawLog { + case legacypool.ErrOutOfOrderTxFromDelegated.Error(), + txpool.ErrInflightTxLimitReached.Error(), + legacypool.ErrAuthorityReserved.Error(), + txpool.ErrUnderpriced.Error(), + legacypool.ErrTxPoolOverflow.Error(), + legacypool.ErrFutureReplacePending.Error(): + return true + case mempool.ErrNonceGap.Error(): + return true + } + return false +} From 2e154e0045dfa0ed5e2e10cd2b7162e37330dd3e Mon Sep 17 00:00:00 2001 From: mmsqe Date: Fri, 22 Aug 2025 09:14:17 +0800 Subject: [PATCH 2/5] add test --- rpc/backend/utils_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 rpc/backend/utils_test.go diff --git a/rpc/backend/utils_test.go b/rpc/backend/utils_test.go new file mode 100644 index 000000000..952703d51 --- /dev/null +++ b/rpc/backend/utils_test.go @@ -0,0 +1,29 @@ +package backend + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/txpool/legacypool" + "github.com/stretchr/testify/require" + + "github.com/cosmos/evm/mempool" + "github.com/cosmos/evm/mempool/txpool" +) + +func TestHandleBroadcastRawLog(t *testing.T) { + txHash := common.HexToHash("0x123") + cases := []string{ + legacypool.ErrOutOfOrderTxFromDelegated.Error(), + txpool.ErrInflightTxLimitReached.Error(), + legacypool.ErrAuthorityReserved.Error(), + txpool.ErrUnderpriced.Error(), + legacypool.ErrTxPoolOverflow.Error(), + legacypool.ErrFutureReplacePending.Error(), + mempool.ErrNonceGap.Error(), + } + for _, rawLog := range cases { + require.True(t, HandleBroadcastRawLog(rawLog, txHash), "expected true for: %s", rawLog) + } + require.False(t, HandleBroadcastRawLog("some other error", txHash)) +} From cd7c7fd22db40f7a6ef8032adce0ecde86b95861 Mon Sep 17 00:00:00 2001 From: mmsqe Date: Fri, 22 Aug 2025 09:17:52 +0800 Subject: [PATCH 3/5] cleanup --- rpc/backend/call_tx.go | 4 ++-- rpc/backend/sign_tx.go | 4 ++-- rpc/backend/utils.go | 11 ++++++----- rpc/backend/utils_test.go | 32 +++++++++++++++++++++----------- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/rpc/backend/call_tx.go b/rpc/backend/call_tx.go index f65684ff7..447b01bc9 100644 --- a/rpc/backend/call_tx.go +++ b/rpc/backend/call_tx.go @@ -148,8 +148,8 @@ func (b *Backend) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) { syncCtx := b.ClientCtx.WithBroadcastMode(flags.BroadcastSync) rsp, err := syncCtx.BroadcastTx(txBytes) if rsp != nil && rsp.Code != 0 { - if HandleBroadcastRawLog(rsp.RawLog, txHash) { - b.Logger.Debug("transaction temporarily rejected or queued", "hash", txHash.Hex()) + if shouldSkip, msg := HandleBroadcastRawLog(rsp.RawLog, txHash); shouldSkip { + b.Logger.Debug(msg, "hash", txHash.Hex()) return txHash, nil } err = errorsmod.ABCIError(rsp.Codespace, rsp.Code, rsp.RawLog) diff --git a/rpc/backend/sign_tx.go b/rpc/backend/sign_tx.go index 5e125299b..8daf1cb9b 100644 --- a/rpc/backend/sign_tx.go +++ b/rpc/backend/sign_tx.go @@ -104,8 +104,8 @@ func (b *Backend) SendTransaction(args evmtypes.TransactionArgs) (common.Hash, e syncCtx := b.ClientCtx.WithBroadcastMode(flags.BroadcastSync) rsp, err := syncCtx.BroadcastTx(txBytes) if rsp != nil && rsp.Code != 0 { - if HandleBroadcastRawLog(rsp.RawLog, txHash) { - b.Logger.Debug("transaction temporarily rejected or queued", "hash", txHash.Hex()) + if shouldSkip, msg := HandleBroadcastRawLog(rsp.RawLog, txHash); shouldSkip { + b.Logger.Debug(msg, "hash", txHash.Hex()) return txHash, nil } err = errorsmod.ABCIError(rsp.Codespace, rsp.Code, rsp.RawLog) diff --git a/rpc/backend/utils.go b/rpc/backend/utils.go index afb3c4672..b58dc5ce4 100644 --- a/rpc/backend/utils.go +++ b/rpc/backend/utils.go @@ -298,7 +298,10 @@ func GetHexProofs(proof *crypto.ProofOps) []string { // HandleBroadcastRawLog checks the RawLog for known temporary rejection or nonce gap error. // NOTE: make sure it sync with the latest go-ethereum logic when upgrade: // https://github.com/ethereum/go-ethereum/blob/master/core/txpool/locals/errors.go#L29 -func HandleBroadcastRawLog(rawLog string, txHash common.Hash) bool { +func HandleBroadcastRawLog(rawLog string, txHash common.Hash) (bool, string) { + if strings.Contains(rawLog, mempool.ErrNonceGap.Error()) { + return true, "transaction queued due to nonce gap" + } switch rawLog { case legacypool.ErrOutOfOrderTxFromDelegated.Error(), txpool.ErrInflightTxLimitReached.Error(), @@ -306,9 +309,7 @@ func HandleBroadcastRawLog(rawLog string, txHash common.Hash) bool { txpool.ErrUnderpriced.Error(), legacypool.ErrTxPoolOverflow.Error(), legacypool.ErrFutureReplacePending.Error(): - return true - case mempool.ErrNonceGap.Error(): - return true + return true, "transaction temporarily rejected or queued" } - return false + return false, "" } diff --git a/rpc/backend/utils_test.go b/rpc/backend/utils_test.go index 952703d51..09bb7f59e 100644 --- a/rpc/backend/utils_test.go +++ b/rpc/backend/utils_test.go @@ -13,17 +13,27 @@ import ( func TestHandleBroadcastRawLog(t *testing.T) { txHash := common.HexToHash("0x123") - cases := []string{ - legacypool.ErrOutOfOrderTxFromDelegated.Error(), - txpool.ErrInflightTxLimitReached.Error(), - legacypool.ErrAuthorityReserved.Error(), - txpool.ErrUnderpriced.Error(), - legacypool.ErrTxPoolOverflow.Error(), - legacypool.ErrFutureReplacePending.Error(), - mempool.ErrNonceGap.Error(), + tmpErrMsg := "transaction temporarily rejected or queued" + cases := []struct { + rawLog string + wantMsg string + }{ + {legacypool.ErrOutOfOrderTxFromDelegated.Error(), tmpErrMsg}, + {txpool.ErrInflightTxLimitReached.Error(), tmpErrMsg}, + {legacypool.ErrAuthorityReserved.Error(), tmpErrMsg}, + {txpool.ErrUnderpriced.Error(), tmpErrMsg}, + {legacypool.ErrTxPoolOverflow.Error(), tmpErrMsg}, + {legacypool.ErrFutureReplacePending.Error(), tmpErrMsg}, + {mempool.ErrNonceGap.Error(), "transaction queued due to nonce gap"}, } - for _, rawLog := range cases { - require.True(t, HandleBroadcastRawLog(rawLog, txHash), "expected true for: %s", rawLog) + + for _, tc := range cases { + ok, msg := HandleBroadcastRawLog(tc.rawLog, txHash) + require.True(t, ok, "expected true for: %s", tc.rawLog) + require.Equal(t, tc.wantMsg, msg, "unexpected message for: %s", tc.rawLog) } - require.False(t, HandleBroadcastRawLog("some other error", txHash)) + + ok, msg := HandleBroadcastRawLog("some other error", txHash) + require.False(t, ok) + require.Equal(t, "", msg) } From d5f869dcc1ed5e07368fac357709166f48962d95 Mon Sep 17 00:00:00 2001 From: mmsqe Date: Sat, 23 Aug 2025 08:11:59 +0800 Subject: [PATCH 4/5] specify reason for not using IsTemporaryReject --- rpc/backend/utils.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpc/backend/utils.go b/rpc/backend/utils.go index b58dc5ce4..70f54f002 100644 --- a/rpc/backend/utils.go +++ b/rpc/backend/utils.go @@ -295,7 +295,8 @@ func GetHexProofs(proof *crypto.ProofOps) []string { return proofs } -// HandleBroadcastRawLog checks the RawLog for known temporary rejection or nonce gap error. +// HandleBroadcastRawLog checks the RawLog for known temporary rejection or nonce gap error, +// since the error message only exists in rawLog of BroadcastTx response so we can only check against error string instead of error instance. // NOTE: make sure it sync with the latest go-ethereum logic when upgrade: // https://github.com/ethereum/go-ethereum/blob/master/core/txpool/locals/errors.go#L29 func HandleBroadcastRawLog(rawLog string, txHash common.Hash) (bool, string) { From 6063d8ca0b4edf5dfe0c941631f819eedd385c30 Mon Sep 17 00:00:00 2001 From: mmsqe Date: Sat, 23 Aug 2025 09:02:48 +0800 Subject: [PATCH 5/5] fix flaky TestAllowedTxSize for more info, https://github.com/ethereum/go-ethereum/pull/31836/files --- mempool/txpool/legacypool/legacypool_test.go | 34 ++++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/mempool/txpool/legacypool/legacypool_test.go b/mempool/txpool/legacypool/legacypool_test.go index 65186b187..a63cb07b4 100644 --- a/mempool/txpool/legacypool/legacypool_test.go +++ b/mempool/txpool/legacypool/legacypool_test.go @@ -110,11 +110,33 @@ func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ec return tx } -func pricedDataTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey, bytes uint64) *types.Transaction { - data := make([]byte, bytes) - crand.Read(data) - - tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(0), gaslimit, gasprice, data), types.HomesteadSigner{}, key) +// pricedDataTransaction generates a signed transaction with fixed-size data, +// and ensures that the resulting signature components (r and s) are exactly 32 bytes each, +// producing transactions with deterministic size. +// +// This avoids variability in transaction size caused by leading zeros being omitted in +// RLP encoding of r/s. Since r and s are derived from ECDSA, they occasionally have leading +// zeros and thus can be shorter than 32 bytes. +// +// For example: +// +// r: 0 leading zeros, bytesSize: 32, bytes: [221 ... 101] +// s: 1 leading zeros, bytesSize: 31, bytes: [0 75 ... 47] +func pricedDataTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey, dataBytes uint64) *types.Transaction { + var tx *types.Transaction + + // 10 attempts is statistically sufficient since leading zeros in ECDSA signatures are rare and randomly distributed. + var retryTimes = 10 + for i := 0; i < retryTimes; i++ { + data := make([]byte, dataBytes) + crand.Read(data) + + tx, _ = types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(0), gaslimit, gasprice, data), types.HomesteadSigner{}, key) + _, r, s := tx.RawSignatureValues() + if len(r.Bytes()) == 32 && len(s.Bytes()) == 32 { + break + } + } return tx } @@ -1241,7 +1263,7 @@ func TestAllowedTxSize(t *testing.T) { const largeDataLength = txMaxSize - 200 // enough to have a 5 bytes RLP encoding of the data length number txWithLargeData := pricedDataTransaction(0, pool.currentHead.Load().GasLimit, big.NewInt(1), key, largeDataLength) maxTxLengthWithoutData := txWithLargeData.Size() - largeDataLength // 103 bytes - maxTxDataLength := txMaxSize - maxTxLengthWithoutData // 131072 - 103 = 130953 bytes + maxTxDataLength := txMaxSize - maxTxLengthWithoutData // 131072 - 103 = 130969 bytes // Try adding a transaction with maximal allowed size tx := pricedDataTransaction(0, pool.currentHead.Load().GasLimit, big.NewInt(1), key, maxTxDataLength)