Skip to content

Commit d023295

Browse files
committed
Deduplicate transactions on BatchTxPool prior to submission
1 parent 638fe2e commit d023295

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

services/requester/batch_tx_pool.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package requester
33
import (
44
"context"
55
"encoding/hex"
6+
"slices"
67
"sort"
78
"sync"
89
"time"
@@ -24,6 +25,7 @@ const eoaActivityCacheSize = 10_000
2425

2526
type pooledEvmTx struct {
2627
txPayload cadence.String
28+
txHash gethCommon.Hash
2729
nonce uint64
2830
}
2931

@@ -143,7 +145,7 @@ func (t *BatchTxPool) Add(
143145
err = t.submitSingleTransaction(ctx, hexEncodedTx)
144146
} else {
145147
// Case 3. EOA activity found AND it was less than [X] seconds ago:
146-
userTx := pooledEvmTx{txPayload: hexEncodedTx, nonce: tx.Nonce()}
148+
userTx := pooledEvmTx{txPayload: hexEncodedTx, txHash: tx.Hash(), nonce: tx.Nonce()}
147149
t.pooledTxs[from] = append(t.pooledTxs[from], userTx)
148150
}
149151

@@ -207,9 +209,13 @@ func (t *BatchTxPool) batchSubmitTransactionsForSameAddress(
207209
sort.Slice(pooledTxs, func(i, j int) bool {
208210
return pooledTxs[i].nonce < pooledTxs[j].nonce
209211
})
212+
// Filter out duplicate transactions, based on their tx hash
213+
uniqueTxs := slices.CompactFunc(pooledTxs, func(a pooledEvmTx, b pooledEvmTx) bool {
214+
return a.txHash == b.txHash
215+
})
210216

211-
hexEncodedTxs := make([]cadence.Value, len(pooledTxs))
212-
for i, txPayload := range pooledTxs {
217+
hexEncodedTxs := make([]cadence.Value, len(uniqueTxs))
218+
for i, txPayload := range uniqueTxs {
213219
hexEncodedTxs[i] = txPayload.txPayload
214220
}
215221

tests/tx_batching_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,53 @@ func Test_MultipleTransactionSubmissionsWithinNonRecentInterval(t *testing.T) {
514514
)
515515
}
516516

517+
func Test_MultipleTransactionSubmissionsWithDuplicates(t *testing.T) {
518+
_, cfg, stop := setupGatewayNode(t)
519+
defer stop()
520+
521+
rpcTester := &rpcTest{
522+
url: fmt.Sprintf("%s:%d", cfg.RPCHost, cfg.RPCPort),
523+
}
524+
525+
eoaKey, err := crypto.HexToECDSA(eoaTestPrivateKey)
526+
require.NoError(t, err)
527+
528+
testAddr := common.HexToAddress("55253ed90B70b96C73092D8680915aaF50081194")
529+
nonce := uint64(0)
530+
hashes := make([]common.Hash, 0)
531+
532+
signed, _, err := evmSign(big.NewInt(10), 21000, eoaKey, nonce, &testAddr, nil)
533+
require.NoError(t, err)
534+
nonce += 1
535+
536+
txHash, err := rpcTester.sendRawTx(signed)
537+
require.NoError(t, err)
538+
hashes = append(hashes, txHash)
539+
540+
for range 5 {
541+
// All these transactions are duplicates, since we don't change any
542+
// of the payload data. These will end up having the same tx hash
543+
// as well.
544+
signed, _, err := evmSign(big.NewInt(10), 15_000_000, eoaKey, nonce, &testAddr, nil)
545+
require.NoError(t, err)
546+
547+
txHash, err := rpcTester.sendRawTx(signed)
548+
require.NoError(t, err)
549+
hashes = append(hashes, txHash)
550+
}
551+
552+
assert.Eventually(t, func() bool {
553+
for _, h := range hashes {
554+
rcp, err := rpcTester.getReceipt(h.String())
555+
if err != nil || rcp == nil || rcp.Status != 1 {
556+
return false
557+
}
558+
}
559+
560+
return true
561+
}, time.Second*15, time.Second*1, "all transactions were not executed")
562+
}
563+
517564
func setupGatewayNode(t *testing.T) (emulator.Emulator, config.Config, func()) {
518565
srv, err := startEmulator(true)
519566
require.NoError(t, err)

0 commit comments

Comments
 (0)