Skip to content

Commit

Permalink
[CAPPL-303] Add commands to deploy the workflow registry
Browse files Browse the repository at this point in the history
  • Loading branch information
cedric-cordenier committed Dec 17, 2024
1 parent b76f9b3 commit 4606111
Show file tree
Hide file tree
Showing 12 changed files with 602 additions and 0 deletions.
33 changes: 33 additions & 0 deletions deployment/keystone/changeset/internal/test/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,47 @@ import (
"github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry"
kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry"
workflow_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/workflow/generated/workflow_registry_wrapper"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
)

type SetupTestWorkflowRegistryResponse struct {
Registry *workflow_registry.WorkflowRegistry
Chain deployment.Chain
RegistrySelector uint64
AddressBook deployment.AddressBook
}

func SetupTestWorkflowRegistry(t *testing.T, lggr logger.Logger, chainSel uint64) *SetupTestWorkflowRegistryResponse {
chain := testChain(t)

deployer, err := kslib.NewWorkflowRegistryDeployer()
require.NoError(t, err)
resp, err := deployer.Deploy(kslib.DeployRequest{Chain: chain})
require.NoError(t, err)

addressBook := deployment.NewMemoryAddressBookFromMap(
map[uint64]map[string]deployment.TypeAndVersion{
chainSel: map[string]deployment.TypeAndVersion{
resp.Address.Hex(): resp.Tv,
},
},
)

return &SetupTestWorkflowRegistryResponse{
Registry: deployer.Contract(),
Chain: chain,
RegistrySelector: chain.Selector,
AddressBook: addressBook,
}
}

type Don struct {
Name string
P2PIDs []p2pkey.PeerID
CapabilityConfigs []internal.CapabilityConfig
}

