From ab6b4e4be75c05872a6a3dd972e5a516d98d4788 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 9 Sep 2024 16:34:51 -0400 Subject: [PATCH 01/17] Port --- .../deployment/ccip/add_chain.go | 207 ++++++++++++++++++ .../deployment/ccip/add_chain_test.go | 74 +++++++ integration-tests/deployment/ccip/add_lane.go | 30 +-- .../deployment/ccip/add_lane_test.go | 56 +++++ .../deployment/ccip/changeset/1_cap_reg.go | 4 +- .../ccip/changeset/2_initial_deploy.go | 6 +- .../ccip/changeset/2_initial_deploy_test.go | 109 ++------- integration-tests/deployment/ccip/deploy.go | 142 ++++++++---- .../deployment/ccip/deploy_home_chain.go | 156 +++++++------ .../deployment/ccip/deploy_test.go | 1 + integration-tests/deployment/ccip/jobs.go | 11 +- integration-tests/deployment/ccip/propose.go | 62 +++--- integration-tests/deployment/ccip/state.go | 44 ++-- .../deployment/ccip/test_helpers.go | 127 ++++++++++- integration-tests/deployment/changeset.go | 20 +- integration-tests/deployment/environment.go | 17 +- integration-tests/go.mod | 4 +- integration-tests/go.sum | 8 +- 18 files changed, 778 insertions(+), 300 deletions(-) create mode 100644 integration-tests/deployment/ccip/add_chain.go create mode 100644 integration-tests/deployment/ccip/add_chain_test.go diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go new file mode 100644 index 00000000000..49e441bf79c --- /dev/null +++ b/integration-tests/deployment/ccip/add_chain.go @@ -0,0 +1,207 @@ +package ccipdeployment + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/mcms" + "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/timelock" + chainsel "github.com/smartcontractkit/chain-selectors" + + "github.com/smartcontractkit/chainlink/integration-tests/deployment" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" +) + +// AddChain deploys chain contracts for a new chain +// and generates 3 proposals to connect that new chain to all existing chains. +// We testing in between each proposal. +func NewChainInbound( + e deployment.Environment, + ab deployment.AddressBook, + homeChainSel uint64, + newChainSel uint64, + sources []uint64, +) ([]timelock.MCMSWithTimelockProposal, deployment.AddressBook, error) { + // 1. Deploy contracts to new chain and wire them. + newAddresses, err := DeployChainContracts(e, e.Chains[newChainSel], deployment.NewMemoryAddressBook()) + if err != nil { + return nil, ab, err + } + if err := ab.Merge(newAddresses); err != nil { + return nil, ab, err + } + state, err := LoadOnchainState(e, ab) + if err != nil { + return nil, ab, err + } + + // 2. Generate proposal which enables new destination (from test router) on all source chains. + var batches []timelock.BatchChainOperation + metaDataPerChain := make(map[mcms.ChainIdentifier]timelock.MCMSWithTimelockChainMetadata) + for _, source := range sources { + chain, _ := chainsel.ChainBySelector(source) + enableOnRampDest, err := state.Chains[source].OnRamp.ApplyDestChainConfigUpdates(SimTransactOpts(), []onramp.OnRampDestChainConfigArgs{ + { + DestChainSelector: newChainSel, + Router: state.Chains[source].TestRouter.Address(), + }, + }) + if err != nil { + return nil, ab, err + } + enablePriceRegDest, err := state.Chains[source].FeeQuoter.ApplyDestChainConfigUpdates( + SimTransactOpts(), + []fee_quoter.FeeQuoterDestChainConfigArgs{ + { + DestChainSelector: newChainSel, + DestChainConfig: defaultPriceRegistryDestChainConfig(), + }, + }) + if err != nil { + return nil, ab, err + } + initialPrices, err := state.Chains[source].FeeQuoter.UpdatePrices( + SimTransactOpts(), + fee_quoter.InternalPriceUpdates{ + TokenPriceUpdates: []fee_quoter.InternalTokenPriceUpdate{}, + GasPriceUpdates: []fee_quoter.InternalGasPriceUpdate{ + { + DestChainSelector: newChainSel, + // TODO: parameterize + UsdPerUnitGas: big.NewInt(2e12), + }, + }}) + if err != nil { + return nil, ab, err + } + batches = append(batches, timelock.BatchChainOperation{ + ChainIdentifier: mcms.ChainIdentifier(chain.Selector), + Batch: []mcms.Operation{ + { + // Enable the source in on ramp + To: state.Chains[source].OnRamp.Address(), + Data: enableOnRampDest.Data(), + Value: big.NewInt(0), + }, + { + // Set initial dest prices to unblock testing. + To: state.Chains[source].FeeQuoter.Address(), + Data: initialPrices.Data(), + Value: big.NewInt(0), + }, + { + // Set initial dest prices to unblock testing. + To: state.Chains[source].FeeQuoter.Address(), + Data: enablePriceRegDest.Data(), + Value: big.NewInt(0), + }, + }, + }) + metaDataPerChain[mcms.ChainIdentifier(chain.Selector)] = timelock.MCMSWithTimelockChainMetadata{ + ChainMetadata: mcms.ChainMetadata{ + NonceOffset: 0, + MCMAddress: state.Chains[source].McmAddr, + }, + TimelockAddress: state.Chains[source].TimelockAddr, + } + } + + // Home chain new don. + // - Add new DONs for destination to home chain + nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) + if err != nil { + return nil, ab, err + } + newDONArgs, err := BuildAddDONArgs(e.Logger, state.Chains[newChainSel].OffRamp, e.Chains[newChainSel], nodes) + if err != nil { + return nil, ab, err + } + addDON, err := state.Chains[homeChainSel].CapabilityRegistry.AddDON(SimTransactOpts(), + nodes.PeerIDs(newChainSel), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: CCIPCapabilityId, + Config: newDONArgs, + }, + }, false, false, nodes.DefaultF()) + if err != nil { + return nil, ab, err + } + homeChain, _ := chainsel.ChainBySelector(homeChainSel) + metaDataPerChain[mcms.ChainIdentifier(homeChain.Selector)] = timelock.MCMSWithTimelockChainMetadata{ + ChainMetadata: mcms.ChainMetadata{ + NonceOffset: 0, + MCMAddress: state.Chains[homeChainSel].McmAddr, + }, + TimelockAddress: state.Chains[homeChainSel].TimelockAddr, + } + batches = append(batches, timelock.BatchChainOperation{ + ChainIdentifier: mcms.ChainIdentifier(homeChain.Selector), + Batch: []mcms.Operation{ + { + // Enable the source in on ramp + To: state.Chains[homeChainSel].CapabilityRegistry.Address(), + Data: addDON.Data(), + Value: big.NewInt(0), + }, + }, + }) + newDestProposal := timelock.MCMSWithTimelockProposal{ + Operation: timelock.Schedule, + MinDelay: "1h", + ChainMetadata: metaDataPerChain, + Transactions: batches, + } + + // New chain we can configure directly with deployer key first. + var offRampEnables []offramp.OffRampSourceChainConfigArgs + for _, source := range sources { + offRampEnables = append(offRampEnables, offramp.OffRampSourceChainConfigArgs{ + Router: state.Chains[newChainSel].Router.Address(), + SourceChainSelector: source, + IsEnabled: true, + OnRamp: common.LeftPadBytes(state.Chains[source].OnRamp.Address().Bytes(), 32), + }) + } + tx, err := state.Chains[newChainSel].OffRamp.ApplySourceChainConfigUpdates(e.Chains[newChainSel].DeployerKey, offRampEnables) + if _, err := deployment.ConfirmIfNoError(e.Chains[newChainSel], tx, err); err != nil { + return nil, ab, err + } + + // We won't actually be able to setOCR3Config on the remote until the first proposal goes through. + // TODO: Outbound + return []timelock.MCMSWithTimelockProposal{newDestProposal}, ab, nil +} + +//func ApplyInboundChainProposal( +// e deployment.Environment, +// ab deployment.AddressBook, +// proposal managed.MCMSWithTimelockProposal, +//) (deployment.AddressBook, error) { +// state, err := LoadOnchainState(e, ab) +// if err != nil { +// return ab, err +// } +// +// // Apply the proposal. +//}) + +// 1. Deploy contracts +// 2. Proposal 1 (allow for inbound testing) +// - Enables new destination in onramps using test router +// - Enables the sources in the offramp and real router. +// - Sets initial prices for destination in price reg. +// - Add new DONs for destination to home chain +// - SetOCR3Config(s) on destination offramp. +// 3. At this point should be able to test from all sources +// and ensure that its writing those source prices to the new chain. +// 4. Proposal 2 (allow for outbound testing) +// - Add new destinations on onramp/price reg can use real router. +// No initial prices needed because DON updating them. +// - Add new sources to the remote offramps (test router). +// - Add ChainConfig to home chain so existing OCR instances become aware of the source. +// 5. Now we can test the other direction. +// 6 . Proposal 3 move onramp/offramps on existing chains to real router. diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go new file mode 100644 index 00000000000..2da0470b776 --- /dev/null +++ b/integration-tests/deployment/ccip/add_chain_test.go @@ -0,0 +1,74 @@ +package ccipdeployment + +import ( + "encoding/json" + "testing" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/mcms" + chainsel "github.com/smartcontractkit/chain-selectors" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func TestAddChain(t *testing.T) { + // 4 chains where the 4th is added after initial deployment. + e := NewEnvironmentWithCRAndJobs(t, logger.TestLogger(t), 4) + state, err := LoadOnchainState(e.Env, e.Ab) + require.NoError(t, err) + sels := e.Env.AllChainSelectors() + initialDeploy := sels[0:3] + newChain := sels[3] + + ab, err := DeployCCIPContracts(e.Env, DeployCCIPContractConfig{ + HomeChainSel: e.HomeChainSel, + ChainsToDeploy: initialDeploy, + CCIPOnChainState: state, + }) + require.NoError(t, err) + require.NoError(t, e.Ab.Merge(ab)) + state, err = LoadOnchainState(e.Env, e.Ab) + require.NoError(t, err) + + // Contracts deployed and initial DONs set up. + // Connect all the lanes + for _, source := range initialDeploy { + for _, dest := range initialDeploy { + if source != dest { + require.NoError(t, AddLane(e.Env, state, uint64(source), uint64(dest))) + } + } + } + + executorClients := make(map[mcms.ChainIdentifier]mcms.ContractDeployBackend) + for _, chain := range e.Env.Chains { + chainselc, exists := chainsel.ChainBySelector(chain.Selector) + require.True(t, exists) + chainSel := mcms.ChainIdentifier(chainselc.Selector) + executorClients[chainSel] = chain.Client + } + + // Enable inbound to new 4th chain. + proposals, ab, err := NewChainInbound(e.Env, e.Ab, e.HomeChainSel, newChain, initialDeploy) + require.NoError(t, err) + require.Equal(t, 3, len(proposals[0].ChainMetadata)) + // Sign this proposal with the deployer key. + executor, err := proposals[0].ToExecutor(executorClients) + payload, err := executor.SigningHash() + require.NoError(t, err) + // Sign the payload + sig, err := crypto.Sign(payload.Bytes(), TestXXXMCMSSigner) + require.NoError(t, err) + + // Sign the payload + unmarshalledSig := mcms.Signature{} + err = json.Unmarshal(sig, &unmarshalledSig) + require.NoError(t, err) + + // Add signature to proposal + proposals[0].Signatures = append(proposals[0].Signatures, unmarshalledSig) + require.NoError(t, proposals[0].Validate()) + + t.Log(proposals, ab) +} diff --git a/integration-tests/deployment/ccip/add_lane.go b/integration-tests/deployment/ccip/add_lane.go index 7ea757f03ac..e7c155161f2 100644 --- a/integration-tests/deployment/ccip/add_lane.go +++ b/integration-tests/deployment/ccip/add_lane.go @@ -7,8 +7,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink/integration-tests/deployment" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" ) @@ -18,13 +19,13 @@ func AddLane(e deployment.Environment, state CCIPOnChainState, from, to uint64) tx, err := state.Chains[from].Router.ApplyRampUpdates(e.Chains[from].DeployerKey, []router.RouterOnRamp{ { DestChainSelector: to, - OnRamp: state.Chains[from].EvmOnRampV160.Address(), + OnRamp: state.Chains[from].OnRamp.Address(), }, }, []router.RouterOffRamp{}, []router.RouterOffRamp{}) if _, err := deployment.ConfirmIfNoError(e.Chains[from], tx, err); err != nil { return err } - tx, err = state.Chains[from].EvmOnRampV160.ApplyDestChainConfigUpdates(e.Chains[from].DeployerKey, + tx, err = state.Chains[from].OnRamp.ApplyDestChainConfigUpdates(e.Chains[from].DeployerKey, []onramp.OnRampDestChainConfigArgs{ { DestChainSelector: to, @@ -35,7 +36,7 @@ func AddLane(e deployment.Environment, state CCIPOnChainState, from, to uint64) return err } - _, err = state.Chains[from].PriceRegistry.UpdatePrices( + _, err = state.Chains[from].FeeQuoter.UpdatePrices( e.Chains[from].DeployerKey, fee_quoter.InternalPriceUpdates{ TokenPriceUpdates: []fee_quoter.InternalTokenPriceUpdate{ { @@ -58,24 +59,24 @@ func AddLane(e deployment.Environment, state CCIPOnChainState, from, to uint64) } // Enable dest in price registry - tx, err = state.Chains[from].PriceRegistry.ApplyDestChainConfigUpdates(e.Chains[from].DeployerKey, + tx, err = state.Chains[from].FeeQuoter.ApplyDestChainConfigUpdates(e.Chains[from].DeployerKey, []fee_quoter.FeeQuoterDestChainConfigArgs{ { DestChainSelector: to, - DestChainConfig: defaultPriceRegistryDestChainConfig(), + DestChainConfig: defaultFeeQuoterDestChainConfig(), }, }) if _, err := deployment.ConfirmIfNoError(e.Chains[from], tx, err); err != nil { return err } - tx, err = state.Chains[to].EvmOffRampV160.ApplySourceChainConfigUpdates(e.Chains[to].DeployerKey, + tx, err = state.Chains[to].OffRamp.ApplySourceChainConfigUpdates(e.Chains[to].DeployerKey, []offramp.OffRampSourceChainConfigArgs{ { Router: state.Chains[to].Router.Address(), SourceChainSelector: from, IsEnabled: true, - OnRamp: common.LeftPadBytes(state.Chains[from].EvmOnRampV160.Address().Bytes(), 32), + OnRamp: common.LeftPadBytes(state.Chains[from].OnRamp.Address().Bytes(), 32), }, }) if _, err := deployment.ConfirmIfNoError(e.Chains[to], tx, err); err != nil { @@ -84,14 +85,14 @@ func AddLane(e deployment.Environment, state CCIPOnChainState, from, to uint64) tx, err = state.Chains[to].Router.ApplyRampUpdates(e.Chains[to].DeployerKey, []router.RouterOnRamp{}, []router.RouterOffRamp{}, []router.RouterOffRamp{ { SourceChainSelector: from, - OffRamp: state.Chains[to].EvmOffRampV160.Address(), + OffRamp: state.Chains[to].OffRamp.Address(), }, }) _, err = deployment.ConfirmIfNoError(e.Chains[to], tx, err) return err } -func defaultPriceRegistryDestChainConfig() fee_quoter.FeeQuoterDestChainConfig { +func defaultFeeQuoterDestChainConfig() fee_quoter.FeeQuoterDestChainConfig { // https://github.com/smartcontractkit/ccip/blob/c4856b64bd766f1ddbaf5d13b42d3c4b12efde3a/contracts/src/v0.8/ccip/libraries/Internal.sol#L337-L337 /* ```Solidity @@ -112,9 +113,10 @@ func defaultPriceRegistryDestChainConfig() fee_quoter.FeeQuoterDestChainConfig { DestGasPerDataAvailabilityByte: 100, DestDataAvailabilityMultiplierBps: 1, DefaultTokenDestGasOverhead: 125_000, - DefaultTxGasLimit: 200_000, - GasMultiplierWeiPerEth: 1, - NetworkFeeUSDCents: 1, - ChainFamilySelector: [4]byte(evmFamilySelector), + //DefaultTokenDestBytesOverhead: 32, + DefaultTxGasLimit: 200_000, + GasMultiplierWeiPerEth: 1, + NetworkFeeUSDCents: 1, + ChainFamilySelector: [4]byte(evmFamilySelector), } } diff --git a/integration-tests/deployment/ccip/add_lane_test.go b/integration-tests/deployment/ccip/add_lane_test.go index 567f5ca6856..a0d5b9a09b6 100644 --- a/integration-tests/deployment/ccip/add_lane_test.go +++ b/integration-tests/deployment/ccip/add_lane_test.go @@ -1 +1,57 @@ package ccipdeployment + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +// TestAddLane covers the workflow of adding a lane +// between existing supported chains in CCIP. +func TestAddLane(t *testing.T) { + // TODO: The offchain code doesn't yet support partial lane + // enablement, need to address then re-enable this test. + t.Skip() + e := NewEnvironmentWithCRAndJobs(t, logger.TestLogger(t), 3) + // Here we have CR + nodes set up, but no CCIP contracts deployed. + state, err := LoadOnchainState(e.Env, e.Ab) + require.NoError(t, err) + // Set up CCIP contracts and a DON per chain. + ab, err := DeployCCIPContracts(e.Env, DeployCCIPContractConfig{ + HomeChainSel: e.HomeChainSel, + CCIPOnChainState: state, + }) + require.NoError(t, err) + require.NoError(t, e.Ab.Merge(ab)) + + // We expect no lanes available on any chain. + state, err = LoadOnchainState(e.Env, e.Ab) + require.NoError(t, err) + for _, chain := range state.Chains { + offRamps, err := chain.Router.GetOffRamps(nil) + require.NoError(t, err) + require.Len(t, offRamps, 0) + } + + // Add one lane and send traffic. + from, to := e.Env.AllChainSelectors()[0], e.Env.AllChainSelectors()[1] + require.NoError(t, AddLane(e.Env, state, from, to)) + + for sel, chain := range state.Chains { + offRamps, err := chain.Router.GetOffRamps(nil) + require.NoError(t, err) + if sel == to { + require.Len(t, offRamps, 1) + } else { + require.Len(t, offRamps, 0) + } + } + seqNum := SendRequest(t, e.Env, state, from, to) + require.Equal(t, uint64(1), seqNum) + ConfirmExecution(t, e.Env.Chains[from], e.Env.Chains[to], state.Chains[to].OffRamp, seqNum) + + // TODO: Add a second lane, then disable the first and + // ensure we can send on the second but not the first. +} diff --git a/integration-tests/deployment/ccip/changeset/1_cap_reg.go b/integration-tests/deployment/ccip/changeset/1_cap_reg.go index 1929aede02f..58634916235 100644 --- a/integration-tests/deployment/ccip/changeset/1_cap_reg.go +++ b/integration-tests/deployment/ccip/changeset/1_cap_reg.go @@ -1,6 +1,8 @@ package changeset import ( + "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/timelock" + "github.com/smartcontractkit/chainlink/integration-tests/deployment" ccipdeployment "github.com/smartcontractkit/chainlink/integration-tests/deployment/ccip" ) @@ -14,7 +16,7 @@ func Apply0001(env deployment.Environment, homeChainSel uint64) (deployment.Chan return deployment.ChangesetOutput{}, err } return deployment.ChangesetOutput{ - Proposals: []deployment.Proposal{}, + Proposals: []timelock.MCMSWithTimelockProposal{}, AddressBook: ab, JobSpecs: nil, }, nil diff --git a/integration-tests/deployment/ccip/changeset/2_initial_deploy.go b/integration-tests/deployment/ccip/changeset/2_initial_deploy.go index b20ffb2d4ac..8d0c90467f6 100644 --- a/integration-tests/deployment/ccip/changeset/2_initial_deploy.go +++ b/integration-tests/deployment/ccip/changeset/2_initial_deploy.go @@ -1,6 +1,8 @@ package changeset import ( + "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/timelock" + "github.com/smartcontractkit/chainlink/integration-tests/deployment" ccipdeployment "github.com/smartcontractkit/chainlink/integration-tests/deployment/ccip" @@ -14,7 +16,7 @@ func Apply0002(env deployment.Environment, c ccipdeployment.DeployCCIPContractCo ab, err := ccipdeployment.DeployCCIPContracts(env, c) if err != nil { env.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "addresses", ab) - return deployment.ChangesetOutput{}, err + return deployment.ChangesetOutput{}, deployment.MaybeDataErr(err) } js, err := ccipdeployment.NewCCIPJobSpecs(env.NodeIDs, env.Offchain) if err != nil { @@ -25,7 +27,7 @@ func Apply0002(env deployment.Environment, c ccipdeployment.DeployCCIPContractCo return deployment.ChangesetOutput{}, err } return deployment.ChangesetOutput{ - Proposals: []deployment.Proposal{proposal}, + Proposals: []timelock.MCMSWithTimelockProposal{proposal}, AddressBook: ab, // Mapping of which nodes get which jobs. JobSpecs: js, diff --git a/integration-tests/deployment/ccip/changeset/2_initial_deploy_test.go b/integration-tests/deployment/ccip/changeset/2_initial_deploy_test.go index 1100978bc8a..09448695032 100644 --- a/integration-tests/deployment/ccip/changeset/2_initial_deploy_test.go +++ b/integration-tests/deployment/ccip/changeset/2_initial_deploy_test.go @@ -8,27 +8,22 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" - "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/integration-tests/deployment" "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" + ccipdeployment "github.com/smartcontractkit/chainlink/integration-tests/deployment/ccip" jobv1 "github.com/smartcontractkit/chainlink/integration-tests/deployment/jd/job/v1" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" - - ccipdeployment "github.com/smartcontractkit/chainlink/integration-tests/deployment/ccip" - "github.com/smartcontractkit/chainlink/integration-tests/deployment/memory" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) func Test0002_InitialDeploy(t *testing.T) { lggr := logger.TestLogger(t) ctx := ccipdeployment.Context(t) - tenv := ccipdeployment.NewDeployedTestEnvironment(t, lggr) + tenv := ccipdeployment.NewEnvironmentWithCR(t, lggr, 3) e := tenv.Env nodes := tenv.Nodes chains := e.Chains @@ -38,7 +33,8 @@ func Test0002_InitialDeploy(t *testing.T) { // Apply migration output, err := Apply0002(tenv.Env, ccipdeployment.DeployCCIPContractConfig{ - HomeChainSel: tenv.HomeChainSel, + HomeChainSel: tenv.HomeChainSel, + ChainsToDeploy: tenv.Env.AllChainSelectors(), // Capreg/config already exist. CCIPOnChainState: state, }) @@ -48,7 +44,7 @@ func Test0002_InitialDeploy(t *testing.T) { require.NoError(t, err) // Ensure capreg logs are up to date. - require.NoError(t, ReplayAllLogs(nodes, chains)) + require.NoError(t, ccipdeployment.ReplayAllLogs(nodes, chains)) // Apply the jobs. for nodeID, jobs := range output.JobSpecs { @@ -67,9 +63,8 @@ func Test0002_InitialDeploy(t *testing.T) { time.Sleep(30 * time.Second) // Ensure job related logs are up to date. - require.NoError(t, ReplayAllLogs(nodes, chains)) + require.NoError(t, ccipdeployment.ReplayAllLogs(nodes, chains)) - // Send a request from every router // Add all lanes for source := range e.Chains { for dest := range e.Chains { @@ -80,47 +75,19 @@ func Test0002_InitialDeploy(t *testing.T) { } // Send a message from each chain to every other chain. - for src, srcChain := range e.Chains { + expectedSeqNum := make(map[uint64]uint64) + for src := range e.Chains { for dest := range e.Chains { if src == dest { continue } - msg := router.ClientEVM2AnyMessage{ - Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32), - Data: []byte("hello"), - TokenAmounts: nil, // TODO: no tokens for now - FeeToken: state.Chains[src].Weth9.Address(), - ExtraArgs: nil, // TODO: no extra args for now, falls back to default - } - fee, err := state.Chains[src].Router.GetFee( - &bind.CallOpts{Context: context.Background()}, dest, msg) - require.NoError(t, err, deployment.MaybeDataErr(err)) - tx, err := state.Chains[src].Weth9.Deposit(&bind.TransactOpts{ - From: e.Chains[src].DeployerKey.From, - Signer: e.Chains[src].DeployerKey.Signer, - Value: fee, - }) - require.NoError(t, err) - _, err = srcChain.Confirm(tx.Hash()) - require.NoError(t, err) - - // TODO: should be able to avoid this by using native? - tx, err = state.Chains[src].Weth9.Approve(e.Chains[src].DeployerKey, - state.Chains[src].Router.Address(), fee) - require.NoError(t, err) - _, err = srcChain.Confirm(tx.Hash()) - require.NoError(t, err) - - t.Logf("Sending CCIP request from chain selector %d to chain selector %d", - src, dest) - tx, err = state.Chains[src].Router.CcipSend(e.Chains[src].DeployerKey, dest, msg) - require.NoError(t, err) - _, err = srcChain.Confirm(tx.Hash()) - require.NoError(t, err) + seqNum := ccipdeployment.SendRequest(t, e, state, src, dest) + expectedSeqNum[dest] = seqNum } } // Wait for all commit reports to land. + cStart := time.Now() var wg sync.WaitGroup for src, srcChain := range e.Chains { for dest, dstChain := range e.Chains { @@ -132,11 +99,13 @@ func Test0002_InitialDeploy(t *testing.T) { wg.Add(1) go func(src, dest uint64) { defer wg.Done() - waitForCommitWithInterval(t, srcChain, dstChain, state.Chains[dest].EvmOffRampV160, ccipocr3.SeqNumRange{1, 1}) + waitForCommitWithInterval(t, srcChain, dstChain, state.Chains[dest].OffRamp, + ccipocr3.SeqNumRange{ccipocr3.SeqNum(expectedSeqNum[dest]), ccipocr3.SeqNum(expectedSeqNum[dest])}) }(src, dest) } } wg.Wait() + cEnd := time.Now() // Wait for all exec reports to land for src, srcChain := range e.Chains { @@ -149,26 +118,19 @@ func Test0002_InitialDeploy(t *testing.T) { wg.Add(1) go func(src, dest deployment.Chain) { defer wg.Done() - waitForExecWithSeqNr(t, src, dest, state.Chains[dest.Selector].EvmOffRampV160, 1) + ccipdeployment.ConfirmExecution(t, + src, dest, state.Chains[dest.Selector].OffRamp, + expectedSeqNum[dest.Selector]) }(srcChain, dstChain) } } wg.Wait() - + eEnd := time.Now() + t.Log("Commit time:", cEnd.Sub(cStart)) + t.Log("Exec time:", eEnd.Sub(cEnd)) // TODO: Apply the proposal. } -func ReplayAllLogs(nodes map[string]memory.Node, chains map[uint64]deployment.Chain) error { - for _, node := range nodes { - for sel := range chains { - if err := node.ReplayLogs(map[uint64]uint64{sel: 1}); err != nil { - return err - } - } - } - return nil -} - func waitForCommitWithInterval( t *testing.T, src deployment.Chain, @@ -211,34 +173,3 @@ func waitForCommitWithInterval( } } } - -func waitForExecWithSeqNr(t *testing.T, - source, dest deployment.Chain, - offramp *offramp.OffRamp, - expectedSeqNr uint64) { - tick := time.NewTicker(5 * time.Second) - defer tick.Stop() - for range tick.C { - // TODO: Clean this up - source.Client.(*backends.SimulatedBackend).Commit() - dest.Client.(*backends.SimulatedBackend).Commit() - scc, err := offramp.GetSourceChainConfig(nil, source.Selector) - require.NoError(t, err) - t.Logf("Waiting for ExecutionStateChanged on chain %d from chain %d with expected sequence number %d, current onchain minSeqNr: %d", - dest.Selector, source.Selector, expectedSeqNr, scc.MinSeqNr) - iter, err := offramp.FilterExecutionStateChanged(nil, - []uint64{source.Selector}, []uint64{expectedSeqNr}, nil) - require.NoError(t, err) - var count int - for iter.Next() { - if iter.Event.SequenceNumber == expectedSeqNr && iter.Event.SourceChainSelector == source.Selector { - count++ - } - } - if count == 1 { - t.Logf("Received ExecutionStateChanged on chain %d from chain %d with expected sequence number %d", - dest.Selector, source.Selector, expectedSeqNr) - return - } - } -} diff --git a/integration-tests/deployment/ccip/deploy.go b/integration-tests/deployment/ccip/deploy.go index 01368c14a40..e0647bc4e8d 100644 --- a/integration-tests/deployment/ccip/deploy.go +++ b/integration-tests/deployment/ccip/deploy.go @@ -1,20 +1,23 @@ package ccipdeployment import ( + "crypto/ecdsa" "fmt" "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/gethwrappers" + "github.com/ethereum/go-ethereum/crypto" + owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/tools/gethwrappers" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/integration-tests/deployment" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/nonce_manager" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" @@ -36,16 +39,28 @@ var ( Router deployment.ContractType = "Router" TokenAdminRegistry deployment.ContractType = "TokenAdminRegistry" NonceManager deployment.ContractType = "NonceManager" - PriceRegistry deployment.ContractType = "PriceRegistry" + FeeQuoter deployment.ContractType = "FeeQuoter" ManyChainMultisig deployment.ContractType = "ManyChainMultiSig" CCIPConfig deployment.ContractType = "CCIPConfig" RBACTimelock deployment.ContractType = "RBACTimelock" OnRamp deployment.ContractType = "OnRamp" OffRamp deployment.ContractType = "OffRamp" - CCIPReceiver deployment.ContractType = "CCIPReceiver" CapabilitiesRegistry deployment.ContractType = "CapabilitiesRegistry" + // Note test router maps to a regular router contract. + TestRouter deployment.ContractType = "TestRouter" + CCIPReceiver deployment.ContractType = "CCIPReceiver" + + TestXXXMCMSSigner *ecdsa.PrivateKey ) +func init() { + key, err := crypto.GenerateKey() + if err != nil { + panic(err) + } + TestXXXMCMSSigner = key +} + type Contracts interface { *capabilities_registry.CapabilitiesRegistry | *rmn_proxy_contract.RMNProxyContract | @@ -101,7 +116,8 @@ func deployContract[C Contracts]( } type DeployCCIPContractConfig struct { - HomeChainSel uint64 + HomeChainSel uint64 + ChainsToDeploy []uint64 // Existing contracts which we want to skip deployment // Leave empty if we want to deploy everything // TODO: Add skips to deploy function. @@ -128,17 +144,23 @@ func DeployCCIPContracts(e deployment.Environment, c DeployCCIPContractConfig) ( e.Logger.Errorw("Failed to get hashed capability id", "err", err) return ab, err } + if cr != CCIPCapabilityId { + return ab, fmt.Errorf("Capability registry does not support CCIP %s %s", hexutil.Encode(cr[:]), hexutil.Encode(CCIPCapabilityId[:])) + } // Signal to CR that our nodes support CCIP capability. if err := AddNodes( c.Chains[c.HomeChainSel].CapabilityRegistry, e.Chains[c.HomeChainSel], nodes.PeerIDs(c.HomeChainSel), // Doesn't actually matter which sel here - [][32]byte{cr}, ); err != nil { return ab, err } - for _, chain := range e.Chains { + for _, chainSel := range c.ChainsToDeploy { + chain, ok := e.Chains[chainSel] + if !ok { + return ab, fmt.Errorf("Chain %d not found", chainSel) + } ab, err = DeployChainContracts(e, chain, ab) if err != nil { return ab, err @@ -153,46 +175,27 @@ func DeployCCIPContracts(e deployment.Environment, c DeployCCIPContractConfig) ( e.Logger.Errorw("Failed to load chain state", "err", err) return ab, err } - // Enable ramps on price registry/nonce manager - tx, err := chainState.PriceRegistry.ApplyAuthorizedCallerUpdates(chain.DeployerKey, fee_quoter.AuthorizedCallersAuthorizedCallerArgs{ - // TODO: We enable the deployer initially to set prices - AddedCallers: []common.Address{chainState.EvmOffRampV160.Address(), chain.DeployerKey.From}, - }) - if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { - e.Logger.Errorw("Failed to confirm price registry authorized caller update", "err", err) - return ab, err - } - - tx, err = chainState.NonceManager.ApplyAuthorizedCallerUpdates(chain.DeployerKey, nonce_manager.AuthorizedCallersAuthorizedCallerArgs{ - AddedCallers: []common.Address{chainState.EvmOffRampV160.Address(), chainState.EvmOnRampV160.Address()}, - }) - if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { - e.Logger.Errorw("Failed to update nonce manager with ramps", "err", err) - return ab, err - } + // TODO: Do we want to extract this? // Add chain config for each chain. - _, err = AddChainConfig(e.Logger, + _, err = AddChainConfig( + e.Logger, e.Chains[c.HomeChainSel], c.Chains[c.HomeChainSel].CCIPConfig, chain.Selector, - nodes.PeerIDs(chain.Selector), - uint8(len(nodes)/3)) + nodes.PeerIDs(chain.Selector)) if err != nil { return ab, err } - // For each chain, we create a DON on the home chain. - if err := AddDON(e.Logger, - cr, + // For each chain, we create a DON on the home chain (2 OCR instances) + if err := AddDON( + e.Logger, c.Chains[c.HomeChainSel].CapabilityRegistry, c.Chains[c.HomeChainSel].CCIPConfig, - chainState.EvmOffRampV160, + chainState.OffRamp, chain, e.Chains[c.HomeChainSel], - uint8(len(nodes)/3), - nodes.BootstrapPeerIDs(chain.Selector)[0], - nodes.PeerIDs(chain.Selector), nodes, ); err != nil { e.Logger.Errorw("Failed to add DON", "err", err) @@ -203,7 +206,11 @@ func DeployCCIPContracts(e deployment.Environment, c DeployCCIPContractConfig) ( return ab, nil } -func DeployChainContracts(e deployment.Environment, chain deployment.Chain, ab deployment.AddressBook) (deployment.AddressBook, error) { +func DeployChainContracts( + e deployment.Environment, + chain deployment.Chain, + ab deployment.AddressBook, +) (deployment.AddressBook, error) { ccipReceiver, err := deployContract(e.Logger, chain, ab, func(chain deployment.Chain) ContractDeploy[*maybe_revert_message_receiver.MaybeRevertMessageReceiver] { receiverAddr, tx, receiver, err2 := maybe_revert_message_receiver.DeployMaybeRevertMessageReceiver( @@ -255,6 +262,23 @@ func DeployChainContracts(e deployment.Environment, chain deployment.Chain, ab d } // TODO: Address soon e.Logger.Infow("deployed mcm", "addr", mcm.Address) + // TODO: Real MCM configuration. + var quorums, parents [32]uint8 + quorums[0] = 1 + publicKey := TestXXXMCMSSigner.Public().(*ecdsa.PublicKey) + // Convert the public key to an Ethereum address + address := crypto.PubkeyToAddress(*publicKey) + tx, err := mcm.Contract.SetConfig(chain.DeployerKey, + []common.Address{address}, + []uint8{0}, // Signer 1 is int group 0 (root group) with quorum 1. + quorums, + parents, + false, + ) + if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { + e.Logger.Errorw("Failed to confirm mcm config", "err", err) + return ab, err + } _, err = deployContract(e.Logger, chain, ab, func(chain deployment.Chain) ContractDeploy[*owner_helpers.RBACTimelock] { @@ -263,6 +287,7 @@ func DeployChainContracts(e deployment.Environment, chain deployment.Chain, ab d chain.Client, big.NewInt(0), // minDelay mcm.Address, + // TODO: Actual MCM groups need to be parameterized. []common.Address{mcm.Address}, // proposers []common.Address{chain.DeployerKey.From}, //executors []common.Address{mcm.Address}, // cancellers @@ -295,6 +320,8 @@ func DeployChainContracts(e deployment.Environment, chain deployment.Chain, ab d } e.Logger.Infow("deployed rmnProxy", "addr", rmnProxy.Address) + // TODO: Need general configuration for using pre-existing weth9 + // link tokens. weth9, err := deployContract(e.Logger, chain, ab, func(chain deployment.Chain) ContractDeploy[*weth9.WETH9] { weth9Addr, tx, weth9c, err2 := weth9.DeployWETH9( @@ -347,6 +374,24 @@ func DeployChainContracts(e deployment.Environment, chain deployment.Chain, ab d } e.Logger.Infow("deployed router", "addr", routerContract) + testRouterContract, err := deployContract(e.Logger, chain, ab, + func(chain deployment.Chain) ContractDeploy[*router.Router] { + routerAddr, tx, routerC, err2 := router.DeployRouter( + chain.DeployerKey, + chain.Client, + weth9.Address, + rmnProxy.Address, + ) + return ContractDeploy[*router.Router]{ + routerAddr, routerC, tx, deployment.NewTypeAndVersion(TestRouter, deployment.Version1_2_0), err2, + } + }) + if err != nil { + e.Logger.Errorw("Failed to deploy test router", "err", err) + return ab, err + } + e.Logger.Infow("deployed test router", "addr", testRouterContract.Address) + tokenAdminRegistry, err := deployContract(e.Logger, chain, ab, func(chain deployment.Chain) ContractDeploy[*token_admin_registry.TokenAdminRegistry] { tokenAdminRegistryAddr, tx, tokenAdminRegistry, err2 := token_admin_registry.DeployTokenAdminRegistry( @@ -378,7 +423,7 @@ func DeployChainContracts(e deployment.Environment, chain deployment.Chain, ab d return ab, err } - feeQuoter, err := deployContract(e.Logger, chain, ab, + priceRegistry, err := deployContract(e.Logger, chain, ab, func(chain deployment.Chain) ContractDeploy[*fee_quoter.FeeQuoter] { prAddr, tx, pr, err2 := fee_quoter.DeployFeeQuoter( chain.DeployerKey, @@ -405,7 +450,7 @@ func DeployChainContracts(e deployment.Environment, chain deployment.Chain, ab d []fee_quoter.FeeQuoterDestChainConfigArgs{}, ) return ContractDeploy[*fee_quoter.FeeQuoter]{ - prAddr, pr, tx, deployment.NewTypeAndVersion(PriceRegistry, deployment.Version1_6_0_dev), err2, + prAddr, pr, tx, deployment.NewTypeAndVersion(FeeQuoter, deployment.Version1_6_0_dev), err2, } }) if err != nil { @@ -425,7 +470,7 @@ func DeployChainContracts(e deployment.Environment, chain deployment.Chain, ab d TokenAdminRegistry: tokenAdminRegistry.Address, }, onramp.OnRampDynamicConfig{ - FeeQuoter: feeQuoter.Address, + FeeQuoter: priceRegistry.Address, FeeAggregator: common.HexToAddress("0x1"), // TODO real fee aggregator }, []onramp.OnRampDestChainConfigArgs{}, @@ -452,7 +497,7 @@ func DeployChainContracts(e deployment.Environment, chain deployment.Chain, ab d TokenAdminRegistry: tokenAdminRegistry.Address, }, offramp.OffRampDynamicConfig{ - FeeQuoter: feeQuoter.Address, + FeeQuoter: priceRegistry.Address, PermissionLessExecutionThresholdSeconds: uint32(86400), MaxTokenTransferGas: uint32(200_000), MaxPoolReleaseOrMintGas: uint32(200_000), @@ -468,5 +513,24 @@ func DeployChainContracts(e deployment.Environment, chain deployment.Chain, ab d return ab, err } e.Logger.Infow("deployed offramp", "addr", offRamp) + + // Basic wiring is always needed. + tx, err = priceRegistry.Contract.ApplyAuthorizedCallerUpdates(chain.DeployerKey, fee_quoter.AuthorizedCallersAuthorizedCallerArgs{ + // TODO: We enable the deployer initially to set prices + // Should be removed after. + AddedCallers: []common.Address{offRamp.Contract.Address(), chain.DeployerKey.From}, + }) + if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { + e.Logger.Errorw("Failed to confirm price registry authorized caller update", "err", err) + return ab, err + } + + tx, err = nonceManager.Contract.ApplyAuthorizedCallerUpdates(chain.DeployerKey, nonce_manager.AuthorizedCallersAuthorizedCallerArgs{ + AddedCallers: []common.Address{offRamp.Contract.Address(), onRamp.Contract.Address()}, + }) + if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { + e.Logger.Errorw("Failed to update nonce manager with ramps", "err", err) + return ab, err + } return ab, nil } diff --git a/integration-tests/deployment/ccip/deploy_home_chain.go b/integration-tests/deployment/ccip/deploy_home_chain.go index 5eb03f1d23e..62b91a23a95 100644 --- a/integration-tests/deployment/ccip/deploy_home_chain.go +++ b/integration-tests/deployment/ccip/deploy_home_chain.go @@ -4,11 +4,12 @@ import ( "bytes" "context" "errors" - "sort" + "fmt" "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" @@ -17,7 +18,6 @@ import ( commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" - "github.com/smartcontractkit/chainlink/integration-tests/deployment" cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" @@ -52,6 +52,21 @@ const ( MaxDurationShouldTransmitAcceptedReport = 10 * time.Second ) +var ( + // TODO: sort out why the mismatch here + //CCIPCapabilityId [32]byte = utils.MustHash(hexutil.Encode(MustABIEncode(`[{"type": "string"}, {"type": "string"}]`, CapabilityLabelledName, CapabilityVersion))) + CCIPCapabilityId [32]byte = MustHashFromBytes(hexutil.MustDecode("0xe0da3c2b9005178f4731c9f40164f1933ad00bac9d6c13ad4ca1a8a763416380")) +) + +func MustHashFromBytes(b []byte) [32]byte { + if len(b) != 32 { + panic("invalid length") + } + var res [32]byte + copy(res[:], b) + return res +} + func DeployCapReg(lggr logger.Logger, chains map[uint64]deployment.Chain, chainSel uint64) (deployment.AddressBook, common.Address, error) { ab := deployment.NewMemoryAddressBook() chain := chains[chainSel] @@ -69,6 +84,7 @@ func DeployCapReg(lggr logger.Logger, chains map[uint64]deployment.Chain, chainS lggr.Errorw("Failed to deploy capreg", "err", err) return ab, common.Address{}, err } + lggr.Infow("deployed capreg", "addr", capReg.Address) ccipConfig, err := deployContract( lggr, chain, ab, @@ -115,27 +131,19 @@ func DeployCapReg(lggr logger.Logger, chains map[uint64]deployment.Chain, chainS return ab, capReg.Address, nil } -func sortP2PIDS(p2pIDs [][32]byte) { - sort.Slice(p2pIDs, func(i, j int) bool { - return bytes.Compare(p2pIDs[i][:], p2pIDs[j][:]) < 0 - }) -} - func AddNodes( capReg *capabilities_registry.CapabilitiesRegistry, chain deployment.Chain, p2pIDs [][32]byte, - capabilityIDs [][32]byte, ) error { // Need to sort, otherwise _checkIsValidUniqueSubset onChain will fail - sortP2PIDS(p2pIDs) var nodeParams []capabilities_registry.CapabilitiesRegistryNodeParams for _, p2pID := range p2pIDs { nodeParam := capabilities_registry.CapabilitiesRegistryNodeParams{ NodeOperatorId: NodeOperatorID, Signer: p2pID, // Not used in tests P2pId: p2pID, - HashedCapabilityIds: capabilityIDs, + HashedCapabilityIds: [][32]byte{CCIPCapabilityId}, } nodeParams = append(nodeParams, nodeParam) } @@ -164,10 +172,9 @@ func AddChainConfig( ccipConfig *ccip_config.CCIPConfig, chainSelector uint64, p2pIDs [][32]byte, - f uint8, ) (ccip_config.CCIPConfigTypesChainConfigInfo, error) { + f := uint8(len(p2pIDs) / 3) // Need to sort, otherwise _checkIsValidUniqueSubset onChain will fail - sortP2PIDS(p2pIDs) // First Add ChainConfig that includes all p2pIDs as readers encodedExtraChainConfig, err := chainconfig.EncodeChainConfig(chainconfig.ChainConfig{ GasPriceDeviationPPB: ccipocr3.NewBigIntFromInt64(1000), @@ -190,20 +197,15 @@ func AddChainConfig( return chainConfig, nil } -func AddDON( +// AddDONToCR adds a DON with the given nodes to the capabilities registry. +func BuildAddDONArgs( lggr logger.Logger, - ccipCapabilityID [32]byte, - capReg *capabilities_registry.CapabilitiesRegistry, - ccipConfig *ccip_config.CCIPConfig, offRamp *offramp.OffRamp, dest deployment.Chain, - home deployment.Chain, - f uint8, - bootstrapP2PID [32]byte, - p2pIDs [][32]byte, - nodes []deployment.Node, -) error { - sortP2PIDS(p2pIDs) + nodes deployment.Nodes, +) ([]byte, error) { + //bootstrapP2PID := nodes.BootstrapPeerIDs(dest.Selector)[0] + p2pIDs := nodes.PeerIDs(dest.Selector) // Get OCR3 Config from helper var schedule []int var oracles []confighelper2.OracleIdentityExtra @@ -222,7 +224,7 @@ func AddDON( tabi, err := ocr3_config_encoder.IOCR3ConfigEncoderMetaData.GetAbi() if err != nil { - return err + return nil, err } // Add DON on capability registry contract @@ -247,7 +249,7 @@ func AddDON( }) } if err2 != nil { - return err2 + return nil, err2 } signers, transmitters, configF, _, offchainConfigVersion, offchainConfig, err2 := ocr3confighelper.ContractSetConfigArgsForTests( DeltaProgress, @@ -265,11 +267,11 @@ func AddDON( MaxDurationObservation, MaxDurationShouldAcceptAttestedReport, MaxDurationShouldTransmitAcceptedReport, - int(f), + int(nodes.DefaultF()), []byte{}, // empty OnChainConfig ) if err2 != nil { - return err2 + return nil, err2 } signersBytes := make([][]byte, len(signers)) @@ -281,7 +283,7 @@ func AddDON( for i, transmitter := range transmitters { parsed, err2 := common.ParseHexOrString(string(transmitter)) if err2 != nil { - return err2 + return nil, err2 } transmittersBytes[i] = parsed } @@ -299,33 +301,44 @@ func AddDON( }) } + // TODO: Can just use utils.ABIEncode directly here. encodedCall, err := tabi.Pack("exposeOCR3Config", ocr3Configs) if err != nil { - return err + return nil, err } // Trim first four bytes to remove function selector. encodedConfigs := encodedCall[4:] + return encodedConfigs, nil +} - tx, err := capReg.AddDON(home.DeployerKey, p2pIDs, []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ +func AddDON( + lggr logger.Logger, + capReg *capabilities_registry.CapabilitiesRegistry, + ccipConfig *ccip_config.CCIPConfig, + offRamp *offramp.OffRamp, + dest deployment.Chain, + home deployment.Chain, + nodes deployment.Nodes, +) error { + encodedConfigs, err := BuildAddDONArgs(lggr, offRamp, dest, nodes) + if err != nil { + return err + } + tx, err := capReg.AddDON(home.DeployerKey, nodes.PeerIDs(dest.Selector), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ { - CapabilityId: ccipCapabilityID, + CapabilityId: CCIPCapabilityId, Config: encodedConfigs, }, - }, false, false, f) - if _, err := deployment.ConfirmIfNoError(home, tx, err); err != nil { - return err - } - - latestBlock, err := home.Client.HeaderByNumber(context.Background(), nil) + }, false, false, nodes.DefaultF()) + blockConfirmed, err := deployment.ConfirmIfNoError(home, tx, err) if err != nil { return err } - endBlock := latestBlock.Number.Uint64() iter, err := capReg.FilterConfigSet(&bind.FilterOpts{ - Start: endBlock - 1, - End: &endBlock, - }, []uint32{}) + Start: blockConfirmed, + End: &blockConfirmed, + }, nil) if err != nil { return err } @@ -337,18 +350,6 @@ func AddDON( if donID == 0 { return errors.New("failed to get donID") } - - var signerAddresses []common.Address - for _, oracle := range oracles { - signerAddresses = append(signerAddresses, common.BytesToAddress(oracle.OnchainPublicKey)) - } - - var transmitterAddresses []common.Address - for _, oracle := range oracles { - transmitterAddresses = append(transmitterAddresses, common.HexToAddress(string(oracle.TransmitAccount))) - } - - // get the config digest from the ccip config contract and set config on the offramp. var offrampOCR3Configs []offramp.MultiOCR3BaseOCRConfigArgs for _, pluginType := range []cctypes.PluginType{cctypes.PluginTypeCCIPCommit, cctypes.PluginTypeCCIPExec} { ocrConfig, err2 := ccipConfig.GetOCRConfig(&bind.CallOpts{ @@ -360,11 +361,20 @@ func AddDON( if len(ocrConfig) != 1 { return errors.New("expected exactly one OCR3 config") } + var signerAddresses []common.Address + for _, signer := range ocrConfig[0].Config.Signers { + signerAddresses = append(signerAddresses, common.BytesToAddress(signer)) + } + + var transmitterAddresses []common.Address + for _, transmitter := range ocrConfig[0].Config.Transmitters { + transmitterAddresses = append(transmitterAddresses, common.BytesToAddress(transmitter)) + } offrampOCR3Configs = append(offrampOCR3Configs, offramp.MultiOCR3BaseOCRConfigArgs{ ConfigDigest: ocrConfig[0].ConfigDigest, OcrPluginType: uint8(pluginType), - F: f, + F: ocrConfig[0].Config.F, IsSignatureVerificationEnabled: pluginType == cctypes.PluginTypeCCIPCommit, Signers: signerAddresses, Transmitters: transmitterAddresses, @@ -377,7 +387,7 @@ func AddDON( } for _, pluginType := range []cctypes.PluginType{cctypes.PluginTypeCCIPCommit, cctypes.PluginTypeCCIPExec} { - _, err = offRamp.LatestConfigDetails(&bind.CallOpts{ + ocrConfig, err := offRamp.LatestConfigDetails(&bind.CallOpts{ Context: context.Background(), }, uint8(pluginType)) if err != nil { @@ -386,16 +396,32 @@ func AddDON( } // TODO: assertions to be done as part of full state // resprentation validation CCIP-3047 - //require.Equalf(t, offrampOCR3Configs[pluginType].ConfigDigest, ocrConfig.ConfigInfo.ConfigDigest, "%s OCR3 config digest mismatch", pluginType.String()) - //require.Equalf(t, offrampOCR3Configs[pluginType].F, ocrConfig.ConfigInfo.F, "%s OCR3 config F mismatch", pluginType.String()) - //require.Equalf(t, offrampOCR3Configs[pluginType].IsSignatureVerificationEnabled, ocrConfig.ConfigInfo.IsSignatureVerificationEnabled, "%s OCR3 config signature verification mismatch", pluginType.String()) - //if pluginType == cctypes.PluginTypeCCIPCommit { - // // only commit will set signers, exec doesn't need them. - // require.Equalf(t, offrampOCR3Configs[pluginType].Signers, ocrConfig.Signers, "%s OCR3 config signers mismatch", pluginType.String()) - //} - //require.Equalf(t, offrampOCR3Configs[pluginType].TransmittersByEVMChainID, ocrConfig.TransmittersByEVMChainID, "%s OCR3 config transmitters mismatch", pluginType.String()) + if offrampOCR3Configs[pluginType].ConfigDigest != ocrConfig.ConfigInfo.ConfigDigest { + return fmt.Errorf("%s OCR3 config digest mismatch", pluginType.String()) + } + if offrampOCR3Configs[pluginType].F != ocrConfig.ConfigInfo.F { + return fmt.Errorf("%s OCR3 config F mismatch", pluginType.String()) + } + if offrampOCR3Configs[pluginType].IsSignatureVerificationEnabled != ocrConfig.ConfigInfo.IsSignatureVerificationEnabled { + return fmt.Errorf("%s OCR3 config signature verification mismatch", pluginType.String()) + } + if pluginType == cctypes.PluginTypeCCIPCommit { + // only commit will set signers, exec doesn't need them. + for i, signer := range offrampOCR3Configs[pluginType].Signers { + if !bytes.Equal(signer.Bytes(), ocrConfig.Signers[i].Bytes()) { + return fmt.Errorf("%s OCR3 config signer mismatch", pluginType.String()) + } + } + //if offrampOCR3Configs[pluginType].Signers != ocrConfig.Signers { + // return fmt.Errorf("%s OCR3 config signers mismatch", pluginType.String()) + //} + } + for i, transmitter := range offrampOCR3Configs[pluginType].Transmitters { + if !bytes.Equal(transmitter.Bytes(), ocrConfig.Transmitters[i].Bytes()) { + return fmt.Errorf("%s OCR3 config transmitter mismatch", pluginType.String()) + } + } } - lggr.Infof("set ocr3 config on the offramp, signers: %+v, transmitters: %+v", signerAddresses, transmitterAddresses) return nil } diff --git a/integration-tests/deployment/ccip/deploy_test.go b/integration-tests/deployment/ccip/deploy_test.go index 7bc56f82f76..e2963b84a3b 100644 --- a/integration-tests/deployment/ccip/deploy_test.go +++ b/integration-tests/deployment/ccip/deploy_test.go @@ -28,6 +28,7 @@ func TestDeployCCIPContracts(t *testing.T) { require.NoError(t, err) ab, err := DeployCCIPContracts(e, DeployCCIPContractConfig{ HomeChainSel: homeChain, + ChainsToDeploy: e.AllChainSelectors(), CCIPOnChainState: s, }) require.NoError(t, err) diff --git a/integration-tests/deployment/ccip/jobs.go b/integration-tests/deployment/ccip/jobs.go index c46ab7270f6..4ad3d29faae 100644 --- a/integration-tests/deployment/ccip/jobs.go +++ b/integration-tests/deployment/ccip/jobs.go @@ -48,17 +48,8 @@ func NewCCIPJobSpecs(nodeIds []string, oc deployment.OffchainClient) (map[string if err != nil { return nil, err } - - // only set P2PV2Bootstrappers in the job spec if the node is a plugin node. - var p2pV2Bootstrappers []string - for _, chainConfig := range nodeChainConfigs.ChainConfigs { - if !chainConfig.Ocr2Config.IsBootstrap { - p2pV2Bootstrappers = bootstraps - break - } - } spec, err := validate.NewCCIPSpecToml(validate.SpecArgs{ - P2PV2Bootstrappers: p2pV2Bootstrappers, + P2PV2Bootstrappers: bootstraps, CapabilityVersion: CapabilityVersion, CapabilityLabelledName: CapabilityLabelledName, OCRKeyBundleIDs: map[string]string{ diff --git a/integration-tests/deployment/ccip/propose.go b/integration-tests/deployment/ccip/propose.go index 4fc38965eaa..5bdf31081bd 100644 --- a/integration-tests/deployment/ccip/propose.go +++ b/integration-tests/deployment/ccip/propose.go @@ -2,12 +2,12 @@ package ccipdeployment import ( "math/big" - "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/gethwrappers" + "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/mcms" + "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/timelock" chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink/integration-tests/deployment" @@ -17,48 +17,52 @@ import ( func SimTransactOpts() *bind.TransactOpts { return &bind.TransactOpts{Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) { return transaction, nil - }, From: common.HexToAddress("0x0"), NoSend: true, GasLimit: 200_000} + }, From: common.HexToAddress("0x0"), NoSend: true, GasLimit: 1_000_000} } func GenerateAcceptOwnershipProposal( e deployment.Environment, chains []uint64, ab deployment.AddressBook, -) (deployment.Proposal, error) { +) (timelock.MCMSWithTimelockProposal, error) { state, err := LoadOnchainState(e, ab) if err != nil { - return deployment.Proposal{}, err + return timelock.MCMSWithTimelockProposal{}, err } // TODO: Just onramp as an example - var ops []owner_helpers.ManyChainMultiSigOp + var batches []timelock.BatchChainOperation + metaDataPerChain := make(map[mcms.ChainIdentifier]timelock.MCMSWithTimelockChainMetadata) for _, sel := range chains { - opCount, err := state.Chains[sel].Mcm.GetOpCount(nil) + chain, _ := chainsel.ChainBySelector(sel) + acceptOnRamp, err := state.Chains[sel].OnRamp.AcceptOwnership(SimTransactOpts()) if err != nil { - return deployment.Proposal{}, err + return timelock.MCMSWithTimelockProposal{}, err } - - txData, err := state.Chains[sel].EvmOnRampV160.AcceptOwnership(SimTransactOpts()) - if err != nil { - return deployment.Proposal{}, err - } - evmID, err := chainsel.ChainIdFromSelector(sel) - if err != nil { - return deployment.Proposal{}, err + chainSel := mcms.ChainIdentifier(chain.Selector) + metaDataPerChain[chainSel] = timelock.MCMSWithTimelockChainMetadata{ + ChainMetadata: mcms.ChainMetadata{ + NonceOffset: 0, + MCMAddress: state.Chains[sel].McmAddr, + }, + TimelockAddress: state.Chains[sel].TimelockAddr, } - ops = append(ops, owner_helpers.ManyChainMultiSigOp{ - ChainId: big.NewInt(int64(evmID)), - MultiSig: state.Chains[sel].McmsAddr, - Nonce: opCount, - To: state.Chains[sel].EvmOnRampV160.Address(), - Value: big.NewInt(0), - Data: txData.Data(), + batches = append(batches, timelock.BatchChainOperation{ + ChainIdentifier: chainSel, + Batch: []mcms.Operation{ + { + // Enable the source in on ramp + To: state.Chains[sel].OnRamp.Address(), + Data: acceptOnRamp.Data(), + Value: big.NewInt(0), + }, + }, }) } // TODO: Real valid until. - return deployment.Proposal{ValidUntil: uint32(time.Now().Unix()), Ops: ops}, nil -} - -func ApplyProposal(env deployment.Environment, p deployment.Proposal, state CCIPOnChainState) error { - // TODO - return nil + return timelock.MCMSWithTimelockProposal{ + Operation: timelock.Schedule, + MinDelay: "1h", + ChainMetadata: metaDataPerChain, + Transactions: batches, + }, nil } diff --git a/integration-tests/deployment/ccip/state.go b/integration-tests/deployment/ccip/state.go index f129650b30b..2d905e9e3f5 100644 --- a/integration-tests/deployment/ccip/state.go +++ b/integration-tests/deployment/ccip/state.go @@ -7,13 +7,15 @@ import ( "github.com/pkg/errors" chainsel "github.com/smartcontractkit/chain-selectors" - owner_wrappers "github.com/smartcontractkit/ccip-owner-contracts/gethwrappers" - "github.com/smartcontractkit/chainlink/integration-tests/deployment" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/maybe_revert_message_receiver" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" + + owner_wrappers "github.com/smartcontractkit/ccip-owner-contracts/tools/gethwrappers" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/nonce_manager" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" @@ -22,14 +24,12 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/token_admin_registry" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/weth9" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" ) type CCIPChainState struct { - EvmOnRampV160 *onramp.OnRamp - EvmOffRampV160 *offramp.OffRamp - PriceRegistry *fee_quoter.FeeQuoter + OnRamp *onramp.OnRamp + OffRamp *offramp.OffRamp + FeeQuoter *fee_quoter.FeeQuoter ArmProxy *rmn_proxy_contract.RMNProxyContract NonceManager *nonce_manager.NonceManager TokenAdminRegistry *token_admin_registry.TokenAdminRegistry @@ -42,12 +42,13 @@ type CCIPChainState struct { CapabilityRegistry *capabilities_registry.CapabilitiesRegistry CCIPConfig *ccip_config.CCIPConfig Mcm *owner_wrappers.ManyChainMultiSig - // TODO: remove once we have Address() on wrappers - McmsAddr common.Address - Timelock *owner_wrappers.RBACTimelock + McmAddr common.Address + Timelock *owner_wrappers.RBACTimelock + TimelockAddr common.Address // Test contracts - Receiver *maybe_revert_message_receiver.MaybeRevertMessageReceiver + Receiver *maybe_revert_message_receiver.MaybeRevertMessageReceiver + TestRouter *router.Router } // Onchain state always derivable from an address book. @@ -183,13 +184,14 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type return state, err } state.Timelock = tl + state.TimelockAddr = common.HexToAddress(address) case deployment.NewTypeAndVersion(ManyChainMultisig, deployment.Version1_0_0).String(): mcms, err := owner_wrappers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) if err != nil { return state, err } state.Mcm = mcms - state.McmsAddr = common.HexToAddress(address) + state.McmAddr = common.HexToAddress(address) case deployment.NewTypeAndVersion(CapabilitiesRegistry, deployment.Version1_0_0).String(): cr, err := capabilities_registry.NewCapabilitiesRegistry(common.HexToAddress(address), chain.Client) if err != nil { @@ -201,13 +203,13 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type if err != nil { return state, err } - state.EvmOnRampV160 = onRampC + state.OnRamp = onRampC case deployment.NewTypeAndVersion(OffRamp, deployment.Version1_6_0_dev).String(): offRamp, err := offramp.NewOffRamp(common.HexToAddress(address), chain.Client) if err != nil { return state, err } - state.EvmOffRampV160 = offRamp + state.OffRamp = offRamp case deployment.NewTypeAndVersion(ARMProxy, deployment.Version1_0_0).String(): armProxy, err := rmn_proxy_contract.NewRMNProxyContract(common.HexToAddress(address), chain.Client) if err != nil { @@ -244,12 +246,18 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type return state, err } state.Router = r - case deployment.NewTypeAndVersion(PriceRegistry, deployment.Version1_6_0_dev).String(): - pr, err := fee_quoter.NewFeeQuoter(common.HexToAddress(address), chain.Client) + case deployment.NewTypeAndVersion(TestRouter, deployment.Version1_2_0).String(): + r, err := router.NewRouter(common.HexToAddress(address), chain.Client) + if err != nil { + return state, err + } + state.TestRouter = r + case deployment.NewTypeAndVersion(FeeQuoter, deployment.Version1_6_0_dev).String(): + fq, err := fee_quoter.NewFeeQuoter(common.HexToAddress(address), chain.Client) if err != nil { return state, err } - state.PriceRegistry = pr + state.FeeQuoter = fq case deployment.NewTypeAndVersion(LinkToken, deployment.Version1_0_0).String(): lt, err := burn_mint_erc677.NewBurnMintERC677(common.HexToAddress(address), chain.Client) if err != nil { diff --git a/integration-tests/deployment/ccip/test_helpers.go b/integration-tests/deployment/ccip/test_helpers.go index 2ec837c9eec..ed1fdc56f35 100644 --- a/integration-tests/deployment/ccip/test_helpers.go +++ b/integration-tests/deployment/ccip/test_helpers.go @@ -2,12 +2,21 @@ package ccipdeployment import ( "context" + "sort" "testing" + "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" chainsel "github.com/smartcontractkit/chain-selectors" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + jobv1 "github.com/smartcontractkit/chainlink/integration-tests/deployment/jd/job/v1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" + "github.com/smartcontractkit/chainlink/integration-tests/deployment" "github.com/smartcontractkit/chainlink/integration-tests/deployment/memory" @@ -40,17 +49,21 @@ type DeployedTestEnvironment struct { // NewDeployedEnvironment creates a new CCIP environment // with capreg and nodes set up. -func NewDeployedTestEnvironment(t *testing.T, lggr logger.Logger) DeployedTestEnvironment { +func NewEnvironmentWithCR(t *testing.T, lggr logger.Logger, numChains int) DeployedTestEnvironment { ctx := Context(t) - chains := memory.NewMemoryChains(t, 3) - homeChainSel := uint64(0) - homeChainEVM := uint64(0) + chains := memory.NewMemoryChains(t, numChains) + // Lower chainSel is home chain. + var chainSels []uint64 // Say first chain is home chain. for chainSel := range chains { - homeChainEVM, _ = chainsel.ChainIdFromSelector(chainSel) - homeChainSel = chainSel - break + chainSels = append(chainSels, chainSel) } + sort.Slice(chainSels, func(i, j int) bool { + return chainSels[i] < chainSels[j] + }) + // Take lowest for determinism. + homeChainSel := chainSels[0] + homeChainEVM, _ := chainsel.ChainIdFromSelector(homeChainSel) ab, capReg, err := DeployCapReg(lggr, chains, homeChainSel) require.NoError(t, err) @@ -73,3 +86,103 @@ func NewDeployedTestEnvironment(t *testing.T, lggr logger.Logger) DeployedTestEn Nodes: nodes, } } + +func NewEnvironmentWithCRAndJobs(t *testing.T, lggr logger.Logger, numChains int) DeployedTestEnvironment { + ctx := Context(t) + e := NewEnvironmentWithCR(t, lggr, numChains) + jbs, err := NewCCIPJobSpecs(e.Env.NodeIDs, e.Env.Offchain) + require.NoError(t, err) + for nodeID, jobs := range jbs { + for _, job := range jobs { + // Note these auto-accept + _, err := e.Env.Offchain.ProposeJob(ctx, + &jobv1.ProposeJobRequest{ + NodeId: nodeID, + Spec: job, + }) + require.NoError(t, err) + } + } + // Wait for plugins to register filters? + // TODO: Investigate how to avoid. + time.Sleep(30 * time.Second) + + // Ensure job related logs are up to date. + require.NoError(t, ReplayAllLogs(e.Nodes, e.Env.Chains)) + return e +} + +func ReplayAllLogs(nodes map[string]memory.Node, chains map[uint64]deployment.Chain) error { + for _, node := range nodes { + for sel := range chains { + if err := node.ReplayLogs(map[uint64]uint64{sel: 1}); err != nil { + return err + } + } + } + return nil +} + +func SendRequest(t *testing.T, e deployment.Environment, state CCIPOnChainState, src, dest uint64) uint64 { + msg := router.ClientEVM2AnyMessage{ + Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32), + Data: []byte("hello"), + TokenAmounts: nil, // TODO: no tokens for now + // Pay native. + FeeToken: common.HexToAddress("0x0"), + ExtraArgs: nil, // TODO: no extra args for now, falls back to default + } + fee, err := state.Chains[src].Router.GetFee( + &bind.CallOpts{Context: context.Background()}, dest, msg) + require.NoError(t, err, deployment.MaybeDataErr(err)) + + t.Logf("Sending CCIP request from chain selector %d to chain selector %d", + src, dest) + e.Chains[src].DeployerKey.Value = fee + tx, err := state.Chains[src].Router.CcipSend( + e.Chains[src].DeployerKey, + dest, + msg) + require.NoError(t, err) + blockNum, err := e.Chains[src].Confirm(tx.Hash()) + require.NoError(t, err) + it, err := state.Chains[src].OnRamp.FilterCCIPMessageSent(&bind.FilterOpts{ + Start: blockNum, + End: &blockNum, + Context: context.Background(), + }, []uint64{dest}) + require.NoError(t, err) + require.True(t, it.Next()) + return it.Event.Message.Header.SequenceNumber +} + +func ConfirmExecution(t *testing.T, + source, dest deployment.Chain, + offramp *offramp.OffRamp, + expectedSeqNr uint64) { + tick := time.NewTicker(5 * time.Second) + defer tick.Stop() + for range tick.C { + // TODO: Clean this up + source.Client.(*backends.SimulatedBackend).Commit() + dest.Client.(*backends.SimulatedBackend).Commit() + scc, err := offramp.GetSourceChainConfig(nil, source.Selector) + require.NoError(t, err) + t.Logf("Waiting for ExecutionStateChanged on chain %d from chain %d with expected sequence number %d, current onchain minSeqNr: %d", + dest.Selector, source.Selector, expectedSeqNr, scc.MinSeqNr) + iter, err := offramp.FilterExecutionStateChanged(nil, + []uint64{source.Selector}, []uint64{expectedSeqNr}, nil) + require.NoError(t, err) + var count int + for iter.Next() { + if iter.Event.SequenceNumber == expectedSeqNr && iter.Event.SourceChainSelector == source.Selector { + count++ + } + } + if count == 1 { + t.Logf("Received ExecutionStateChanged on chain %d from chain %d with expected sequence number %d", + dest.Selector, source.Selector, expectedSeqNr) + return + } + } +} diff --git a/integration-tests/deployment/changeset.go b/integration-tests/deployment/changeset.go index d929022ed96..aeac3e8e722 100644 --- a/integration-tests/deployment/changeset.go +++ b/integration-tests/deployment/changeset.go @@ -1,28 +1,12 @@ package deployment import ( - owner_wrappers "github.com/smartcontractkit/ccip-owner-contracts/gethwrappers" + "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/timelock" ) -// TODO: Move to real MCM structs once available. -type Proposal struct { - // keccak256(abi.encode(root, validUntil)) is what is signed by MCMS - // signers. - ValidUntil uint32 - // Leaves are the items in the proposal. - // Uses these to generate the root as well as display whats in the root. - // These Ops may be destined for distinct chains. - Ops []owner_wrappers.ManyChainMultiSigOp -} - -func (p Proposal) String() string { - // TODO - return "" -} - // Services as input to CI/Async tasks type ChangesetOutput struct { JobSpecs map[string][]string - Proposals []Proposal + Proposals []timelock.MCMSWithTimelockProposal AddressBook AddressBook } diff --git a/integration-tests/deployment/environment.go b/integration-tests/deployment/environment.go index 2e28bff5ab4..f3aa3c6ac37 100644 --- a/integration-tests/deployment/environment.go +++ b/integration-tests/deployment/environment.go @@ -1,10 +1,12 @@ package deployment import ( + "bytes" "context" "errors" "fmt" "math/big" + "sort" "strconv" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -28,6 +30,7 @@ type OnchainClient interface { // For EVM specifically we can use existing geth interface // to abstract chain clients. bind.ContractBackend + bind.DeployBackend } type OffchainClient interface { @@ -43,7 +46,8 @@ type Chain struct { Client OnchainClient // Note the Sign function can be abstract supporting a variety of key storage mechanisms (e.g. KMS etc). DeployerKey *bind.TransactOpts - Confirm func(tx common.Hash) (uint64, error) + // Includes block number + Confirm func(tx common.Hash) (uint64, error) } type Environment struct { @@ -72,7 +76,8 @@ func ConfirmIfNoError(chain Chain, tx *types.Transaction, err error) (uint64, er } return 0, err } - return chain.Confirm(tx.Hash()) + b, err := chain.Confirm(tx.Hash()) + return b, err } func MaybeDataErr(err error) error { @@ -106,6 +111,7 @@ type OCRConfig struct { type Nodes []Node +// PeerIDs returns peerIDs in a sorted list func (n Nodes) PeerIDs(chainSel uint64) [][32]byte { var peerIDs [][32]byte for _, node := range n { @@ -114,9 +120,16 @@ func (n Nodes) PeerIDs(chainSel uint64) [][32]byte { // Might make sense to change proto as peerID is 1-1 with node? peerIDs = append(peerIDs, cfg.PeerID) } + sort.Slice(peerIDs, func(i, j int) bool { + return bytes.Compare(peerIDs[i][:], peerIDs[j][:]) < 0 + }) return peerIDs } +func (n Nodes) DefaultF() uint8 { + return uint8(len(n) / 3) +} + func (n Nodes) BootstrapPeerIDs(chainSel uint64) [][32]byte { var peerIDs [][32]byte for _, node := range n { diff --git a/integration-tests/go.mod b/integration-tests/go.mod index f36a2ccb8fb..dd75fb2df9b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -33,8 +33,8 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/shopspring/decimal v1.4.0 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240808195812-ae0378684685 - github.com/smartcontractkit/chain-selectors v1.0.21 + github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240905142116-8eb0d5b8f98d + github.com/smartcontractkit/chain-selectors v1.0.23 github.com/smartcontractkit/chainlink-automation v1.0.4 github.com/smartcontractkit/chainlink-ccip v0.0.0-20240909152240-a6969c1002e6 github.com/smartcontractkit/chainlink-common v0.2.2-0.20240909141252-663388d38293 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 642679410e4..7fb32cd9f23 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1417,10 +1417,10 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ= github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= -github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240808195812-ae0378684685 h1:jakAsdhDxV4cMgRAcSvHraXjyePi8umG5SEUTGFvuy8= -github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240808195812-ae0378684685/go.mod h1:p7L/xNEQpHDdZtgFA6/FavuZHqvV3kYhQysxBywmq1k= -github.com/smartcontractkit/chain-selectors v1.0.21 h1:KCR9SA7PhOexaBzFieHoLv1WonwhVOPtOStpqTmLC4E= -github.com/smartcontractkit/chain-selectors v1.0.21/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= +github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240905142116-8eb0d5b8f98d h1:NWk2gQpK5EGrjqUFGxJPQ3/Opl8S/tkbLoxdwx8LKm8= +github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240905142116-8eb0d5b8f98d/go.mod h1:N60/wwocvZ5A3RGmYaMWo0fPFa5tTMlhI9lJ22DRktM= +github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnjjIQAEBnutCtksPzVDY= +github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= github.com/smartcontractkit/chainlink-ccip v0.0.0-20240909152240-a6969c1002e6 h1:3VbeaqoBblboQ3ytpM7UZzL7MXoHctaaXGGkJ8XkDhY= From bdef10ffd44368c6ab925dc6d796bf0e1e338f78 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 9 Sep 2024 17:02:52 -0400 Subject: [PATCH 02/17] Use mcms lib --- .../deployment/ccip/add_chain.go | 21 ++++++++++++------- integration-tests/deployment/ccip/propose.go | 21 +++++++++++-------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index 49e441bf79c..6349f56dc41 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -2,6 +2,7 @@ package ccipdeployment import ( "math/big" + "time" "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/mcms" @@ -58,7 +59,7 @@ func NewChainInbound( []fee_quoter.FeeQuoterDestChainConfigArgs{ { DestChainSelector: newChainSel, - DestChainConfig: defaultPriceRegistryDestChainConfig(), + DestChainConfig: defaultFeeQuoterDestChainConfig(), }, }) if err != nil { @@ -149,11 +150,17 @@ func NewChainInbound( }, }, }) - newDestProposal := timelock.MCMSWithTimelockProposal{ - Operation: timelock.Schedule, - MinDelay: "1h", - ChainMetadata: metaDataPerChain, - Transactions: batches, + newDestProposal, err := timelock.NewMCMSWithTimelockProposal( + "1", + uint32(time.Now().Add(1*time.Hour).Unix()), + []mcms.Signature{}, + false, + metaDataPerChain, + "blah", + batches, + timelock.Schedule, "1h") + if err != nil { + return nil, ab, err } // New chain we can configure directly with deployer key first. @@ -173,7 +180,7 @@ func NewChainInbound( // We won't actually be able to setOCR3Config on the remote until the first proposal goes through. // TODO: Outbound - return []timelock.MCMSWithTimelockProposal{newDestProposal}, ab, nil + return []timelock.MCMSWithTimelockProposal{*newDestProposal}, ab, nil } //func ApplyInboundChainProposal( diff --git a/integration-tests/deployment/ccip/propose.go b/integration-tests/deployment/ccip/propose.go index 5bdf31081bd..f70c00dea98 100644 --- a/integration-tests/deployment/ccip/propose.go +++ b/integration-tests/deployment/ccip/propose.go @@ -24,10 +24,10 @@ func GenerateAcceptOwnershipProposal( e deployment.Environment, chains []uint64, ab deployment.AddressBook, -) (timelock.MCMSWithTimelockProposal, error) { +) (*timelock.MCMSWithTimelockProposal, error) { state, err := LoadOnchainState(e, ab) if err != nil { - return timelock.MCMSWithTimelockProposal{}, err + return nil, err } // TODO: Just onramp as an example var batches []timelock.BatchChainOperation @@ -36,7 +36,7 @@ func GenerateAcceptOwnershipProposal( chain, _ := chainsel.ChainBySelector(sel) acceptOnRamp, err := state.Chains[sel].OnRamp.AcceptOwnership(SimTransactOpts()) if err != nil { - return timelock.MCMSWithTimelockProposal{}, err + return nil, err } chainSel := mcms.ChainIdentifier(chain.Selector) metaDataPerChain[chainSel] = timelock.MCMSWithTimelockChainMetadata{ @@ -59,10 +59,13 @@ func GenerateAcceptOwnershipProposal( }) } // TODO: Real valid until. - return timelock.MCMSWithTimelockProposal{ - Operation: timelock.Schedule, - MinDelay: "1h", - ChainMetadata: metaDataPerChain, - Transactions: batches, - }, nil + return timelock.NewMCMSWithTimelockProposal( + "1", + 1, + []mcms.Signature{}, + false, + metaDataPerChain, + "blah", + batches, + timelock.Schedule, "1h") } From a5c48c381c56095827adcff226a17b7bcca20ba1 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Mon, 9 Sep 2024 17:32:25 -0400 Subject: [PATCH 03/17] Proposal signing working --- .../deployment/ccip/add_chain_test.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go index 2da0470b776..35d3a355834 100644 --- a/integration-tests/deployment/ccip/add_chain_test.go +++ b/integration-tests/deployment/ccip/add_chain_test.go @@ -1,7 +1,6 @@ package ccipdeployment import ( - "encoding/json" "testing" "github.com/ethereum/go-ethereum/crypto" @@ -52,22 +51,21 @@ func TestAddChain(t *testing.T) { // Enable inbound to new 4th chain. proposals, ab, err := NewChainInbound(e.Env, e.Ab, e.HomeChainSel, newChain, initialDeploy) require.NoError(t, err) - require.Equal(t, 3, len(proposals[0].ChainMetadata)) + //require.Equal(t, 3, len(proposals[0].ChainMetadata)) // Sign this proposal with the deployer key. - executor, err := proposals[0].ToExecutor(executorClients) + realProposal, err := proposals[0].ToMCMSOnlyProposal() + require.NoError(t, err) + + executor, err := realProposal.ToExecutor(executorClients) payload, err := executor.SigningHash() require.NoError(t, err) // Sign the payload sig, err := crypto.Sign(payload.Bytes(), TestXXXMCMSSigner) require.NoError(t, err) - + mcmSig, err := mcms.NewSignatureFromBytes(sig) // Sign the payload - unmarshalledSig := mcms.Signature{} - err = json.Unmarshal(sig, &unmarshalledSig) - require.NoError(t, err) - // Add signature to proposal - proposals[0].Signatures = append(proposals[0].Signatures, unmarshalledSig) + proposals[0].Signatures = append(proposals[0].Signatures, mcmSig) require.NoError(t, proposals[0].Validate()) t.Log(proposals, ab) From 902155c87a76b148ec4ccf463a99f392e4def8c5 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Tue, 10 Sep 2024 17:29:52 -0400 Subject: [PATCH 04/17] Almost working. Just need ramps to make timelock owner first --- .../deployment/ccip/add_chain.go | 131 +++++++----------- .../deployment/ccip/add_chain_test.go | 107 ++++++++++++-- integration-tests/deployment/ccip/deploy.go | 18 ++- 3 files changed, 158 insertions(+), 98 deletions(-) diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index 6349f56dc41..d696376da15 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -2,45 +2,27 @@ package ccipdeployment import ( "math/big" - "time" - "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/mcms" "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/timelock" chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink/integration-tests/deployment" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" ) -// AddChain deploys chain contracts for a new chain -// and generates 3 proposals to connect that new chain to all existing chains. -// We testing in between each proposal. -func NewChainInbound( +// NewChainInboundProposal generates a proposal +// to connect the new chain to the existing chains. +func NewChainInboundProposal( e deployment.Environment, - ab deployment.AddressBook, + state CCIPOnChainState, homeChainSel uint64, newChainSel uint64, sources []uint64, -) ([]timelock.MCMSWithTimelockProposal, deployment.AddressBook, error) { - // 1. Deploy contracts to new chain and wire them. - newAddresses, err := DeployChainContracts(e, e.Chains[newChainSel], deployment.NewMemoryAddressBook()) - if err != nil { - return nil, ab, err - } - if err := ab.Merge(newAddresses); err != nil { - return nil, ab, err - } - state, err := LoadOnchainState(e, ab) - if err != nil { - return nil, ab, err - } - - // 2. Generate proposal which enables new destination (from test router) on all source chains. +) ([]timelock.MCMSWithTimelockProposal, error) { + // Generate proposal which enables new destination (from test router) on all source chains. var batches []timelock.BatchChainOperation metaDataPerChain := make(map[mcms.ChainIdentifier]timelock.MCMSWithTimelockChainMetadata) for _, source := range sources { @@ -52,33 +34,35 @@ func NewChainInbound( }, }) if err != nil { - return nil, ab, err + return nil, err } - enablePriceRegDest, err := state.Chains[source].FeeQuoter.ApplyDestChainConfigUpdates( - SimTransactOpts(), - []fee_quoter.FeeQuoterDestChainConfigArgs{ - { - DestChainSelector: newChainSel, - DestChainConfig: defaultFeeQuoterDestChainConfig(), - }, - }) - if err != nil { - return nil, ab, err - } - initialPrices, err := state.Chains[source].FeeQuoter.UpdatePrices( - SimTransactOpts(), - fee_quoter.InternalPriceUpdates{ - TokenPriceUpdates: []fee_quoter.InternalTokenPriceUpdate{}, - GasPriceUpdates: []fee_quoter.InternalGasPriceUpdate{ + /* + enablePriceRegDest, err := state.Chains[source].FeeQuoter.ApplyDestChainConfigUpdates( + SimTransactOpts(), + []fee_quoter.FeeQuoterDestChainConfigArgs{ { DestChainSelector: newChainSel, - // TODO: parameterize - UsdPerUnitGas: big.NewInt(2e12), + DestChainConfig: defaultFeeQuoterDestChainConfig(), }, - }}) - if err != nil { - return nil, ab, err - } + }) + if err != nil { + return nil, err + } + initialPrices, err := state.Chains[source].FeeQuoter.UpdatePrices( + SimTransactOpts(), + fee_quoter.InternalPriceUpdates{ + TokenPriceUpdates: []fee_quoter.InternalTokenPriceUpdate{}, + GasPriceUpdates: []fee_quoter.InternalGasPriceUpdate{ + { + DestChainSelector: newChainSel, + // TODO: parameterize + UsdPerUnitGas: big.NewInt(2e12), + }, + }}) + if err != nil { + return nil, err + } + */ batches = append(batches, timelock.BatchChainOperation{ ChainIdentifier: mcms.ChainIdentifier(chain.Selector), Batch: []mcms.Operation{ @@ -88,18 +72,18 @@ func NewChainInbound( Data: enableOnRampDest.Data(), Value: big.NewInt(0), }, - { - // Set initial dest prices to unblock testing. - To: state.Chains[source].FeeQuoter.Address(), - Data: initialPrices.Data(), - Value: big.NewInt(0), - }, - { - // Set initial dest prices to unblock testing. - To: state.Chains[source].FeeQuoter.Address(), - Data: enablePriceRegDest.Data(), - Value: big.NewInt(0), - }, + //{ + // // Set initial dest prices to unblock testing. + // To: state.Chains[source].FeeQuoter.Address(), + // Data: initialPrices.Data(), + // Value: big.NewInt(0), + //}, + //{ + // // Set initial dest prices to unblock testing. + // To: state.Chains[source].FeeQuoter.Address(), + // Data: enablePriceRegDest.Data(), + // Value: big.NewInt(0), + //}, }, }) metaDataPerChain[mcms.ChainIdentifier(chain.Selector)] = timelock.MCMSWithTimelockChainMetadata{ @@ -115,11 +99,11 @@ func NewChainInbound( // - Add new DONs for destination to home chain nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) if err != nil { - return nil, ab, err + return nil, err } newDONArgs, err := BuildAddDONArgs(e.Logger, state.Chains[newChainSel].OffRamp, e.Chains[newChainSel], nodes) if err != nil { - return nil, ab, err + return nil, err } addDON, err := state.Chains[homeChainSel].CapabilityRegistry.AddDON(SimTransactOpts(), nodes.PeerIDs(newChainSel), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ @@ -129,7 +113,7 @@ func NewChainInbound( }, }, false, false, nodes.DefaultF()) if err != nil { - return nil, ab, err + return nil, err } homeChain, _ := chainsel.ChainBySelector(homeChainSel) metaDataPerChain[mcms.ChainIdentifier(homeChain.Selector)] = timelock.MCMSWithTimelockChainMetadata{ @@ -152,35 +136,20 @@ func NewChainInbound( }) newDestProposal, err := timelock.NewMCMSWithTimelockProposal( "1", - uint32(time.Now().Add(1*time.Hour).Unix()), + 2004259681, []mcms.Signature{}, false, metaDataPerChain, "blah", batches, - timelock.Schedule, "1h") + timelock.Schedule, "0s") if err != nil { - return nil, ab, err - } - - // New chain we can configure directly with deployer key first. - var offRampEnables []offramp.OffRampSourceChainConfigArgs - for _, source := range sources { - offRampEnables = append(offRampEnables, offramp.OffRampSourceChainConfigArgs{ - Router: state.Chains[newChainSel].Router.Address(), - SourceChainSelector: source, - IsEnabled: true, - OnRamp: common.LeftPadBytes(state.Chains[source].OnRamp.Address().Bytes(), 32), - }) - } - tx, err := state.Chains[newChainSel].OffRamp.ApplySourceChainConfigUpdates(e.Chains[newChainSel].DeployerKey, offRampEnables) - if _, err := deployment.ConfirmIfNoError(e.Chains[newChainSel], tx, err); err != nil { - return nil, ab, err + return nil, err } // We won't actually be able to setOCR3Config on the remote until the first proposal goes through. // TODO: Outbound - return []timelock.MCMSWithTimelockProposal{*newDestProposal}, ab, nil + return []timelock.MCMSWithTimelockProposal{*newDestProposal}, nil } //func ApplyInboundChainProposal( diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go index 35d3a355834..325c5104ffc 100644 --- a/integration-tests/deployment/ccip/add_chain_test.go +++ b/integration-tests/deployment/ccip/add_chain_test.go @@ -1,13 +1,20 @@ package ccipdeployment import ( + "bytes" + "context" "testing" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/tools/gethwrappers" "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/mcms" chainsel "github.com/smartcontractkit/chain-selectors" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/integration-tests/deployment" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -47,26 +54,106 @@ func TestAddChain(t *testing.T) { chainSel := mcms.ChainIdentifier(chainselc.Selector) executorClients[chainSel] = chain.Client } + // Deploy contracts to new chain + newAddresses, err := DeployChainContracts(e.Env, e.Env.Chains[newChain], deployment.NewMemoryAddressBook()) + require.NoError(t, err) + require.NoError(t, e.Ab.Merge(newAddresses)) + state, err = LoadOnchainState(e.Env, e.Ab) + require.NoError(t, err) - // Enable inbound to new 4th chain. - proposals, ab, err := NewChainInbound(e.Env, e.Ab, e.HomeChainSel, newChain, initialDeploy) + // We can directly enable the sources on the new chain with deployer key. + var offRampEnables []offramp.OffRampSourceChainConfigArgs + for _, source := range initialDeploy { + offRampEnables = append(offRampEnables, offramp.OffRampSourceChainConfigArgs{ + Router: state.Chains[newChain].Router.Address(), + SourceChainSelector: source, + IsEnabled: true, + OnRamp: common.LeftPadBytes(state.Chains[source].OnRamp.Address().Bytes(), 32), + }) + } + tx, err := state.Chains[newChain].OffRamp.ApplySourceChainConfigUpdates(e.Env.Chains[newChain].DeployerKey, offRampEnables) require.NoError(t, err) - //require.Equal(t, 3, len(proposals[0].ChainMetadata)) - // Sign this proposal with the deployer key. - realProposal, err := proposals[0].ToMCMSOnlyProposal() + _, err = deployment.ConfirmIfNoError(e.Env.Chains[newChain], tx, err) require.NoError(t, err) + // Generate and sign inbound proposal to new 4th chain. + proposals, err := NewChainInboundProposal(e.Env, state, e.HomeChainSel, newChain, initialDeploy) + require.NoError(t, err) + realProposal, err := proposals[0].ToMCMSOnlyProposal() + require.NoError(t, err) executor, err := realProposal.ToExecutor(executorClients) + require.NoError(t, err) payload, err := executor.SigningHash() require.NoError(t, err) // Sign the payload sig, err := crypto.Sign(payload.Bytes(), TestXXXMCMSSigner) require.NoError(t, err) mcmSig, err := mcms.NewSignatureFromBytes(sig) - // Sign the payload - // Add signature to proposal - proposals[0].Signatures = append(proposals[0].Signatures, mcmSig) - require.NoError(t, proposals[0].Validate()) + executor.Proposal.Signatures = append(executor.Proposal.Signatures, mcmSig) + require.NoError(t, executor.Proposal.Validate()) + + // Apply the proposal to all the chains. + for _, sel := range sels { + if sel == newChain { + continue + } + // Set the root. + tx, err2 := executor.SetRootOnChain(e.Env.Chains[sel].DeployerKey, mcms.ChainIdentifier(sel)) + require.NoError(t, err2) + _, err2 = e.Env.Chains[sel].Confirm(tx.Hash()) + require.NoError(t, err2) + + // Execute all the transactions in the proposal which are for this chain. + for _, chainOp := range executor.Operations[mcms.ChainIdentifier(sel)] { + for idx, op := range executor.ChainAgnosticOps { + if bytes.Equal(op.Data, chainOp.Data) && op.To == chainOp.To { + opTx, err3 := executor.ExecuteOnChain(e.Env.Chains[sel].DeployerKey, idx) + require.NoError(t, err3) + block, err3 := e.Env.Chains[sel].Confirm(opTx.Hash()) + require.NoError(t, err3) + t.Log("executed", chainOp) + it, err3 := state.Chains[sel].Timelock.FilterCallScheduled(&bind.FilterOpts{ + Start: block, + End: &block, + Context: context.Background(), + }, nil, nil) + require.NoError(t, err3) + var calls []owner_helpers.RBACTimelockCall + var pred, salt [32]byte + for it.Next() { + // Note these are the same for the whole batch, can overwrite + pred = it.Event.Predecessor + salt = it.Event.Salt + t.Log("scheduled", it.Event) + calls = append(calls, owner_helpers.RBACTimelockCall{ + Target: it.Event.Target, + Data: it.Event.Data, + Value: it.Event.Value, + }) + } + tx, err := state.Chains[sel].Timelock.ExecuteBatch( + e.Env.Chains[sel].DeployerKey, calls, pred, salt) + require.NoError(t, err) + _, err = e.Env.Chains[sel].Confirm(tx.Hash()) + require.NoError(t, err) + } + } + } + } + + state, err = LoadOnchainState(e.Env, e.Ab) + require.NoError(t, err) + for _, chain := range initialDeploy { + cfg, err2 := state.Chains[chain].OnRamp.GetDestChainConfig(nil, newChain) + require.NoError(t, err2) + t.Log("config", cfg) + s, err2 := state.Chains[newChain].OffRamp.GetSourceChainConfig(nil, chain) + require.NoError(t, err2) + t.Log("config", s) + } + + // Now that the proposal has been executed we expect to be able to send traffic to this new 4th chain. + //SendRequest(t, e.Env, state, initialDeploy[0], newChain) + //e.Env.initialDeploy[0] - t.Log(proposals, ab) } diff --git a/integration-tests/deployment/ccip/deploy.go b/integration-tests/deployment/ccip/deploy.go index e0647bc4e8d..22ebfe84e9b 100644 --- a/integration-tests/deployment/ccip/deploy.go +++ b/integration-tests/deployment/ccip/deploy.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/smartcontractkit/ccip-owner-contracts/tools/configwrappers" owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/tools/gethwrappers" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -262,17 +263,20 @@ func DeployChainContracts( } // TODO: Address soon e.Logger.Infow("deployed mcm", "addr", mcm.Address) - // TODO: Real MCM configuration. - var quorums, parents [32]uint8 - quorums[0] = 1 publicKey := TestXXXMCMSSigner.Public().(*ecdsa.PublicKey) // Convert the public key to an Ethereum address address := crypto.PubkeyToAddress(*publicKey) + c, err := configwrappers.NewConfig(1, []common.Address{address}, []configwrappers.Config{}) + if err != nil { + e.Logger.Errorw("Failed to create config", "err", err) + return ab, err + } + groupQuorums, groupParents, signerAddresses, signerGroups := c.ExtractSetConfigInputs() tx, err := mcm.Contract.SetConfig(chain.DeployerKey, - []common.Address{address}, - []uint8{0}, // Signer 1 is int group 0 (root group) with quorum 1. - quorums, - parents, + signerAddresses, + signerGroups, // Signer 1 is int group 0 (root group) with quorum 1. + groupQuorums, + groupParents, false, ) if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { From dda8ca85f9b82d558fccd30cd68dc7f085bbea56 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Tue, 10 Sep 2024 18:00:27 -0400 Subject: [PATCH 05/17] Accept ownership proposal working --- .../deployment/ccip/add_chain.go | 9 +- .../deployment/ccip/add_chain_test.go | 93 +++++-------------- integration-tests/deployment/ccip/propose.go | 80 +++++++++++++++- 3 files changed, 102 insertions(+), 80 deletions(-) diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index d696376da15..cf2f154f102 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -21,7 +21,7 @@ func NewChainInboundProposal( homeChainSel uint64, newChainSel uint64, sources []uint64, -) ([]timelock.MCMSWithTimelockProposal, error) { +) (*timelock.MCMSWithTimelockProposal, error) { // Generate proposal which enables new destination (from test router) on all source chains. var batches []timelock.BatchChainOperation metaDataPerChain := make(map[mcms.ChainIdentifier]timelock.MCMSWithTimelockChainMetadata) @@ -134,7 +134,7 @@ func NewChainInboundProposal( }, }, }) - newDestProposal, err := timelock.NewMCMSWithTimelockProposal( + return timelock.NewMCMSWithTimelockProposal( "1", 2004259681, []mcms.Signature{}, @@ -143,13 +143,8 @@ func NewChainInboundProposal( "blah", batches, timelock.Schedule, "0s") - if err != nil { - return nil, err - } - // We won't actually be able to setOCR3Config on the remote until the first proposal goes through. // TODO: Outbound - return []timelock.MCMSWithTimelockProposal{*newDestProposal}, nil } //func ApplyInboundChainProposal( diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go index 325c5104ffc..c9b0c83868f 100644 --- a/integration-tests/deployment/ccip/add_chain_test.go +++ b/integration-tests/deployment/ccip/add_chain_test.go @@ -1,16 +1,9 @@ package ccipdeployment import ( - "bytes" - "context" "testing" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/tools/gethwrappers" - "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/mcms" - chainsel "github.com/smartcontractkit/chain-selectors" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/integration-tests/deployment" @@ -47,13 +40,6 @@ func TestAddChain(t *testing.T) { } } - executorClients := make(map[mcms.ChainIdentifier]mcms.ContractDeployBackend) - for _, chain := range e.Env.Chains { - chainselc, exists := chainsel.ChainBySelector(chain.Selector) - require.True(t, exists) - chainSel := mcms.ChainIdentifier(chainselc.Selector) - executorClients[chainSel] = chain.Client - } // Deploy contracts to new chain newAddresses, err := DeployChainContracts(e.Env, e.Env.Chains[newChain], deployment.NewMemoryAddressBook()) require.NoError(t, err) @@ -76,74 +62,41 @@ func TestAddChain(t *testing.T) { _, err = deployment.ConfirmIfNoError(e.Env.Chains[newChain], tx, err) require.NoError(t, err) - // Generate and sign inbound proposal to new 4th chain. - proposals, err := NewChainInboundProposal(e.Env, state, e.HomeChainSel, newChain, initialDeploy) - require.NoError(t, err) - realProposal, err := proposals[0].ToMCMSOnlyProposal() - require.NoError(t, err) - executor, err := realProposal.ToExecutor(executorClients) - require.NoError(t, err) - payload, err := executor.SigningHash() - require.NoError(t, err) - // Sign the payload - sig, err := crypto.Sign(payload.Bytes(), TestXXXMCMSSigner) + // Transfer onramp ownership to timelock. + for _, source := range initialDeploy { + tx, err := state.Chains[source].OnRamp.TransferOwnership(e.Env.Chains[source].DeployerKey, state.Chains[source].Timelock.Address()) + require.NoError(t, err) + _, err = deployment.ConfirmIfNoError(e.Env.Chains[source], tx, err) + require.NoError(t, err) + } + acceptOwnershipProposal, err := GenerateAcceptOwnershipProposal(e.Env, initialDeploy, e.Ab) require.NoError(t, err) - mcmSig, err := mcms.NewSignatureFromBytes(sig) - executor.Proposal.Signatures = append(executor.Proposal.Signatures, mcmSig) - require.NoError(t, executor.Proposal.Validate()) - + acceptOwnershipExec := SignProposal(t, e.Env, acceptOwnershipProposal) // Apply the proposal to all the chains. for _, sel := range sels { if sel == newChain { continue } - // Set the root. - tx, err2 := executor.SetRootOnChain(e.Env.Chains[sel].DeployerKey, mcms.ChainIdentifier(sel)) - require.NoError(t, err2) - _, err2 = e.Env.Chains[sel].Confirm(tx.Hash()) - require.NoError(t, err2) - - // Execute all the transactions in the proposal which are for this chain. - for _, chainOp := range executor.Operations[mcms.ChainIdentifier(sel)] { - for idx, op := range executor.ChainAgnosticOps { - if bytes.Equal(op.Data, chainOp.Data) && op.To == chainOp.To { - opTx, err3 := executor.ExecuteOnChain(e.Env.Chains[sel].DeployerKey, idx) - require.NoError(t, err3) - block, err3 := e.Env.Chains[sel].Confirm(opTx.Hash()) - require.NoError(t, err3) - t.Log("executed", chainOp) - it, err3 := state.Chains[sel].Timelock.FilterCallScheduled(&bind.FilterOpts{ - Start: block, - End: &block, - Context: context.Background(), - }, nil, nil) - require.NoError(t, err3) - var calls []owner_helpers.RBACTimelockCall - var pred, salt [32]byte - for it.Next() { - // Note these are the same for the whole batch, can overwrite - pred = it.Event.Predecessor - salt = it.Event.Salt - t.Log("scheduled", it.Event) - calls = append(calls, owner_helpers.RBACTimelockCall{ - Target: it.Event.Target, - Data: it.Event.Data, - Value: it.Event.Value, - }) - } - tx, err := state.Chains[sel].Timelock.ExecuteBatch( - e.Env.Chains[sel].DeployerKey, calls, pred, salt) - require.NoError(t, err) - _, err = e.Env.Chains[sel].Confirm(tx.Hash()) - require.NoError(t, err) - } - } + ExecuteProposal(t, e.Env, acceptOwnershipExec, state, sel) + } + // Generate and sign inbound proposal to new 4th chain. + chainInboundProposal, err := NewChainInboundProposal(e.Env, state, e.HomeChainSel, newChain, initialDeploy) + require.NoError(t, err) + chainInboundExec := SignProposal(t, e.Env, chainInboundProposal) + for _, sel := range sels { + if sel == newChain { + continue } + ExecuteProposal(t, e.Env, chainInboundExec, state, sel) } state, err = LoadOnchainState(e.Env, e.Ab) require.NoError(t, err) for _, chain := range initialDeploy { + owner, err2 := state.Chains[chain].OnRamp.Owner(nil) + require.NoError(t, err2) + t.Log("owner", owner, state.Chains[chain].TimelockAddr) + cfg, err2 := state.Chains[chain].OnRamp.GetDestChainConfig(nil, newChain) require.NoError(t, err2) t.Log("config", cfg) diff --git a/integration-tests/deployment/ccip/propose.go b/integration-tests/deployment/ccip/propose.go index f70c00dea98..fbcce5eed0a 100644 --- a/integration-tests/deployment/ccip/propose.go +++ b/integration-tests/deployment/ccip/propose.go @@ -1,14 +1,20 @@ package ccipdeployment import ( + "bytes" + "context" "math/big" + "testing" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + owner_helpers "github.com/smartcontractkit/ccip-owner-contracts/tools/gethwrappers" "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/mcms" "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/timelock" chainsel "github.com/smartcontractkit/chain-selectors" + "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/integration-tests/deployment" ) @@ -20,6 +26,75 @@ func SimTransactOpts() *bind.TransactOpts { }, From: common.HexToAddress("0x0"), NoSend: true, GasLimit: 1_000_000} } +func SignProposal(t *testing.T, env deployment.Environment, proposal *timelock.MCMSWithTimelockProposal) *mcms.Executor { + executorClients := make(map[mcms.ChainIdentifier]mcms.ContractDeployBackend) + for _, chain := range env.Chains { + chainselc, exists := chainsel.ChainBySelector(chain.Selector) + require.True(t, exists) + chainSel := mcms.ChainIdentifier(chainselc.Selector) + executorClients[chainSel] = chain.Client + } + realProposal, err := proposal.ToMCMSOnlyProposal() + require.NoError(t, err) + executor, err := realProposal.ToExecutor(executorClients) + require.NoError(t, err) + payload, err := executor.SigningHash() + require.NoError(t, err) + // Sign the payload + sig, err := crypto.Sign(payload.Bytes(), TestXXXMCMSSigner) + require.NoError(t, err) + mcmSig, err := mcms.NewSignatureFromBytes(sig) + executor.Proposal.Signatures = append(executor.Proposal.Signatures, mcmSig) + require.NoError(t, executor.Proposal.Validate()) + return executor +} + +func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Executor, + state CCIPOnChainState, sel uint64) { + // Set the root. + tx, err2 := executor.SetRootOnChain(env.Chains[sel].DeployerKey, mcms.ChainIdentifier(sel)) + require.NoError(t, err2) + _, err2 = env.Chains[sel].Confirm(tx.Hash()) + require.NoError(t, err2) + + // Execute all the transactions in the proposal which are for this chain. + for _, chainOp := range executor.Operations[mcms.ChainIdentifier(sel)] { + for idx, op := range executor.ChainAgnosticOps { + if bytes.Equal(op.Data, chainOp.Data) && op.To == chainOp.To { + opTx, err3 := executor.ExecuteOnChain(env.Chains[sel].DeployerKey, idx) + require.NoError(t, err3) + block, err3 := env.Chains[sel].Confirm(opTx.Hash()) + require.NoError(t, err3) + t.Log("executed", chainOp) + it, err3 := state.Chains[sel].Timelock.FilterCallScheduled(&bind.FilterOpts{ + Start: block, + End: &block, + Context: context.Background(), + }, nil, nil) + require.NoError(t, err3) + var calls []owner_helpers.RBACTimelockCall + var pred, salt [32]byte + for it.Next() { + // Note these are the same for the whole batch, can overwrite + pred = it.Event.Predecessor + salt = it.Event.Salt + t.Log("scheduled", it.Event) + calls = append(calls, owner_helpers.RBACTimelockCall{ + Target: it.Event.Target, + Data: it.Event.Data, + Value: it.Event.Value, + }) + } + tx, err := state.Chains[sel].Timelock.ExecuteBatch( + env.Chains[sel].DeployerKey, calls, pred, salt) + require.NoError(t, err) + _, err = env.Chains[sel].Confirm(tx.Hash()) + require.NoError(t, err) + } + } + } +} + func GenerateAcceptOwnershipProposal( e deployment.Environment, chains []uint64, @@ -58,14 +133,13 @@ func GenerateAcceptOwnershipProposal( }, }) } - // TODO: Real valid until. return timelock.NewMCMSWithTimelockProposal( "1", - 1, + 2004259681, []mcms.Signature{}, false, metaDataPerChain, "blah", batches, - timelock.Schedule, "1h") + timelock.Schedule, "0s") } From d75fbec5182c27e491db3407dbb25747f3b5bea2 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Wed, 11 Sep 2024 12:43:34 -0400 Subject: [PATCH 06/17] Fee quoter accept ownership --- .../deployment/ccip/add_chain.go | 138 +++++++++--------- .../deployment/ccip/add_chain_test.go | 59 +++++--- integration-tests/deployment/ccip/propose.go | 51 ++++++- integration-tests/deployment/environment.go | 17 +++ 4 files changed, 167 insertions(+), 98 deletions(-) diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index cf2f154f102..a2b51a8bd6f 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -8,8 +8,7 @@ import ( chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink/integration-tests/deployment" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" - + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" ) @@ -36,33 +35,31 @@ func NewChainInboundProposal( if err != nil { return nil, err } - /* - enablePriceRegDest, err := state.Chains[source].FeeQuoter.ApplyDestChainConfigUpdates( - SimTransactOpts(), - []fee_quoter.FeeQuoterDestChainConfigArgs{ - { - DestChainSelector: newChainSel, - DestChainConfig: defaultFeeQuoterDestChainConfig(), - }, - }) - if err != nil { - return nil, err - } - initialPrices, err := state.Chains[source].FeeQuoter.UpdatePrices( - SimTransactOpts(), - fee_quoter.InternalPriceUpdates{ - TokenPriceUpdates: []fee_quoter.InternalTokenPriceUpdate{}, - GasPriceUpdates: []fee_quoter.InternalGasPriceUpdate{ - { - DestChainSelector: newChainSel, - // TODO: parameterize - UsdPerUnitGas: big.NewInt(2e12), - }, - }}) - if err != nil { - return nil, err - } - */ + enablePriceRegDest, err := state.Chains[source].FeeQuoter.ApplyDestChainConfigUpdates( + SimTransactOpts(), + []fee_quoter.FeeQuoterDestChainConfigArgs{ + { + DestChainSelector: newChainSel, + DestChainConfig: defaultFeeQuoterDestChainConfig(), + }, + }) + if err != nil { + return nil, err + } + //initialPrices, err := state.Chains[source].FeeQuoter.UpdatePrices( + // SimTransactOpts(), + // fee_quoter.InternalPriceUpdates{ + // TokenPriceUpdates: []fee_quoter.InternalTokenPriceUpdate{}, + // GasPriceUpdates: []fee_quoter.InternalGasPriceUpdate{ + // { + // DestChainSelector: newChainSel, + // // TODO: parameterize + // UsdPerUnitGas: big.NewInt(2e12), + // }, + // }}) + if err != nil { + return nil, err + } batches = append(batches, timelock.BatchChainOperation{ ChainIdentifier: mcms.ChainIdentifier(chain.Selector), Batch: []mcms.Operation{ @@ -78,12 +75,11 @@ func NewChainInboundProposal( // Data: initialPrices.Data(), // Value: big.NewInt(0), //}, - //{ - // // Set initial dest prices to unblock testing. - // To: state.Chains[source].FeeQuoter.Address(), - // Data: enablePriceRegDest.Data(), - // Value: big.NewInt(0), - //}, + { + To: state.Chains[source].FeeQuoter.Address(), + Data: enablePriceRegDest.Data(), + Value: big.NewInt(0), + }, }, }) metaDataPerChain[mcms.ChainIdentifier(chain.Selector)] = timelock.MCMSWithTimelockChainMetadata{ @@ -97,43 +93,45 @@ func NewChainInboundProposal( // Home chain new don. // - Add new DONs for destination to home chain - nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) - if err != nil { - return nil, err - } - newDONArgs, err := BuildAddDONArgs(e.Logger, state.Chains[newChainSel].OffRamp, e.Chains[newChainSel], nodes) - if err != nil { - return nil, err - } - addDON, err := state.Chains[homeChainSel].CapabilityRegistry.AddDON(SimTransactOpts(), - nodes.PeerIDs(newChainSel), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: CCIPCapabilityId, - Config: newDONArgs, + /* + nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) + if err != nil { + return nil, err + } + newDONArgs, err := BuildAddDONArgs(e.Logger, state.Chains[newChainSel].OffRamp, e.Chains[newChainSel], nodes) + if err != nil { + return nil, err + } + addDON, err := state.Chains[homeChainSel].CapabilityRegistry.AddDON(SimTransactOpts(), + nodes.PeerIDs(newChainSel), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: CCIPCapabilityId, + Config: newDONArgs, + }, + }, false, false, nodes.DefaultF()) + if err != nil { + return nil, err + } + homeChain, _ := chainsel.ChainBySelector(homeChainSel) + metaDataPerChain[mcms.ChainIdentifier(homeChain.Selector)] = timelock.MCMSWithTimelockChainMetadata{ + ChainMetadata: mcms.ChainMetadata{ + NonceOffset: 0, + MCMAddress: state.Chains[homeChainSel].McmAddr, }, - }, false, false, nodes.DefaultF()) - if err != nil { - return nil, err - } - homeChain, _ := chainsel.ChainBySelector(homeChainSel) - metaDataPerChain[mcms.ChainIdentifier(homeChain.Selector)] = timelock.MCMSWithTimelockChainMetadata{ - ChainMetadata: mcms.ChainMetadata{ - NonceOffset: 0, - MCMAddress: state.Chains[homeChainSel].McmAddr, - }, - TimelockAddress: state.Chains[homeChainSel].TimelockAddr, - } - batches = append(batches, timelock.BatchChainOperation{ - ChainIdentifier: mcms.ChainIdentifier(homeChain.Selector), - Batch: []mcms.Operation{ - { - // Enable the source in on ramp - To: state.Chains[homeChainSel].CapabilityRegistry.Address(), - Data: addDON.Data(), - Value: big.NewInt(0), + TimelockAddress: state.Chains[homeChainSel].TimelockAddr, + } + batches = append(batches, timelock.BatchChainOperation{ + ChainIdentifier: mcms.ChainIdentifier(homeChain.Selector), + Batch: []mcms.Operation{ + { + // Enable the source in on ramp + To: state.Chains[homeChainSel].CapabilityRegistry.Address(), + Data: addDON.Data(), + Value: big.NewInt(0), + }, }, - }, - }) + }) + */ return timelock.NewMCMSWithTimelockProposal( "1", 2004259681, diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go index c9b0c83868f..e86b7cd2ed4 100644 --- a/integration-tests/deployment/ccip/add_chain_test.go +++ b/integration-tests/deployment/ccip/add_chain_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/integration-tests/deployment" @@ -16,9 +17,11 @@ func TestAddChain(t *testing.T) { e := NewEnvironmentWithCRAndJobs(t, logger.TestLogger(t), 4) state, err := LoadOnchainState(e.Env, e.Ab) require.NoError(t, err) - sels := e.Env.AllChainSelectors() - initialDeploy := sels[0:3] - newChain := sels[3] + // Take first non-home chain as the new chain. + newChain := e.Env.AllChainSelectorsExcluding([]uint64{e.HomeChainSel})[0] + // We deploy to the rest. + initialDeploy := e.Env.AllChainSelectorsExcluding([]uint64{newChain}) + t.Logf("Home %d new %d initial %d\n", e.HomeChainSel, newChain, initialDeploy) ab, err := DeployCCIPContracts(e.Env, DeployCCIPContractConfig{ HomeChainSel: e.HomeChainSel, @@ -35,7 +38,7 @@ func TestAddChain(t *testing.T) { for _, source := range initialDeploy { for _, dest := range initialDeploy { if source != dest { - require.NoError(t, AddLane(e.Env, state, uint64(source), uint64(dest))) + require.NoError(t, AddLane(e.Env, state, source, dest)) } } } @@ -62,41 +65,57 @@ func TestAddChain(t *testing.T) { _, err = deployment.ConfirmIfNoError(e.Env.Chains[newChain], tx, err) require.NoError(t, err) - // Transfer onramp ownership to timelock. + // Transfer onramp/fq ownership to timelock. for _, source := range initialDeploy { - tx, err := state.Chains[source].OnRamp.TransferOwnership(e.Env.Chains[source].DeployerKey, state.Chains[source].Timelock.Address()) + tx, err := state.Chains[source].OnRamp.TransferOwnership(e.Env.Chains[source].DeployerKey, state.Chains[source].TimelockAddr) + require.NoError(t, err) + _, err = deployment.ConfirmIfNoError(e.Env.Chains[source], tx, err) + require.NoError(t, err) + tx, err = state.Chains[source].FeeQuoter.TransferOwnership(e.Env.Chains[source].DeployerKey, state.Chains[source].TimelockAddr) require.NoError(t, err) _, err = deployment.ConfirmIfNoError(e.Env.Chains[source], tx, err) require.NoError(t, err) } - acceptOwnershipProposal, err := GenerateAcceptOwnershipProposal(e.Env, initialDeploy, e.Ab) + // Transfer CR contract ownership + tx, err = state.Chains[e.HomeChainSel].CapabilityRegistry.TransferOwnership(e.Env.Chains[e.HomeChainSel].DeployerKey, state.Chains[e.HomeChainSel].TimelockAddr) + require.NoError(t, err) + _, err = deployment.ConfirmIfNoError(e.Env.Chains[e.HomeChainSel], tx, err) + require.NoError(t, err) + tx, err = state.Chains[e.HomeChainSel].CCIPConfig.TransferOwnership(e.Env.Chains[e.HomeChainSel].DeployerKey, state.Chains[e.HomeChainSel].TimelockAddr) + require.NoError(t, err) + _, err = deployment.ConfirmIfNoError(e.Env.Chains[e.HomeChainSel], tx, err) + require.NoError(t, err) + + acceptOwnershipProposal, err := GenerateAcceptOwnershipProposal(state, e.HomeChainSel, initialDeploy) require.NoError(t, err) acceptOwnershipExec := SignProposal(t, e.Env, acceptOwnershipProposal) - // Apply the proposal to all the chains. - for _, sel := range sels { - if sel == newChain { - continue - } + // Apply the accept ownership proposal to all the chains. + for _, sel := range initialDeploy { ExecuteProposal(t, e.Env, acceptOwnershipExec, state, sel) } + for _, chain := range initialDeploy { + owner, err2 := state.Chains[chain].OnRamp.Owner(nil) + require.NoError(t, err2) + assert.Equal(t, state.Chains[chain].TimelockAddr, owner) + } + cfgOwner, err := state.Chains[e.HomeChainSel].CCIPConfig.Owner(nil) + require.NoError(t, err) + crOwner, err := state.Chains[e.HomeChainSel].CapabilityRegistry.Owner(nil) + require.NoError(t, err) + assert.Equal(t, state.Chains[e.HomeChainSel].TimelockAddr, cfgOwner) + assert.Equal(t, state.Chains[e.HomeChainSel].TimelockAddr, crOwner) + // Generate and sign inbound proposal to new 4th chain. chainInboundProposal, err := NewChainInboundProposal(e.Env, state, e.HomeChainSel, newChain, initialDeploy) require.NoError(t, err) chainInboundExec := SignProposal(t, e.Env, chainInboundProposal) - for _, sel := range sels { - if sel == newChain { - continue - } + for _, sel := range initialDeploy { ExecuteProposal(t, e.Env, chainInboundExec, state, sel) } state, err = LoadOnchainState(e.Env, e.Ab) require.NoError(t, err) for _, chain := range initialDeploy { - owner, err2 := state.Chains[chain].OnRamp.Owner(nil) - require.NoError(t, err2) - t.Log("owner", owner, state.Chains[chain].TimelockAddr) - cfg, err2 := state.Chains[chain].OnRamp.GetDestChainConfig(nil, newChain) require.NoError(t, err2) t.Log("config", cfg) diff --git a/integration-tests/deployment/ccip/propose.go b/integration-tests/deployment/ccip/propose.go index fbcce5eed0a..c465e9071f3 100644 --- a/integration-tests/deployment/ccip/propose.go +++ b/integration-tests/deployment/ccip/propose.go @@ -96,15 +96,11 @@ func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Ex } func GenerateAcceptOwnershipProposal( - e deployment.Environment, + state CCIPOnChainState, + homeChain uint64, chains []uint64, - ab deployment.AddressBook, ) (*timelock.MCMSWithTimelockProposal, error) { - state, err := LoadOnchainState(e, ab) - if err != nil { - return nil, err - } - // TODO: Just onramp as an example + // TODO: Accept rest of contracts var batches []timelock.BatchChainOperation metaDataPerChain := make(map[mcms.ChainIdentifier]timelock.MCMSWithTimelockChainMetadata) for _, sel := range chains { @@ -113,6 +109,10 @@ func GenerateAcceptOwnershipProposal( if err != nil { return nil, err } + acceptFeeQuoter, err := state.Chains[sel].FeeQuoter.AcceptOwnership(SimTransactOpts()) + if err != nil { + return nil, err + } chainSel := mcms.ChainIdentifier(chain.Selector) metaDataPerChain[chainSel] = timelock.MCMSWithTimelockChainMetadata{ ChainMetadata: mcms.ChainMetadata{ @@ -125,14 +125,49 @@ func GenerateAcceptOwnershipProposal( ChainIdentifier: chainSel, Batch: []mcms.Operation{ { - // Enable the source in on ramp To: state.Chains[sel].OnRamp.Address(), Data: acceptOnRamp.Data(), Value: big.NewInt(0), }, + { + To: state.Chains[sel].FeeQuoter.Address(), + Data: acceptFeeQuoter.Data(), + Value: big.NewInt(0), + }, }, }) } + acceptCR, err := state.Chains[homeChain].CapabilityRegistry.AcceptOwnership(SimTransactOpts()) + if err != nil { + return nil, err + } + acceptCCIPConfig, err := state.Chains[homeChain].CCIPConfig.AcceptOwnership(SimTransactOpts()) + if err != nil { + return nil, err + } + homeChainID := mcms.ChainIdentifier(homeChain) + metaDataPerChain[homeChainID] = timelock.MCMSWithTimelockChainMetadata{ + ChainMetadata: mcms.ChainMetadata{ + NonceOffset: 0, + MCMAddress: state.Chains[homeChain].McmAddr, + }, + TimelockAddress: state.Chains[homeChain].TimelockAddr, + } + batches = append(batches, timelock.BatchChainOperation{ + ChainIdentifier: homeChainID, + Batch: []mcms.Operation{ + { + To: state.Chains[homeChain].CapabilityRegistry.Address(), + Data: acceptCR.Data(), + Value: big.NewInt(0), + }, + { + To: state.Chains[homeChain].CCIPConfig.Address(), + Data: acceptCCIPConfig.Data(), + Value: big.NewInt(0), + }, + }, + }) return timelock.NewMCMSWithTimelockProposal( "1", 2004259681, diff --git a/integration-tests/deployment/environment.go b/integration-tests/deployment/environment.go index f3aa3c6ac37..d46dd9fe690 100644 --- a/integration-tests/deployment/environment.go +++ b/integration-tests/deployment/environment.go @@ -66,6 +66,23 @@ func (e Environment) AllChainSelectors() []uint64 { return selectors } +func (e Environment) AllChainSelectorsExcluding(excluding []uint64) []uint64 { + var selectors []uint64 + for sel := range e.Chains { + excluded := false + for _, toExclude := range excluding { + if sel == toExclude { + excluded = true + } + } + if excluded { + continue + } + selectors = append(selectors, sel) + } + return selectors +} + func ConfirmIfNoError(chain Chain, tx *types.Transaction, err error) (uint64, error) { if err != nil { //revive:disable From f79b3b04037dc6e62657cab9cf3b57b04c968c14 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Wed, 11 Sep 2024 13:33:47 -0400 Subject: [PATCH 07/17] Fix existing tests --- .../deployment/ccip/add_chain.go | 107 +++++++++--------- .../deployment/ccip/add_chain_test.go | 4 +- .../ccip/changeset/2_initial_deploy.go | 14 ++- integration-tests/deployment/ccip/deploy.go | 4 +- 4 files changed, 66 insertions(+), 63 deletions(-) diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index a2b51a8bd6f..c43c2ff8957 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/deployment" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" ) // NewChainInboundProposal generates a proposal @@ -46,17 +47,17 @@ func NewChainInboundProposal( if err != nil { return nil, err } - //initialPrices, err := state.Chains[source].FeeQuoter.UpdatePrices( - // SimTransactOpts(), - // fee_quoter.InternalPriceUpdates{ - // TokenPriceUpdates: []fee_quoter.InternalTokenPriceUpdate{}, - // GasPriceUpdates: []fee_quoter.InternalGasPriceUpdate{ - // { - // DestChainSelector: newChainSel, - // // TODO: parameterize - // UsdPerUnitGas: big.NewInt(2e12), - // }, - // }}) + initialPrices, err := state.Chains[source].FeeQuoter.UpdatePrices( + SimTransactOpts(), + fee_quoter.InternalPriceUpdates{ + TokenPriceUpdates: []fee_quoter.InternalTokenPriceUpdate{}, + GasPriceUpdates: []fee_quoter.InternalGasPriceUpdate{ + { + DestChainSelector: newChainSel, + // TODO: parameterize + UsdPerUnitGas: big.NewInt(2e12), + }, + }}) if err != nil { return nil, err } @@ -69,12 +70,12 @@ func NewChainInboundProposal( Data: enableOnRampDest.Data(), Value: big.NewInt(0), }, - //{ - // // Set initial dest prices to unblock testing. - // To: state.Chains[source].FeeQuoter.Address(), - // Data: initialPrices.Data(), - // Value: big.NewInt(0), - //}, + { + // Set initial dest prices to unblock testing. + To: state.Chains[source].FeeQuoter.Address(), + Data: initialPrices.Data(), + Value: big.NewInt(0), + }, { To: state.Chains[source].FeeQuoter.Address(), Data: enablePriceRegDest.Data(), @@ -93,45 +94,43 @@ func NewChainInboundProposal( // Home chain new don. // - Add new DONs for destination to home chain - /* - nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) - if err != nil { - return nil, err - } - newDONArgs, err := BuildAddDONArgs(e.Logger, state.Chains[newChainSel].OffRamp, e.Chains[newChainSel], nodes) - if err != nil { - return nil, err - } - addDON, err := state.Chains[homeChainSel].CapabilityRegistry.AddDON(SimTransactOpts(), - nodes.PeerIDs(newChainSel), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ - { - CapabilityId: CCIPCapabilityId, - Config: newDONArgs, - }, - }, false, false, nodes.DefaultF()) - if err != nil { - return nil, err - } - homeChain, _ := chainsel.ChainBySelector(homeChainSel) - metaDataPerChain[mcms.ChainIdentifier(homeChain.Selector)] = timelock.MCMSWithTimelockChainMetadata{ - ChainMetadata: mcms.ChainMetadata{ - NonceOffset: 0, - MCMAddress: state.Chains[homeChainSel].McmAddr, + nodes, err := deployment.NodeInfo(e.NodeIDs, e.Offchain) + if err != nil { + return nil, err + } + newDONArgs, err := BuildAddDONArgs(e.Logger, state.Chains[newChainSel].OffRamp, e.Chains[newChainSel], nodes) + if err != nil { + return nil, err + } + addDON, err := state.Chains[homeChainSel].CapabilityRegistry.AddDON(SimTransactOpts(), + nodes.PeerIDs(newChainSel), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: CCIPCapabilityId, + Config: newDONArgs, }, - TimelockAddress: state.Chains[homeChainSel].TimelockAddr, - } - batches = append(batches, timelock.BatchChainOperation{ - ChainIdentifier: mcms.ChainIdentifier(homeChain.Selector), - Batch: []mcms.Operation{ - { - // Enable the source in on ramp - To: state.Chains[homeChainSel].CapabilityRegistry.Address(), - Data: addDON.Data(), - Value: big.NewInt(0), - }, + }, false, false, nodes.DefaultF()) + if err != nil { + return nil, err + } + homeChain, _ := chainsel.ChainBySelector(homeChainSel) + metaDataPerChain[mcms.ChainIdentifier(homeChain.Selector)] = timelock.MCMSWithTimelockChainMetadata{ + ChainMetadata: mcms.ChainMetadata{ + NonceOffset: 0, + MCMAddress: state.Chains[homeChainSel].McmAddr, + }, + TimelockAddress: state.Chains[homeChainSel].TimelockAddr, + } + batches = append(batches, timelock.BatchChainOperation{ + ChainIdentifier: mcms.ChainIdentifier(homeChain.Selector), + Batch: []mcms.Operation{ + { + // Enable the source in on ramp + To: state.Chains[homeChainSel].CapabilityRegistry.Address(), + Data: addDON.Data(), + Value: big.NewInt(0), }, - }) - */ + }, + }) return timelock.NewMCMSWithTimelockProposal( "1", 2004259681, diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go index e86b7cd2ed4..98ae26f061f 100644 --- a/integration-tests/deployment/ccip/add_chain_test.go +++ b/integration-tests/deployment/ccip/add_chain_test.go @@ -118,10 +118,10 @@ func TestAddChain(t *testing.T) { for _, chain := range initialDeploy { cfg, err2 := state.Chains[chain].OnRamp.GetDestChainConfig(nil, newChain) require.NoError(t, err2) - t.Log("config", cfg) + assert.Equal(t, cfg.Router, state.Chains[chain].TestRouter.Address()) s, err2 := state.Chains[newChain].OffRamp.GetSourceChainConfig(nil, chain) require.NoError(t, err2) - t.Log("config", s) + assert.Equal(t, common.LeftPadBytes(state.Chains[chain].OnRamp.Address().Bytes(), 32), s.OnRamp) } // Now that the proposal has been executed we expect to be able to send traffic to this new 4th chain. diff --git a/integration-tests/deployment/ccip/changeset/2_initial_deploy.go b/integration-tests/deployment/ccip/changeset/2_initial_deploy.go index 8d0c90467f6..22986906c94 100644 --- a/integration-tests/deployment/ccip/changeset/2_initial_deploy.go +++ b/integration-tests/deployment/ccip/changeset/2_initial_deploy.go @@ -22,12 +22,16 @@ func Apply0002(env deployment.Environment, c ccipdeployment.DeployCCIPContractCo if err != nil { return deployment.ChangesetOutput{}, err } - proposal, err := ccipdeployment.GenerateAcceptOwnershipProposal(env, env.AllChainSelectors(), ab) - if err != nil { - return deployment.ChangesetOutput{}, err - } + //state, err := ccipdeployment.LoadOnchainState(env, ab) + //if err != nil { + // return deployment.ChangesetOutput{}, err + //} + //proposal, err := ccipdeployment.GenerateAcceptOwnershipProposal(state, c.HomeChainSel, c.ChainsToDeploy) + //if err != nil { + // return deployment.ChangesetOutput{}, err + //} return deployment.ChangesetOutput{ - Proposals: []timelock.MCMSWithTimelockProposal{proposal}, + Proposals: []timelock.MCMSWithTimelockProposal{}, AddressBook: ab, // Mapping of which nodes get which jobs. JobSpecs: js, diff --git a/integration-tests/deployment/ccip/deploy.go b/integration-tests/deployment/ccip/deploy.go index 22ebfe84e9b..f737bf94b96 100644 --- a/integration-tests/deployment/ccip/deploy.go +++ b/integration-tests/deployment/ccip/deploy.go @@ -284,7 +284,7 @@ func DeployChainContracts( return ab, err } - _, err = deployContract(e.Logger, chain, ab, + timelock, err := deployContract(e.Logger, chain, ab, func(chain deployment.Chain) ContractDeploy[*owner_helpers.RBACTimelock] { timelock, tx, cc, err2 := owner_helpers.DeployRBACTimelock( chain.DeployerKey, @@ -437,7 +437,7 @@ func DeployChainContracts( LinkToken: linkToken.Address, StalenessThreshold: uint32(24 * 60 * 60), }, - []common.Address{}, // ramps added after + []common.Address{timelock.Address}, // timelock should be able to update, ramps added after []common.Address{weth9.Address, linkToken.Address}, // fee tokens []fee_quoter.FeeQuoterTokenPriceFeedUpdate{}, []fee_quoter.FeeQuoterTokenTransferFeeConfigArgs{}, // TODO: tokens From 41b314e7278cd3fdfb7c4fe1a4d089a58fafef4f Mon Sep 17 00:00:00 2001 From: connorwstein Date: Thu, 12 Sep 2024 11:02:39 -0400 Subject: [PATCH 08/17] Enable to new chain traffic working --- .../deployment/ccip/add_chain.go | 27 ++++++- .../deployment/ccip/add_chain_test.go | 70 +++++++++++++------ .../deployment/ccip/add_lane_test.go | 2 +- .../ccip/changeset/2_initial_deploy_test.go | 2 +- .../deployment/ccip/deploy_home_chain.go | 59 ++++++++++++++-- integration-tests/deployment/ccip/state.go | 18 +++++ .../deployment/ccip/test_helpers.go | 10 ++- integration-tests/deployment/environment.go | 4 ++ 8 files changed, 159 insertions(+), 33 deletions(-) diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index c43c2ff8957..f3935d89c13 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -7,7 +7,10 @@ import ( "github.com/smartcontractkit/ccip-owner-contracts/tools/proposal/timelock" chainsel "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/chainlink-ccip/chainconfig" + "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" "github.com/smartcontractkit/chainlink/integration-tests/deployment" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" @@ -98,6 +101,23 @@ func NewChainInboundProposal( if err != nil { return nil, err } + encodedExtraChainConfig, err := chainconfig.EncodeChainConfig(chainconfig.ChainConfig{ + GasPriceDeviationPPB: ccipocr3.NewBigIntFromInt64(1000), + DAGasPriceDeviationPPB: ccipocr3.NewBigIntFromInt64(0), + FinalityDepth: 10, + OptimisticConfirmations: 1, + }) + if err != nil { + return nil, err + } + chainConfig := SetupConfigInfo(newChainSel, nodes.PeerIDs(newChainSel), nodes.DefaultF(), encodedExtraChainConfig) + addChain, err := state.Chains[homeChainSel].CCIPConfig.ApplyChainConfigUpdates(SimTransactOpts(), nil, []ccip_config.CCIPConfigTypesChainConfigInfo{ + chainConfig, + }) + if err != nil { + return nil, err + } + newDONArgs, err := BuildAddDONArgs(e.Logger, state.Chains[newChainSel].OffRamp, e.Chains[newChainSel], nodes) if err != nil { return nil, err @@ -124,7 +144,12 @@ func NewChainInboundProposal( ChainIdentifier: mcms.ChainIdentifier(homeChain.Selector), Batch: []mcms.Operation{ { - // Enable the source in on ramp + // Add the chain first, don needs it to be there. + To: state.Chains[homeChainSel].CCIPConfig.Address(), + Data: addChain.Data(), + Value: big.NewInt(0), + }, + { To: state.Chains[homeChainSel].CapabilityRegistry.Address(), Data: addDON.Data(), Value: big.NewInt(0), diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go index 98ae26f061f..d82d4eb4545 100644 --- a/integration-tests/deployment/ccip/add_chain_test.go +++ b/integration-tests/deployment/ccip/add_chain_test.go @@ -2,6 +2,7 @@ package ccipdeployment import ( "testing" + "time" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" @@ -9,12 +10,14 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/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" ) func TestAddChain(t *testing.T) { // 4 chains where the 4th is added after initial deployment. e := NewEnvironmentWithCRAndJobs(t, logger.TestLogger(t), 4) + require.Equal(t, len(e.Nodes), 5) state, err := LoadOnchainState(e.Env, e.Ab) require.NoError(t, err) // Take first non-home chain as the new chain. @@ -50,22 +53,8 @@ func TestAddChain(t *testing.T) { state, err = LoadOnchainState(e.Env, e.Ab) require.NoError(t, err) - // We can directly enable the sources on the new chain with deployer key. - var offRampEnables []offramp.OffRampSourceChainConfigArgs - for _, source := range initialDeploy { - offRampEnables = append(offRampEnables, offramp.OffRampSourceChainConfigArgs{ - Router: state.Chains[newChain].Router.Address(), - SourceChainSelector: source, - IsEnabled: true, - OnRamp: common.LeftPadBytes(state.Chains[source].OnRamp.Address().Bytes(), 32), - }) - } - tx, err := state.Chains[newChain].OffRamp.ApplySourceChainConfigUpdates(e.Env.Chains[newChain].DeployerKey, offRampEnables) - require.NoError(t, err) - _, err = deployment.ConfirmIfNoError(e.Env.Chains[newChain], tx, err) - require.NoError(t, err) - // Transfer onramp/fq ownership to timelock. + // Enable the new dest on the test router. for _, source := range initialDeploy { tx, err := state.Chains[source].OnRamp.TransferOwnership(e.Env.Chains[source].DeployerKey, state.Chains[source].TimelockAddr) require.NoError(t, err) @@ -75,9 +64,17 @@ func TestAddChain(t *testing.T) { require.NoError(t, err) _, err = deployment.ConfirmIfNoError(e.Env.Chains[source], tx, err) require.NoError(t, err) + tx, err = state.Chains[source].TestRouter.ApplyRampUpdates(e.Env.Chains[source].DeployerKey, []router.RouterOnRamp{ + { + DestChainSelector: newChain, + OnRamp: state.Chains[source].OnRamp.Address(), + }, + }, nil, nil) + _, err = deployment.ConfirmIfNoError(e.Env.Chains[source], tx, err) + require.NoError(t, err) } // Transfer CR contract ownership - tx, err = state.Chains[e.HomeChainSel].CapabilityRegistry.TransferOwnership(e.Env.Chains[e.HomeChainSel].DeployerKey, state.Chains[e.HomeChainSel].TimelockAddr) + tx, err := state.Chains[e.HomeChainSel].CapabilityRegistry.TransferOwnership(e.Env.Chains[e.HomeChainSel].DeployerKey, state.Chains[e.HomeChainSel].TimelockAddr) require.NoError(t, err) _, err = deployment.ConfirmIfNoError(e.Env.Chains[e.HomeChainSel], tx, err) require.NoError(t, err) @@ -96,14 +93,14 @@ func TestAddChain(t *testing.T) { for _, chain := range initialDeploy { owner, err2 := state.Chains[chain].OnRamp.Owner(nil) require.NoError(t, err2) - assert.Equal(t, state.Chains[chain].TimelockAddr, owner) + require.Equal(t, state.Chains[chain].TimelockAddr, owner) } cfgOwner, err := state.Chains[e.HomeChainSel].CCIPConfig.Owner(nil) require.NoError(t, err) crOwner, err := state.Chains[e.HomeChainSel].CapabilityRegistry.Owner(nil) require.NoError(t, err) - assert.Equal(t, state.Chains[e.HomeChainSel].TimelockAddr, cfgOwner) - assert.Equal(t, state.Chains[e.HomeChainSel].TimelockAddr, crOwner) + require.Equal(t, state.Chains[e.HomeChainSel].TimelockAddr, cfgOwner) + require.Equal(t, state.Chains[e.HomeChainSel].TimelockAddr, crOwner) // Generate and sign inbound proposal to new 4th chain. chainInboundProposal, err := NewChainInboundProposal(e.Env, state, e.HomeChainSel, newChain, initialDeploy) @@ -113,19 +110,48 @@ func TestAddChain(t *testing.T) { ExecuteProposal(t, e.Env, chainInboundExec, state, sel) } + // Now configure the new chain using deployer key (not transferred to timelock yet). + var offRampEnables []offramp.OffRampSourceChainConfigArgs + for _, source := range initialDeploy { + offRampEnables = append(offRampEnables, offramp.OffRampSourceChainConfigArgs{ + Router: state.Chains[newChain].Router.Address(), + SourceChainSelector: source, + IsEnabled: true, + OnRamp: common.LeftPadBytes(state.Chains[source].OnRamp.Address().Bytes(), 32), + }) + } + tx, err = state.Chains[newChain].OffRamp.ApplySourceChainConfigUpdates(e.Env.Chains[newChain].DeployerKey, offRampEnables) + require.NoError(t, err) + _, err = deployment.ConfirmIfNoError(e.Env.Chains[newChain], tx, err) + require.NoError(t, err) + // Set the OCR3 config on new 4th chain to enable the plugin. + latestDON, err := LatestCCIPDON(state.Chains[e.HomeChainSel].CapabilityRegistry) + require.NoError(t, err) + ocrConfigs, err := BuildSetOCR3ConfigArgs(latestDON.Id, state.Chains[e.HomeChainSel].CCIPConfig) + require.NoError(t, err) + tx, err = state.Chains[newChain].OffRamp.SetOCR3Configs(e.Env.Chains[newChain].DeployerKey, ocrConfigs) + require.NoError(t, err) + _, err = deployment.ConfirmIfNoError(e.Env.Chains[newChain], tx, err) + require.NoError(t, err) + state, err = LoadOnchainState(e.Env, e.Ab) require.NoError(t, err) for _, chain := range initialDeploy { cfg, err2 := state.Chains[chain].OnRamp.GetDestChainConfig(nil, newChain) require.NoError(t, err2) assert.Equal(t, cfg.Router, state.Chains[chain].TestRouter.Address()) + fqCfg, err2 := state.Chains[chain].FeeQuoter.GetDestChainConfig(nil, newChain) + require.NoError(t, err2) + assert.True(t, fqCfg.IsEnabled) s, err2 := state.Chains[newChain].OffRamp.GetSourceChainConfig(nil, chain) require.NoError(t, err2) assert.Equal(t, common.LeftPadBytes(state.Chains[chain].OnRamp.Address().Bytes(), 32), s.OnRamp) } + // Ensure job related logs are up to date. + time.Sleep(30 * time.Second) + require.NoError(t, ReplayAllLogs(e.Nodes, e.Env.Chains)) // Now that the proposal has been executed we expect to be able to send traffic to this new 4th chain. - //SendRequest(t, e.Env, state, initialDeploy[0], newChain) - //e.Env.initialDeploy[0] - + seqNr := SendRequest(t, e.Env, state, initialDeploy[0], newChain, true) + ConfirmExecution(t, e.Env.Chains[initialDeploy[0]], e.Env.Chains[newChain], state.Chains[newChain].OffRamp, seqNr) } diff --git a/integration-tests/deployment/ccip/add_lane_test.go b/integration-tests/deployment/ccip/add_lane_test.go index a0d5b9a09b6..77b82348e4a 100644 --- a/integration-tests/deployment/ccip/add_lane_test.go +++ b/integration-tests/deployment/ccip/add_lane_test.go @@ -48,7 +48,7 @@ func TestAddLane(t *testing.T) { require.Len(t, offRamps, 0) } } - seqNum := SendRequest(t, e.Env, state, from, to) + seqNum := SendRequest(t, e.Env, state, from, to, false) require.Equal(t, uint64(1), seqNum) ConfirmExecution(t, e.Env.Chains[from], e.Env.Chains[to], state.Chains[to].OffRamp, seqNum) diff --git a/integration-tests/deployment/ccip/changeset/2_initial_deploy_test.go b/integration-tests/deployment/ccip/changeset/2_initial_deploy_test.go index 09448695032..fa0fbb9141b 100644 --- a/integration-tests/deployment/ccip/changeset/2_initial_deploy_test.go +++ b/integration-tests/deployment/ccip/changeset/2_initial_deploy_test.go @@ -81,7 +81,7 @@ func Test0002_InitialDeploy(t *testing.T) { if src == dest { continue } - seqNum := ccipdeployment.SendRequest(t, e, state, src, dest) + seqNum := ccipdeployment.SendRequest(t, e, state, src, dest, false) expectedSeqNum[dest] = seqNum } } diff --git a/integration-tests/deployment/ccip/deploy_home_chain.go b/integration-tests/deployment/ccip/deploy_home_chain.go index 62b91a23a95..c046dd47150 100644 --- a/integration-tests/deployment/ccip/deploy_home_chain.go +++ b/integration-tests/deployment/ccip/deploy_home_chain.go @@ -173,7 +173,6 @@ func AddChainConfig( chainSelector uint64, p2pIDs [][32]byte, ) (ccip_config.CCIPConfigTypesChainConfigInfo, error) { - f := uint8(len(p2pIDs) / 3) // Need to sort, otherwise _checkIsValidUniqueSubset onChain will fail // First Add ChainConfig that includes all p2pIDs as readers encodedExtraChainConfig, err := chainconfig.EncodeChainConfig(chainconfig.ChainConfig{ @@ -185,11 +184,10 @@ func AddChainConfig( if err != nil { return ccip_config.CCIPConfigTypesChainConfigInfo{}, err } - chainConfig := SetupConfigInfo(chainSelector, p2pIDs, f, encodedExtraChainConfig) - inputConfig := []ccip_config.CCIPConfigTypesChainConfigInfo{ + chainConfig := SetupConfigInfo(chainSelector, p2pIDs, uint8(len(p2pIDs)/3), encodedExtraChainConfig) + tx, err := ccipConfig.ApplyChainConfigUpdates(h.DeployerKey, nil, []ccip_config.CCIPConfigTypesChainConfigInfo{ chainConfig, - } - tx, err := ccipConfig.ApplyChainConfigUpdates(h.DeployerKey, nil, inputConfig) + }) if _, err := deployment.ConfirmIfNoError(h, tx, err); err != nil { return ccip_config.CCIPConfigTypesChainConfigInfo{}, err } @@ -312,6 +310,57 @@ func BuildAddDONArgs( return encodedConfigs, nil } +func LatestCCIPDON(registry *capabilities_registry.CapabilitiesRegistry) (*capabilities_registry.CapabilitiesRegistryDONInfo, error) { + dons, err := registry.GetDONs(nil) + if err != nil { + return nil, err + } + var ccipDON capabilities_registry.CapabilitiesRegistryDONInfo + for _, don := range dons { + if len(don.CapabilityConfigurations) == 1 && + don.CapabilityConfigurations[0].CapabilityId == CCIPCapabilityId && + don.Id > ccipDON.Id { + ccipDON = don + } + } + return &ccipDON, nil +} + +func BuildSetOCR3ConfigArgs(donID uint32, + ccipConfig *ccip_config.CCIPConfig) ([]offramp.MultiOCR3BaseOCRConfigArgs, error) { + var offrampOCR3Configs []offramp.MultiOCR3BaseOCRConfigArgs + for _, pluginType := range []cctypes.PluginType{cctypes.PluginTypeCCIPCommit, cctypes.PluginTypeCCIPExec} { + ocrConfig, err2 := ccipConfig.GetOCRConfig(&bind.CallOpts{ + Context: context.Background(), + }, donID, uint8(pluginType)) + if err2 != nil { + return nil, err2 + } + if len(ocrConfig) != 1 { + return nil, errors.New("expected exactly one OCR3 config") + } + var signerAddresses []common.Address + for _, signer := range ocrConfig[0].Config.Signers { + signerAddresses = append(signerAddresses, common.BytesToAddress(signer)) + } + + var transmitterAddresses []common.Address + for _, transmitter := range ocrConfig[0].Config.Transmitters { + transmitterAddresses = append(transmitterAddresses, common.BytesToAddress(transmitter)) + } + + offrampOCR3Configs = append(offrampOCR3Configs, offramp.MultiOCR3BaseOCRConfigArgs{ + ConfigDigest: ocrConfig[0].ConfigDigest, + OcrPluginType: uint8(pluginType), + F: ocrConfig[0].Config.F, + IsSignatureVerificationEnabled: pluginType == cctypes.PluginTypeCCIPCommit, + Signers: signerAddresses, + Transmitters: transmitterAddresses, + }) + } + return offrampOCR3Configs, nil +} + func AddDON( lggr logger.Logger, capReg *capabilities_registry.CapabilitiesRegistry, diff --git a/integration-tests/deployment/ccip/state.go b/integration-tests/deployment/ccip/state.go index 2d905e9e3f5..21accc36de2 100644 --- a/integration-tests/deployment/ccip/state.go +++ b/integration-tests/deployment/ccip/state.go @@ -141,6 +141,24 @@ func (s CCIPOnChainState) Snapshot(chains []uint64) (CCIPSnapShot, error) { AuthorizedCallers: authorizedCallers, } } + if nm != nil { + authorizedCallers, err := nm.GetAllAuthorizedCallers(nil) + if err != nil { + return snapshot, err + } + tv, err := nm.TypeAndVersion(nil) + if err != nil { + return snapshot, err + } + c.NonceManager = NonceManagerView{ + Contract: Contract{ + TypeAndVersion: tv, + Address: nm.Address(), + }, + // TODO: these can be resolved using an address book + AuthorizedCallers: authorizedCallers, + } + } snapshot.Chains[chainName] = c } return snapshot, nil diff --git a/integration-tests/deployment/ccip/test_helpers.go b/integration-tests/deployment/ccip/test_helpers.go index ed1fdc56f35..3e8f9886f73 100644 --- a/integration-tests/deployment/ccip/test_helpers.go +++ b/integration-tests/deployment/ccip/test_helpers.go @@ -123,7 +123,7 @@ func ReplayAllLogs(nodes map[string]memory.Node, chains map[uint64]deployment.Ch return nil } -func SendRequest(t *testing.T, e deployment.Environment, state CCIPOnChainState, src, dest uint64) uint64 { +func SendRequest(t *testing.T, e deployment.Environment, state CCIPOnChainState, src, dest uint64, testRouter bool) uint64 { msg := router.ClientEVM2AnyMessage{ Receiver: common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32), Data: []byte("hello"), @@ -132,14 +132,18 @@ func SendRequest(t *testing.T, e deployment.Environment, state CCIPOnChainState, FeeToken: common.HexToAddress("0x0"), ExtraArgs: nil, // TODO: no extra args for now, falls back to default } - fee, err := state.Chains[src].Router.GetFee( + router := state.Chains[src].Router + if testRouter { + router = state.Chains[src].TestRouter + } + fee, err := router.GetFee( &bind.CallOpts{Context: context.Background()}, dest, msg) require.NoError(t, err, deployment.MaybeDataErr(err)) t.Logf("Sending CCIP request from chain selector %d to chain selector %d", src, dest) e.Chains[src].DeployerKey.Value = fee - tx, err := state.Chains[src].Router.CcipSend( + tx, err := router.CcipSend( e.Chains[src].DeployerKey, dest, msg) diff --git a/integration-tests/deployment/environment.go b/integration-tests/deployment/environment.go index d46dd9fe690..c0fe975a9d5 100644 --- a/integration-tests/deployment/environment.go +++ b/integration-tests/deployment/environment.go @@ -190,6 +190,10 @@ func NodeInfo(nodeIDs []string, oc OffchainClient) (Nodes, error) { } selToOCRConfig := make(map[uint64]OCRConfig) for _, chainConfig := range nodeChainConfigs.ChainConfigs { + //if chainConfig.Ocr2Config.IsBootstrap { + // // Bootstrap nodes are handled separately + // continue + //} if chainConfig.Chain.Type == nodev1.ChainType_CHAIN_TYPE_SOLANA { // Note supported for CCIP yet. continue From 4ab3826cab824337a84f67758a69487cdfa6d6ef Mon Sep 17 00:00:00 2001 From: connorwstein Date: Thu, 12 Sep 2024 12:24:05 -0400 Subject: [PATCH 09/17] Mod tidy --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- integration-tests/load/go.mod | 2 +- integration-tests/load/go.sum | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 1a19bf69273..039c58543b2 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -269,7 +269,7 @@ require ( github.com/sethvargo/go-retry v0.2.4 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shirou/gopsutil/v3 v3.24.3 // indirect - github.com/smartcontractkit/chain-selectors v1.0.21 // indirect + github.com/smartcontractkit/chain-selectors v1.0.23 // indirect github.com/smartcontractkit/chainlink-ccip v0.0.0-20240909152240-a6969c1002e6 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45 // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240904093355-e40169857652 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 5644fafbb2f..6e37c918dde 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1184,8 +1184,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartcontractkit/chain-selectors v1.0.21 h1:KCR9SA7PhOexaBzFieHoLv1WonwhVOPtOStpqTmLC4E= -github.com/smartcontractkit/chain-selectors v1.0.21/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= +github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnjjIQAEBnutCtksPzVDY= +github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= github.com/smartcontractkit/chainlink-ccip v0.0.0-20240909152240-a6969c1002e6 h1:3VbeaqoBblboQ3ytpM7UZzL7MXoHctaaXGGkJ8XkDhY= diff --git a/go.mod b/go.mod index da25f470f06..9ef5b02de3d 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/scylladb/go-reflectx v1.0.1 github.com/shirou/gopsutil/v3 v3.24.3 github.com/shopspring/decimal v1.4.0 - github.com/smartcontractkit/chain-selectors v1.0.21 + github.com/smartcontractkit/chain-selectors v1.0.23 github.com/smartcontractkit/chainlink-automation v1.0.4 github.com/smartcontractkit/chainlink-ccip v0.0.0-20240909152240-a6969c1002e6 github.com/smartcontractkit/chainlink-common v0.2.2-0.20240909141252-663388d38293 diff --git a/go.sum b/go.sum index 748b5fb1e94..f9df795f736 100644 --- a/go.sum +++ b/go.sum @@ -1141,8 +1141,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartcontractkit/chain-selectors v1.0.21 h1:KCR9SA7PhOexaBzFieHoLv1WonwhVOPtOStpqTmLC4E= -github.com/smartcontractkit/chain-selectors v1.0.21/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= +github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnjjIQAEBnutCtksPzVDY= +github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= github.com/smartcontractkit/chainlink-ccip v0.0.0-20240909152240-a6969c1002e6 h1:3VbeaqoBblboQ3ytpM7UZzL7MXoHctaaXGGkJ8XkDhY= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index dd75fb2df9b..58fe447bdac 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -33,7 +33,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/shopspring/decimal v1.4.0 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240905142116-8eb0d5b8f98d + github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240910151738-3f318badcfb5 github.com/smartcontractkit/chain-selectors v1.0.23 github.com/smartcontractkit/chainlink-automation v1.0.4 github.com/smartcontractkit/chainlink-ccip v0.0.0-20240909152240-a6969c1002e6 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 7fb32cd9f23..29bcc342382 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1417,8 +1417,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ= github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= -github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240905142116-8eb0d5b8f98d h1:NWk2gQpK5EGrjqUFGxJPQ3/Opl8S/tkbLoxdwx8LKm8= -github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240905142116-8eb0d5b8f98d/go.mod h1:N60/wwocvZ5A3RGmYaMWo0fPFa5tTMlhI9lJ22DRktM= +github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240910151738-3f318badcfb5 h1:m0HuGuVdRHqBBkHJpSR/QBV7gtLB+hFkXZQ9tEkjdzo= +github.com/smartcontractkit/ccip-owner-contracts v0.0.0-20240910151738-3f318badcfb5/go.mod h1:N60/wwocvZ5A3RGmYaMWo0fPFa5tTMlhI9lJ22DRktM= github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnjjIQAEBnutCtksPzVDY= github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 22c44365bcd..7fde15e5e49 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -395,7 +395,7 @@ require ( github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/smartcontractkit/chain-selectors v1.0.21 // indirect + github.com/smartcontractkit/chain-selectors v1.0.23 // indirect github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240904093355-e40169857652 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240909142234-299749c4c0b5 // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240904154226-abc1ed5c962a // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 07b77243402..7f1a725dbc2 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1389,8 +1389,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ= github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= -github.com/smartcontractkit/chain-selectors v1.0.21 h1:KCR9SA7PhOexaBzFieHoLv1WonwhVOPtOStpqTmLC4E= -github.com/smartcontractkit/chain-selectors v1.0.21/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= +github.com/smartcontractkit/chain-selectors v1.0.23 h1:D2Eaex4Cw/O7Lg3tX6WklOqnjjIQAEBnutCtksPzVDY= +github.com/smartcontractkit/chain-selectors v1.0.23/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= github.com/smartcontractkit/chainlink-ccip v0.0.0-20240909152240-a6969c1002e6 h1:3VbeaqoBblboQ3ytpM7UZzL7MXoHctaaXGGkJ8XkDhY= From 186c7fcd85ec39783464475b400caffd1797be7f Mon Sep 17 00:00:00 2001 From: connorwstein Date: Thu, 12 Sep 2024 12:49:05 -0400 Subject: [PATCH 10/17] Fix build --- .../deployment/ccip/add_chain.go | 33 ------------------- .../deployment/ccip/add_chain_test.go | 7 ++-- .../ccip/changeset/2_initial_deploy.go | 8 ----- integration-tests/deployment/ccip/propose.go | 6 ++-- .../deployment/ccip/test_helpers.go | 2 +- 5 files changed, 7 insertions(+), 49 deletions(-) diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index f3935d89c13..af318be0729 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -104,7 +104,6 @@ func NewChainInboundProposal( encodedExtraChainConfig, err := chainconfig.EncodeChainConfig(chainconfig.ChainConfig{ GasPriceDeviationPPB: ccipocr3.NewBigIntFromInt64(1000), DAGasPriceDeviationPPB: ccipocr3.NewBigIntFromInt64(0), - FinalityDepth: 10, OptimisticConfirmations: 1, }) if err != nil { @@ -165,36 +164,4 @@ func NewChainInboundProposal( "blah", batches, timelock.Schedule, "0s") - // We won't actually be able to setOCR3Config on the remote until the first proposal goes through. - // TODO: Outbound } - -//func ApplyInboundChainProposal( -// e deployment.Environment, -// ab deployment.AddressBook, -// proposal managed.MCMSWithTimelockProposal, -//) (deployment.AddressBook, error) { -// state, err := LoadOnchainState(e, ab) -// if err != nil { -// return ab, err -// } -// -// // Apply the proposal. -//}) - -// 1. Deploy contracts -// 2. Proposal 1 (allow for inbound testing) -// - Enables new destination in onramps using test router -// - Enables the sources in the offramp and real router. -// - Sets initial prices for destination in price reg. -// - Add new DONs for destination to home chain -// - SetOCR3Config(s) on destination offramp. -// 3. At this point should be able to test from all sources -// and ensure that its writing those source prices to the new chain. -// 4. Proposal 2 (allow for outbound testing) -// - Add new destinations on onramp/price reg can use real router. -// No initial prices needed because DON updating them. -// - Add new sources to the remote offramps (test router). -// - Add ChainConfig to home chain so existing OCR instances become aware of the source. -// 5. Now we can test the other direction. -// 6 . Proposal 3 move onramp/offramps on existing chains to real router. diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go index d82d4eb4545..e55586136cb 100644 --- a/integration-tests/deployment/ccip/add_chain_test.go +++ b/integration-tests/deployment/ccip/add_chain_test.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" ) -func TestAddChain(t *testing.T) { +func TestAddChainInbound(t *testing.T) { // 4 chains where the 4th is added after initial deployment. e := NewEnvironmentWithCRAndJobs(t, logger.TestLogger(t), 4) require.Equal(t, len(e.Nodes), 5) @@ -24,7 +24,6 @@ func TestAddChain(t *testing.T) { newChain := e.Env.AllChainSelectorsExcluding([]uint64{e.HomeChainSel})[0] // We deploy to the rest. initialDeploy := e.Env.AllChainSelectorsExcluding([]uint64{newChain}) - t.Logf("Home %d new %d initial %d\n", e.HomeChainSel, newChain, initialDeploy) ab, err := DeployCCIPContracts(e.Env, DeployCCIPContractConfig{ HomeChainSel: e.HomeChainSel, @@ -36,8 +35,7 @@ func TestAddChain(t *testing.T) { state, err = LoadOnchainState(e.Env, e.Ab) require.NoError(t, err) - // Contracts deployed and initial DONs set up. - // Connect all the lanes + // Connect all the existing lanes. for _, source := range initialDeploy { for _, dest := range initialDeploy { if source != dest { @@ -134,6 +132,7 @@ func TestAddChain(t *testing.T) { _, err = deployment.ConfirmIfNoError(e.Env.Chains[newChain], tx, err) require.NoError(t, err) + // Assert the inbound lanes to the new chain are wired correctly. state, err = LoadOnchainState(e.Env, e.Ab) require.NoError(t, err) for _, chain := range initialDeploy { diff --git a/integration-tests/deployment/ccip/changeset/2_initial_deploy.go b/integration-tests/deployment/ccip/changeset/2_initial_deploy.go index 22986906c94..93c408bf45a 100644 --- a/integration-tests/deployment/ccip/changeset/2_initial_deploy.go +++ b/integration-tests/deployment/ccip/changeset/2_initial_deploy.go @@ -22,14 +22,6 @@ func Apply0002(env deployment.Environment, c ccipdeployment.DeployCCIPContractCo if err != nil { return deployment.ChangesetOutput{}, err } - //state, err := ccipdeployment.LoadOnchainState(env, ab) - //if err != nil { - // return deployment.ChangesetOutput{}, err - //} - //proposal, err := ccipdeployment.GenerateAcceptOwnershipProposal(state, c.HomeChainSel, c.ChainsToDeploy) - //if err != nil { - // return deployment.ChangesetOutput{}, err - //} return deployment.ChangesetOutput{ Proposals: []timelock.MCMSWithTimelockProposal{}, AddressBook: ab, diff --git a/integration-tests/deployment/ccip/propose.go b/integration-tests/deployment/ccip/propose.go index c465e9071f3..0e4592bee03 100644 --- a/integration-tests/deployment/ccip/propose.go +++ b/integration-tests/deployment/ccip/propose.go @@ -54,7 +54,7 @@ func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Ex // Set the root. tx, err2 := executor.SetRootOnChain(env.Chains[sel].DeployerKey, mcms.ChainIdentifier(sel)) require.NoError(t, err2) - _, err2 = env.Chains[sel].Confirm(tx.Hash()) + _, err2 = env.Chains[sel].Confirm(tx) require.NoError(t, err2) // Execute all the transactions in the proposal which are for this chain. @@ -63,7 +63,7 @@ func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Ex if bytes.Equal(op.Data, chainOp.Data) && op.To == chainOp.To { opTx, err3 := executor.ExecuteOnChain(env.Chains[sel].DeployerKey, idx) require.NoError(t, err3) - block, err3 := env.Chains[sel].Confirm(opTx.Hash()) + block, err3 := env.Chains[sel].Confirm(opTx) require.NoError(t, err3) t.Log("executed", chainOp) it, err3 := state.Chains[sel].Timelock.FilterCallScheduled(&bind.FilterOpts{ @@ -88,7 +88,7 @@ func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Ex tx, err := state.Chains[sel].Timelock.ExecuteBatch( env.Chains[sel].DeployerKey, calls, pred, salt) require.NoError(t, err) - _, err = env.Chains[sel].Confirm(tx.Hash()) + _, err = env.Chains[sel].Confirm(tx) require.NoError(t, err) } } diff --git a/integration-tests/deployment/ccip/test_helpers.go b/integration-tests/deployment/ccip/test_helpers.go index 3e8f9886f73..779b29a7496 100644 --- a/integration-tests/deployment/ccip/test_helpers.go +++ b/integration-tests/deployment/ccip/test_helpers.go @@ -148,7 +148,7 @@ func SendRequest(t *testing.T, e deployment.Environment, state CCIPOnChainState, dest, msg) require.NoError(t, err) - blockNum, err := e.Chains[src].Confirm(tx.Hash()) + blockNum, err := e.Chains[src].Confirm(tx) require.NoError(t, err) it, err := state.Chains[src].OnRamp.FilterCCIPMessageSent(&bind.FilterOpts{ Start: blockNum, From 554769f09b2f2f8e342c1f1fd445e2ad82a77957 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Thu, 12 Sep 2024 12:59:26 -0400 Subject: [PATCH 11/17] lint --- integration-tests/deployment/ccip/deploy.go | 8 ++++---- integration-tests/deployment/ccip/deploy_home_chain.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/integration-tests/deployment/ccip/deploy.go b/integration-tests/deployment/ccip/deploy.go index 533904158f8..28652faca97 100644 --- a/integration-tests/deployment/ccip/deploy.go +++ b/integration-tests/deployment/ccip/deploy.go @@ -261,7 +261,7 @@ func DeployChainContracts( e.Logger.Errorw("Failed to deploy mcm", "err", err) return ab, err } - // TODO: Address soon + // TODO: Parameterize this. e.Logger.Infow("deployed mcm", "addr", mcm.Address) publicKey := TestXXXMCMSSigner.Public().(*ecdsa.PublicKey) // Convert the public key to an Ethereum address @@ -272,14 +272,14 @@ func DeployChainContracts( return ab, err } groupQuorums, groupParents, signerAddresses, signerGroups := c.ExtractSetConfigInputs() - tx, err := mcm.Contract.SetConfig(chain.DeployerKey, + mcmsTx, err := mcm.Contract.SetConfig(chain.DeployerKey, signerAddresses, signerGroups, // Signer 1 is int group 0 (root group) with quorum 1. groupQuorums, groupParents, false, ) - if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { + if _, err := deployment.ConfirmIfNoError(chain, mcmsTx, err); err != nil { e.Logger.Errorw("Failed to confirm mcm config", "err", err) return ab, err } @@ -519,7 +519,7 @@ func DeployChainContracts( e.Logger.Infow("deployed offramp", "addr", offRamp) // Basic wiring is always needed. - tx, err = priceRegistry.Contract.ApplyAuthorizedCallerUpdates(chain.DeployerKey, fee_quoter.AuthorizedCallersAuthorizedCallerArgs{ + tx, err := priceRegistry.Contract.ApplyAuthorizedCallerUpdates(chain.DeployerKey, fee_quoter.AuthorizedCallersAuthorizedCallerArgs{ // TODO: We enable the deployer initially to set prices // Should be removed after. AddedCallers: []common.Address{offRamp.Contract.Address(), chain.DeployerKey.From}, diff --git a/integration-tests/deployment/ccip/deploy_home_chain.go b/integration-tests/deployment/ccip/deploy_home_chain.go index f73ce15279f..b97c5fcb8e7 100644 --- a/integration-tests/deployment/ccip/deploy_home_chain.go +++ b/integration-tests/deployment/ccip/deploy_home_chain.go @@ -55,7 +55,7 @@ const ( var ( // TODO: sort out why the mismatch here //CCIPCapabilityId [32]byte = utils.MustHash(hexutil.Encode(MustABIEncode(`[{"type": "string"}, {"type": "string"}]`, CapabilityLabelledName, CapabilityVersion))) - CCIPCapabilityId [32]byte = MustHashFromBytes(hexutil.MustDecode("0xe0da3c2b9005178f4731c9f40164f1933ad00bac9d6c13ad4ca1a8a763416380")) + CCIPCapabilityId = MustHashFromBytes(hexutil.MustDecode("0xe0da3c2b9005178f4731c9f40164f1933ad00bac9d6c13ad4ca1a8a763416380")) ) func MustHashFromBytes(b []byte) [32]byte { From 92052a3e4d98da427ee23d05ae231830561e14a6 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Thu, 12 Sep 2024 13:46:53 -0400 Subject: [PATCH 12/17] More lint --- integration-tests/deployment/ccip/propose.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/integration-tests/deployment/ccip/propose.go b/integration-tests/deployment/ccip/propose.go index 0e4592bee03..cc972abe9b9 100644 --- a/integration-tests/deployment/ccip/propose.go +++ b/integration-tests/deployment/ccip/propose.go @@ -44,6 +44,7 @@ func SignProposal(t *testing.T, env deployment.Environment, proposal *timelock.M sig, err := crypto.Sign(payload.Bytes(), TestXXXMCMSSigner) require.NoError(t, err) mcmSig, err := mcms.NewSignatureFromBytes(sig) + require.NoError(t, err) executor.Proposal.Signatures = append(executor.Proposal.Signatures, mcmSig) require.NoError(t, executor.Proposal.Validate()) return executor @@ -57,6 +58,7 @@ func ExecuteProposal(t *testing.T, env deployment.Environment, executor *mcms.Ex _, err2 = env.Chains[sel].Confirm(tx) require.NoError(t, err2) + // TODO: This sort of helper probably should move to the MCMS lib. // Execute all the transactions in the proposal which are for this chain. for _, chainOp := range executor.Operations[mcms.ChainIdentifier(sel)] { for idx, op := range executor.ChainAgnosticOps { From fe703f94392f9f51251a54acca20ca3ff34270f0 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Thu, 12 Sep 2024 14:00:59 -0400 Subject: [PATCH 13/17] Use Address() for MCM/Timelock --- integration-tests/deployment/ccip/add_chain.go | 16 +++++++++------- .../deployment/ccip/add_chain_test.go | 14 +++++++------- integration-tests/deployment/ccip/propose.go | 12 ++++++------ integration-tests/deployment/ccip/state.go | 5 +---- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index af318be0729..921583f310b 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -89,9 +89,9 @@ func NewChainInboundProposal( metaDataPerChain[mcms.ChainIdentifier(chain.Selector)] = timelock.MCMSWithTimelockChainMetadata{ ChainMetadata: mcms.ChainMetadata{ NonceOffset: 0, - MCMAddress: state.Chains[source].McmAddr, + MCMAddress: state.Chains[source].Mcm.Address(), }, - TimelockAddress: state.Chains[source].TimelockAddr, + TimelockAddress: state.Chains[source].Timelock.Address(), } } @@ -135,9 +135,9 @@ func NewChainInboundProposal( metaDataPerChain[mcms.ChainIdentifier(homeChain.Selector)] = timelock.MCMSWithTimelockChainMetadata{ ChainMetadata: mcms.ChainMetadata{ NonceOffset: 0, - MCMAddress: state.Chains[homeChainSel].McmAddr, + MCMAddress: state.Chains[homeChainSel].Mcm.Address(), }, - TimelockAddress: state.Chains[homeChainSel].TimelockAddr, + TimelockAddress: state.Chains[homeChainSel].Timelock.Address(), } batches = append(batches, timelock.BatchChainOperation{ ChainIdentifier: mcms.ChainIdentifier(homeChain.Selector), @@ -157,11 +157,13 @@ func NewChainInboundProposal( }) return timelock.NewMCMSWithTimelockProposal( "1", - 2004259681, + 2004259681, // TODO: should be parameterized and based on current block timestamp. []mcms.Signature{}, false, metaDataPerChain, - "blah", + "blah", // TODO batches, - timelock.Schedule, "0s") + timelock.Schedule, + "0s", // TODO: Should be parameterized. + ) } diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go index e55586136cb..15b1b266ce0 100644 --- a/integration-tests/deployment/ccip/add_chain_test.go +++ b/integration-tests/deployment/ccip/add_chain_test.go @@ -54,11 +54,11 @@ func TestAddChainInbound(t *testing.T) { // Transfer onramp/fq ownership to timelock. // Enable the new dest on the test router. for _, source := range initialDeploy { - tx, err := state.Chains[source].OnRamp.TransferOwnership(e.Env.Chains[source].DeployerKey, state.Chains[source].TimelockAddr) + tx, err := state.Chains[source].OnRamp.TransferOwnership(e.Env.Chains[source].DeployerKey, state.Chains[source].Timelock.Address()) require.NoError(t, err) _, err = deployment.ConfirmIfNoError(e.Env.Chains[source], tx, err) require.NoError(t, err) - tx, err = state.Chains[source].FeeQuoter.TransferOwnership(e.Env.Chains[source].DeployerKey, state.Chains[source].TimelockAddr) + tx, err = state.Chains[source].FeeQuoter.TransferOwnership(e.Env.Chains[source].DeployerKey, state.Chains[source].Timelock.Address()) require.NoError(t, err) _, err = deployment.ConfirmIfNoError(e.Env.Chains[source], tx, err) require.NoError(t, err) @@ -72,11 +72,11 @@ func TestAddChainInbound(t *testing.T) { require.NoError(t, err) } // Transfer CR contract ownership - tx, err := state.Chains[e.HomeChainSel].CapabilityRegistry.TransferOwnership(e.Env.Chains[e.HomeChainSel].DeployerKey, state.Chains[e.HomeChainSel].TimelockAddr) + tx, err := state.Chains[e.HomeChainSel].CapabilityRegistry.TransferOwnership(e.Env.Chains[e.HomeChainSel].DeployerKey, state.Chains[e.HomeChainSel].Timelock.Address()) require.NoError(t, err) _, err = deployment.ConfirmIfNoError(e.Env.Chains[e.HomeChainSel], tx, err) require.NoError(t, err) - tx, err = state.Chains[e.HomeChainSel].CCIPConfig.TransferOwnership(e.Env.Chains[e.HomeChainSel].DeployerKey, state.Chains[e.HomeChainSel].TimelockAddr) + tx, err = state.Chains[e.HomeChainSel].CCIPConfig.TransferOwnership(e.Env.Chains[e.HomeChainSel].DeployerKey, state.Chains[e.HomeChainSel].Timelock.Address()) require.NoError(t, err) _, err = deployment.ConfirmIfNoError(e.Env.Chains[e.HomeChainSel], tx, err) require.NoError(t, err) @@ -91,14 +91,14 @@ func TestAddChainInbound(t *testing.T) { for _, chain := range initialDeploy { owner, err2 := state.Chains[chain].OnRamp.Owner(nil) require.NoError(t, err2) - require.Equal(t, state.Chains[chain].TimelockAddr, owner) + require.Equal(t, state.Chains[chain].Timelock.Address(), owner) } cfgOwner, err := state.Chains[e.HomeChainSel].CCIPConfig.Owner(nil) require.NoError(t, err) crOwner, err := state.Chains[e.HomeChainSel].CapabilityRegistry.Owner(nil) require.NoError(t, err) - require.Equal(t, state.Chains[e.HomeChainSel].TimelockAddr, cfgOwner) - require.Equal(t, state.Chains[e.HomeChainSel].TimelockAddr, crOwner) + require.Equal(t, state.Chains[e.HomeChainSel].Timelock.Address(), cfgOwner) + require.Equal(t, state.Chains[e.HomeChainSel].Timelock.Address(), crOwner) // Generate and sign inbound proposal to new 4th chain. chainInboundProposal, err := NewChainInboundProposal(e.Env, state, e.HomeChainSel, newChain, initialDeploy) diff --git a/integration-tests/deployment/ccip/propose.go b/integration-tests/deployment/ccip/propose.go index cc972abe9b9..eced93dde0f 100644 --- a/integration-tests/deployment/ccip/propose.go +++ b/integration-tests/deployment/ccip/propose.go @@ -119,9 +119,9 @@ func GenerateAcceptOwnershipProposal( metaDataPerChain[chainSel] = timelock.MCMSWithTimelockChainMetadata{ ChainMetadata: mcms.ChainMetadata{ NonceOffset: 0, - MCMAddress: state.Chains[sel].McmAddr, + MCMAddress: state.Chains[sel].Mcm.Address(), }, - TimelockAddress: state.Chains[sel].TimelockAddr, + TimelockAddress: state.Chains[sel].Timelock.Address(), } batches = append(batches, timelock.BatchChainOperation{ ChainIdentifier: chainSel, @@ -151,9 +151,9 @@ func GenerateAcceptOwnershipProposal( metaDataPerChain[homeChainID] = timelock.MCMSWithTimelockChainMetadata{ ChainMetadata: mcms.ChainMetadata{ NonceOffset: 0, - MCMAddress: state.Chains[homeChain].McmAddr, + MCMAddress: state.Chains[homeChain].Mcm.Address(), }, - TimelockAddress: state.Chains[homeChain].TimelockAddr, + TimelockAddress: state.Chains[homeChain].Timelock.Address(), } batches = append(batches, timelock.BatchChainOperation{ ChainIdentifier: homeChainID, @@ -172,11 +172,11 @@ func GenerateAcceptOwnershipProposal( }) return timelock.NewMCMSWithTimelockProposal( "1", - 2004259681, + 2004259681, // TODO []mcms.Signature{}, false, metaDataPerChain, - "blah", + "blah", // TODO batches, timelock.Schedule, "0s") } diff --git a/integration-tests/deployment/ccip/state.go b/integration-tests/deployment/ccip/state.go index 21accc36de2..8abdbeb3a5e 100644 --- a/integration-tests/deployment/ccip/state.go +++ b/integration-tests/deployment/ccip/state.go @@ -42,9 +42,7 @@ type CCIPChainState struct { CapabilityRegistry *capabilities_registry.CapabilitiesRegistry CCIPConfig *ccip_config.CCIPConfig Mcm *owner_wrappers.ManyChainMultiSig - McmAddr common.Address Timelock *owner_wrappers.RBACTimelock - TimelockAddr common.Address // Test contracts Receiver *maybe_revert_message_receiver.MaybeRevertMessageReceiver @@ -202,14 +200,13 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type return state, err } state.Timelock = tl - state.TimelockAddr = common.HexToAddress(address) + tl.Address() case deployment.NewTypeAndVersion(ManyChainMultisig, deployment.Version1_0_0).String(): mcms, err := owner_wrappers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) if err != nil { return state, err } state.Mcm = mcms - state.McmAddr = common.HexToAddress(address) case deployment.NewTypeAndVersion(CapabilitiesRegistry, deployment.Version1_0_0).String(): cr, err := capabilities_registry.NewCapabilitiesRegistry(common.HexToAddress(address), chain.Client) if err != nil { From 4be4c6edfde92310b23f7e6beb71a75e5dc4317c Mon Sep 17 00:00:00 2001 From: connorwstein Date: Thu, 12 Sep 2024 14:22:42 -0400 Subject: [PATCH 14/17] Rename --- integration-tests/deployment/ccip/add_chain.go | 4 ++-- integration-tests/deployment/ccip/add_lane.go | 2 +- integration-tests/deployment/ccip/deploy.go | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index 921583f310b..2d08bf2d320 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -39,7 +39,7 @@ func NewChainInboundProposal( if err != nil { return nil, err } - enablePriceRegDest, err := state.Chains[source].FeeQuoter.ApplyDestChainConfigUpdates( + enableFeeQuoterDest, err := state.Chains[source].FeeQuoter.ApplyDestChainConfigUpdates( SimTransactOpts(), []fee_quoter.FeeQuoterDestChainConfigArgs{ { @@ -81,7 +81,7 @@ func NewChainInboundProposal( }, { To: state.Chains[source].FeeQuoter.Address(), - Data: enablePriceRegDest.Data(), + Data: enableFeeQuoterDest.Data(), Value: big.NewInt(0), }, }, diff --git a/integration-tests/deployment/ccip/add_lane.go b/integration-tests/deployment/ccip/add_lane.go index e7c155161f2..0e7c01405b1 100644 --- a/integration-tests/deployment/ccip/add_lane.go +++ b/integration-tests/deployment/ccip/add_lane.go @@ -58,7 +58,7 @@ func AddLane(e deployment.Environment, state CCIPOnChainState, from, to uint64) return err } - // Enable dest in price registry + // Enable dest in fee quoter tx, err = state.Chains[from].FeeQuoter.ApplyDestChainConfigUpdates(e.Chains[from].DeployerKey, []fee_quoter.FeeQuoterDestChainConfigArgs{ { diff --git a/integration-tests/deployment/ccip/deploy.go b/integration-tests/deployment/ccip/deploy.go index 28652faca97..5d757fd52a2 100644 --- a/integration-tests/deployment/ccip/deploy.go +++ b/integration-tests/deployment/ccip/deploy.go @@ -427,7 +427,7 @@ func DeployChainContracts( return ab, err } - priceRegistry, err := deployContract(e.Logger, chain, ab, + feeQuoter, err := deployContract(e.Logger, chain, ab, func(chain deployment.Chain) ContractDeploy[*fee_quoter.FeeQuoter] { prAddr, tx, pr, err2 := fee_quoter.DeployFeeQuoter( chain.DeployerKey, @@ -458,7 +458,7 @@ func DeployChainContracts( } }) if err != nil { - e.Logger.Errorw("Failed to deploy price registry", "err", err) + e.Logger.Errorw("Failed to deploy fee quoter", "err", err) return ab, err } @@ -474,7 +474,7 @@ func DeployChainContracts( TokenAdminRegistry: tokenAdminRegistry.Address, }, onramp.OnRampDynamicConfig{ - FeeQuoter: priceRegistry.Address, + FeeQuoter: feeQuoter.Address, FeeAggregator: common.HexToAddress("0x1"), // TODO real fee aggregator }, []onramp.OnRampDestChainConfigArgs{}, @@ -501,7 +501,7 @@ func DeployChainContracts( TokenAdminRegistry: tokenAdminRegistry.Address, }, offramp.OffRampDynamicConfig{ - FeeQuoter: priceRegistry.Address, + FeeQuoter: feeQuoter.Address, PermissionLessExecutionThresholdSeconds: uint32(86400), MaxTokenTransferGas: uint32(200_000), MaxPoolReleaseOrMintGas: uint32(200_000), @@ -519,13 +519,13 @@ func DeployChainContracts( e.Logger.Infow("deployed offramp", "addr", offRamp) // Basic wiring is always needed. - tx, err := priceRegistry.Contract.ApplyAuthorizedCallerUpdates(chain.DeployerKey, fee_quoter.AuthorizedCallersAuthorizedCallerArgs{ + tx, err := feeQuoter.Contract.ApplyAuthorizedCallerUpdates(chain.DeployerKey, fee_quoter.AuthorizedCallersAuthorizedCallerArgs{ // TODO: We enable the deployer initially to set prices // Should be removed after. AddedCallers: []common.Address{offRamp.Contract.Address(), chain.DeployerKey.From}, }) if _, err := deployment.ConfirmIfNoError(chain, tx, err); err != nil { - e.Logger.Errorw("Failed to confirm price registry authorized caller update", "err", err) + e.Logger.Errorw("Failed to confirm fee quoter authorized caller update", "err", err) return ab, err } From 300676cc85721eda1931c95bab87b3e1738fa56a Mon Sep 17 00:00:00 2001 From: connorwstein Date: Thu, 12 Sep 2024 14:42:13 -0400 Subject: [PATCH 15/17] Self review cleanup --- .../deployment/ccip/deploy_home_chain.go | 62 +++---------------- integration-tests/deployment/ccip/state.go | 1 - integration-tests/deployment/environment.go | 4 -- 3 files changed, 10 insertions(+), 57 deletions(-) diff --git a/integration-tests/deployment/ccip/deploy_home_chain.go b/integration-tests/deployment/ccip/deploy_home_chain.go index b97c5fcb8e7..2dacbc147b1 100644 --- a/integration-tests/deployment/ccip/deploy_home_chain.go +++ b/integration-tests/deployment/ccip/deploy_home_chain.go @@ -194,14 +194,12 @@ func AddChainConfig( return chainConfig, nil } -// AddDONToCR adds a DON with the given nodes to the capabilities registry. func BuildAddDONArgs( lggr logger.Logger, offRamp *offramp.OffRamp, dest deployment.Chain, nodes deployment.Nodes, ) ([]byte, error) { - //bootstrapP2PID := nodes.BootstrapPeerIDs(dest.Selector)[0] p2pIDs := nodes.PeerIDs(dest.Selector) // Get OCR3 Config from helper var schedule []int @@ -325,8 +323,10 @@ func LatestCCIPDON(registry *capabilities_registry.CapabilitiesRegistry) (*capab return &ccipDON, nil } -func BuildSetOCR3ConfigArgs(donID uint32, - ccipConfig *ccip_config.CCIPConfig) ([]offramp.MultiOCR3BaseOCRConfigArgs, error) { +func BuildSetOCR3ConfigArgs( + donID uint32, + ccipConfig *ccip_config.CCIPConfig, +) ([]offramp.MultiOCR3BaseOCRConfigArgs, error) { var offrampOCR3Configs []offramp.MultiOCR3BaseOCRConfigArgs for _, pluginType := range []cctypes.PluginType{cctypes.PluginTypeCCIPCommit, cctypes.PluginTypeCCIPExec} { ocrConfig, err2 := ccipConfig.GetOCRConfig(&bind.CallOpts{ @@ -379,54 +379,16 @@ func AddDON( Config: encodedConfigs, }, }, false, false, nodes.DefaultF()) - blockConfirmed, err := deployment.ConfirmIfNoError(home, tx, err) - if err != nil { + if _, err := deployment.ConfirmIfNoError(home, tx, err); err != nil { return err } - iter, err := capReg.FilterConfigSet(&bind.FilterOpts{ - Start: blockConfirmed, - End: &blockConfirmed, - }, nil) + don, err := LatestCCIPDON(capReg) if err != nil { return err } - var donID uint32 - for iter.Next() { - donID = iter.Event.DonId - break - } - if donID == 0 { - return errors.New("failed to get donID") - } - var offrampOCR3Configs []offramp.MultiOCR3BaseOCRConfigArgs - for _, pluginType := range []cctypes.PluginType{cctypes.PluginTypeCCIPCommit, cctypes.PluginTypeCCIPExec} { - ocrConfig, err2 := ccipConfig.GetOCRConfig(&bind.CallOpts{ - Context: context.Background(), - }, donID, uint8(pluginType)) - if err2 != nil { - return err2 - } - if len(ocrConfig) != 1 { - return errors.New("expected exactly one OCR3 config") - } - var signerAddresses []common.Address - for _, signer := range ocrConfig[0].Config.Signers { - signerAddresses = append(signerAddresses, common.BytesToAddress(signer)) - } - - var transmitterAddresses []common.Address - for _, transmitter := range ocrConfig[0].Config.Transmitters { - transmitterAddresses = append(transmitterAddresses, common.BytesToAddress(transmitter)) - } - - offrampOCR3Configs = append(offrampOCR3Configs, offramp.MultiOCR3BaseOCRConfigArgs{ - ConfigDigest: ocrConfig[0].ConfigDigest, - OcrPluginType: uint8(pluginType), - F: ocrConfig[0].Config.F, - IsSignatureVerificationEnabled: pluginType == cctypes.PluginTypeCCIPCommit, - Signers: signerAddresses, - Transmitters: transmitterAddresses, - }) + offrampOCR3Configs, err := BuildSetOCR3ConfigArgs(don.Id, ccipConfig) + if err != nil { + return err } tx, err = offRamp.SetOCR3Configs(dest.DeployerKey, offrampOCR3Configs) @@ -439,8 +401,7 @@ func AddDON( Context: context.Background(), }, uint8(pluginType)) if err != nil { - //return err - return deployment.MaybeDataErr(err) + return err } // TODO: assertions to be done as part of full state // resprentation validation CCIP-3047 @@ -460,9 +421,6 @@ func AddDON( return fmt.Errorf("%s OCR3 config signer mismatch", pluginType.String()) } } - //if offrampOCR3Configs[pluginType].Signers != ocrConfig.Signers { - // return fmt.Errorf("%s OCR3 config signers mismatch", pluginType.String()) - //} } for i, transmitter := range offrampOCR3Configs[pluginType].Transmitters { if !bytes.Equal(transmitter.Bytes(), ocrConfig.Transmitters[i].Bytes()) { diff --git a/integration-tests/deployment/ccip/state.go b/integration-tests/deployment/ccip/state.go index 8abdbeb3a5e..50ed396d9d8 100644 --- a/integration-tests/deployment/ccip/state.go +++ b/integration-tests/deployment/ccip/state.go @@ -200,7 +200,6 @@ func LoadChainState(chain deployment.Chain, addresses map[string]deployment.Type return state, err } state.Timelock = tl - tl.Address() case deployment.NewTypeAndVersion(ManyChainMultisig, deployment.Version1_0_0).String(): mcms, err := owner_wrappers.NewManyChainMultiSig(common.HexToAddress(address), chain.Client) if err != nil { diff --git a/integration-tests/deployment/environment.go b/integration-tests/deployment/environment.go index e6fd144d319..b26eff3ea40 100644 --- a/integration-tests/deployment/environment.go +++ b/integration-tests/deployment/environment.go @@ -188,10 +188,6 @@ func NodeInfo(nodeIDs []string, oc OffchainClient) (Nodes, error) { } selToOCRConfig := make(map[uint64]OCRConfig) for _, chainConfig := range nodeChainConfigs.ChainConfigs { - //if chainConfig.Ocr2Config.IsBootstrap { - // // Bootstrap nodes are handled separately - // continue - //} if chainConfig.Chain.Type == nodev1.ChainType_CHAIN_TYPE_SOLANA { // Note supported for CCIP yet. continue From 0a638186a314656426e923aa0e4cc8b2d90d804b Mon Sep 17 00:00:00 2001 From: connorwstein Date: Thu, 12 Sep 2024 16:53:11 -0400 Subject: [PATCH 16/17] Clean up job management and bootstrap handling --- .../deployment/ccip/add_chain.go | 9 ++- .../deployment/ccip/add_chain_test.go | 1 + integration-tests/deployment/ccip/deploy.go | 6 +- .../deployment/ccip/deploy_home_chain.go | 4 +- integration-tests/deployment/ccip/jobs.go | 79 ++++++++---------- integration-tests/deployment/environment.go | 80 +++++++++++++------ 6 files changed, 101 insertions(+), 78 deletions(-) diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index 2d08bf2d320..b94a05c2ea5 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -109,7 +109,8 @@ func NewChainInboundProposal( if err != nil { return nil, err } - chainConfig := SetupConfigInfo(newChainSel, nodes.PeerIDs(newChainSel), nodes.DefaultF(), encodedExtraChainConfig) + chainConfig := SetupConfigInfo(newChainSel, nodes.NonBootstraps().PeerIDs(), + nodes.DefaultF(), encodedExtraChainConfig) addChain, err := state.Chains[homeChainSel].CCIPConfig.ApplyChainConfigUpdates(SimTransactOpts(), nil, []ccip_config.CCIPConfigTypesChainConfigInfo{ chainConfig, }) @@ -117,17 +118,17 @@ func NewChainInboundProposal( return nil, err } - newDONArgs, err := BuildAddDONArgs(e.Logger, state.Chains[newChainSel].OffRamp, e.Chains[newChainSel], nodes) + newDONArgs, err := BuildAddDONArgs(e.Logger, state.Chains[newChainSel].OffRamp, e.Chains[newChainSel], nodes.NonBootstraps()) if err != nil { return nil, err } addDON, err := state.Chains[homeChainSel].CapabilityRegistry.AddDON(SimTransactOpts(), - nodes.PeerIDs(newChainSel), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ + nodes.NonBootstraps().PeerIDs(), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ { CapabilityId: CCIPCapabilityId, Config: newDONArgs, }, - }, false, false, nodes.DefaultF()) + }, false, false, nodes.NonBootstraps().DefaultF()) if err != nil { return nil, err } diff --git a/integration-tests/deployment/ccip/add_chain_test.go b/integration-tests/deployment/ccip/add_chain_test.go index 15b1b266ce0..a484bda0f2d 100644 --- a/integration-tests/deployment/ccip/add_chain_test.go +++ b/integration-tests/deployment/ccip/add_chain_test.go @@ -150,6 +150,7 @@ func TestAddChainInbound(t *testing.T) { time.Sleep(30 * time.Second) require.NoError(t, ReplayAllLogs(e.Nodes, e.Env.Chains)) + // TODO: Send via all inbound lanes and use parallel helper // Now that the proposal has been executed we expect to be able to send traffic to this new 4th chain. seqNr := SendRequest(t, e.Env, state, initialDeploy[0], newChain, true) ConfirmExecution(t, e.Env.Chains[initialDeploy[0]], e.Env.Chains[newChain], state.Chains[newChain].OffRamp, seqNr) diff --git a/integration-tests/deployment/ccip/deploy.go b/integration-tests/deployment/ccip/deploy.go index 5d757fd52a2..9ffcc1e17e2 100644 --- a/integration-tests/deployment/ccip/deploy.go +++ b/integration-tests/deployment/ccip/deploy.go @@ -152,7 +152,7 @@ func DeployCCIPContracts(e deployment.Environment, c DeployCCIPContractConfig) ( if err := AddNodes( c.Chains[c.HomeChainSel].CapabilityRegistry, e.Chains[c.HomeChainSel], - nodes.PeerIDs(c.HomeChainSel), // Doesn't actually matter which sel here + nodes.NonBootstraps().PeerIDs(), ); err != nil { return ab, err } @@ -184,7 +184,7 @@ func DeployCCIPContracts(e deployment.Environment, c DeployCCIPContractConfig) ( e.Chains[c.HomeChainSel], c.Chains[c.HomeChainSel].CCIPConfig, chain.Selector, - nodes.PeerIDs(chain.Selector)) + nodes.NonBootstraps().PeerIDs()) if err != nil { return ab, err } @@ -197,7 +197,7 @@ func DeployCCIPContracts(e deployment.Environment, c DeployCCIPContractConfig) ( chainState.OffRamp, chain, e.Chains[c.HomeChainSel], - nodes, + nodes.NonBootstraps(), ); err != nil { e.Logger.Errorw("Failed to add DON", "err", err) return ab, err diff --git a/integration-tests/deployment/ccip/deploy_home_chain.go b/integration-tests/deployment/ccip/deploy_home_chain.go index 2dacbc147b1..8d8e132532e 100644 --- a/integration-tests/deployment/ccip/deploy_home_chain.go +++ b/integration-tests/deployment/ccip/deploy_home_chain.go @@ -200,7 +200,7 @@ func BuildAddDONArgs( dest deployment.Chain, nodes deployment.Nodes, ) ([]byte, error) { - p2pIDs := nodes.PeerIDs(dest.Selector) + p2pIDs := nodes.PeerIDs() // Get OCR3 Config from helper var schedule []int var oracles []confighelper2.OracleIdentityExtra @@ -373,7 +373,7 @@ func AddDON( if err != nil { return err } - tx, err := capReg.AddDON(home.DeployerKey, nodes.PeerIDs(dest.Selector), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ + tx, err := capReg.AddDON(home.DeployerKey, nodes.PeerIDs(), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ { CapabilityId: CCIPCapabilityId, Config: encodedConfigs, diff --git a/integration-tests/deployment/ccip/jobs.go b/integration-tests/deployment/ccip/jobs.go index 4ad3d29faae..49fb1e95422 100644 --- a/integration-tests/deployment/ccip/jobs.go +++ b/integration-tests/deployment/ccip/jobs.go @@ -1,11 +1,7 @@ package ccipdeployment import ( - "context" - "fmt" - "github.com/smartcontractkit/chainlink/integration-tests/deployment" - nodev1 "github.com/smartcontractkit/chainlink/integration-tests/deployment/jd/node/v1" "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/validate" "github.com/smartcontractkit/chainlink/v2/core/services/relay" ) @@ -13,58 +9,49 @@ import ( // In our case, the only address needed is the cap registry which is actually an env var. // and will pre-exist for our deployment. So the job specs only depend on the environment operators. func NewCCIPJobSpecs(nodeIds []string, oc deployment.OffchainClient) (map[string][]string, error) { + nodes, err := deployment.NodeInfo(nodeIds, oc) + if err != nil { + return nil, err + } // Generate a set of brand new job specs for CCIP for a specific environment // (including NOPs) and new addresses. // We want to assign one CCIP capability job to each node. And node with // an addr we'll list as bootstrapper. // Find the bootstrap nodes - bootstrapMp := make(map[string]struct{}) - for _, node := range nodeIds { - // TODO: Filter should accept multiple nodes - nodeChainConfigs, err := oc.ListNodeChainConfigs(context.Background(), &nodev1.ListNodeChainConfigsRequest{Filter: &nodev1.ListNodeChainConfigsRequest_Filter{ - NodeIds: []string{node}, - }}) - if err != nil { - return nil, err - } - for _, chainConfig := range nodeChainConfigs.ChainConfigs { - if chainConfig.Ocr2Config.IsBootstrap { - bootstrapMp[fmt.Sprintf("%s@%s", - // p2p_12D3... -> 12D3... - chainConfig.Ocr2Config.P2PKeyBundle.PeerId[4:], chainConfig.Ocr2Config.Multiaddr)] = struct{}{} - } - } - } - var bootstraps []string - for b := range bootstrapMp { - bootstraps = append(bootstraps, b) - } + nodesToJobSpecs := make(map[string][]string) - for _, node := range nodeIds { - // TODO: Filter should accept multiple. - nodeChainConfigs, err := oc.ListNodeChainConfigs(context.Background(), &nodev1.ListNodeChainConfigsRequest{Filter: &nodev1.ListNodeChainConfigsRequest_Filter{ - NodeIds: []string{node}, - }}) - if err != nil { - return nil, err + for _, node := range nodes { + var spec string + var err error + if !node.IsBootstrap { + spec, err = validate.NewCCIPSpecToml(validate.SpecArgs{ + P2PV2Bootstrappers: nodes.BootstrapLocators(), + CapabilityVersion: CapabilityVersion, + CapabilityLabelledName: CapabilityLabelledName, + OCRKeyBundleIDs: map[string]string{ + // TODO: Validate that that all EVM chains are using the same keybundle. + relay.NetworkEVM: node.FirstOCRKeybundle().KeyBundleID, + }, + P2PKeyID: node.PeerID.String(), + RelayConfigs: nil, + PluginConfig: map[string]any{}, + }) + } else { + spec, err = validate.NewCCIPSpecToml(validate.SpecArgs{ + P2PV2Bootstrappers: []string{}, // Intentionally empty for bootstraps. + CapabilityVersion: CapabilityVersion, + CapabilityLabelledName: CapabilityLabelledName, + OCRKeyBundleIDs: map[string]string{}, + // TODO: validate that all EVM chains are using the same keybundle + P2PKeyID: node.PeerID.String(), + RelayConfigs: nil, + PluginConfig: map[string]any{}, + }) } - spec, err := validate.NewCCIPSpecToml(validate.SpecArgs{ - P2PV2Bootstrappers: bootstraps, - CapabilityVersion: CapabilityVersion, - CapabilityLabelledName: CapabilityLabelledName, - OCRKeyBundleIDs: map[string]string{ - // TODO: Validate that that all EVM chains are using the same keybundle. - relay.NetworkEVM: nodeChainConfigs.ChainConfigs[0].Ocr2Config.OcrKeyBundle.BundleId, - }, - // TODO: validate that all EVM chains are using the same keybundle - P2PKeyID: nodeChainConfigs.ChainConfigs[0].Ocr2Config.P2PKeyBundle.PeerId, - RelayConfigs: nil, - PluginConfig: map[string]any{}, - }) if err != nil { return nil, err } - nodesToJobSpecs[node] = append(nodesToJobSpecs[node], spec) + nodesToJobSpecs[node.NodeID] = append(nodesToJobSpecs[node.NodeID], spec) } return nodesToJobSpecs, nil } diff --git a/integration-tests/deployment/environment.go b/integration-tests/deployment/environment.go index b26eff3ea40..692d7744b12 100644 --- a/integration-tests/deployment/environment.go +++ b/integration-tests/deployment/environment.go @@ -120,20 +120,17 @@ type OCRConfig struct { PeerID p2pkey.PeerID TransmitAccount types2.Account ConfigEncryptionPublicKey types3.ConfigEncryptionPublicKey - IsBootstrap bool - MultiAddr string // TODO: type + KeyBundleID string } +// Nodes includes is a group CL nodes. type Nodes []Node // PeerIDs returns peerIDs in a sorted list -func (n Nodes) PeerIDs(chainSel uint64) [][32]byte { +func (n Nodes) PeerIDs() [][32]byte { var peerIDs [][32]byte for _, node := range n { - cfg := node.SelToOCRConfig[chainSel] - // NOTE: Assume same peerID for all chains. - // Might make sense to change proto as peerID is 1-1 with node? - peerIDs = append(peerIDs, cfg.PeerID) + peerIDs = append(peerIDs, node.PeerID) } sort.Slice(peerIDs, func(i, j int) bool { return bytes.Compare(peerIDs[i][:], peerIDs[j][:]) < 0 @@ -141,29 +138,50 @@ func (n Nodes) PeerIDs(chainSel uint64) [][32]byte { return peerIDs } +func (n Nodes) NonBootstraps() Nodes { + var nonBootstraps Nodes + for _, node := range n { + if node.IsBootstrap { + continue + } + nonBootstraps = append(nonBootstraps, node) + } + return nonBootstraps +} + func (n Nodes) DefaultF() uint8 { return uint8(len(n) / 3) } -func (n Nodes) BootstrapPeerIDs(chainSel uint64) [][32]byte { - var peerIDs [][32]byte +func (n Nodes) BootstrapLocators() []string { + bootstrapMp := make(map[string]struct{}) for _, node := range n { - cfg := node.SelToOCRConfig[chainSel] - if !cfg.IsBootstrap { - continue + if node.IsBootstrap { + bootstrapMp[fmt.Sprintf("%s@%s", + // p2p_12D3... -> 12D3... + node.PeerID.String()[4:], node.MultiAddr)] = struct{}{} } - peerIDs = append(peerIDs, cfg.PeerID) } - return peerIDs + var locators []string + for b := range bootstrapMp { + locators = append(locators, b) + } + return locators } -// OffchainPublicKey types.OffchainPublicKey -// // For EVM-chains, this an *address*. -// OnchainPublicKey types.OnchainPublicKey -// PeerID string -// TransmitAccount types.Account type Node struct { + NodeID string SelToOCRConfig map[uint64]OCRConfig + PeerID p2pkey.PeerID + IsBootstrap bool + MultiAddr string +} + +func (n Node) FirstOCRKeybundle() OCRConfig { + for _, ocrConfig := range n.SelToOCRConfig { + return ocrConfig + } + return OCRConfig{} } func MustPeerIDFromString(s string) p2pkey.PeerID { @@ -178,20 +196,33 @@ func MustPeerIDFromString(s string) p2pkey.PeerID { // OCR config for example. func NodeInfo(nodeIDs []string, oc OffchainClient) (Nodes, error) { var nodes []Node - for _, node := range nodeIDs { + for _, nodeID := range nodeIDs { // TODO: Filter should accept multiple nodes nodeChainConfigs, err := oc.ListNodeChainConfigs(context.Background(), &nodev1.ListNodeChainConfigsRequest{Filter: &nodev1.ListNodeChainConfigsRequest_Filter{ - NodeIds: []string{node}, + NodeIds: []string{nodeID}, }}) if err != nil { return nil, err } selToOCRConfig := make(map[uint64]OCRConfig) + bootstrap := false + var peerID p2pkey.PeerID + var multiAddr string for _, chainConfig := range nodeChainConfigs.ChainConfigs { if chainConfig.Chain.Type == nodev1.ChainType_CHAIN_TYPE_SOLANA { // Note supported for CCIP yet. continue } + // NOTE: Assume same peerID/multiAddr for all chains. + // Might make sense to change proto as peerID/multiAddr is 1-1 with nodeID? + peerID = MustPeerIDFromString(chainConfig.Ocr2Config.P2PKeyBundle.PeerId) + multiAddr = chainConfig.Ocr2Config.Multiaddr + if chainConfig.Ocr2Config.IsBootstrap { + // NOTE: Assume same peerID for all chains. + // Might make sense to change proto as peerID is 1-1 with nodeID? + bootstrap = true + break + } evmChainID, err := strconv.Atoi(chainConfig.Chain.Id) if err != nil { return nil, err @@ -214,12 +245,15 @@ func NodeInfo(nodeIDs []string, oc OffchainClient) (Nodes, error) { PeerID: MustPeerIDFromString(chainConfig.Ocr2Config.P2PKeyBundle.PeerId), TransmitAccount: types2.Account(chainConfig.AccountAddress), ConfigEncryptionPublicKey: cpk, - IsBootstrap: chainConfig.Ocr2Config.IsBootstrap, - MultiAddr: chainConfig.Ocr2Config.Multiaddr, + KeyBundleID: chainConfig.Ocr2Config.OcrKeyBundle.BundleId, } } nodes = append(nodes, Node{ + NodeID: nodeID, SelToOCRConfig: selToOCRConfig, + IsBootstrap: bootstrap, + PeerID: peerID, + MultiAddr: multiAddr, }) } From 4ea8911e299eadd5814ca4ae6d98dcbb6597c248 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Fri, 13 Sep 2024 09:30:45 -0400 Subject: [PATCH 17/17] Comments --- .../deployment/ccip/add_chain.go | 2 +- integration-tests/deployment/ccip/deploy.go | 4 +-- .../deployment/ccip/deploy_home_chain.go | 25 ++++++++----------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/integration-tests/deployment/ccip/add_chain.go b/integration-tests/deployment/ccip/add_chain.go index b94a05c2ea5..bc997f0dc5e 100644 --- a/integration-tests/deployment/ccip/add_chain.go +++ b/integration-tests/deployment/ccip/add_chain.go @@ -125,7 +125,7 @@ func NewChainInboundProposal( addDON, err := state.Chains[homeChainSel].CapabilityRegistry.AddDON(SimTransactOpts(), nodes.NonBootstraps().PeerIDs(), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ { - CapabilityId: CCIPCapabilityId, + CapabilityId: CCIPCapabilityID, Config: newDONArgs, }, }, false, false, nodes.NonBootstraps().DefaultF()) diff --git a/integration-tests/deployment/ccip/deploy.go b/integration-tests/deployment/ccip/deploy.go index 9ffcc1e17e2..72ab5d7d6ee 100644 --- a/integration-tests/deployment/ccip/deploy.go +++ b/integration-tests/deployment/ccip/deploy.go @@ -145,8 +145,8 @@ func DeployCCIPContracts(e deployment.Environment, c DeployCCIPContractConfig) ( e.Logger.Errorw("Failed to get hashed capability id", "err", err) return ab, err } - if cr != CCIPCapabilityId { - return ab, fmt.Errorf("Capability registry does not support CCIP %s %s", hexutil.Encode(cr[:]), hexutil.Encode(CCIPCapabilityId[:])) + if cr != CCIPCapabilityID { + return ab, fmt.Errorf("Capability registry does not support CCIP %s %s", hexutil.Encode(cr[:]), hexutil.Encode(CCIPCapabilityID[:])) } // Signal to CR that our nodes support CCIP capability. if err := AddNodes( diff --git a/integration-tests/deployment/ccip/deploy_home_chain.go b/integration-tests/deployment/ccip/deploy_home_chain.go index 8d8e132532e..ec078e4a9db 100644 --- a/integration-tests/deployment/ccip/deploy_home_chain.go +++ b/integration-tests/deployment/ccip/deploy_home_chain.go @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" @@ -20,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" "github.com/smartcontractkit/chainlink/integration-tests/deployment" cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ocr3_config_encoder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" @@ -53,18 +53,15 @@ const ( ) var ( - // TODO: sort out why the mismatch here - //CCIPCapabilityId [32]byte = utils.MustHash(hexutil.Encode(MustABIEncode(`[{"type": "string"}, {"type": "string"}]`, CapabilityLabelledName, CapabilityVersion))) - CCIPCapabilityId = MustHashFromBytes(hexutil.MustDecode("0xe0da3c2b9005178f4731c9f40164f1933ad00bac9d6c13ad4ca1a8a763416380")) + CCIPCapabilityID = utils.Keccak256Fixed(MustABIEncode(`[{"type": "string"}, {"type": "string"}]`, CapabilityLabelledName, CapabilityVersion)) ) -func MustHashFromBytes(b []byte) [32]byte { - if len(b) != 32 { - panic("invalid length") +func MustABIEncode(abiString string, args ...interface{}) []byte { + encoded, err := utils.ABIEncode(abiString, args...) + if err != nil { + panic(err) } - var res [32]byte - copy(res[:], b) - return res + return encoded } func DeployCapReg(lggr logger.Logger, chains map[uint64]deployment.Chain, chainSel uint64) (deployment.AddressBook, common.Address, error) { @@ -136,14 +133,13 @@ func AddNodes( chain deployment.Chain, p2pIDs [][32]byte, ) error { - // Need to sort, otherwise _checkIsValidUniqueSubset onChain will fail var nodeParams []capabilities_registry.CapabilitiesRegistryNodeParams for _, p2pID := range p2pIDs { nodeParam := capabilities_registry.CapabilitiesRegistryNodeParams{ NodeOperatorId: NodeOperatorID, Signer: p2pID, // Not used in tests P2pId: p2pID, - HashedCapabilityIds: [][32]byte{CCIPCapabilityId}, + HashedCapabilityIds: [][32]byte{CCIPCapabilityID}, } nodeParams = append(nodeParams, nodeParam) } @@ -173,7 +169,6 @@ func AddChainConfig( chainSelector uint64, p2pIDs [][32]byte, ) (ccip_config.CCIPConfigTypesChainConfigInfo, error) { - // Need to sort, otherwise _checkIsValidUniqueSubset onChain will fail // First Add ChainConfig that includes all p2pIDs as readers encodedExtraChainConfig, err := chainconfig.EncodeChainConfig(chainconfig.ChainConfig{ GasPriceDeviationPPB: ccipocr3.NewBigIntFromInt64(1000), @@ -315,7 +310,7 @@ func LatestCCIPDON(registry *capabilities_registry.CapabilitiesRegistry) (*capab var ccipDON capabilities_registry.CapabilitiesRegistryDONInfo for _, don := range dons { if len(don.CapabilityConfigurations) == 1 && - don.CapabilityConfigurations[0].CapabilityId == CCIPCapabilityId && + don.CapabilityConfigurations[0].CapabilityId == CCIPCapabilityID && don.Id > ccipDON.Id { ccipDON = don } @@ -375,7 +370,7 @@ func AddDON( } tx, err := capReg.AddDON(home.DeployerKey, nodes.PeerIDs(), []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ { - CapabilityId: CCIPCapabilityId, + CapabilityId: CCIPCapabilityID, Config: encodedConfigs, }, }, false, false, nodes.DefaultF())