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 {