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
16 changes: 8 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 @@ -1125,7 +1125,7 @@ func DefaultFeeQuoterDestChainConfig() fee_quoter.FeeQuoterDestChainConfig {
*/
evmFamilySelector, _ := hex.DecodeString("2812d52c")
return fee_quoter.FeeQuoterDestChainConfig{
IsEnabled: true,
IsEnabled: configEnabled,
MaxNumberOfTokensPerMsg: 10,
MaxDataBytes: 256,
MaxPerMsgGasLimit: 3_000_000,
Expand Down
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
48 changes: 47 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,52 @@ 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,
},
},
},
},
},
}
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 +540,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
142 changes: 142 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,142 @@
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 and in-flight requests are delivered.
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 []testhelpers.SourceDestPair
linkPrice = deployment.E18Mult(100)
wethPrice = deployment.E18Mult(4000)
noOfRequests = 3
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
pairs = append(pairs, testhelpers.SourceDestPair{
SourceChainSelector: chainA,
DestChainSelector: chainB,
})
testhelpers.RemoveLane(t, &tenv, chainA, chainB, false)
// send a message to confirm it is reverted between A -> B
assertSendRequestReverted(chainA, chainB, e.Chains[chainA].Users[0])

// send a message in other direction B -> A to confirm it is delivered
assertRequestSent(chainB, chainA, e.Chains[chainB].Users[0])
testhelpers.ConfirmExecWithSeqNrsForAll(t, e, state, expectedSeqNumExec, startBlocks)

// send a multiple message between A -> C and disable the lane while the requests are in-flight
expectedSeqNumExec = make(map[testhelpers.SourceDestPair][]uint64)
for range noOfRequests {
assertRequestSent(chainA, chainC, e.Chains[chainA].Users[1])
}
// disable lane A -> C while requests are getting sent in that lane
pairs = append(pairs, testhelpers.SourceDestPair{
SourceChainSelector: chainA,
DestChainSelector: chainC,
})
testhelpers.RemoveLane(t, &tenv, chainA, chainC, false)
b-gopalswami marked this conversation as resolved.
Show resolved Hide resolved

// confirm all in-flight messages are delivered in A -> C lane
testhelpers.ConfirmExecWithSeqNrsForAll(t, e, state, expectedSeqNumExec, startBlocks)

// now, as the lane is disabled, confirm that message sent in A -> C is reverted
assertSendRequestReverted(chainA, chainC, e.Chains[chainA].Users[0])

// check getting token and gas price form fee quoter returns error when A -> C lane is disabled
gp, err := state.Chains[chainA].FeeQuoter.GetTokenAndGasPrices(&bind.CallOpts{
Context: tests.Context(t),
}, state.Chains[chainC].Weth9.Address(), chainC)
require.Error(t, err)
require.Contains(t, err.Error(), "execution reverted")
require.Nil(t, gp.GasPriceValue)
require.Nil(t, gp.TokenPrice)

// re-enable all the disabled lanes
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