Skip to content

Commit

Permalink
deployment/ccip/changeset: add messaging test (#15166)
Browse files Browse the repository at this point in the history
* deployment/ccip/changeset: add messaging test

Small refactor to the request helpers to allow for specifying all fields
of the message.

* fix lint

* use cl-ccip version with fix

* refactor test to dedup logic

* combine var decls

* fix ccip reader nonces test

* bump cl-ccip to latest main
  • Loading branch information
makramkd authored Nov 11, 2024
1 parent 03827b9 commit 00d9661
Show file tree
Hide file tree
Showing 21 changed files with 388 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ func TestCCIPReader_Nonces(t *testing.T) {
// Add some nonces.
for chain, addrs := range nonces {
for addr, nonce := range addrs {
_, err := s.contract.SetInboundNonce(s.auth, uint64(chain), nonce, addr.Bytes())
_, err := s.contract.SetInboundNonce(s.auth, uint64(chain), nonce, common.LeftPadBytes(addr.Bytes(), 32))
assert.NoError(t, err)
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/scripts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ require (
github.com/shirou/gopsutil/v3 v3.24.3 // indirect
github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240926212305-a6deabdfce86 // indirect
github.com/smartcontractkit/chain-selectors v1.0.27 // indirect
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 // indirect
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94 // indirect
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f // indirect
github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20241018134907-a00ba3729b5e // indirect
github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions core/scripts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1090,8 +1090,8 @@ github.com/smartcontractkit/chain-selectors v1.0.27 h1:VE/ftX9Aae4gnw67yR1raKi+3
github.com/smartcontractkit/chain-selectors v1.0.27/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE=
github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU=
github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422 h1:VfH/AW5NtTmroY9zz6OYCPFbFTqpMyJ2ubgT9ahYf3U=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241106140121-4c9ee21ab422/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94 h1:BeLnOf2KKQpJj9nzfnE7QEg9ZqJ2jy/sbpNYVixVM2Y=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20241111114733-aa3b2f8e9f94/go.mod h1:4adKaHNaxFsRvV/lYfqtbsWyyvIPUMLR0FdOJN/ljis=
github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4 h1:e+uFsxQ21tMQKRu4oBXKycNzoR30vO/7STBtqtDvQJQ=
github.com/smartcontractkit/chainlink-common v0.3.1-0.20241109002240-af894848b3b4/go.mod h1:ny87uTW6hLjCTLiBqBRNFEhETSXhHWevYlPclT5lSco=
github.com/smartcontractkit/chainlink-cosmos v0.5.2-0.20241017133723-5277829bd53f h1:BwrIaQIx5Iy6eT+DfLhFfK2XqjxRm74mVdlX8gbu4dw=
Expand Down
17 changes: 15 additions & 2 deletions deployment/ccip/add_lane_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router"
"github.com/smartcontractkit/chainlink/v2/core/logger"
)

Expand Down Expand Up @@ -94,7 +95,13 @@ func TestAddLane(t *testing.T) {
startBlock := latesthdr.Number.Uint64()
// Send traffic on the first lane and it should not be processed by the plugin as onRamp is disabled
// we will check this by confirming that the message is not executed by the end of the test
seqNum1 := TestSendRequest(t, e.Env, state, chain1, chain2, false, nil)
seqNum1 := TestSendRequest(t, e.Env, state, chain1, chain2, false, router.ClientEVM2AnyMessage{
Receiver: common.LeftPadBytes(state.Chains[chain2].Receiver.Address().Bytes(), 32),
Data: []byte("hello world"),
TokenAmounts: nil,
FeeToken: common.HexToAddress("0x0"),
ExtraArgs: nil,
})
require.Equal(t, uint64(1), seqNum1)

// Add another lane
Expand All @@ -104,7 +111,13 @@ func TestAddLane(t *testing.T) {
latesthdr, err = e.Env.Chains[chain1].Client.HeaderByNumber(testcontext.Get(t), nil)
require.NoError(t, err)
startBlock2 := latesthdr.Number.Uint64()
seqNum2 := TestSendRequest(t, e.Env, state, chain2, chain1, false, nil)
seqNum2 := TestSendRequest(t, e.Env, state, chain2, chain1, false, router.ClientEVM2AnyMessage{
Receiver: common.LeftPadBytes(state.Chains[chain2].Receiver.Address().Bytes(), 32),
Data: []byte("hello world"),
TokenAmounts: nil,
FeeToken: common.HexToAddress("0x0"),
ExtraArgs: nil,
})
require.Equal(t, uint64(1), seqNum2)
require.NoError(t, ConfirmExecWithSeqNr(t, e.Env.Chains[chain2], e.Env.Chains[chain1], state.Chains[chain1].OffRamp, &startBlock2, seqNum2))

Expand Down
10 changes: 9 additions & 1 deletion deployment/ccip/changeset/active_candidate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package changeset
import (
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"

"github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"

cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router"

"github.com/smartcontractkit/chainlink/deployment"

Expand Down Expand Up @@ -82,7 +84,13 @@ func TestActiveCandidate(t *testing.T) {
require.NoError(t, err)
block := latesthdr.Number.Uint64()
startBlocks[dest] = &block
seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, nil)
seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, router.ClientEVM2AnyMessage{
Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32),
Data: []byte("hello world"),
TokenAmounts: nil,
FeeToken: common.HexToAddress("0x0"),
ExtraArgs: nil,
})
expectedSeqNum[dest] = seqNum
}
}
Expand Down
8 changes: 7 additions & 1 deletion deployment/ccip/changeset/add_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,13 @@ func TestAddChainInbound(t *testing.T) {
latesthdr, err := e.Env.Chains[newChain].Client.HeaderByNumber(testcontext.Get(t), nil)
require.NoError(t, err)
startBlock := latesthdr.Number.Uint64()
seqNr := ccipdeployment.TestSendRequest(t, e.Env, state, initialDeploy[0], newChain, true, nil)
seqNr := ccipdeployment.TestSendRequest(t, e.Env, state, initialDeploy[0], newChain, true, router.ClientEVM2AnyMessage{
Receiver: common.LeftPadBytes(state.Chains[newChain].Receiver.Address().Bytes(), 32),
Data: []byte("hello world"),
TokenAmounts: nil,
FeeToken: common.HexToAddress("0x0"),
ExtraArgs: nil,
})
require.NoError(t,
ccipdeployment.ConfirmCommitWithExpectedSeqNumRange(t, e.Env.Chains[initialDeploy[0]], e.Env.Chains[newChain], state.Chains[newChain].OffRamp, &startBlock, cciptypes.SeqNumRange{
cciptypes.SeqNum(1),
Expand Down
10 changes: 9 additions & 1 deletion deployment/ccip/changeset/initial_deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package changeset
import (
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/chainlink/deployment"

ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip"
Expand All @@ -11,6 +12,7 @@ import (

"github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"

"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router"
"github.com/smartcontractkit/chainlink/v2/core/logger"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -72,7 +74,13 @@ func TestInitialDeploy(t *testing.T) {
require.NoError(t, err)
block := latesthdr.Number.Uint64()
startBlocks[dest] = &block
seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, nil)
seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, router.ClientEVM2AnyMessage{
Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32),
Data: []byte("hello"),
TokenAmounts: nil,
FeeToken: common.HexToAddress("0x0"),
ExtraArgs: nil,
})
expectedSeqNum[dest] = seqNum
}
}
Expand Down
209 changes: 209 additions & 0 deletions deployment/ccip/changeset/messaging_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
package changeset

import (
"testing"
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/chainlink-common/pkg/utils/tests"
"github.com/smartcontractkit/chainlink/deployment"
ccipdeployment "github.com/smartcontractkit/chainlink/deployment/ccip"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/test-go/testify/require"
"golang.org/x/exp/maps"
)

type testCaseSetup struct {
t *testing.T
sender []byte
deployedEnv ccipdeployment.DeployedEnv
onchainState ccipdeployment.CCIPOnChainState
sourceChain, destChain uint64
}

type messagingTestCase struct {
testCaseSetup
replayed bool
nonce uint64
}

type messagingTestCaseOutput struct {
replayed bool
nonce uint64
}

func Test_Messaging(t *testing.T) {
t.Parallel()

// Setup 2 chains and a single lane.
e := ccipdeployment.NewMemoryEnvironmentWithJobs(t, logger.TestLogger(t), 2, 4)
state, err := ccipdeployment.LoadOnchainState(e.Env)
require.NoError(t, err)

allChainSelectors := maps.Keys(e.Env.Chains)
require.Len(t, allChainSelectors, 2)
sourceChain := allChainSelectors[0]
destChain := allChainSelectors[1]
t.Log("All chain selectors:", allChainSelectors,
", home chain selector:", e.HomeChainSel,
", feed chain selector:", e.FeedChainSel,
", source chain selector:", sourceChain,
", dest chain selector:", destChain,
)

tokenConfig := ccipdeployment.NewTestTokenConfig(state.Chains[e.FeedChainSel].USDFeeds)
newAddresses := deployment.NewMemoryAddressBook()
err = ccipdeployment.DeployCCIPContracts(e.Env, newAddresses, ccipdeployment.DeployCCIPContractConfig{
HomeChainSel: e.HomeChainSel,
FeedChainSel: e.FeedChainSel,
ChainsToDeploy: allChainSelectors,
TokenConfig: tokenConfig,
MCMSConfig: ccipdeployment.NewTestMCMSConfig(t, e.Env),
OCRSecrets: deployment.XXXGenerateTestOCRSecrets(),
})
require.NoError(t, err)
require.NoError(t, e.Env.ExistingAddresses.Merge(newAddresses))
state, err = ccipdeployment.LoadOnchainState(e.Env)
require.NoError(t, err)

// connect a single lane, source to dest
require.NoError(t, ccipdeployment.AddLane(e.Env, state, sourceChain, destChain))

var (
replayed bool
nonce uint64
sender = common.LeftPadBytes(e.Env.Chains[sourceChain].DeployerKey.From.Bytes(), 32)
out messagingTestCaseOutput
setup = testCaseSetup{
t: t,
sender: sender,
deployedEnv: e,
onchainState: state,
sourceChain: sourceChain,
destChain: destChain,
}
)

t.Run("data message to eoa", func(t *testing.T) {
out = runMessagingTestCase(messagingTestCase{
testCaseSetup: setup,
replayed: replayed,
nonce: nonce,
},
common.HexToAddress("0xdead"),
[]byte("hello eoa"),
)
})

t.Run("message to contract not implementing CCIPReceiver", func(t *testing.T) {
out = runMessagingTestCase(
messagingTestCase{
testCaseSetup: setup,
replayed: out.replayed,
nonce: out.nonce,
},
state.Chains[destChain].FeeQuoter.Address(),
[]byte("hello FeeQuoter"),
)
})

t.Run("message to contract implementing CCIPReceiver", func(t *testing.T) {
out = runMessagingTestCase(
messagingTestCase{
testCaseSetup: setup,
replayed: out.replayed,
nonce: out.nonce,
},
state.Chains[destChain].Receiver.Address(),
[]byte("hello CCIPReceiver"),
func(t *testing.T) {
iter, err := state.Chains[destChain].Receiver.FilterMessageReceived(nil)
require.NoError(t, err)
require.True(t, iter.Next())
// MessageReceived doesn't emit the data unfortunately, so can't check that.
},
)
})

t.Run("message to contract implementing CCIPReceiver with low exec gas", func(t *testing.T) {
out = runMessagingTestCase(
messagingTestCase{
testCaseSetup: setup,
replayed: out.replayed,
nonce: out.nonce,
},
state.Chains[destChain].Receiver.Address(),
[]byte("hello CCIPReceiver with low exec gas"),
func(t *testing.T) {
// Message should not be emitted, not enough gas to emit log.
// TODO: this is still returning a log, probably the older one since FAILURE is the execution state.
// Not enough ctx in the message received log to confirm that it's from another test.
// Maybe check the log block number and assert that its < the header before block number from above?
// iter, err := ccipReceiver.FilterMessageReceived(&bind.FilterOpts{
// Start: headerBefore.Number.Uint64(),
// })
// require.NoError(t, err)
// require.False(t, iter.Next(), "MessageReceived should not be emitted in this test case since gas is too low")
},
)
})
}

func sleepAndReplay(t *testing.T, e ccipdeployment.DeployedEnv, sourceChain, destChain uint64) {
time.Sleep(30 * time.Second)
replayBlocks := make(map[uint64]uint64)
replayBlocks[sourceChain] = 1
replayBlocks[destChain] = 1
ccipdeployment.ReplayLogs(t, e.Env.Offchain, replayBlocks)
}

func runMessagingTestCase(
tc messagingTestCase,
receiver common.Address,
msgData []byte,
extraAssertions ...func(t *testing.T),
) (out messagingTestCaseOutput) {
// check latest nonce
latestNonce, err := tc.onchainState.Chains[tc.destChain].NonceManager.GetInboundNonce(&bind.CallOpts{
Context: tests.Context(tc.t),
}, tc.sourceChain, tc.sender)
require.NoError(tc.t, err)
require.Equal(tc.t, tc.nonce, latestNonce)

startBlocks := make(map[uint64]*uint64)
seqNum := ccipdeployment.TestSendRequest(tc.t, tc.deployedEnv.Env, tc.onchainState, tc.sourceChain, tc.destChain, false, router.ClientEVM2AnyMessage{
Receiver: common.LeftPadBytes(receiver.Bytes(), 32),
Data: msgData,
TokenAmounts: nil,
FeeToken: common.HexToAddress("0x0"),
ExtraArgs: nil,
})
expectedSeqNum := make(map[uint64]uint64)
expectedSeqNum[tc.destChain] = seqNum

// hack
if !tc.replayed {
sleepAndReplay(tc.t, tc.deployedEnv, tc.sourceChain, tc.destChain)
out.replayed = true
}

ccipdeployment.ConfirmCommitForAllWithExpectedSeqNums(tc.t, tc.deployedEnv.Env, tc.onchainState, expectedSeqNum, startBlocks)
ccipdeployment.ConfirmExecWithSeqNrForAll(tc.t, tc.deployedEnv.Env, tc.onchainState, expectedSeqNum, startBlocks)

// check the sender latestNonce on the dest, should be incremented
latestNonce, err = tc.onchainState.Chains[tc.destChain].NonceManager.GetInboundNonce(&bind.CallOpts{
Context: tests.Context(tc.t),
}, tc.sourceChain, tc.sender)
require.NoError(tc.t, err)
require.Equal(tc.t, tc.nonce+1, latestNonce)
out.nonce = latestNonce
tc.t.Logf("confirmed nonce bump for sender %x, latestNonce %d", tc.sender, latestNonce)

for _, assertion := range extraAssertions {
assertion(tc.t)
}

return
}
9 changes: 8 additions & 1 deletion deployment/ccip/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,8 +573,15 @@ func DeployChainContracts(
tx, err = tokenAdminRegistry.Contract.AddRegistryModule(chain.DeployerKey, customRegistryModule.Address)
if err != nil {
e.Logger.Errorw("Failed to assign registry module on token admin registry", "err", err)
return err
return fmt.Errorf("failed to assign registry module on token admin registry: %w", err)
}

_, err = chain.Confirm(tx)
if err != nil {
e.Logger.Errorw("Failed to confirm assign registry module on token admin registry", "err", err)
return fmt.Errorf("failed to confirm assign registry module on token admin registry: %w", err)
}

e.Logger.Infow("assigned registry module on token admin registry")

nonceManager, err := deployContract(e.Logger, chain, ab,
Expand Down
Loading

0 comments on commit 00d9661

Please sign in to comment.