Skip to content

Commit c2b098a

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

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

services/requester/batch_tx_pool.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const eoaActivityCacheSize = 10_000
2424

2525
type pooledEvmTx struct {
2626
txPayload cadence.String
27+
txHash gethCommon.Hash
2728
nonce uint64
2829
}
2930

@@ -143,7 +144,7 @@ func (t *BatchTxPool) Add(
143144
err = t.submitSingleTransaction(ctx, hexEncodedTx)
144145
} else {
145146
// Case 3. EOA activity found AND it was less than [X] seconds ago:
146-
userTx := pooledEvmTx{txPayload: hexEncodedTx, nonce: tx.Nonce()}
147+
userTx := pooledEvmTx{txPayload: hexEncodedTx, txHash: tx.Hash(), nonce: tx.Nonce()}
147148
t.pooledTxs[from] = append(t.pooledTxs[from], userTx)
148149
}
149150

@@ -207,9 +208,19 @@ func (t *BatchTxPool) batchSubmitTransactionsForSameAddress(
207208
sort.Slice(pooledTxs, func(i, j int) bool {
208209
return pooledTxs[i].nonce < pooledTxs[j].nonce
209210
})
211+
// Filter out duplicate transactions, based on their tx hash
212+
seen := make(map[gethCommon.Hash]struct{}, len(pooledTxs))
213+
uniqueTxs := pooledTxs[:0]
214+
for _, tx := range pooledTxs {
215+
if _, ok := seen[tx.txHash]; ok {
216+
continue
217+
}
218+
seen[tx.txHash] = struct{}{}
219+
uniqueTxs = append(uniqueTxs, tx)
220+
}
210221

211-
hexEncodedTxs := make([]cadence.Value, len(pooledTxs))
212-
for i, txPayload := range pooledTxs {
222+
hexEncodedTxs := make([]cadence.Value, len(uniqueTxs))
223+
for i, txPayload := range uniqueTxs {
213224
hexEncodedTxs[i] = txPayload.txPayload
214225
}
215226

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)