From 6b09b7e362ea8f8831f5f379823cd35060465c06 Mon Sep 17 00:00:00 2001 From: Balamurali Gopalswami <167726375+b-gopalswami@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:35:00 -0400 Subject: [PATCH] CCIP-3420: Fix IsRequestTriggeredWithinTimeframe (#1445) ## Motivation The prior approach to find if there is traffic in a lane was not detecting the transactions properly. It could be due to the `CCIPSendRequestedWatcher` captures the event after it is initiated not any transaction before that. ## Solution The revised approach is to use the FilterCCIPSendRequested by a past derived block number and see if there is any traffic. The block number is derived by this formula: `filterFromBlock = latestBlockNumber - (SkipRequestIfAnotherRequestTriggeredWithin/avgBlockTime)` By this approach, we will be able to find traffic better. https://smartcontract-it.atlassian.net/browse/CCIP-3420 --- .../ccip-tests/actions/ccip_helpers.go | 44 +++++++++++++++++++ .../ccip-tests/load/ccip_loadgen.go | 15 ++++++- .../ccip-tests/testconfig/ccip.go | 3 ++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/integration-tests/ccip-tests/actions/ccip_helpers.go b/integration-tests/ccip-tests/actions/ccip_helpers.go index 06334560e05..da2e870f3ec 100644 --- a/integration-tests/ccip-tests/actions/ccip_helpers.go +++ b/integration-tests/ccip-tests/actions/ccip_helpers.go @@ -1620,6 +1620,8 @@ func (sourceCCIP *SourceCCIPModule) AssertSendRequestedLogFinalized( return finalizedAt, finalizedBlockNum.Uint64(), nil } +// IsRequestTriggeredWithinTimeframe monitors for live events occurring within the specified timeframe. +// Live events refer to those that are triggered after subscribing to the CCIP Send Requested event. func (sourceCCIP *SourceCCIPModule) IsRequestTriggeredWithinTimeframe(timeframe *commonconfig.Duration) *time.Time { if timeframe == nil { return nil @@ -1644,6 +1646,48 @@ func (sourceCCIP *SourceCCIPModule) IsRequestTriggeredWithinTimeframe(timeframe return foundAt } +// IsPastRequestTriggeredWithinTimeframe determines the average block time and calculates the block numbers +// within the specified timeframe. It then uses FilterCCIPSendRequested to identify the past events. +func (sourceCCIP *SourceCCIPModule) IsPastRequestTriggeredWithinTimeframe(ctx context.Context, timeframe *commonconfig.Duration) (*time.Time, error) { + if timeframe == nil { + return nil, nil + } + //var foundAt *time.Time + latestBlock, err := sourceCCIP.Common.ChainClient.LatestBlockNumber(ctx) + if err != nil { + return nil, fmt.Errorf("error while getting latest source block number. Error: %w", err) + } + avgBlockTime, err := sourceCCIP.Common.ChainClient.AvgBlockTime(ctx) + if err != nil { + return nil, fmt.Errorf("error while getting average source block time. Error: %w", err) + } + filterFromBlock := latestBlock - uint64(timeframe.Duration()/avgBlockTime) + + onRampContract, err := evm_2_evm_onramp.NewEVM2EVMOnRamp(common.HexToAddress(sourceCCIP.OnRamp.EthAddress.Hex()), + sourceCCIP.Common.ChainClient.Backend()) + if err != nil { + return nil, fmt.Errorf("error while on ramp contract. Error: %w", err) + } + iterator, err := onRampContract.FilterCCIPSendRequested(&bind.FilterOpts{ + Start: filterFromBlock, + }) + if err != nil { + return nil, fmt.Errorf("error while filtering CCIP send requested starting block number: %d. Error: %w", filterFromBlock, err) + } + defer func() { + _ = iterator.Close() + }() + if iterator.Next() { + hdr, err := sourceCCIP.Common.ChainClient.HeaderByNumber(context.Background(), big.NewInt(int64(iterator.Event.Raw.BlockNumber))) + if err != nil { + return nil, fmt.Errorf("error getting header for block: %d, Error: %w", iterator.Event.Raw.BlockNumber, err) + } + return pointer.ToTime(hdr.Timestamp), nil + } + + return nil, nil +} + func (sourceCCIP *SourceCCIPModule) AssertEventCCIPSendRequested( lggr *zerolog.Logger, txHash string, diff --git a/integration-tests/ccip-tests/load/ccip_loadgen.go b/integration-tests/ccip-tests/load/ccip_loadgen.go index bc7627b4e08..3ce770d31bc 100644 --- a/integration-tests/ccip-tests/load/ccip_loadgen.go +++ b/integration-tests/ccip-tests/load/ccip_loadgen.go @@ -10,6 +10,8 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/AlekSi/pointer" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -222,7 +224,18 @@ func (c *CCIPE2ELoad) CCIPMsg() (router.ClientEVM2AnyMessage, *testreporters.Req func (c *CCIPE2ELoad) Call(_ *wasp.Generator) *wasp.Response { res := &wasp.Response{} sourceCCIP := c.Lane.Source - recentRequestFoundAt := sourceCCIP.IsRequestTriggeredWithinTimeframe(c.SkipRequestIfAnotherRequestTriggeredWithin) + var recentRequestFoundAt *time.Time + var err error + // Use IsPastRequestTriggeredWithinTimeframe to check for any historical CCIP send request events + // within the specified timeframe for the first message. Subsequently, use the watcher method to monitor + // and detect any new events as they occur. + if c.CurrentMsgSerialNo.Load() == int64(1) { + recentRequestFoundAt, err = sourceCCIP.IsPastRequestTriggeredWithinTimeframe(testcontext.Get(c.t), c.SkipRequestIfAnotherRequestTriggeredWithin) + require.NoError(c.t, err, "error while filtering past requests") + } else { + recentRequestFoundAt = sourceCCIP.IsRequestTriggeredWithinTimeframe(c.SkipRequestIfAnotherRequestTriggeredWithin) + } + if recentRequestFoundAt != nil { c.Lane.Logger. Info(). diff --git a/integration-tests/ccip-tests/testconfig/ccip.go b/integration-tests/ccip-tests/testconfig/ccip.go index 76e00ecf496..0a53ee18732 100644 --- a/integration-tests/ccip-tests/testconfig/ccip.go +++ b/integration-tests/ccip-tests/testconfig/ccip.go @@ -242,6 +242,9 @@ func (l *LoadProfile) Validate() error { if l.TestDuration == nil || l.TestDuration.Duration().Minutes() == 0 { return fmt.Errorf("test duration should be set") } + if l.SkipRequestIfAnotherRequestTriggeredWithin != nil && l.TimeUnit.Duration() < l.SkipRequestIfAnotherRequestTriggeredWithin.Duration() { + return fmt.Errorf("SkipRequestIfAnotherRequestTriggeredWithin should be set below the TimeUnit duration") + } return nil }