Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CCIP-4948: Adding new test for disable and enable lane #16038

Merged
merged 12 commits into from
Jan 28, 2025
8 changes: 8 additions & 0 deletions .github/integration-in-memory-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,13 @@ runner-test-matrix:
- PR Integration CCIP Tests
test_cmd: cd integration-tests/ && go test smoke/ccip/ccip_cs_rmn_curse_uncurse_test.go -timeout 10m -test.parallel=1 -count=1 -json

- id: smoke/ccip/ccip_disable_lane_test.go:*
path: integration-tests/smoke/ccip/ccip_disable_lane_test.go
test_env_type: in-memory
runs_on: ubuntu-latest
triggers:
- PR Integration CCIP Tests
test_cmd: cd integration-tests/smoke/ccip/ && go test ccip_disable_lane_test.go -timeout 10m -test.parallel=1 -count=1 -json


# END: CCIP tests
2 changes: 1 addition & 1 deletion deployment/ccip/changeset/cs_active_candidate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func Test_ActiveCandidate(t *testing.T) {
Config: changeset.UpdateFeeQuoterDestsConfig{
UpdatesByChain: map[uint64]map[uint64]fee_quoter.FeeQuoterDestChainConfig{
source: {
dest: changeset.DefaultFeeQuoterDestChainConfig(),
dest: changeset.DefaultFeeQuoterDestChainConfig(true),
},
},
},
Expand Down
20 changes: 12 additions & 8 deletions deployment/ccip/changeset/cs_chain_contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -706,12 +706,10 @@ func UpdateOffRampSourcesChangeset(e deployment.Environment, cfg UpdateOffRampSo
var args []offramp.OffRampSourceChainConfigArgs
for source, update := range updates {
router := common.HexToAddress("0x0")
if update.IsEnabled {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

router can't be 0x0 address as Offramp expects valid router address ow it will throw ZeroAddressNotAllowed error message.

revert ZeroAddressNotAllowed();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not possible to disable a router in offRamp, the parameter IsEnabled should be removed

Copy link
Contributor Author

@b-gopalswami b-gopalswami Jan 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is still needed to make here

IsEnabled: update.IsEnabled,

if update.TestRouter {
router = s.Chains[chainSel].TestRouter.Address()
} else {
router = s.Chains[chainSel].Router.Address()
}
if update.TestRouter {
router = s.Chains[chainSel].TestRouter.Address()
} else {
router = s.Chains[chainSel].Router.Address()
}
onRamp := s.Chains[source].OnRamp
args = append(args, offramp.OffRampSourceChainConfigArgs{
Expand Down Expand Up @@ -1115,7 +1113,9 @@ func isOCR3ConfigSetOnOffRamp(
return true, nil
}

func DefaultFeeQuoterDestChainConfig() fee_quoter.FeeQuoterDestChainConfig {
// DefaultFeeQuoterDestChainConfig returns the default FeeQuoterDestChainConfig
// with the config enabled/disabled based on the configEnabled flag.
func DefaultFeeQuoterDestChainConfig(configEnabled bool) fee_quoter.FeeQuoterDestChainConfig {
// https://github.com/smartcontractkit/ccip/blob/c4856b64bd766f1ddbaf5d13b42d3c4b12efde3a/contracts/src/v0.8/ccip/libraries/Internal.sol#L337-L337
/*
```Solidity
Expand All @@ -1124,7 +1124,7 @@ func DefaultFeeQuoterDestChainConfig() fee_quoter.FeeQuoterDestChainConfig {
```
*/
evmFamilySelector, _ := hex.DecodeString("2812d52c")
return fee_quoter.FeeQuoterDestChainConfig{
feeQuoterDestChainConfig := fee_quoter.FeeQuoterDestChainConfig{
IsEnabled: true,
b-gopalswami marked this conversation as resolved.
Show resolved Hide resolved
b-gopalswami marked this conversation as resolved.
Show resolved Hide resolved
MaxNumberOfTokensPerMsg: 10,
MaxDataBytes: 256,
Expand All @@ -1143,4 +1143,8 @@ func DefaultFeeQuoterDestChainConfig() fee_quoter.FeeQuoterDestChainConfig {
NetworkFeeUSDCents: 1,
ChainFamilySelector: [4]byte(evmFamilySelector),
}
if !configEnabled {
feeQuoterDestChainConfig.IsEnabled = false
}
return feeQuoterDestChainConfig
b-gopalswami marked this conversation as resolved.
Show resolved Hide resolved
}
4 changes: 2 additions & 2 deletions deployment/ccip/changeset/cs_chain_contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,8 @@ func TestUpdateFQDests(t *testing.T) {
}
}

fqCfg1 := changeset.DefaultFeeQuoterDestChainConfig()
fqCfg2 := changeset.DefaultFeeQuoterDestChainConfig()
fqCfg1 := changeset.DefaultFeeQuoterDestChainConfig(true)
fqCfg2 := changeset.DefaultFeeQuoterDestChainConfig(true)
fqCfg2.DestGasOverhead = 1000
_, err = commonchangeset.ApplyChangesets(t, tenv.Env, tenv.TimelockContracts(t), []commonchangeset.ChangesetApplication{
{
Expand Down
61 changes: 60 additions & 1 deletion deployment/ccip/changeset/testhelpers/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,65 @@ func AddLane(
require.NoError(t, err)
}

// RemoveLane removes a lane between the source and destination chains in the deployed environment.
func RemoveLane(t *testing.T, e *DeployedEnv, src, dest uint64, isTestRouter bool) {
var err error
apps := []commoncs.ChangesetApplication{
{
Changeset: commoncs.WrapChangeSet(changeset.UpdateRouterRampsChangeset),
Config: changeset.UpdateRouterRampsConfig{
UpdatesByChain: map[uint64]changeset.RouterUpdates{
// onRamp update on source chain
src: {
OnRampUpdates: map[uint64]bool{
dest: false,
},
},
},
},
},
{
Changeset: commoncs.WrapChangeSet(changeset.UpdateFeeQuoterDestsChangeset),
Config: changeset.UpdateFeeQuoterDestsConfig{
UpdatesByChain: map[uint64]map[uint64]fee_quoter.FeeQuoterDestChainConfig{
src: {
dest: changeset.DefaultFeeQuoterDestChainConfig(false),
},
},
},
},
{
Changeset: commoncs.WrapChangeSet(changeset.UpdateOnRampsDestsChangeset),
Config: changeset.UpdateOnRampDestsConfig{
UpdatesByChain: map[uint64]map[uint64]changeset.OnRampDestinationUpdate{
src: {
dest: {
IsEnabled: false,
TestRouter: isTestRouter,
AllowListEnabled: false,
},
},
},
},
},
{
Changeset: commoncs.WrapChangeSet(changeset.UpdateOffRampSourcesChangeset),
Config: changeset.UpdateOffRampSourcesConfig{
UpdatesByChain: map[uint64]map[uint64]changeset.OffRampSourceUpdate{
dest: {
src: {
IsEnabled: false,
TestRouter: isTestRouter,
},
},
},
},
},
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@makramkd would it affect the inflight requests?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, no offramp changes should be made

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my observation, if no offramp changes are made, then in-flight requests are getting delivered. Let's say, we made the offramp changes as well, in that case the in-flight requests are not getting delivered. Haven't seen reverting as well not sure If I need to wait longer for that.

So, we should not make offramp changes when there is in-flight request. Will remove this part.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if no offramp changes are made, then in-flight requests are getting delivered.

Yeah, just to be clear this is the desired behavior.

e.Env, err = commoncs.ApplyChangesets(t, e.Env, e.TimelockContracts(t), apps)
require.NoError(t, err)
}

func AddLaneWithDefaultPricesAndFeeQuoterConfig(t *testing.T, e *DeployedEnv, state changeset.CCIPOnChainState, from, to uint64, isTestRouter bool) {
stateChainFrom := state.Chains[from]
AddLane(
Expand All @@ -494,7 +553,7 @@ func AddLaneWithDefaultPricesAndFeeQuoterConfig(t *testing.T, e *DeployedEnv, st
}, map[common.Address]*big.Int{
stateChainFrom.LinkToken.Address(): DefaultLinkPrice,
stateChainFrom.Weth9.Address(): DefaultWethPrice,
}, changeset.DefaultFeeQuoterDestChainConfig())
}, changeset.DefaultFeeQuoterDestChainConfig(true))
}

// AddLanesForAll adds densely connected lanes for all chains in the environment so that each chain
Expand Down
2 changes: 1 addition & 1 deletion deployment/environment/crib/ccip_deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ func DeployCCIPAndAddLanes(ctx context.Context, lggr logger.Logger, envConfig de
Config: changeset.UpdateFeeQuoterDestsConfig{
UpdatesByChain: map[uint64]map[uint64]fee_quoter.FeeQuoterDestChainConfig{
src: {
dst: changeset.DefaultFeeQuoterDestChainConfig(),
dst: changeset.DefaultFeeQuoterDestChainConfig(true),
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/smoke/ccip/ccip_add_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ func feeQuoterDestUpdates(t *testing.T, dests []uint64, sources []uint64) (updat
if _, ok := updates[source]; !ok {
updates[source] = make(map[uint64]fee_quoter.FeeQuoterDestChainConfig)
}
updates[source][dest] = ccipcs.DefaultFeeQuoterDestChainConfig()
updates[source][dest] = ccipcs.DefaultFeeQuoterDestChainConfig(true)
}
}
return
Expand Down
137 changes: 137 additions & 0 deletions integration-tests/smoke/ccip/ccip_disable_lane_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package ccip

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"

"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
"github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers"
testsetups "github.com/smartcontractkit/chainlink/integration-tests/testsetups/ccip"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router"
)

// Intention of this test is to ensure that the lane can be disabled and enabled correctly without disrupting the other lanes.
func TestDisableLane(t *testing.T) {
tenv, _, _ := testsetups.NewIntegrationEnvironment(t,
testhelpers.WithNumOfChains(3),
testhelpers.WithNumOfUsersPerChain(2),
)

e := tenv.Env
state, err := changeset.LoadOnchainState(e)
require.NoError(t, err)

// add all lanes
testhelpers.AddLanesForAll(t, &tenv, state)

var (
chains = e.AllChainSelectors()
chainA, chainB, chainC = chains[0], chains[1], chains[2]
expectedSeqNumExec = make(map[testhelpers.SourceDestPair][]uint64)
startBlocks = make(map[uint64]*uint64)
pairs = testsetups.GetSourceDestPairs([]uint64{chainA, chainB, chainC})

sendmessage = func(src, dest uint64, deployer *bind.TransactOpts) (*onramp.OnRampCCIPMessageSent, error) {
return testhelpers.DoSendRequest(
t,
e,
state,
testhelpers.WithSender(deployer),
testhelpers.WithSourceChain(src),
testhelpers.WithDestChain(dest),
testhelpers.WithTestRouter(false),
testhelpers.WithEvm2AnyMessage(router.ClientEVM2AnyMessage{
Receiver: common.LeftPadBytes(state.Chains[chainB].Receiver.Address().Bytes(), 32),
Data: []byte("hello"),
TokenAmounts: nil,
FeeToken: common.HexToAddress("0x0"),
ExtraArgs: nil,
}))
}

assertSendRequestReverted = func(src, dest uint64, deployer *bind.TransactOpts) {
_, err = sendmessage(src, dest, deployer)
require.Error(t, err)
require.Contains(t, err.Error(), "execution reverted")
}

assertRequestSent = func(src, dest uint64, deployer *bind.TransactOpts) {
latestHeader, err := e.Chains[dest].Client.HeaderByNumber(testcontext.Get(t), nil)
require.NoError(t, err)
block := latestHeader.Number.Uint64()
messageSentEvent, err := sendmessage(src, dest, e.Chains[src].DeployerKey)
require.NoError(t, err)
expectedSeqNumExec[testhelpers.SourceDestPair{
SourceChainSelector: src,
DestChainSelector: dest,
}] = []uint64{messageSentEvent.SequenceNumber}
startBlocks[dest] = &block
}
)

// disable lane A -> B
testhelpers.RemoveLane(t, &tenv, chainA, chainB, false)
// send a message to confirm it is reverted between chainA and chainB
assertSendRequestReverted(chainA, chainB, e.Chains[chainA].Users[0])
// send a message between chainB and chainA to confirm it is not reverted
assertRequestSent(chainB, chainA, e.Chains[chainB].Users[0])
// disable lane B -> A
testhelpers.RemoveLane(t, &tenv, chainB, chainA, false)
assertSendRequestReverted(chainB, chainA, e.Chains[chainB].Users[0])

// send message in other lanes and ensure they are delivered
go func() {
assertRequestSent(chainA, chainC, e.Chains[chainA].Users[1])
assertRequestSent(chainC, chainA, e.Chains[chainC].Users[1])
assertRequestSent(chainB, chainC, e.Chains[chainB].Users[1])
assertRequestSent(chainC, chainB, e.Chains[chainC].Users[1])
}()
// disable lanes between A & C and C & B while requests are getting sent
testhelpers.RemoveLane(t, &tenv, chainA, chainC, false)
b-gopalswami marked this conversation as resolved.
Show resolved Hide resolved
testhelpers.RemoveLane(t, &tenv, chainC, chainA, false)
testhelpers.RemoveLane(t, &tenv, chainB, chainC, false)
testhelpers.RemoveLane(t, &tenv, chainC, chainB, false)
b-gopalswami marked this conversation as resolved.
Show resolved Hide resolved
// check fee quoter returns error when the lane is disabled
gp, err := state.Chains[chainA].FeeQuoter.GetTokenAndGasPrices(&bind.CallOpts{
Context: tests.Context(t),
}, state.Chains[chainB].Weth9.Address(), chainB)
require.Error(t, err)
require.Contains(t, err.Error(), "execution reverted")
require.Nil(t, gp.GasPriceValue)
require.Nil(t, gp.TokenPrice)
// confirm that message sent in all lanes are reverted after disabling the lanes
for _, pair := range pairs {
assertSendRequestReverted(pair.SourceChainSelector, pair.DestChainSelector, e.Chains[pair.SourceChainSelector].Users[0])
}
// re-enable all the lanes
var (
linkPrice = deployment.E18Mult(100)
wethPrice = deployment.E18Mult(4000)
)
for _, pair := range pairs {
testhelpers.AddLane(t, &tenv, pair.SourceChainSelector, pair.DestChainSelector, false,
map[uint64]*big.Int{
pair.DestChainSelector: testhelpers.DefaultGasPrice,
},
map[common.Address]*big.Int{
state.Chains[pair.SourceChainSelector].LinkToken.Address(): linkPrice,
state.Chains[pair.SourceChainSelector].Weth9.Address(): wethPrice,
},
changeset.DefaultFeeQuoterDestChainConfig(true))
}
// send a message in all the lane including re-enabled lanes
for _, pair := range pairs {
assertRequestSent(pair.SourceChainSelector, pair.DestChainSelector, e.Chains[pair.SourceChainSelector].Users[0])
}
// confirm all messages are delivered
testhelpers.ConfirmExecWithSeqNrsForAll(t, e, state, expectedSeqNumExec, startBlocks)
}
2 changes: 1 addition & 1 deletion integration-tests/smoke/ccip/ccip_fee_boosting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func Test_CCIPFeeBoosting(t *testing.T) {
)
t.Log("Adjusted gas price on dest chain:", adjustedGasPriceDest)

feeQuoterCfg := changeset.DefaultFeeQuoterDestChainConfig()
feeQuoterCfg := changeset.DefaultFeeQuoterDestChainConfig(true)
// the default adds 10% to the gas price, we want to increase it
// to make sure the fee boosting will be finished in proper time for testing
feeQuoterCfg.GasMultiplierWeiPerEth = 120e16
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/smoke/ccip/ccip_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ func Test_GetMedianDataAvailabilityGasConfig(t *testing.T) {
boundContracts := map[cciptypes.ChainSelector][]types.BoundContract{}
for i, selector := range env.Env.AllChainSelectorsExcluding([]uint64{destChain}) {
feeQuoter := state.Chains[selector].FeeQuoter
destChainCfg := changeset.DefaultFeeQuoterDestChainConfig()
destChainCfg := changeset.DefaultFeeQuoterDestChainConfig(true)
//nolint:gosec // disable G115
destChainCfg.DestDataAvailabilityOverheadGas = uint32(100 + i)
//nolint:gosec // disable G115
Expand Down
15 changes: 15 additions & 0 deletions integration-tests/testsetups/ccip/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -721,3 +721,18 @@ func SetNodeConfig(nets []blockchain.EVMNetwork, nodeConfig, commonChain string,
tomlStr, err := tomlCfg.TOMLString()
return tomlCfg, tomlStr, err
}

func GetSourceDestPairs(chains []uint64) []testhelpers.SourceDestPair {
var pairs []testhelpers.SourceDestPair
for i, src := range chains {
for j, dest := range chains {
if i != j {
pairs = append(pairs, testhelpers.SourceDestPair{
SourceChainSelector: src,
DestChainSelector: dest,
})
}
}
}
return pairs
}
Loading