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 - new integration tests #15473

Merged
merged 8 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,45 @@ runner-test-matrix:
E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2,SIMULATED_3
E2E_JD_VERSION: 0.6.0

- id: smoke/ccip/ccip_message_limitations_test.go:*
path: integration-tests/smoke/ccip/ccip_message_limitations_test.go
test_env_type: docker
runs_on: ubuntu-latest
triggers:
- PR E2E Core Tests
- Nightly E2E Tests
test_cmd: cd integration-tests/smoke/ccip && go test -run '^Test_CCIPMessageLimitations' -timeout 18m -test.parallel=1 -count=1 -json ./...
pyroscope_env: ci-smoke-ccipv1_6-evm-simulated
test_env_vars:
E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2,SIMULATED_3
E2E_JD_VERSION: 0.6.0

- id: smoke/ccip/ccip_token_price_updates_test.go:*
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need to define 3 chains if you need only 2 of them. Let's keep only as many chains as we need for tests, that will make them faster

Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess same applied to other tests

path: integration-tests/smoke/ccip/ccip_token_price_updates_test.go
test_env_type: docker
runs_on: ubuntu-latest
triggers:
- PR E2E Core Tests
- Nightly E2E Tests
test_cmd: cd integration-tests/smoke/ccip && go test ccip_token_price_updates_test.go -timeout 18m -test.parallel=1 -count=1 -json
pyroscope_env: ci-smoke-ccipv1_6-evm-simulated
test_env_vars:
E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2,SIMULATED_3
E2E_JD_VERSION: 0.6.0

- id: smoke/ccip/ccip_gas_price_updates_test.go:*
path: integration-tests/smoke/ccip/ccip_gas_price_updates_test.go
test_env_type: docker
runs_on: ubuntu-latest
triggers:
- PR E2E Core Tests
- Nightly E2E Tests
test_cmd: cd integration-tests/smoke/ccip && go test ccip_gas_price_updates_test.go -timeout 18m -test.parallel=1 -count=1 -json
pyroscope_env: ci-smoke-ccipv1_6-evm-simulated
test_env_vars:
E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2,SIMULATED_3
E2E_JD_VERSION: 0.6.0

- id: smoke/ccip/ccip_rmn_test.go:^TestRMN_TwoMessagesOnTwoLanesIncludingBatching$
path: integration-tests/smoke/ccip/ccip_rmn_test.go
test_env_type: docker
Expand Down
26 changes: 23 additions & 3 deletions deployment/ccip/changeset/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,20 @@ func TestSendRequest(
testRouter bool,
evm2AnyMessage router.ClientEVM2AnyMessage,
) (msgSentEvent *onramp.OnRampCCIPMessageSent) {
msgSentEvent, err := DoSendRequest(t, e, state, src, dest, testRouter, evm2AnyMessage)
require.NoError(t, err)
return msgSentEvent
}