type SetupTestRegistryRequest struct {
P2pToCapabilities map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability
NopToNodes map[kcr.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc
Expand Down
26 changes: 26 additions & 0 deletions deployment/keystone/changeset/workflowregistry/deploy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package workflowregistry

import (
"fmt"

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

var _ deployment.ChangeSet[uint64] = Deploy

func Deploy(env deployment.Environment, registrySelector uint64) (deployment.ChangesetOutput, error) {
lggr := env.Logger
chain, ok := env.Chains[registrySelector]
if !ok {
return deployment.ChangesetOutput{}, fmt.Errorf("chain not found in environment")
}
ab := deployment.NewMemoryAddressBook()
wrResp, err := kslib.DeployWorkflowRegistry(chain, ab)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to deploy CapabilitiesRegistry: %w", err)
}
lggr.Infof("Deployed %s chain selector %d addr %s", wrResp.Tv.String(), chain.Selector, wrResp.Address.String())

return deployment.ChangesetOutput{AddressBook: ab}, nil
}
38 changes: 38 additions & 0 deletions deployment/keystone/changeset/workflowregistry/deploy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package workflowregistry

import (
"testing"

"go.uber.org/zap/zapcore"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/deployment/environment/memory"
)

func Test_Deploy(t *testing.T) {
t.Parallel()
lggr := logger.Test(t)
cfg := memory.MemoryEnvironmentConfig{
Nodes: 1, // nodes unused but required in config
Chains: 2,
}
env := memory.NewMemoryEnvironment(t, lggr, zapcore.DebugLevel, cfg)

registrySel := env.AllChainSelectors()[0]

resp, err := Deploy(env, registrySel)
require.NoError(t, err)
require.NotNil(t, resp)
// OCR3 should be deployed on chain 0
addrs, err := resp.AddressBook.AddressesForChain(registrySel)
require.NoError(t, err)
require.Len(t, addrs, 1)

// nothing on chain 1
require.NotEqual(t, registrySel, env.AllChainSelectors()[1])
oaddrs, _ := resp.AddressBook.AddressesForChain(env.AllChainSelectors()[1])
assert.Len(t, oaddrs, 0)
}
86 changes: 86 additions & 0 deletions deployment/keystone/changeset/workflowregistry/strategies.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package workflowregistry

import (
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"

"github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers"
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms"
"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"
"github.com/smartcontractkit/chainlink/deployment/keystone/changeset"
)

type strategy interface {
Apply(callFn func(opts *bind.TransactOpts) (*types.Transaction, error)) (deployment.ChangesetOutput, error)
}

type simpleTransaction struct {
chain deployment.Chain
}

func (s *simpleTransaction) Apply(callFn func(opts *bind.TransactOpts) (*types.Transaction, error)) (deployment.ChangesetOutput, error) {
tx, err := callFn(s.chain.DeployerKey)
if err != nil {
return deployment.ChangesetOutput{}, err
}

_, err = s.chain.Confirm(tx)
return deployment.ChangesetOutput{}, err
}

type mcmsTransaction struct {
Config *changeset.MCMSConfig
Description string
Address common.Address
ChainSel uint64
ContractSet *kslib.ContractSet
}

func (m *mcmsTransaction) Apply(callFn func(opts *bind.TransactOpts) (*types.Transaction, error)) (deployment.ChangesetOutput, error) {
opts := deployment.SimTransactOpts()

tx, err := callFn(opts)
if err != nil {
return deployment.ChangesetOutput{}, err
}

op := timelock.BatchChainOperation{
ChainIdentifier: mcms.ChainIdentifier(m.ChainSel),
Batch: []mcms.Operation{
{
Data: tx.Data(),
To: m.Address,
Value: big.NewInt(0),
},
},
}

timelocksPerChain := map[uint64]common.Address{
m.ChainSel: m.ContractSet.Timelock.Address(),
}
proposerMCMSes := map[uint64]*gethwrappers.ManyChainMultiSig{
m.ChainSel: m.ContractSet.ProposerMcm,
}

proposal, err := proposalutils.BuildProposalFromBatches(
timelocksPerChain,
proposerMCMSes,
[]timelock.BatchChainOperation{op},
m.Description,
m.Config.MinDuration,
)
if err != nil {
return deployment.ChangesetOutput{}, err
}

return deployment.ChangesetOutput{
Proposals: []timelock.MCMSWithTimelockProposal{*proposal},
}, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package workflowregistry

import (
"errors"
"fmt"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/core/types"

"github.com/smartcontractkit/chainlink/deployment"

kslib "github.com/smartcontractkit/chainlink/deployment/keystone"
"github.com/smartcontractkit/chainlink/deployment/keystone/changeset"
workflow_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/workflow/generated/workflow_registry_wrapper"
)

var _ deployment.ChangeSet[*UpdateAllowedDonsRequest] = UpdateAllowedDons

type UpdateAllowedDonsRequest struct {
RegistryChainSel uint64
DonIDs []uint32
Allowed bool

MCMSConfig *changeset.MCMSConfig
}

func (r *UpdateAllowedDonsRequest) Validate() error {
if len(r.DonIDs) == 0 {
return errors.New("Must provide at least one DonID")
}
return nil
}

// UpdateAllowedDons updates the list of DONs that workflows can be sent to.
func UpdateAllowedDons(env deployment.Environment, req *UpdateAllowedDonsRequest) (deployment.ChangesetOutput, error) {
if err := req.Validate(); err != nil {
return deployment.ChangesetOutput{}, err
}

resp, 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)
}

cs := resp.ContractSets[req.RegistryChainSel]
if cs.WorkflowRegistry == nil {
return deployment.ChangesetOutput{}, errors.New("could not find workflow registry")
}
registry := cs.WorkflowRegistry

chain, ok := env.Chains[req.RegistryChainSel]
if !ok {
return deployment.ChangesetOutput{}, fmt.Errorf("registry chain selector %d does not exist in environment", req.RegistryChainSel)
}

var s strategy
if req.MCMSConfig != nil {
s = &mcmsTransaction{
Config: req.MCMSConfig,
Description: "proposal to update allowed dons",
Address: registry.Address(),
ChainSel: chain.Selector,
ContractSet: &cs,
}
} else {
s = &simpleTransaction{
chain: chain,
}
}

return s.Apply(func(opts *bind.TransactOpts) (*types.Transaction, error) {
tx, err := registry.UpdateAllowedDONs(chain.DeployerKey, req.DonIDs, req.Allowed)
if err != nil {
err = kslib.DecodeErr(workflow_registry.WorkflowRegistryABI, err)
}
return tx, err
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package workflowregistry_test

import (
"testing"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink-common/pkg/logger"

chain_selectors "github.com/smartcontractkit/chain-selectors"

"github.com/smartcontractkit/chainlink/deployment"
kstest "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal/test"
"github.com/smartcontractkit/chainlink/deployment/keystone/changeset/workflowregistry"
)

func TestUpdateAllowedDons(t *testing.T) {
lggr := logger.Test(t)

chainSel := chain_selectors.ETHEREUM_TESTNET_SEPOLIA.Selector
resp := kstest.SetupTestWorkflowRegistry(t, lggr, chainSel)
registry := resp.Registry

dons, err := registry.GetAllAllowedDONs(&bind.CallOpts{})
require.NoError(t, err)

assert.Len(t, dons, 0)

env := deployment.Environment{
Logger: lggr,
Chains: map[uint64]deployment.Chain{
chainSel: resp.Chain,
},
ExistingAddresses: resp.AddressBook,
}

_, err = workflowregistry.UpdateAllowedDons(
env,
&workflowregistry.UpdateAllowedDonsRequest{
RegistryChainSel: chainSel,
DonIDs: []uint32{1},
Allowed: true,
},
)
require.NoError(t, err)

dons, err = registry.GetAllAllowedDONs(&bind.CallOpts{})
require.NoError(t, err)

assert.Len(t, dons, 1)
assert.Equal(t, dons[0], uint32(1))

_, err = workflowregistry.UpdateAllowedDons(
env,
&workflowregistry.UpdateAllowedDonsRequest{
RegistryChainSel: chainSel,
DonIDs: []uint32{1},
Allowed: false,
},
)
require.NoError(t, err)

dons, err = registry.GetAllAllowedDONs(&bind.CallOpts{})
require.NoError(t, err)

assert.Len(t, dons, 0)
}
Loading

0 comments on commit 4606111

Please sign in to comment.