Skip to content

Commit

Permalink
refactor update don capability (#15623)
Browse files Browse the repository at this point in the history
* refactor update & append capabilities

* update don changeset mcms

* update don with test

* cleanup dupes

* configurable MCMS; infered MCMS usage from it
  • Loading branch information
krehermann authored Dec 11, 2024
1 parent bb66cc2 commit d1caaa3
Show file tree
Hide file tree
Showing 17 changed files with 456 additions and 112 deletions.
6 changes: 3 additions & 3 deletions deployment/keystone/changeset/append_node_capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func AppendNodeCapabilities(env deployment.Environment, req *AppendNodeCapabilit
return deployment.ChangesetOutput{}, err
}
out := deployment.ChangesetOutput{}
if req.UseMCMS {
if req.UseMCMS() {
if r.Ops == nil {
return out, fmt.Errorf("expected MCMS operation to be non-nil")
}
Expand All @@ -45,7 +45,7 @@ func AppendNodeCapabilities(env deployment.Environment, req *AppendNodeCapabilit
proposerMCMSes,
[]timelock.BatchChainOperation{*r.Ops},
"proposal to set update node capabilities",
0,
req.MCMSConfig.MinDuration,
)
if err != nil {
return out, fmt.Errorf("failed to build proposal: %w", err)
Expand Down Expand Up @@ -76,6 +76,6 @@ func (req *AppendNodeCapabilitiesRequest) convert(e deployment.Environment) (*in
Chain: registryChain,
ContractSet: &contracts,
P2pToCapabilities: req.P2pToCapabilities,
UseMCMS: req.UseMCMS,
UseMCMS: req.UseMCMS(),
}, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func TestAppendNodeCapabilities(t *testing.T) {
cfg := changeset.AppendNodeCapabilitiesRequest{
RegistryChainSel: te.RegistrySelector,
P2pToCapabilities: newCapabilities,
UseMCMS: true,
MCMSConfig: &changeset.MCMSConfig{MinDuration: 0},
}

csOut, err := changeset.AppendNodeCapabilities(te.Env, &cfg)
Expand Down
53 changes: 48 additions & 5 deletions deployment/keystone/changeset/deploy_forwarder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ package changeset
import (
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils"
kslib "github.com/smartcontractkit/chainlink/deployment/keystone"
)

Expand Down Expand Up @@ -35,7 +39,8 @@ type ConfigureForwardContractsRequest struct {
WFNodeIDs []string
RegistryChainSel uint64

UseMCMS bool
// MCMSConfig is optional. If non-nil, the changes will be proposed using MCMS.
MCMSConfig *MCMSConfig
}

func (r ConfigureForwardContractsRequest) Validate() error {
Expand All @@ -45,6 +50,10 @@ func (r ConfigureForwardContractsRequest) Validate() error {
return nil
}

func (r ConfigureForwardContractsRequest) UseMCMS() bool {
return r.MCMSConfig != nil
}

func ConfigureForwardContracts(env deployment.Environment, req ConfigureForwardContractsRequest) (deployment.ChangesetOutput, error) {
wfDon, err := kslib.NewRegisteredDon(env, kslib.RegisteredDonConfig{
NodeIDs: req.WFNodeIDs,
Expand All @@ -56,12 +65,46 @@ func ConfigureForwardContracts(env deployment.Environment, req ConfigureForwardC
}
r, err := kslib.ConfigureForwardContracts(&env, kslib.ConfigureForwarderContractsRequest{
Dons: []kslib.RegisteredDon{*wfDon},
UseMCMS: req.UseMCMS,
UseMCMS: req.UseMCMS(),
})
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to configure forward contracts: %w", err)
}
return deployment.ChangesetOutput{
Proposals: r.Proposals,
}, nil

cresp, err := kslib.GetContractSets(env.Logger, &kslib.GetContractSetsRequest{
Chains: env.Chains,
AddressBook: env.ExistingAddresses,
})
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to get contract sets: %w", err)
}

var out deployment.ChangesetOutput
if req.UseMCMS() {
if len(r.OpsPerChain) == 0 {
return out, fmt.Errorf("expected MCMS operation to be non-nil")
}
for chainSelector, op := range r.OpsPerChain {
contracts := cresp.ContractSets[chainSelector]
timelocksPerChain := map[uint64]common.Address{
chainSelector: contracts.Timelock.Address(),
}
proposerMCMSes := map[uint64]*gethwrappers.ManyChainMultiSig{
chainSelector: contracts.ProposerMcm,
}

proposal, err := proposalutils.BuildProposalFromBatches(
timelocksPerChain,
proposerMCMSes,
[]timelock.BatchChainOperation{op},
"proposal to set update nodes",
req.MCMSConfig.MinDuration,
)
if err != nil {
return out, fmt.Errorf("failed to build proposal: %w", err)
}
out.Proposals = append(out.Proposals, *proposal)
}
}
return out, nil
}
2 changes: 1 addition & 1 deletion deployment/keystone/changeset/deploy_forwarder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func TestConfigureForwarders(t *testing.T) {
WFDonName: "test-wf-don",
WFNodeIDs: wfNodes,
RegistryChainSel: te.RegistrySelector,
UseMCMS: true,
MCMSConfig: &changeset.MCMSConfig{MinDuration: 0},
}
csOut, err := changeset.ConfigureForwardContracts(te.Env, cfg)
require.NoError(t, err)
Expand Down
51 changes: 43 additions & 8 deletions deployment/keystone/changeset/deploy_ocr3.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import (
"fmt"
"io"

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

"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils"
kslib "github.com/smartcontractkit/chainlink/deployment/keystone"
)

Expand Down Expand Up @@ -38,7 +41,12 @@ type ConfigureOCR3Config struct {
DryRun bool
WriteGeneratedConfig io.Writer // if not nil, write the generated config to this writer as JSON [OCR2OracleConfig]

UseMCMS bool
// MCMSConfig is optional. If non-nil, the changes will be proposed using MCMS.
MCMSConfig *MCMSConfig
}

func (cfg ConfigureOCR3Config) UseMCMS() bool {
return cfg.MCMSConfig != nil
}

func ConfigureOCR3Contract(env deployment.Environment, cfg ConfigureOCR3Config) (deployment.ChangesetOutput, error) {
Expand All @@ -47,7 +55,7 @@ func ConfigureOCR3Contract(env deployment.Environment, cfg ConfigureOCR3Config)
NodeIDs: cfg.NodeIDs,
OCR3Config: cfg.OCR3Config,
DryRun: cfg.DryRun,
UseMCMS: cfg.UseMCMS,
UseMCMS: cfg.UseMCMS(),
})
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to configure OCR3Capability: %w", err)
Expand All @@ -67,11 +75,38 @@ func ConfigureOCR3Contract(env deployment.Environment, cfg ConfigureOCR3Config)
}
}
// does not create any new addresses
var proposals []timelock.MCMSWithTimelockProposal
if cfg.UseMCMS {
proposals = append(proposals, *resp.Proposal)
var out deployment.ChangesetOutput
if cfg.UseMCMS() {
if resp.Ops == nil {
return out, fmt.Errorf("expected MCMS operation to be non-nil")
}
r, err := kslib.GetContractSets(env.Logger, &kslib.GetContractSetsRequest{
Chains: env.Chains,
AddressBook: env.ExistingAddresses,
})
if err != nil {
return out, fmt.Errorf("failed to get contract sets: %w", err)
}
contracts := r.ContractSets[cfg.ChainSel]
timelocksPerChain := map[uint64]common.Address{
cfg.ChainSel: contracts.Timelock.Address(),
}
proposerMCMSes := map[uint64]*gethwrappers.ManyChainMultiSig{
cfg.ChainSel: contracts.ProposerMcm,
}

proposal, err := proposalutils.BuildProposalFromBatches(
timelocksPerChain,
proposerMCMSes,
[]timelock.BatchChainOperation{*resp.Ops},
"proposal to set update nodes",
cfg.MCMSConfig.MinDuration,
)
if err != nil {
return out, fmt.Errorf("failed to build proposal: %w", err)
}
out.Proposals = []timelock.MCMSWithTimelockProposal{*proposal}

}
return deployment.ChangesetOutput{
Proposals: proposals,
}, nil
return out, nil
}
3 changes: 1 addition & 2 deletions deployment/keystone/changeset/deploy_ocr3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ func TestConfigureOCR3(t *testing.T) {
NodeIDs: wfNodes,
OCR3Config: &c,
WriteGeneratedConfig: w,
UseMCMS: false,
}

csOut, err := changeset.ConfigureOCR3Contract(te.Env, cfg)
Expand Down Expand Up @@ -104,7 +103,7 @@ func TestConfigureOCR3(t *testing.T) {
NodeIDs: wfNodes,
OCR3Config: &c,
WriteGeneratedConfig: w,
UseMCMS: true,
MCMSConfig: &changeset.MCMSConfig{MinDuration: 0},
}

csOut, err := changeset.ConfigureOCR3Contract(te.Env, cfg)
Expand Down
41 changes: 28 additions & 13 deletions deployment/keystone/changeset/internal/update_don.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
"sort"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/deployment"
Expand Down Expand Up @@ -36,7 +38,7 @@ type UpdateDonRequest struct {
UseMCMS bool
}

func (r *UpdateDonRequest) appendNodeCapabilitiesRequest() *AppendNodeCapabilitiesRequest {
func (r *UpdateDonRequest) AppendNodeCapabilitiesRequest() *AppendNodeCapabilitiesRequest {
out := &AppendNodeCapabilitiesRequest{
Chain: r.Chain,
ContractSet: r.ContractSet,
Expand Down Expand Up @@ -65,8 +67,8 @@ func (r *UpdateDonRequest) Validate() error {
}

type UpdateDonResponse struct {
DonInfo kcr.CapabilitiesRegistryDONInfo
Proposals []timelock.MCMSWithTimelockProposal
DonInfo kcr.CapabilitiesRegistryDONInfo
Ops *timelock.BatchChainOperation
}

func UpdateDon(lggr logger.Logger, req *UpdateDonRequest) (*UpdateDonResponse, error) {
Expand All @@ -89,24 +91,37 @@ func UpdateDon(lggr logger.Logger, req *UpdateDonRequest) (*UpdateDonResponse, e
return nil, fmt.Errorf("failed to compute configs: %w", err)
}

_, err = AppendNodeCapabilitiesImpl(lggr, req.appendNodeCapabilitiesRequest())
if err != nil {
return nil, fmt.Errorf("failed to append node capabilities: %w", err)
txOpts := req.Chain.DeployerKey
if req.UseMCMS {
txOpts = deployment.SimTransactOpts()
}

tx, err := registry.UpdateDON(req.Chain.DeployerKey, don.Id, don.NodeP2PIds, cfgs, don.IsPublic, don.F)
tx, err := registry.UpdateDON(txOpts, don.Id, don.NodeP2PIds, cfgs, don.IsPublic, don.F)
if err != nil {
err = kslib.DecodeErr(kcr.CapabilitiesRegistryABI, err)
return nil, fmt.Errorf("failed to call UpdateDON: %w", err)
}

_, err = req.Chain.Confirm(tx)
if err != nil {
return nil, fmt.Errorf("failed to confirm UpdateDON transaction %s: %w", tx.Hash().String(), err)
var ops *timelock.BatchChainOperation
if !req.UseMCMS {
_, err = req.Chain.Confirm(tx)
if err != nil {
return nil, fmt.Errorf("failed to confirm UpdateDON transaction %s: %w", tx.Hash().String(), err)
}
} else {
ops = &timelock.BatchChainOperation{
ChainIdentifier: mcms.ChainIdentifier(req.Chain.Selector),
Batch: []mcms.Operation{
{
To: registry.Address(),
Data: tx.Data(),
Value: big.NewInt(0),
},
},
}
}

out := don
out.CapabilityConfigurations = cfgs
return &UpdateDonResponse{DonInfo: out}, nil
return &UpdateDonResponse{DonInfo: out, Ops: ops}, nil
}

func PeerIDsToBytes(p2pIDs []p2pkey.PeerID) [][32]byte {
Expand Down
29 changes: 21 additions & 8 deletions deployment/keystone/changeset/internal/update_don_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@ func TestUpdateDon(t *testing.T) {
admin: admin_4,
})
// capabilities
cap_A = kcr.CapabilitiesRegistryCapability{
initialCap = kcr.CapabilitiesRegistryCapability{
LabelledName: "test",
Version: "1.0.0",
CapabilityType: 0,
}

cap_B = kcr.CapabilitiesRegistryCapability{
capToAdd = kcr.CapabilitiesRegistryCapability{
LabelledName: "cap b",
Version: "1.0.0",
CapabilityType: 1,
Expand All @@ -104,7 +104,7 @@ func TestUpdateDon(t *testing.T) {
{
Name: "don 1",
Nodes: []deployment.Node{node_1, node_2, node_3, node_4},
Capabilities: []kcr.CapabilitiesRegistryCapability{cap_A},
Capabilities: []kcr.CapabilitiesRegistryCapability{initialCap},
},
},
nops: []keystone.NOP{
Expand All @@ -115,14 +115,26 @@ func TestUpdateDon(t *testing.T) {
},
}

testCfg := setupUpdateDonTest(t, lggr, cfg)
testCfg := registerTestDon(t, lggr, cfg)
// add the new capabilities to registry
m := make(map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability)
for _, node := range cfg.dons[0].Nodes {
m[node.PeerID] = append(m[node.PeerID], capToAdd)
}

_, err := internal.AppendNodeCapabilitiesImpl(lggr, &internal.AppendNodeCapabilitiesRequest{
Chain: testCfg.Chain,
ContractSet: testCfg.ContractSet,
P2pToCapabilities: m,
})
require.NoError(t, err)

req := &internal.UpdateDonRequest{
ContractSet: testCfg.ContractSet,
Chain: testCfg.Chain,
P2PIDs: []p2pkey.PeerID{p2p_1.PeerID(), p2p_2.PeerID(), p2p_3.PeerID(), p2p_4.PeerID()},
CapabilityConfigs: []internal.CapabilityConfig{
{Capability: cap_A}, {Capability: cap_B},
{Capability: initialCap}, {Capability: capToAdd},
},
}
want := &internal.UpdateDonResponse{
Expand All @@ -131,8 +143,8 @@ func TestUpdateDon(t *testing.T) {
ConfigCount: 1,
NodeP2PIds: internal.PeerIDsToBytes([]p2pkey.PeerID{p2p_1.PeerID(), p2p_2.PeerID(), p2p_3.PeerID(), p2p_4.PeerID()}),
CapabilityConfigurations: []kcr.CapabilitiesRegistryCapabilityConfiguration{
{CapabilityId: kstest.MustCapabilityId(t, testCfg.Registry, cap_A)},
{CapabilityId: kstest.MustCapabilityId(t, testCfg.Registry, cap_B)},
{CapabilityId: kstest.MustCapabilityId(t, testCfg.Registry, initialCap)},
{CapabilityId: kstest.MustCapabilityId(t, testCfg.Registry, capToAdd)},
},
},
}
Expand Down Expand Up @@ -220,10 +232,11 @@ type setupUpdateDonTestResult struct {
chain deployment.Chain
}

func setupUpdateDonTest(t *testing.T, lggr logger.Logger, cfg setupUpdateDonTestConfig) *kstest.SetupTestRegistryResponse {
func registerTestDon(t *testing.T, lggr logger.Logger, cfg setupUpdateDonTestConfig) *kstest.SetupTestRegistryResponse {
t.Helper()
req := newSetupTestRegistryRequest(t, cfg.dons, cfg.nops)
return kstest.SetupTestRegistry(t, lggr, req)

}

func newSetupTestRegistryRequest(t *testing.T, dons []kslib.DonInfo, nops []keystone.NOP) *kstest.SetupTestRegistryRequest {
Expand Down
Loading

0 comments on commit d1caaa3

Please sign in to comment.