diff --git a/integration-tests/common/gauntlet_common.go b/integration-tests/common/gauntlet_common.go index 02bedb6c0..173b64dc5 100644 --- a/integration-tests/common/gauntlet_common.go +++ b/integration-tests/common/gauntlet_common.go @@ -27,7 +27,7 @@ func (m *OCRv2TestState) fundNodes() ([]string, error) { for _, key := range nAccounts { // We are not deploying in parallel here due to testnet limitations (429 too many requests) l.Debug().Msg(fmt.Sprintf("Funding node with address: %s", key)) - _, err := m.Clients.GauntletClient.TransferToken(m.Common.ChainDetails.StarkTokenAddress, key, "1000000000000000000") // Transferring 0.1 STRK to each node + _, err := m.Clients.GauntletClient.TransferToken(m.Common.ChainDetails.StarkTokenAddress, key, "10000000000000000000") // Transferring 10 STRK to each node if err != nil { return nil, err } diff --git a/ops/test_helpers.go b/ops/test_helpers.go index 8641e1739..0395fec21 100644 --- a/ops/test_helpers.go +++ b/ops/test_helpers.go @@ -44,11 +44,12 @@ var TestOCR2Config = OCR2Config{ // Transmitters: txKeys, // user defined OnchainConfig: "", OffchainConfig: &OffchainConfig{ - DeltaProgressNanoseconds: 8000000000, - DeltaResendNanoseconds: 30000000000, - DeltaRoundNanoseconds: 3000000000, - DeltaGraceNanoseconds: 1000000000, - DeltaStageNanoseconds: 20000000000, + // todo: increase delta round but decrease delta stage + DeltaProgressNanoseconds: 150000000000, // 120s + DeltaResendNanoseconds: 150000000000, // 150s + DeltaRoundNanoseconds: 90000000000, // 90s + DeltaGraceNanoseconds: 5000000000, // 5s + DeltaStageNanoseconds: 30000000000, // 20s RMax: 5, S: []int{1, 2}, // OffchainPublicKeys: offChainKeys, // user defined diff --git a/relayer/pkg/chainlink/txm/txm.go b/relayer/pkg/chainlink/txm/txm.go index 964f640d0..ddd2bc242 100644 --- a/relayer/pkg/chainlink/txm/txm.go +++ b/relayer/pkg/chainlink/txm/txm.go @@ -46,15 +46,77 @@ type StarkTXM interface { TxManager } +type ExpTimer struct { + waitTime time.Duration + minDuration time.Duration + maxDuration time.Duration + resetDuration time.Duration + lock chan struct{} + done sync.WaitGroup +} + +func NewExpTimer(minDuration, maxDuration, resetDuration time.Duration) *ExpTimer { + return &ExpTimer{ + waitTime: minDuration, + minDuration: minDuration, + maxDuration: maxDuration, + resetDuration: resetDuration, + lock: make(chan struct{}, 1), + } +} + +func (e *ExpTimer) Start(stop <-chan struct{}) { + e.resetLoop(stop) +} + +func (e *ExpTimer) Stop() { + e.done.Wait() +} + +func (e *ExpTimer) Wait(ctx context.Context) { + e.lock <- struct{}{} + + <-time.After(e.waitTime) + + e.waitTime *= 2 + if e.waitTime > e.maxDuration { + e.waitTime = e.maxDuration + } + + <-e.lock +} + +func (e *ExpTimer) resetLoop(stop <-chan struct{}) { + e.done.Add(1) + + go func() { + loop: + for { + select { + case <-stop: + break loop + case <-time.After(e.resetDuration): + e.lock <- struct{}{} + e.waitTime = e.minDuration + <-e.lock + continue + } + } + + e.done.Done() + }() +} + type starktxm struct { - starter utils.StartStopOnce - lggr logger.Logger - done sync.WaitGroup - stop chan struct{} - queue chan Tx - ks KeystoreAdapter - cfg Config - nonce NonceManager + starter utils.StartStopOnce + lggr logger.Logger + done sync.WaitGroup + stop chan struct{} + queue chan Tx + ks KeystoreAdapter + cfg Config + nonce NonceManager + nonceRetryTimer ExpTimer client *utils.LazyLoad[*starknet.Client] feederClient *utils.LazyLoad[*starknet.FeederClient] @@ -64,14 +126,15 @@ type starktxm struct { func New(lggr logger.Logger, keystore loop.Keystore, cfg Config, getClient func() (*starknet.Client, error), getFeederClient func() (*starknet.FeederClient, error)) (StarkTXM, error) { txm := &starktxm{ - lggr: logger.Named(lggr, "StarknetTxm"), - queue: make(chan Tx, MaxQueueLen), - stop: make(chan struct{}), - client: utils.NewLazyLoad(getClient), - feederClient: utils.NewLazyLoad(getFeederClient), - ks: NewKeystoreAdapter(keystore), - cfg: cfg, - txStore: NewChainTxStore(), + lggr: logger.Named(lggr, "StarknetTxm"), + queue: make(chan Tx, MaxQueueLen), + stop: make(chan struct{}), + client: utils.NewLazyLoad(getClient), + feederClient: utils.NewLazyLoad(getFeederClient), + ks: NewKeystoreAdapter(keystore), + cfg: cfg, + txStore: NewChainTxStore(), + nonceRetryTimer: *NewExpTimer(2*time.Second, 10*time.Second, 20*time.Second), } txm.nonce = NewNonceManager(txm.lggr) @@ -91,6 +154,9 @@ func (txm *starktxm) Start(ctx context.Context) error { txm.done.Add(2) // waitgroup: tx sender + confirmer go txm.broadcastLoop() go txm.confirmLoop() + + txm.nonceRetryTimer.Start(txm.stop) + return nil }) } @@ -156,8 +222,8 @@ func (txm *starktxm) handleNonceErr(ctx context.Context, accountAddress *felt.Fe txm.lggr.Debugw("Handling Nonce Validation Error By Resubmitting Txs...", "account", accountAddress) - // wait for rpc starknet_estimateFee to catch up with nonce returned by starknet_getNonce - <-time.After(utils.WithJitter(time.Second)) + // exponential backoff wait for rpc starknet_estimateFee to catch up with nonce returned by starknet_getNonce + txm.nonceRetryTimer.Wait(ctx) // resync nonce so that new queued txs can be unblocked client, err := txm.client.Get() @@ -300,8 +366,6 @@ func (txm *starktxm) broadcast(ctx context.Context, publicKey *felt.Felt, accoun return txhash, fmt.Errorf("failed to get FRI estimate") } - txm.lggr.Infow("Fee estimate", "in units", friEstimate.FeeUnit) - // pad estimate to 150% (add extra because estimate did not include validation) gasConsumed := friEstimate.GasConsumed.BigInt(new(big.Int)) expandedGas := new(big.Int).Mul(gasConsumed, big.NewInt(150)) @@ -460,6 +524,7 @@ func (txm *starktxm) Close() error { return txm.starter.StopOnce("starktxm", func() error { close(txm.stop) txm.done.Wait() + txm.nonceRetryTimer.Stop() return nil }) }