From 3eb0a3736ff1cc885dd7ff9c978c09218f61e7a6 Mon Sep 17 00:00:00 2001 From: Cedric Date: Wed, 19 Jun 2024 11:25:24 +0100 Subject: [PATCH] [chore] Add script to deploy CapabilityRegistry with hardcoded config (#13621) * [chore] Add script to deploy CapabilityRegistry with hardcoded config * gomodtidy --- .changeset/thick-lemons-beam.md | 5 + core/scripts/go.mod | 2 +- ...initialize_capabilities_registry-sample.sh | 8 + core/scripts/keystone/main.go | 1 + ...deploy_initialize_capabilities_registry.go | 374 ++++++++++++++++++ 5 files changed, 389 insertions(+), 1 deletion(-) create mode 100644 .changeset/thick-lemons-beam.md create mode 100755 core/scripts/keystone/05_deploy_and_initialize_capabilities_registry-sample.sh create mode 100644 core/scripts/keystone/src/05_deploy_initialize_capabilities_registry.go diff --git a/.changeset/thick-lemons-beam.md b/.changeset/thick-lemons-beam.md new file mode 100644 index 00000000000..0ce6e04780e --- /dev/null +++ b/.changeset/thick-lemons-beam.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#internal Add script to provision capability registry diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 0bcda489659..fef147b14bf 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -35,6 +35,7 @@ require ( github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 github.com/urfave/cli v1.22.14 go.dedis.ch/kyber/v3 v3.1.0 + google.golang.org/protobuf v1.33.0 k8s.io/api v0.30.0 k8s.io/apimachinery v0.30.0 k8s.io/client-go v0.30.0 @@ -347,7 +348,6 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/grpc v1.59.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect gopkg.in/guregu/null.v4 v4.0.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/core/scripts/keystone/05_deploy_and_initialize_capabilities_registry-sample.sh b/core/scripts/keystone/05_deploy_and_initialize_capabilities_registry-sample.sh new file mode 100755 index 00000000000..21c764be0e8 --- /dev/null +++ b/core/scripts/keystone/05_deploy_and_initialize_capabilities_registry-sample.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +go run main.go \ + deploy-and-initialize-capabilities-registry \ + --chainid=11155111 \ + --ethurl=$ETH_URL \ + --accountkey=$ACCOUNT_KEY \ + --craddress=$CR_ADDRESS \ // 0x0d36aAC2Fd9d6d1C1F59251be6A2B337af27C52B diff --git a/core/scripts/keystone/main.go b/core/scripts/keystone/main.go index 4b48ba84f58..571623578ac 100644 --- a/core/scripts/keystone/main.go +++ b/core/scripts/keystone/main.go @@ -19,6 +19,7 @@ func main() { src.NewDeployJobSpecsCommand(), src.NewGenerateCribClusterOverridesCommand(), src.NewDeleteJobsCommand(), + src.NewDeployAndInitializeCapabilitiesRegistryCommand(), } commandsList := func(commands []command) string { diff --git a/core/scripts/keystone/src/05_deploy_initialize_capabilities_registry.go b/core/scripts/keystone/src/05_deploy_initialize_capabilities_registry.go new file mode 100644 index 00000000000..291ef131496 --- /dev/null +++ b/core/scripts/keystone/src/05_deploy_initialize_capabilities_registry.go @@ -0,0 +1,374 @@ +package src + +import ( + "context" + "encoding/hex" + "flag" + "fmt" + "log" + "os" + "strings" + + "github.com/ethereum/go-ethereum/common" + ragetypes "github.com/smartcontractkit/libocr/ragep2p/types" + "google.golang.org/protobuf/proto" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + + helpers "github.com/smartcontractkit/chainlink/core/scripts/common" + remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" + kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" +) + +type peer struct { + PeerID string + Signer string +} + +var ( + workflowDonPeers = []peer{ + { + PeerID: "12D3KooWBCF1XT5Wi8FzfgNCqRL76Swv8TRU3TiD4QiJm8NMNX7N", + Signer: "0x9639dCc7D0ca4468B5f684ef89F12F0B365c9F6d", + }, + { + PeerID: "12D3KooWG1AyvwmCpZ93J8pBQUE1SuzrjDXnT4BeouncHR3jWLCG", + Signer: "0x8f0fAE64f5f75067833ed5deDC2804B62b21383d", + }, + { + PeerID: "12D3KooWGeUKZBRMbx27FUTgBwZa9Ap9Ym92mywwpuqkEtz8XWyv", + Signer: "0xf09A863D920840c13277e76F43CFBdfB22b8FB7C", + }, + { + PeerID: "12D3KooW9zYWQv3STmDeNDidyzxsJSTxoCTLicafgfeEz9nhwhC4", + Signer: "0x7eD90b519bC3054a575C464dBf39946b53Ff90EF", + }, + { + PeerID: "12D3KooWG1AeBnSJH2mdcDusXQVye2jqodZ6pftTH98HH6xvrE97", + Signer: "0x8F572978673d711b2F061EB7d514BD46EAD6668A", + }, + { + PeerID: "12D3KooWBf3PrkhNoPEmp7iV291YnPuuTsgEDHTscLajxoDvwHGA", + Signer: "0x21eF07Dfaf8f7C10CB0d53D18b641ee690541f9D", + }, + { + PeerID: "12D3KooWP3FrMTFXXRU2tBC8aYvEBgUX6qhcH9q2JZCUi9Wvc2GX", + Signer: "0x7Fa21F6f716CFaF8f249564D72Ce727253186C89", + }, + } + triggerDonPeers = []peer{ + { + PeerID: "12D3KooWBaiTbbRwwt2fbNifiL7Ew9tn3vds9AJE3Nf3eaVBX36m", + Signer: "0x9CcE7293a4Cc2621b61193135A95928735e4795F", + }, + { + PeerID: "12D3KooWS7JSY9fzSfWgbCE1S3W2LNY6ZVpRuun74moVBkKj6utE", + Signer: "0x3c775F20bCB2108C1A818741Ce332Bb5fe0dB925", + }, + { + PeerID: "12D3KooWMMTDXcWhpVnwrdAer1jnVARTmnr3RyT3v7Djg8ZuoBh9", + Signer: "0x50314239e2CF05555ceeD53E7F47eB2A8Eab0dbB", + }, + { + PeerID: "12D3KooWGzVXsKxXsF4zLgxSDM8Gzx1ywq2pZef4PrHMKuVg4K3P", + Signer: "0xd76A4f98898c3b9A72b244476d7337b50D54BCd8", + }, + { + PeerID: "12D3KooWSyjmmzjVtCzwN7bXzZQFmWiJRuVcKBerNjVgL7HdLJBW", + Signer: "0x656A873f6895b8a03Fb112dE927d43FA54B2c92A", + }, + { + PeerID: "12D3KooWLGz9gzhrNsvyM6XnXS3JRkZoQdEzuAvysovnSChNK5ZK", + Signer: "0x5d1e87d87bF2e0cD4Ea64F381a2dbF45e5f0a553", + }, + { + PeerID: "12D3KooWAvZnvknFAfSiUYjATyhzEJLTeKvAzpcLELHi4ogM3GET", + Signer: "0x91d9b0062265514f012Eb8fABA59372fD9520f56", + }, + } + targetDonPeers = []peer{ + { + PeerID: "12D3KooWJrthXtnPHw7xyHFAxo6NxifYTvc8igKYaA6wRRRqtsMb", + Signer: "0x3F82750353Ea7a051ec9bA011BC628284f9a5327", + }, + { + PeerID: "12D3KooWFQekP9sGex4XhqEJav5EScjTpDVtDqJFg1JvrePBCEGJ", + Signer: "0xc23545876A208AA0443B1b8d552c7be4FF4b53F0", + }, + { + PeerID: "12D3KooWFLEq4hYtdyKWwe47dXGEbSiHMZhmr5xLSJNhpfiEz8NF", + Signer: "0x82601Fa43d8B1dC1d4eB640451aC86a7CDA37011", + }, + { + PeerID: "12D3KooWN2hztiXNNS1jMQTTvvPRYcarK1C7T3Mdqk4x4gwyo5WS", + Signer: "0x1a684B3d8f917fe496b7B1A8b29EDDAED64F649f", + }, + } +) + +type deployAndInitializeCapabilitiesRegistryCommand struct{} + +func NewDeployAndInitializeCapabilitiesRegistryCommand() *deployAndInitializeCapabilitiesRegistryCommand { + return &deployAndInitializeCapabilitiesRegistryCommand{} +} + +func (c *deployAndInitializeCapabilitiesRegistryCommand) Name() string { + return "deploy-and-initialize-capabilities-registry" +} + +func peerIDToB(peerID string) ([32]byte, error) { + var peerIDB ragetypes.PeerID + err := peerIDB.UnmarshalText([]byte(peerID)) + if err != nil { + return [32]byte{}, err + } + + return peerIDB, nil +} + +func peers(ps []peer) ([][32]byte, error) { + out := [][32]byte{} + for _, p := range ps { + b, err := peerIDToB(p.PeerID) + if err != nil { + return nil, err + } + + out = append(out, b) + } + + return out, nil +} + +func peerToNode(nopID uint32, p peer) (kcr.CapabilitiesRegistryNodeParams, error) { + peerIDB, err := peerIDToB(p.PeerID) + if err != nil { + return kcr.CapabilitiesRegistryNodeParams{}, fmt.Errorf("failed to convert peerID: %w", err) + } + + sig := strings.TrimPrefix(p.Signer, "0x") + signerB, err := hex.DecodeString(sig) + if err != nil { + return kcr.CapabilitiesRegistryNodeParams{}, fmt.Errorf("failed to convert signer: %w", err) + } + + var sigb [32]byte + copy(sigb[:], signerB) + + return kcr.CapabilitiesRegistryNodeParams{ + NodeOperatorId: nopID, + P2pId: peerIDB, + Signer: sigb, + }, nil +} + +// Run expects the following environment variables to be set: +// +// 1. Deploys the CapabilitiesRegistry contract +// 2. Configures it with a hardcode DON setup, as used by our staging environment. +func (c *deployAndInitializeCapabilitiesRegistryCommand) Run(args []string) { + ctx := context.Background() + + fs := flag.NewFlagSet(c.Name(), flag.ExitOnError) + // create flags for all of the env vars then set the env vars to normalize the interface + // this is a bit of a hack but it's the easiest way to make this work + ethUrl := fs.String("ethurl", "", "URL of the Ethereum node") + chainID := fs.Int64("chainid", 11155111, "chain ID of the Ethereum network to deploy to") + accountKey := fs.String("accountkey", "", "private key of the account to deploy from") + capabilityRegistryAddress := fs.String("craddress", "", "address of the capability registry") + + err := fs.Parse(args) + if err != nil || + *ethUrl == "" || ethUrl == nil || + *chainID == 0 || chainID == nil || + *accountKey == "" || accountKey == nil { + fs.Usage() + os.Exit(1) + } + + os.Setenv("ETH_URL", *ethUrl) + os.Setenv("ETH_CHAIN_ID", fmt.Sprintf("%d", *chainID)) + os.Setenv("ACCOUNT_KEY", *accountKey) + + env := helpers.SetupEnv(false) + + var reg *kcr.CapabilitiesRegistry + if *capabilityRegistryAddress == "" { + reg = deployCapabilitiesRegistry(env) + } else { + addr := common.HexToAddress(*capabilityRegistryAddress) + r, innerErr := kcr.NewCapabilitiesRegistry(addr, env.Ec) + if err != nil { + panic(innerErr) + } + + reg = r + } + + streamsTrigger := kcr.CapabilitiesRegistryCapability{ + LabelledName: "streams-trigger", + Version: "1.0.0", + CapabilityType: uint8(0), // trigger + } + sid, err := reg.GetHashedCapabilityId(&bind.CallOpts{}, streamsTrigger.LabelledName, streamsTrigger.Version) + if err != nil { + panic(err) + } + + writeChain := kcr.CapabilitiesRegistryCapability{ + LabelledName: "write_ethereum-testnet-sepolia", + Version: "1.0.0", + CapabilityType: uint8(3), // target + } + wid, err := reg.GetHashedCapabilityId(&bind.CallOpts{}, writeChain.LabelledName, writeChain.Version) + if err != nil { + log.Printf("failed to call GetHashedCapabilityId: %s", err) + } + + ocr := kcr.CapabilitiesRegistryCapability{ + LabelledName: "offchain_reporting", + Version: "1.0.0", + CapabilityType: uint8(2), // consensus + } + ocrid, err := reg.GetHashedCapabilityId(&bind.CallOpts{}, ocr.LabelledName, ocr.Version) + if err != nil { + log.Printf("failed to call GetHashedCapabilityId: %s", err) + } + + tx, err := reg.AddCapabilities(env.Owner, []kcr.CapabilitiesRegistryCapability{ + streamsTrigger, + writeChain, + ocr, + }) + if err != nil { + log.Printf("failed to call AddCapabilities: %s", err) + } + + helpers.ConfirmTXMined(ctx, env.Ec, tx, env.ChainID) + + tx, err = reg.AddNodeOperators(env.Owner, []kcr.CapabilitiesRegistryNodeOperator{ + { + Admin: env.Owner.From, + Name: "STAGING_NODE_OPERATOR", + }, + }) + if err != nil { + log.Printf("failed to AddNodeOperators: %s", err) + } + + receipt := helpers.ConfirmTXMined(ctx, env.Ec, tx, env.ChainID) + + recLog, err := reg.ParseNodeOperatorAdded(*receipt.Logs[0]) + if err != nil { + panic(err) + } + + nopID := recLog.NodeOperatorId + nodes := []kcr.CapabilitiesRegistryNodeParams{} + for _, wfPeer := range workflowDonPeers { + n, innerErr := peerToNode(nopID, wfPeer) + if innerErr != nil { + panic(innerErr) + } + + n.HashedCapabilityIds = [][32]byte{ocrid} + nodes = append(nodes, n) + } + + for _, triggerPeer := range triggerDonPeers { + n, innerErr := peerToNode(nopID, triggerPeer) + if innerErr != nil { + panic(innerErr) + } + + n.HashedCapabilityIds = [][32]byte{sid} + nodes = append(nodes, n) + } + + for _, targetPeer := range targetDonPeers { + n, innerErr := peerToNode(nopID, targetPeer) + if innerErr != nil { + panic(innerErr) + } + + n.HashedCapabilityIds = [][32]byte{wid} + nodes = append(nodes, n) + } + + tx, err = reg.AddNodes(env.Owner, nodes) + if err != nil { + log.Printf("failed to AddNodes: %s", err) + } + + helpers.ConfirmTXMined(ctx, env.Ec, tx, env.ChainID) + + // workflow DON + ps, err := peers(workflowDonPeers) + if err != nil { + panic(err) + } + + cfgs := []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: ocrid, + }, + } + _, err = reg.AddDON(env.Owner, ps, cfgs, false, true, 2) + if err != nil { + log.Printf("workflowDON: failed to AddDON: %s", err) + } + + // trigger DON + ps, err = peers(triggerDonPeers) + if err != nil { + panic(err) + } + + config := &remotetypes.RemoteTriggerConfig{ + RegistrationRefreshMs: 20000, + RegistrationExpiryMs: 60000, + // F + 1 + MinResponsesToAggregate: uint32(1) + 1, + } + configb, err := proto.Marshal(config) + if err != nil { + panic(err) + } + cfgs = []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: sid, + Config: configb, + }, + } + _, err = reg.AddDON(env.Owner, ps, cfgs, true, false, 1) + if err != nil { + log.Printf("triggerDON: failed to AddDON: %s", err) + } + + // target DON + ps, err = peers(targetDonPeers) + if err != nil { + panic(err) + } + + cfgs = []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: wid, + }, + } + _, err = reg.AddDON(env.Owner, ps, cfgs, true, false, 1) + if err != nil { + log.Printf("targetDON: failed to AddDON: %s", err) + } +} + +func deployCapabilitiesRegistry(env helpers.Environment) *kcr.CapabilitiesRegistry { + _, tx, contract, err := kcr.DeployCapabilitiesRegistry(env.Owner, env.Ec) + if err != nil { + panic(err) + } + + addr := helpers.ConfirmContractDeployed(context.Background(), env.Ec, tx, env.ChainID) + fmt.Printf("CapabilitiesRegistry address: %s", addr) + return contract +}