// DoSendRequest similar to TestSendRequest but returns an error.
func DoSendRequest(
t *testing.T,
e deployment.Environment,
state CCIPOnChainState,
src, dest uint64,
testRouter bool,
evm2AnyMessage router.ClientEVM2AnyMessage,
) (*onramp.OnRampCCIPMessageSent, error) {
t.Logf("Sending CCIP request from chain selector %d to chain selector %d",
src, dest)
tx, blockNum, err := CCIPSendRequest(
Expand All @@ -498,13 +512,19 @@ func TestSendRequest(
testRouter,
evm2AnyMessage,
)
require.NoError(t, err)
if err != nil {
return nil, err
}

it, err := state.Chains[src].OnRamp.FilterCCIPMessageSent(&bind.FilterOpts{
Start: blockNum,
End: &blockNum,
Context: context.Background(),
}, []uint64{dest}, []uint64{})
require.NoError(t, err)
if err != nil {
return nil, err
}

require.True(t, it.Next())
t.Logf("CCIP message (id %x) sent from chain selector %d to chain selector %d tx %s seqNum %d nonce %d sender %s",
it.Event.Message.Header.MessageId[:],
Expand All @@ -515,7 +535,7 @@ func TestSendRequest(
it.Event.Message.Header.Nonce,
it.Event.Message.Sender.String(),
)
return it.Event
return it.Event, nil
}

// MakeEVMExtraArgsV2 creates the extra args for the EVM2Any message that is destined
Expand Down
125 changes: 125 additions & 0 deletions integration-tests/smoke/ccip/ccip_gas_price_updates_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package smoke

import (
"math/big"
"testing"
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"

"github.com/smartcontractkit/chainlink-common/pkg/config"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
testsetups "github.com/smartcontractkit/chainlink/integration-tests/testsetups/ccip"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter"
"github.com/smartcontractkit/chainlink/v2/core/logger"
)

// Test_CCIPGasPriceUpdates tests that chain fee price updates are propagated correctly when
// price reaches some deviation threshold or when the price has expired.
func Test_CCIPGasPriceUpdates(t *testing.T) {
lggr := logger.TestLogger(t)
ctx := changeset.Context(t)

var gasPriceExpiry = 5 * time.Second
e, _, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr, &changeset.TestConfigs{
OCRConfigOverride: func(params changeset.CCIPOCRParams) changeset.CCIPOCRParams {
params.CommitOffChainConfig.RemoteGasPriceBatchWriteFrequency = *config.MustNewDuration(gasPriceExpiry)
return params
},
})
state, err := changeset.LoadOnchainState(e.Env)
require.NoError(t, err)
require.NoError(t, changeset.AddLanesForAll(e.Env, state))

allChainSelectors := maps.Keys(e.Env.Chains)
assert.GreaterOrEqual(t, len(allChainSelectors), 2, "test requires at least 2 chains")

sourceChain1 := allChainSelectors[0]
sourceChain2 := allChainSelectors[1]

feeQuoter1 := state.Chains[sourceChain1].FeeQuoter
feeQuoter2 := state.Chains[sourceChain2].FeeQuoter

// get initial chain fees
chainFee2, err := feeQuoter1.GetDestinationChainGasPrice(&bind.CallOpts{Context: ctx}, sourceChain2)
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit, but it seems we use &bind.CallOpts{Context: ctx} multiple times, might be worth extracting to some variable or func

require.NoError(t, err)
chainFee1, err := feeQuoter2.GetDestinationChainGasPrice(&bind.CallOpts{Context: ctx}, sourceChain1)
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe something like initialChainFee and updatedChainFee after the price updates?

require.NoError(t, err)
t.Logf("initial chain1 fee (stored in chain2): %v", chainFee1)
t.Logf("initial chain2 fee (stored in chain1): %v", chainFee2)

// get latest price updates sequence number from the offRamps
offRampChain1 := state.Chains[sourceChain1].OffRamp
offRampChain2 := state.Chains[sourceChain2].OffRamp
priceUpdatesSeqNumChain1, err := offRampChain1.GetLatestPriceSequenceNumber(&bind.CallOpts{Context: ctx})
require.NoError(t, err)
priceUpdatesSeqNumChain2, err := offRampChain2.GetLatestPriceSequenceNumber(&bind.CallOpts{Context: ctx})
require.NoError(t, err)
t.Logf("priceUpdatesSeqNumChain1: %v", priceUpdatesSeqNumChain1)
t.Logf("priceUpdatesSeqNumChain2: %v", priceUpdatesSeqNumChain2)

// update the price of chain2
tx, err := feeQuoter1.UpdatePrices(e.Env.Chains[sourceChain1].DeployerKey, fee_quoter.InternalPriceUpdates{
TokenPriceUpdates: nil,
GasPriceUpdates: []fee_quoter.InternalGasPriceUpdate{
{DestChainSelector: sourceChain2, UsdPerUnitGas: big.NewInt(5123)},
},
})
require.NoError(t, err)
_, err = deployment.ConfirmIfNoError(e.Env.Chains[sourceChain1], tx, err)
require.NoError(t, err)

// assert that the chain fees are updated by the commit plugin reports
priceDeviationChecked := false // flag to check if price deviation condition was met
assert.Eventually(t, func() bool {
// offRamps should have updated the sequence number
priceUpdatesSeqNumChain1Now, err := offRampChain1.GetLatestPriceSequenceNumber(&bind.CallOpts{Context: ctx})
require.NoError(t, err)
priceUpdatesSeqNumChain2Now, err := offRampChain2.GetLatestPriceSequenceNumber(&bind.CallOpts{Context: ctx})
require.NoError(t, err)
t.Logf("priceUpdatesSeqNumChain1: %v", priceUpdatesSeqNumChain1Now)
t.Logf("priceUpdatesSeqNumChain2: %v", priceUpdatesSeqNumChain2Now)
if priceUpdatesSeqNumChain1Now <= priceUpdatesSeqNumChain1 {
return false
}
if priceUpdatesSeqNumChain2Now <= priceUpdatesSeqNumChain2 {
return false
}

chainFee2Now, err := feeQuoter1.GetDestinationChainGasPrice(&bind.CallOpts{Context: ctx}, sourceChain2)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just an idea, but maybe it would be easier to understand the code if we name these as sourceChain and destChain. I think it would be easier to read it than chain1 and chain2

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's not really true though, because lanes are bidirectional so both are source/dest

require.NoError(t, err)
chainFee1Now, err := feeQuoter2.GetDestinationChainGasPrice(&bind.CallOpts{Context: ctx}, sourceChain1)
require.NoError(t, err)
t.Logf("chainFee1 (stored in chain2): %v", chainFee1Now)
t.Logf("chainFee2 (stored in chain1): %v", chainFee2Now)

if !priceDeviationChecked {
// make sure there was a price update for chain2 when price deviation was reached
if chainFee2Now.Value.Cmp(chainFee2.Value) == 0 {
t.Logf("chainFee2 not updated: %v original=%v", chainFee2Now, chainFee2.Value)
return false
}
require.NotEqual(t, chainFee2Now.Timestamp, chainFee2.Timestamp)
priceDeviationChecked = true
}

// make sure there was a price update for chain1 but with the same price - when expiration is reached
if chainFee1Now.Timestamp == chainFee1.Timestamp {
t.Logf("chainFee1 timestamp not updated: %v original=%v", chainFee1Now, chainFee1.Timestamp)
chainFee1 = chainFee1Now
return false
}
if chainFee1Now.Value.Cmp(chainFee1.Value) != 0 {
t.Logf("chainFee1 changed: %v prev:%v", chainFee1Now, chainFee1.Value)
chainFee1 = chainFee1Now
return false
}

return priceDeviationChecked
}, tests.WaitTimeout(t), 500*time.Millisecond)
}
Loading
Loading