Skip to content

Commit

Permalink
keystone: Start migration from CLO to JD
Browse files Browse the repository at this point in the history
  • Loading branch information
archseer committed Oct 18, 2024
1 parent 7281edc commit 465fd3c
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 73 deletions.
57 changes: 53 additions & 4 deletions integration-tests/deployment/keystone/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/ethereum/go-ethereum/rpc"

"github.com/smartcontractkit/chainlink/integration-tests/deployment"
nodev1 "github.com/smartcontractkit/chainlink/integration-tests/deployment/jd/node/v1"

"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/durationpb"
Expand Down Expand Up @@ -93,8 +94,13 @@ func ConfigureContracts(ctx context.Context, lggr logger.Logger, req ConfigureCo
return nil, fmt.Errorf("failed to configure registry: %w", err)
}

donInfos, err := DonInfos(req.Dons, req.Env.Offchain)
if err != nil {
return nil, fmt.Errorf("failed to get don infos: %w", err)
}

// now we have the capability registry set up we need to configure the forwarder contracts and the OCR3 contract
dons, err := joinInfoAndNodes(cfgRegistryResp.DonInfos, req.Dons)
dons, err := joinInfoAndNodes(cfgRegistryResp.DonInfos, donInfos)
if err != nil {
return nil, fmt.Errorf("failed to assimilate registry to Dons: %w", err)
}
Expand Down Expand Up @@ -140,6 +146,44 @@ func DeployContracts(lggr logger.Logger, e *deployment.Environment, chainSel uin
}, nil
}

// DonInfo is DonCapabilities, but expanded to contain node information
type DonInfo struct {
Name string
Nodes []Node
Capabilities []kcr.CapabilitiesRegistryCapability // every capability is hosted on each node
}

type Node struct {
ID string
Name string // TODO: map name = req.Nops.name
PublicKey *string
ChainConfigs []*nodev1.ChainConfig
}

func DonInfos(dons []DonCapabilities, jd deployment.OffchainClient) ([]DonInfo, error) {
var donInfos []DonInfo
for _, don := range dons {
var nodes []Node
for _, nodeID := range don.Nops {

// TODO: Filter should accept multiple nodes
nodeChainConfigs, err := jd.ListNodeChainConfigs(context.Background(), &nodev1.ListNodeChainConfigsRequest{Filter: &nodev1.ListNodeChainConfigsRequest_Filter{
NodeIds: []string{nodeID},
}})
if err != nil {
return nil, err
}
nodes = append(nodes, Node{
ID: nodeID,
// Name TODO
// PublicKey TODO
ChainConfigs: nodeChainConfigs.GetChainConfigs(),
})
}
}
return donInfos, nil
}

