Skip to content

Commit 3599c64

Browse files
sameh-faroukclaude
andcommitted
fix(bridge): cap BatchResult FailedCount and increase test timeouts
BatchCalls reads ALL events from the block, not per-extrinsic. In multi-validator setups where multiple force_batch extrinsics land in the same block, Utility.ItemFailed events from other validators' batches inflate the FailedCount, producing negative SuccessCount. Cap FailedCount at len(calls) to prevent misleading negative counts. The on-chain behavior was already correct — this is a reporting fix. Also increase T9/MV9 timeout from 600s to 900s and T7/MV7 from 60s to 300s. With batched execution, 50 burns require ~3 expiry cycles (~22 burns per cycle × ~3 min per cycle) to fully complete. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e74071f commit 3599c64

3 files changed

Lines changed: 15 additions & 6 deletions

File tree

clients/tfchain-client-go/batch.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,16 @@ func (s *Substrate) BatchCalls(identity Identity, calls []types.Call) (*BatchRes
4949
// ItemFailed events are emitted by force_batch for each failed call.
5050
// The event payload does not carry the batch-call index — only a DispatchError.
5151
// We count failures but cannot reliably map them to specific call positions.
52+
//
53+
// IMPORTANT: getEventRecords reads ALL events from the block, not just
54+
// those emitted by this extrinsic. In multi-validator setups, multiple
55+
// validators may submit force_batch extrinsics in the same block. Each
56+
// validator sees ItemFailed events from ALL batches, inflating the count.
57+
// Cap failedCount at len(calls) to prevent negative SuccessCount.
5258
failedCount := len(resp.Events.Utility_ItemFailed)
59+
if failedCount > len(calls) {
60+
failedCount = len(calls)
61+
}
5362
if failedCount > 0 {
5463
result.FailedCount = failedCount
5564
result.SuccessCount = len(calls) - failedCount

scripts/bridge_mv_tests.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -549,8 +549,8 @@ async function testMV9_expiredBatchRecovery () {
549549
// handleWithdrawReady submits them sequentially — each using its stored sequence
550550
// number, so all succeed in one pass.
551551
//
552-
// After all Stellar payments complete, each handleWithdrawReady calls
553-
// SetWithdrawExecuted individually (could be batched in a future optimization).
552+
// After all Stellar payments in a cycle complete, BatchSetWithdrawExecuted
553+
// confirms all in one force_batch. Multiple expiry cycles may be needed.
554554
for (let i = 1; i <= 3; i++) startValidator(i)
555555
log('All 3 validators restarted. Recovering expired burns via batch re-proposal...')
556556
log(`Expected: 1 force_batch re-proposes all ${N}, then all ${N} Stellar payments execute in sequence`)
@@ -567,7 +567,7 @@ async function testMV9_expiredBatchRecovery () {
567567
lastReported = count
568568
}
569569
if (bal >= beforeStellar + expectedNet - 1e-7) return bal
570-
}, { timeoutMs: 600_000, intervalMs: 10_000, desc: `all ${N} burns delivered (+${expectedNet} TFT)` })
570+
}, { timeoutMs: 900_000, intervalMs: 10_000, desc: `all ${N} burns delivered (+${expectedNet} TFT)` })
571571

572572
const delta = Math.round((finalStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS
573573
log(`All ${N} burns delivered: +${delta} TFT (expected +${expectedNet})`)
@@ -597,7 +597,7 @@ async function testMV7_cleanState () {
597597
const refunds = await api.query.tftBridgeModule.refundTransactions.entries()
598598
const mints = await api.query.tftBridgeModule.mintTransactions.entries()
599599
return burns.length === 0 && refunds.length === 0 && mints.length === 0
600-
}, { timeoutMs: 60_000, intervalMs: 5000, desc: 'all active tx maps to drain' })
600+
}, { timeoutMs: 300_000, intervalMs: 5000, desc: 'all active tx maps to drain' })
601601
pass(name, counter)
602602
} catch (e) {
603603
// On timeout, report what's left

scripts/bridge_tests.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ async function test9_expiredBatchRecovery () {
551551
lastReported = count
552552
}
553553
if (bal >= beforeStellar + expectedNet - 1e-7) return bal
554-
}, { timeoutMs: 600_000, intervalMs: 10_000, desc: `all ${N} burns delivered (+${expectedNet} TFT)` })
554+
}, { timeoutMs: 900_000, intervalMs: 10_000, desc: `all ${N} burns delivered (+${expectedNet} TFT)` })
555555

556556
const delta = Math.round((finalStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS
557557
log(`All ${N} burns delivered: +${delta} TFT (expected +${expectedNet})`)
@@ -581,7 +581,7 @@ async function test7_cleanState () {
581581
const refunds = await api.query.tftBridgeModule.refundTransactions.entries()
582582
const mints = await api.query.tftBridgeModule.mintTransactions.entries()
583583
return burns.length === 0 && refunds.length === 0 && mints.length === 0
584-
}, { timeoutMs: 60_000, intervalMs: 5000, desc: 'all active tx maps to drain' })
584+
}, { timeoutMs: 300_000, intervalMs: 5000, desc: 'all active tx maps to drain' })
585585
pass(name, counter)
586586
} catch (e) {
587587
// On timeout, report what's left

0 commit comments

Comments
 (0)