From 399949cfa2d5307812a574b715936b9813266bdf Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 18 Feb 2025 14:12:08 +0100 Subject: [PATCH] Use predetermined generation of transactions - Same ECDSA key - Deterministic generation of data --- core/txpool/legacypool/legacypool_test.go | 74 ++++++++++++----------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index 39e67f647a..3dc11a8e09 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -18,6 +18,7 @@ package legacypool import ( "crypto/ecdsa" + "crypto/elliptic" crand "crypto/rand" "errors" "fmt" @@ -1197,10 +1198,19 @@ func TestPendingGlobalLimiting(t *testing.T) { func TestAllowedTxSize(t *testing.T) { t.Parallel() - // Create a test account and fund it - pool, key := setupPool() + pool, _ := setupPool() defer pool.Close() + // We use a deterministic ecdsa key to obtain signed transactions of pre-determined sizes. + key := &ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: new(big.Int).SetBytes(common.Hex2Bytes("78e68ff485cc934e4096f24091931e91334d6841c6c2be93d1a930e59783902d")), + Y: new(big.Int).SetBytes(common.Hex2Bytes("ca8db229299a4f937eeb0a36c451751cd59df978aceaaf190c28783c321cf267")), + }, + D: new(big.Int).SetBytes(common.Hex2Bytes("e521e757356c1f197f704502dfa10333993e3705ca3d304d6a349461ed67ae71")), + } + account := crypto.PubkeyToAddress(key.PublicKey) testAddBalance(pool, account, big.NewInt(1000000000)) @@ -1212,7 +1222,7 @@ func TestAllowedTxSize(t *testing.T) { t.Fatalf("failed to add transaction of size %d, close to maximal: %v", int(tx.Size()), err) } // Try adding a transaction with random allowed size - tx = sizedDataTransaction(t, uint64(rand.Intn(txMaxSize+1)), 1, gasLimit, key) + tx = sizedDataTransaction(t, txMaxSize/2, 1, gasLimit, key) if err := pool.addRemoteSync(tx); err != nil { t.Fatalf("failed to add transaction of random allowed size: %v", err) } @@ -1221,8 +1231,8 @@ func TestAllowedTxSize(t *testing.T) { if err := pool.addRemoteSync(tx); err == nil { t.Fatalf("expected rejection on slightly oversize transaction") } - // Try adding a transaction above maximum size by more than one - tx = sizedDataTransaction(t, txMaxSize+2+uint64(rand.Intn(10*txMaxSize)), 2, gasLimit, key) + // Try adding a transaction above maximum size by two + tx = sizedDataTransaction(t, txMaxSize+2, 2, gasLimit, key) if err := pool.addRemoteSync(tx); err == nil { t.Fatalf("expected rejection on oversize transaction") } @@ -1241,7 +1251,7 @@ func TestAllowedTxSize(t *testing.T) { // sizedDataTransaction generates a transaction with the size matching the `targetSize` given // as argument. It uses the nonce, gasLimit and key given as arguments to generate the transaction. -// Note some target sizes cannot be generated, notably: 99, 154, 258, 356, 65539 and 65538. +// Note around 1% of all possible sizes under [txMaxSize] cannot be generated by this function. func sizedDataTransaction(t *testing.T, targetSize, nonce, gasLimit uint64, key *ecdsa.PrivateKey) ( tx *types.Transaction) { t.Helper() @@ -1258,38 +1268,32 @@ func sizedDataTransaction(t *testing.T, targetSize, nonce, gasLimit uint64, key // Find data length to reach the target size, assuming a signature of length 65. // This is done because the data length header varies from 0 to 5 bytes. - var dataLength uint64 - for dataLength = targetSize - minimumSize; dataLength > 0; dataLength-- { - data := make([]byte, dataLength) - // Base this transaction on the transaction created in [pricedDataTransaction]. + dataLength := targetSize - minimumSize + data := make([]byte, dataLength) + for dataLength > 0 { txWithData := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), gasLimit, gasPrice, data) - sizeWithSignature := txWithData.Size() + targetSignatureLength - // Note sizeWithSignature can decrease by 2 bytes eventhough the data length was decreased by 1 only. - if sizeWithSignature < targetSize { - break + signedTx, err := types.SignTx(txWithData, types.HomesteadSigner{}, key) + if txSignatureLen(signedTx) != targetSignatureLength { + // try again with other data to get a signature of the desired length + for i := range data { + data[i]++ + } + continue + } + switch { + case err != nil: + require.NoError(t, err, "signing transaction") + case signedTx.Size() == targetSize: + return signedTx + case signedTx.Size() < targetSize: + t.Fatalf("target size %d cannot be reached (%d smaller for data length %d)", targetSize, signedTx.Size(), dataLength) + default: // larger than targetSize + dataLength-- + data = data[:dataLength] } } - - previousDataLength := dataLength - for { - tx = pricedDataTransaction(nonce, gasLimit, gasPrice, key, dataLength) - size := tx.Size() - if size == targetSize { - return tx - } else if txSignatureLen(tx) != targetSignatureLength { - continue // re-generate with new random data to obtain a signature of 65 bytes. - } - // The final RLP encoding size sometimes varies by 1 to 2 bytes compared to the target size. - newDataLength := dataLength + 1 - if size > targetSize { - newDataLength = dataLength - 1 - } - if newDataLength == previousDataLength { - t.Fatalf("impossible to generate a transaction of length %d", targetSize) - } - previousDataLength = dataLength - dataLength = newDataLength - } + t.Fatalf("target size %d cannot be reached", targetSize) + return nil } func txSignatureLen(tx *types.Transaction) uint64 {