Skip to content

Commit

Permalink
Almost working. Just need ramps to make timelock owner first
Browse files Browse the repository at this point in the history
  • Loading branch information
connorwstein committed Sep 10, 2024
1 parent a5c48c3 commit 902155c
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 98 deletions.
131 changes: 50 additions & 81 deletions integration-tests/deployment/ccip/add_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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{
Expand All @@ -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{
Expand All @@ -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{
Expand All @@ -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{
Expand All @@ -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(
Expand Down
107 changes: 97 additions & 10 deletions integration-tests/deployment/ccip/add_chain_test.go
Original file line number Diff line number Diff line change
@@ -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"
)

Expand Down Expand Up @@ -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)
}
18 changes: 11 additions & 7 deletions integration-tests/deployment/ccip/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 902155c

Please sign in to comment.