Skip to content

Commit

Permalink
CCIP-4234 CCIP Chain Reader E2E Tests DiscoverContract & GetContractA…
Browse files Browse the repository at this point in the history
…ddress (#16515)

* feat: E2E test CCIP chain reader GetContractAddress

* feat: E2E test Discover contract, interplay with Sync

* remove printing contract address

* minor style fix

* fix: pass address param codec in DiscoverContract test

* fix lint

* fix comment

* remove unecessary comment

* update DiscoverContract test to assert with require.Eventually

* replace with tests.WaitTimeout(t)
  • Loading branch information
0xsuryansh authored and krehermann committed Feb 27, 2025
1 parent d73ee5f commit 9bee27e
Showing 1 changed file with 252 additions and 2 deletions.
254 changes: 252 additions & 2 deletions integration-tests/smoke/ccip/ccip_reader_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ccip

import (
"bytes"
"context"
"fmt"
"math/big"
Expand Down Expand Up @@ -731,7 +732,6 @@ func TestCCIPReader_NextSeqNum(t *testing.T) {
func TestCCIPReader_GetExpectedNextSequenceNumber(t *testing.T) {
t.Parallel()
ctx := tests.Context(t)
//env := NewMemoryEnvironmentContractsOnly(t, logger.TestLogger(t), 2, 4, nil)
env, _ := testhelpers.NewMemoryEnvironment(t)
state, err := changeset.LoadOnchainState(env.Env)
require.NoError(t, err)
Expand Down Expand Up @@ -839,6 +839,257 @@ func TestCCIPReader_Nonces(t *testing.T) {
}
}

func TestCCIPReader_GetContractAddress(t *testing.T) {
t.Parallel()
ctx := tests.Context(t)
sb, auth := setupSimulatedBackendAndAuth(t)

s := testSetup(ctx, t, testSetupParams{
ReaderChain: chainS1,
DestChain: chainD,
OnChainSeqNums: nil,
Cfg: evmconfig.DestReaderConfig,
BindTester: true,
ContractNameToBind: consts.ContractNameOffRamp,
SimulatedBackend: sb,
Auth: auth,
UseHeavyDB: false,
})

t.Run("success - single bound address", func(t *testing.T) {
myContractName := consts.ContractNameOffRamp
myAddress := s.contractAddr

err := s.extendedCR.Bind(ctx, []types.BoundContract{
{
Address: myAddress.String(),
Name: myContractName,
},
})
require.NoError(t, err)

gotBytes, err := s.reader.GetContractAddress(myContractName, chainS1)
require.NoError(t, err)

require.Equal(t, myAddress.Bytes(), gotBytes, "expected the bound contract address to match")
})

t.Run("error - no bindings found", func(t *testing.T) {
_, err := s.reader.GetContractAddress("UnboundContract", chainS1)
require.Error(t, err)
require.Contains(t, err.Error(), "expected one binding for the UnboundContract contract, got 0")
})

t.Run("success - multiple bindings, return override binding", func(t *testing.T) {
myContractName := consts.ContractNameOffRamp
addr1 := s.contractAddr
addr2, _, _, err := ccip_reader_tester.DeployCCIPReaderTester(auth, sb.Client())
require.NoError(t, err)
sb.Commit()

err = s.extendedCR.Bind(ctx, []types.BoundContract{
{
Address: addr1.String(),
Name: myContractName,
},
{
Address: addr2.String(),
Name: myContractName,
},
})
require.NoError(t, err)

gotBytes, err := s.reader.GetContractAddress(myContractName, chainS1)
require.NoError(t, err)

require.Equal(t, addr2.Bytes(), gotBytes, "expected the bound contract override address to match")
})

t.Run("error - chain not supported", func(t *testing.T) {
// Suppose chainS2 is not set up in this test environment (no contract reader).
// The call should fail with "contract reader not found for chain".
_, err := s.reader.GetContractAddress("TestContract", chainS2)
require.Error(t, err)
require.Contains(t, err.Error(), "contract reader not found for chain 2")
})
}

func TestCCIPReader_DiscoverContracts(t *testing.T) {
t.Parallel()
ctx := tests.Context(t)
sb, auth := setupSimulatedBackendAndAuth(t)

//--------------------------------Setup--------------------------------//
onRampS1StaticConfig := onramp.OnRampStaticConfig{
ChainSelector: uint64(chainS1),
RmnRemote: utils.RandomAddress(),
NonceManager: utils.RandomAddress(),
TokenAdminRegistry: utils.RandomAddress(),
}

onRampS1DynamicConfig := onramp.OnRampDynamicConfig{
FeeQuoter: utils.RandomAddress(),
ReentrancyGuardEntered: false,
MessageInterceptor: utils.ZeroAddress,
FeeAggregator: utils.RandomAddress(),
AllowlistAdmin: utils.RandomAddress(),
}

destinationChainConfigArgs := []onramp.OnRampDestChainConfigArgs{
{
DestChainSelector: uint64(chainD),
Router: utils.RandomAddress(),
AllowlistEnabled: false,
},
}
onRampS1Addr, _, _, err := onramp.DeployOnRamp(auth, sb.Client(), onRampS1StaticConfig, onRampS1DynamicConfig, destinationChainConfigArgs)
require.NoError(t, err)
sb.Commit()

offRampDStaticConfig := offramp.OffRampStaticConfig{
ChainSelector: uint64(chainD),
GasForCallExactCheck: 0,
RmnRemote: utils.RandomAddress(),
TokenAdminRegistry: utils.RandomAddress(),
NonceManager: utils.RandomAddress(),
}

offRampDDynamicConfig := offramp.OffRampDynamicConfig{
FeeQuoter: utils.RandomAddress(),
PermissionLessExecutionThresholdSeconds: 1,
MessageInterceptor: utils.ZeroAddress,
}

offRampDSourceChainConfigArgs := []offramp.OffRampSourceChainConfigArgs{
{
Router: destinationChainConfigArgs[0].Router,
SourceChainSelector: onRampS1StaticConfig.ChainSelector,
IsEnabled: true,
IsRMNVerificationDisabled: true,
OnRamp: common.LeftPadBytes(onRampS1Addr.Bytes(), 32),
},
}
offRampDestAddr, _, _, err := offramp.DeployOffRamp(auth, sb.Client(), offRampDStaticConfig, offRampDDynamicConfig, offRampDSourceChainConfigArgs)
require.NoError(t, err)
sb.Commit()

clS1 := client.NewSimulatedBackendClient(t, sb, big.NewInt(0).SetUint64(uint64(chainS1)))
headTrackerS1 := headstest.NewSimulatedHeadTracker(clS1, true, 1)
ormS1 := logpoller.NewORM(big.NewInt(0).SetUint64(uint64(chainS1)), pgtest.NewSqlxDB(t), logger.TestLogger(t))
lpOpts := logpoller.Opts{
PollPeriod: time.Millisecond,
FinalityDepth: 0,
BackfillBatchSize: 10,
RPCBatchSize: 10,
KeepFinalizedBlocksDepth: 100000,
}
lpS1 := logpoller.NewLogPoller(
ormS1,
clS1,
logger.TestLogger(t),
headTrackerS1,
lpOpts,
)
require.NoError(t, lpS1.Start(ctx))

clD := client.NewSimulatedBackendClient(t, sb, big.NewInt(0).SetUint64(uint64(chainD)))
headTrackerD := headstest.NewSimulatedHeadTracker(clD, true, 1)
ormD := logpoller.NewORM(big.NewInt(0).SetUint64(uint64(chainD)), pgtest.NewSqlxDB(t), logger.TestLogger(t))
lpD := logpoller.NewLogPoller(
ormD,
clD,
logger.TestLogger(t),
headTrackerD,
lpOpts,
)
require.NoError(t, lpD.Start(ctx))

crS1, err := evm.NewChainReaderService(ctx, logger.TestLogger(t), lpS1, headTrackerS1, clS1, evmconfig.SourceReaderConfig)
require.NoError(t, err)
extendedCrS1 := contractreader.NewExtendedContractReader(crS1)

crD, err := evm.NewChainReaderService(ctx, logger.TestLogger(t), lpD, headTrackerD, clD, evmconfig.DestReaderConfig)
require.NoError(t, err)
extendedCrD := contractreader.NewExtendedContractReader(crD)
err = extendedCrD.Bind(ctx, []types.BoundContract{
{
Address: offRampDestAddr.String(),
Name: consts.ContractNameOffRamp,
},
})
require.NoError(t, err)

err = crS1.Start(ctx)
require.NoError(t, err)
err = crD.Start(ctx)
require.NoError(t, err)

contractReaders := map[cciptypes.ChainSelector]contractreader.Extended{}
contractReaders[chainS1] = extendedCrS1
contractReaders[chainD] = extendedCrD

contractWriters := make(map[cciptypes.ChainSelector]types.ContractWriter)

addrCodec := ccipcommon.NewAddressCodec(ccipcommon.NewAddressCodecParams(ccipevm.AddressCodec{}, ccipsolana.AddressCodec{}))
reader := ccipreaderpkg.NewCCIPReaderWithExtendedContractReaders(ctx, logger.TestLogger(t), contractReaders, contractWriters, chainD, offRampDestAddr.Bytes(), addrCodec)

t.Cleanup(func() {
assert.NoError(t, crS1.Close())
assert.NoError(t, lpS1.Close())
assert.NoError(t, crD.Close())
assert.NoError(t, lpD.Close())
})
//--------------------------------Setup done--------------------------------//

// Call the ccip chain reader with DiscoverContracts for test
contractAddresses, err := reader.DiscoverContracts(ctx, []cciptypes.ChainSelector{chainS1, chainD})
require.NoError(t, err)

require.Equal(t, contractAddresses[consts.ContractNameOnRamp][chainS1], cciptypes.UnknownAddress(common.LeftPadBytes(onRampS1Addr.Bytes(), 32)))
require.Equal(t, contractAddresses[consts.ContractNameRouter][chainD], cciptypes.UnknownAddress(destinationChainConfigArgs[0].Router.Bytes()))
require.Equal(t, contractAddresses[consts.ContractNameRMNRemote][chainD], cciptypes.UnknownAddress(offRampDStaticConfig.RmnRemote.Bytes()))
require.Equal(t, contractAddresses[consts.ContractNameNonceManager][chainD], cciptypes.UnknownAddress(offRampDStaticConfig.NonceManager.Bytes()))
require.Equal(t, contractAddresses[consts.ContractNameFeeQuoter][chainD], cciptypes.UnknownAddress(offRampDDynamicConfig.FeeQuoter.Bytes()))

// Now Sync the CCIP Reader's S1 chain contract reader with OnRamp binding
onRampContractMapping := make(ccipreaderpkg.ContractAddresses)
onRampContractMapping[consts.ContractNameOnRamp] = make(map[cciptypes.ChainSelector]cciptypes.UnknownAddress)
onRampContractMapping[consts.ContractNameOnRamp][chainS1] = onRampS1Addr.Bytes()

err = reader.Sync(ctx, onRampContractMapping)
require.NoError(t, err)

// Since config poller has default refresh interval of 30s, we need to wait for the contract to be discovered
require.Eventually(t, func() bool {
contractAddresses, err = reader.DiscoverContracts(ctx, []cciptypes.ChainSelector{chainS1, chainD})
if err != nil {
return false
}

// Check if router and FeeQuoter addresses on source chain are now discovered
routerS1, routerExists := contractAddresses[consts.ContractNameRouter][chainS1]
feeQuoterS1, feeQuoterExists := contractAddresses[consts.ContractNameFeeQuoter][chainS1]

return routerExists && feeQuoterExists &&
bytes.Equal(routerS1, destinationChainConfigArgs[0].Router.Bytes()) &&
bytes.Equal(feeQuoterS1, onRampS1DynamicConfig.FeeQuoter.Bytes())
}, tests.WaitTimeout(t), 100*time.Millisecond, "Router and FeeQuoter addresses were not discovered on source chain in time")

// Final assertions again for completeness:
contractAddresses, err = reader.DiscoverContracts(ctx, []cciptypes.ChainSelector{chainS1, chainD})
require.NoError(t, err)

require.Equal(t, contractAddresses[consts.ContractNameOnRamp][chainS1], cciptypes.UnknownAddress(common.LeftPadBytes(onRampS1Addr.Bytes(), 32)))
require.Equal(t, contractAddresses[consts.ContractNameRouter][chainD], cciptypes.UnknownAddress(destinationChainConfigArgs[0].Router.Bytes()))
require.Equal(t, contractAddresses[consts.ContractNameRMNRemote][chainD], cciptypes.UnknownAddress(offRampDStaticConfig.RmnRemote.Bytes()))
require.Equal(t, contractAddresses[consts.ContractNameNonceManager][chainD], cciptypes.UnknownAddress(offRampDStaticConfig.NonceManager.Bytes()))
require.Equal(t, contractAddresses[consts.ContractNameFeeQuoter][chainD], cciptypes.UnknownAddress(offRampDDynamicConfig.FeeQuoter.Bytes()))

// Final assert to confirm source chain addresses discovered
require.Equal(t, contractAddresses[consts.ContractNameRouter][chainS1], cciptypes.UnknownAddress(destinationChainConfigArgs[0].Router.Bytes()))
require.Equal(t, contractAddresses[consts.ContractNameFeeQuoter][chainS1], cciptypes.UnknownAddress(onRampS1DynamicConfig.FeeQuoter.Bytes()))
}

func Test_GetChainFeePriceUpdates(t *testing.T) {
t.Parallel()
ctx := tests.Context(t)
Expand Down Expand Up @@ -876,7 +1127,6 @@ func Test_GetChainFeePriceUpdates(t *testing.T) {
ctx,
t,
chain1,
//evmconfig.DestReaderConfig,
map[cciptypes.ChainSelector][]types.BoundContract{
cciptypes.ChainSelector(chain1): {
{
Expand Down

0 comments on commit 9bee27e

Please sign in to comment.