// ConfigureRegistry configures the registry contract with the given DONS and their capabilities
// the address book is required to contain the addresses of the deployed registry contract
func ConfigureRegistry(ctx context.Context, lggr logger.Logger, req ConfigureContractsRequest, addrBook deployment.AddressBook) (*ConfigureContractsResponse, error) {
Expand All @@ -155,6 +199,11 @@ func ConfigureRegistry(ctx context.Context, lggr logger.Logger, req ConfigureCon
if err != nil {
return nil, fmt.Errorf("failed to get contract sets: %w", err)
}
// TODO:
donInfos, err := DonInfos(req.Dons, req.Env.Offchain)
if err != nil {
return nil, fmt.Errorf("failed to get don infos: %w", err)
}

// ensure registry is deployed and get the registry contract and chain
var registry *capabilities_registry.CapabilitiesRegistry
Expand All @@ -170,15 +219,15 @@ func ConfigureRegistry(ctx context.Context, lggr logger.Logger, req ConfigureCon

// all the subsequent calls to the registry are in terms of nodes
// compute the mapping of dons to their nodes for reuse in various registry calls
donToOcr2Nodes, err := mapDonsToNodes(req.Dons, true)
donToOcr2Nodes, err := mapDonsToNodes(donInfos, true)
if err != nil {
return nil, fmt.Errorf("failed to map dons to nodes: %w", err)
}

// TODO: we can remove this abstractions and refactor the functions that accept them to accept []DonCapabilities
// they are unnecessary indirection
donToCapabilities := mapDonsToCaps(req.Dons)
nodeIdToNop, err := nodesToNops(req.Dons, req.RegistryChainSel)
donToCapabilities := mapDonsToCaps(donInfos)
nodeIdToNop, err := nodesToNops(donInfos, req.RegistryChainSel)
if err != nil {
return nil, fmt.Errorf("failed to map nodes to nops: %w", err)
}
Expand Down
109 changes: 40 additions & 69 deletions integration-tests/deployment/keystone/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
chainsel "github.com/smartcontractkit/chain-selectors"

"github.com/smartcontractkit/chainlink/integration-tests/deployment"
"github.com/smartcontractkit/chainlink/integration-tests/deployment/clo/models"
v1 "github.com/smartcontractkit/chainlink/integration-tests/deployment/jd/node/v1"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry"
kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry"
Expand Down Expand Up @@ -148,41 +147,40 @@ func makeNodeKeysSlice(nodes []*ocr2Node) []NodeKeys {
// in is in a convenient form to handle the CLO representation of the nop data
type DonCapabilities struct {
Name string
Nops []*models.NodeOperator // each nop is a node operator and may have multiple nodes
Nops []string // each nop is a node operator and may have multiple nodes
Capabilities []kcr.CapabilitiesRegistryCapability // every capability is hosted on each nop
}

// map the node id to the NOP
func (dc DonCapabilities) nodeIdToNop(cs uint64) (map[string]capabilities_registry.CapabilitiesRegistryNodeOperator, error) {
func (dc DonInfo) nodeIdToNop(cs uint64) (map[string]capabilities_registry.CapabilitiesRegistryNodeOperator, error) {
cid, err := chainsel.ChainIdFromSelector(cs)
if err != nil {
return nil, fmt.Errorf("failed to get chain id from selector %d: %w", cs, err)
}
cidStr := strconv.FormatUint(cid, 10)
out := make(map[string]capabilities_registry.CapabilitiesRegistryNodeOperator)
for _, nop := range dc.Nops {
for _, node := range nop.Nodes {
found := false
for _, chain := range node.ChainConfigs {
if chain.Network.ChainID == cidStr {
found = true
out[node.ID] = capabilities_registry.CapabilitiesRegistryNodeOperator{
Name: nop.Name,
Admin: adminAddr(chain.AdminAddress),
}
for _, node := range dc.Nodes {
found := false
for _, chain := range node.ChainConfigs {
//TODO validate chainType field
if chain.Chain.Id == cidStr {
found = true
out[node.ID] = capabilities_registry.CapabilitiesRegistryNodeOperator{
Name: node.Name,
Admin: adminAddr(chain.AdminAddress),
}
}
if !found {
return nil, fmt.Errorf("node '%s' %s does not support chain %d", node.Name, node.ID, cid)
}
}
if !found {
return nil, fmt.Errorf("node '%s' %s does not support chain %d", node.Name, node.ID, cid)
}
}
return out, nil
}

// helpers to maintain compatibility with the existing registration functions
// nodesToNops converts a list of DonCapabilities to a map of node id to NOP
func nodesToNops(dons []DonCapabilities, chainSel uint64) (map[string]capabilities_registry.CapabilitiesRegistryNodeOperator, error) {
func nodesToNops(dons []DonInfo, chainSel uint64) (map[string]capabilities_registry.CapabilitiesRegistryNodeOperator, error) {
out := make(map[string]capabilities_registry.CapabilitiesRegistryNodeOperator)
for _, don := range dons {
nops, err := don.nodeIdToNop(chainSel)
Expand All @@ -201,7 +199,7 @@ func nodesToNops(dons []DonCapabilities, chainSel uint64) (map[string]capabiliti
}

// mapDonsToCaps converts a list of DonCapabilities to a map of don name to capabilities
func mapDonsToCaps(dons []DonCapabilities) map[string][]kcr.CapabilitiesRegistryCapability {
func mapDonsToCaps(dons []DonInfo) map[string][]kcr.CapabilitiesRegistryCapability {
out := make(map[string][]kcr.CapabilitiesRegistryCapability)
for _, don := range dons {
out[don.Name] = don.Capabilities
Expand All @@ -210,37 +208,35 @@ func mapDonsToCaps(dons []DonCapabilities) map[string][]kcr.CapabilitiesRegistry
}

// mapDonsToNodes returns a map of don name to simplified representation of their nodes
func mapDonsToNodes(dons []DonCapabilities, excludeBootstraps bool) (map[string][]*ocr2Node, error) {
func mapDonsToNodes(dons []DonInfo, excludeBootstraps bool) (map[string][]*ocr2Node, error) {
donToOcr2Nodes := make(map[string][]*ocr2Node)
// get the nodes for each don from the offchain client, get ocr2 config from one of the chain configs for the node b/c
// they are equivalent, and transform to ocr2node representation

for _, don := range dons {
for _, nop := range don.Nops {
for _, node := range nop.Nodes {
csaPubKey := node.PublicKey
if csaPubKey == nil {
return nil, fmt.Errorf("no public key for node %s", node.ID)
}
// the chain configs are equivalent as far as the ocr2 config is concerned so take the first one
if len(node.ChainConfigs) == 0 {
return nil, fmt.Errorf("no chain configs for node %s. cannot obtain keys", node.ID)
}
chain := node.ChainConfigs[0]
ccfg := chainConfigFromClo(chain)
ocr2n, err := newOcr2Node(node.ID, ccfg, *csaPubKey)
if err != nil {
return nil, fmt.Errorf("failed to create ocr2 node for node %s: %w", node.ID, err)
}
if excludeBootstraps && ocr2n.IsBoostrap {
continue
}
if _, ok := donToOcr2Nodes[don.Name]; !ok {
donToOcr2Nodes[don.Name] = make([]*ocr2Node, 0)
}
donToOcr2Nodes[don.Name] = append(donToOcr2Nodes[don.Name], ocr2n)

// TODO: load from jd instead
for _, node := range don.Nodes {
csaPubKey := node.PublicKey
if csaPubKey == nil {
return nil, fmt.Errorf("no public key for node %s", node.ID)
}
// the chain configs are equivalent as far as the ocr2 config is concerned so take the first one
if len(node.ChainConfigs) == 0 {
return nil, fmt.Errorf("no chain configs for node %s. cannot obtain keys", node.ID)
}
ccfg := node.ChainConfigs[0] // TODO: multiple sets?
ocr2n, err := newOcr2Node(node.ID, ccfg, *csaPubKey)
if err != nil {
return nil, fmt.Errorf("failed to create ocr2 node for node %s: %w", node.ID, err)
}
if excludeBootstraps && ocr2n.IsBoostrap {
continue
}
if _, ok := donToOcr2Nodes[don.Name]; !ok {
donToOcr2Nodes[don.Name] = make([]*ocr2Node, 0)
}
donToOcr2Nodes[don.Name] = append(donToOcr2Nodes[don.Name], ocr2n)

}
}

Expand Down Expand Up @@ -268,7 +264,7 @@ func (d RegisteredDon) signers() []common.Address {
return out
}

func joinInfoAndNodes(donInfos map[string]kcr.CapabilitiesRegistryDONInfo, dons []DonCapabilities) ([]RegisteredDon, error) {
func joinInfoAndNodes(donInfos map[string]kcr.CapabilitiesRegistryDONInfo, dons []DonInfo) ([]RegisteredDon, error) {
// all maps should have the same keys
nodes, err := mapDonsToNodes(dons, true)
if err != nil {
Expand All @@ -294,31 +290,6 @@ func joinInfoAndNodes(donInfos map[string]kcr.CapabilitiesRegistryDONInfo, dons
return out, nil
}

func chainConfigFromClo(chain *models.NodeChainConfig) *v1.ChainConfig {
return &v1.ChainConfig{
Chain: &v1.Chain{
Id: chain.Network.ChainID,
Type: v1.ChainType_CHAIN_TYPE_EVM, // TODO: support other chain types
},

AccountAddress: chain.AccountAddress,
AdminAddress: chain.AdminAddress,
Ocr2Config: &v1.OCR2Config{
Enabled: chain.Ocr2Config.Enabled,
P2PKeyBundle: &v1.OCR2Config_P2PKeyBundle{
PeerId: chain.Ocr2Config.P2pKeyBundle.PeerID,
PublicKey: chain.Ocr2Config.P2pKeyBundle.PublicKey,
},
OcrKeyBundle: &v1.OCR2Config_OCRKeyBundle{
BundleId: chain.Ocr2Config.OcrKeyBundle.BundleID,
OnchainSigningAddress: chain.Ocr2Config.OcrKeyBundle.OnchainSigningAddress,
OffchainPublicKey: chain.Ocr2Config.OcrKeyBundle.OffchainPublicKey,
ConfigPublicKey: chain.Ocr2Config.OcrKeyBundle.ConfigPublicKey,
},
},
}
}

var emptyAddr = "0x0000000000000000000000000000000000000000"

// compute the admin address from the string. If the address is empty, replaces the 0s with fs
Expand Down

0 comments on commit 465fd3c

Please sign in to comment.