Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace NodesFromJD with NodeInfo #15256

Merged
merged 10 commits into from
Nov 22, 2024
Merged
88 changes: 72 additions & 16 deletions deployment/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"math/big"
"sort"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -21,6 +22,7 @@ import (
csav1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/csa"
jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job"
nodev1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/node"
"github.com/smartcontractkit/chainlink-protos/job-distributor/v1/shared/ptypes"

"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
)
Expand Down Expand Up @@ -222,11 +224,14 @@ func (n Nodes) BootstrapLocators() []string {

type Node struct {
NodeID string
Name string
CSAKey string
SelToOCRConfig map[chain_selectors.ChainDetails]OCRConfig
PeerID p2pkey.PeerID
IsBootstrap bool
MultiAddr string
AdminAddr string
Labels []*ptypes.Label
}

func (n Node) OCRConfigForChainDetails(details chain_selectors.ChainDetails) (OCRConfig, bool) {
Expand Down Expand Up @@ -269,17 +274,50 @@ func MustPeerIDFromString(s string) p2pkey.PeerID {
}

type NodeChainConfigsLister interface {
ListNodes(ctx context.Context, in *nodev1.ListNodesRequest, opts ...grpc.CallOption) (*nodev1.ListNodesResponse, error)
ListNodeChainConfigs(ctx context.Context, in *nodev1.ListNodeChainConfigsRequest, opts ...grpc.CallOption) (*nodev1.ListNodeChainConfigsResponse, error)
}

// Gathers all the node info through JD required to be able to set
// OCR config for example.
// OCR config for example. nodeIDs can be JD IDs or PeerIDs
func NodeInfo(nodeIDs []string, oc NodeChainConfigsLister) (Nodes, error) {
if len(nodeIDs) == 0 {
return nil, nil
}
// if nodeIDs starts with `p2p_` lookup by p2p_id instead
filterByPeerIDs := strings.HasPrefix(nodeIDs[0], "p2p_")
var filter *nodev1.ListNodesRequest_Filter
if filterByPeerIDs {
selector := strings.Join(nodeIDs, ",")
filter = &nodev1.ListNodesRequest_Filter{
Enabled: 1,
Selectors: []*ptypes.Selector{
{
Key: "p2p_id",
Op: ptypes.SelectorOp_IN,
Value: &selector,
},
},
}
} else {
filter = &nodev1.ListNodesRequest_Filter{
Enabled: 1,
Ids: nodeIDs,
}

}
nodesFromJD, err := oc.ListNodes(context.Background(), &nodev1.ListNodesRequest{
Filter: filter,
})
if err != nil {
return nil, fmt.Errorf("failed to list nodes: %w", err)
}

var nodes []Node
for _, nodeID := range nodeIDs {
for _, node := range nodesFromJD.GetNodes() {
// TODO: Filter should accept multiple nodes
nodeChainConfigs, err := oc.ListNodeChainConfigs(context.Background(), &nodev1.ListNodeChainConfigsRequest{Filter: &nodev1.ListNodeChainConfigsRequest_Filter{
NodeIds: []string{nodeID},
NodeIds: []string{node.Id},
}})
if err != nil {
return nil, err
Expand All @@ -294,7 +332,6 @@ func NodeInfo(nodeIDs []string, oc NodeChainConfigsLister) (Nodes, error) {
// Might make sense to change proto as peerID/multiAddr is 1-1 with nodeID?
peerID = MustPeerIDFromString(chainConfig.Ocr2Config.P2PKeyBundle.PeerId)
multiAddr = chainConfig.Ocr2Config.Multiaddr
adminAddr = chainConfig.AdminAddress
if chainConfig.Ocr2Config.IsBootstrap {
// NOTE: Assume same peerID for all chains.
// Might make sense to change proto as peerID is 1-1 with nodeID?
Expand All @@ -309,40 +346,59 @@ func NodeInfo(nodeIDs []string, oc NodeChainConfigsLister) (Nodes, error) {
var cpk types3.ConfigEncryptionPublicKey
copy(cpk[:], b)

var pubkey types3.OnchainPublicKey
if chainConfig.Chain.Type == nodev1.ChainType_CHAIN_TYPE_EVM {
// convert from pubkey to address
pubkey = common.HexToAddress(chainConfig.Ocr2Config.OcrKeyBundle.OnchainSigningAddress).Bytes()
} else {
pubkey = common.Hex2Bytes(chainConfig.Ocr2Config.OcrKeyBundle.OnchainSigningAddress)
}

ocrConfig := OCRConfig{
OffchainPublicKey: opk,
OnchainPublicKey: common.HexToAddress(chainConfig.Ocr2Config.OcrKeyBundle.OnchainSigningAddress).Bytes(),
OnchainPublicKey: pubkey,
PeerID: MustPeerIDFromString(chainConfig.Ocr2Config.P2PKeyBundle.PeerId),
TransmitAccount: types2.Account(chainConfig.AccountAddress),
ConfigEncryptionPublicKey: cpk,
KeyBundleID: chainConfig.Ocr2Config.OcrKeyBundle.BundleId,
}

var details chain_selectors.ChainDetails
if chainConfig.Chain.Type == nodev1.ChainType_CHAIN_TYPE_EVM {
// NOTE: Assume same adminAddr for all chains. We always use EVM addr
adminAddr = chainConfig.AdminAddress
}

var family string
switch chainConfig.Chain.Type {
case nodev1.ChainType_CHAIN_TYPE_APTOS:
details, err = chain_selectors.GetChainDetailsByChainIDAndFamily(chainConfig.Chain.Id, chain_selectors.FamilyAptos)
if err != nil {
return nil, err
}
case nodev1.ChainType_CHAIN_TYPE_EVM:
details, err = chain_selectors.GetChainDetailsByChainIDAndFamily(chainConfig.Chain.Id, chain_selectors.FamilyEVM)
if err != nil {
return nil, err
}
family = chain_selectors.FamilyEVM
case nodev1.ChainType_CHAIN_TYPE_APTOS:
family = chain_selectors.FamilyAptos
case nodev1.ChainType_CHAIN_TYPE_SOLANA:
family = chain_selectors.FamilySolana
case nodev1.ChainType_CHAIN_TYPE_STARKNET:
family = chain_selectors.FamilyStarknet
default:
return nil, fmt.Errorf("unsupported chain type %s", chainConfig.Chain.Type)
}

details, err := chain_selectors.GetChainDetailsByChainIDAndFamily(chainConfig.Chain.Id, family)
if err != nil {
return nil, err
}

selToOCRConfig[details] = ocrConfig
}
nodes = append(nodes, Node{
NodeID: nodeID,
NodeID: node.Id,
Name: node.Name,
CSAKey: node.PublicKey,
SelToOCRConfig: selToOCRConfig,
IsBootstrap: bootstrap,
PeerID: peerID,
MultiAddr: multiAddr,
AdminAddr: adminAddr,
Labels: node.Labels,
})
}

Expand Down
15 changes: 8 additions & 7 deletions deployment/environment/memory/job_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,16 +190,17 @@ func (j JobClient) ListNodeChainConfigs(ctx context.Context, in *nodev1.ListNode
if err != nil {
return nil, err
}
chainID, err := chainsel.ChainIdFromSelector(selector)
if err != nil {
return nil, err
}

if family == chainsel.FamilyEVM {
// already handled above
continue
}

// NOTE: this supports non-EVM too
chainID, err := chainsel.GetChainIDFromSelector(selector)
if err != nil {
return nil, err
}

var ocrtype chaintype.ChainType
switch family {
case chainsel.FamilyEVM:
Expand All @@ -213,7 +214,7 @@ func (j JobClient) ListNodeChainConfigs(ctx context.Context, in *nodev1.ListNode
case chainsel.FamilyAptos:
ocrtype = chaintype.Aptos
default:
panic(fmt.Sprintf("Unsupported chain family %v", family))
return nil, fmt.Errorf("Unsupported chain family %v", family)
}

bundle := n.Keys.OCRKeyBundles[ocrtype]
Expand Down Expand Up @@ -244,7 +245,7 @@ func (j JobClient) ListNodeChainConfigs(ctx context.Context, in *nodev1.ListNode

chainConfigs = append(chainConfigs, &nodev1.ChainConfig{
Chain: &nodev1.Chain{
Id: strconv.Itoa(int(chainID)),
Id: chainID,
Type: ctype,
},
AccountAddress: "", // TODO: support AccountAddress
Expand Down
4 changes: 4 additions & 0 deletions deployment/environment/memory/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ func NewNode(
) *Node {
evmchains := make(map[uint64]EVMChain)
for _, chain := range chains {
// we're only mapping evm chains here
if family, err := chainsel.GetSelectorFamily(chain.Selector); err != nil || family != chainsel.FamilyEVM {
continue
}
evmChainID, err := chainsel.ChainIdFromSelector(chain.Selector)
if err != nil {
t.Fatal(err)
Expand Down
90 changes: 48 additions & 42 deletions deployment/keystone/changeset/internal/update_don_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ package internal_test

import (
"bytes"
"encoding/hex"
"fmt"
"math/big"
"sort"
"strconv"
"testing"

"github.com/ethereum/go-ethereum/common"
chainsel "github.com/smartcontractkit/chain-selectors"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
nodev1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/node"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/keystone"
kslib "github.com/smartcontractkit/chainlink/deployment/keystone"
Expand All @@ -22,9 +24,12 @@ import (
"github.com/stretchr/testify/require"
)

var (
registryChain = chainsel.TEST_90000001
)

func TestUpdateDon(t *testing.T) {
var (
registryChain = chainsel.TEST_90000001
// nodes
p2p_1 = p2pkey.MustNewV2XXXTestingOnly(big.NewInt(100))
pubKey_1 = "11114981a6119ca3f932cdb8c402d71a72d672adae7849f581ecff8b8e1098e7" // valid csa key
Expand Down Expand Up @@ -98,14 +103,14 @@ func TestUpdateDon(t *testing.T) {
dons: []kslib.DonInfo{
{
Name: "don 1",
Nodes: []keystone.Node{node_1, node_2, node_3, node_4},
Nodes: []deployment.Node{node_1, node_2, node_3, node_4},
Capabilities: []kcr.CapabilitiesRegistryCapability{cap_A},
},
},
nops: []keystone.NOP{
{
Name: "nop 1",
Nodes: []string{node_1.ID, node_2.ID, node_3.ID, node_4.ID},
Nodes: []string{node_1.NodeID, node_2.NodeID, node_3.NodeID, node_4.NodeID},
},
},
}
Expand Down Expand Up @@ -170,27 +175,36 @@ type minimalNodeCfg struct {
admin common.Address
}

func newNode(t *testing.T, cfg minimalNodeCfg) keystone.Node {
func newNode(t *testing.T, cfg minimalNodeCfg) deployment.Node {
t.Helper()

return keystone.Node{
ID: cfg.id,
PublicKey: &cfg.pubKey,
ChainConfigs: []*nodev1.ChainConfig{
{
Chain: &nodev1.Chain{
Id: "test chain",
Type: nodev1.ChainType_CHAIN_TYPE_EVM,
},
AdminAddress: cfg.admin.String(),
Ocr2Config: &nodev1.OCR2Config{
P2PKeyBundle: &nodev1.OCR2Config_P2PKeyBundle{
PeerId: cfg.p2p.PeerID().String(),
},
OcrKeyBundle: &nodev1.OCR2Config_OCRKeyBundle{
OnchainSigningAddress: cfg.signingAddr,
},
},
registryChainID, err := chainsel.ChainIdFromSelector(registryChain.Selector)
if err != nil {
panic(err)
}
registryChainDetails, err := chainsel.GetChainDetailsByChainIDAndFamily(strconv.Itoa(int(registryChainID)), chainsel.FamilyEVM)
if err != nil {
panic(err)
}

signingAddr, err := hex.DecodeString(cfg.signingAddr)
require.NoError(t, err)

var pubkey [32]byte
if _, err := hex.Decode(pubkey[:], []byte(cfg.pubKey)); err != nil {
panic(fmt.Sprintf("failed to decode pubkey %s: %v", pubkey, err))
}

return deployment.Node{
NodeID: cfg.id,
PeerID: cfg.p2p.PeerID(),
CSAKey: cfg.pubKey,
AdminAddr: cfg.admin.String(),
SelToOCRConfig: map[chainsel.ChainDetails]deployment.OCRConfig{
registryChainDetails: {
OnchainPublicKey: signingAddr,
PeerID: cfg.p2p.PeerID(),
ConfigEncryptionPublicKey: pubkey,
},
},
}
Expand All @@ -214,10 +228,10 @@ func setupUpdateDonTest(t *testing.T, lggr logger.Logger, cfg setupUpdateDonTest

func newSetupTestRegistryRequest(t *testing.T, dons []kslib.DonInfo, nops []keystone.NOP) *kstest.SetupTestRegistryRequest {
t.Helper()
nodes := make(map[string]keystone.Node)
nodes := make(map[string]deployment.Node)
for _, don := range dons {
for _, node := range don.Nodes {
nodes[node.ID] = node
nodes[node.NodeID] = node
}
}
nopsToNodes := makeNopToNodes(t, nops, nodes)
Expand All @@ -231,23 +245,23 @@ func newSetupTestRegistryRequest(t *testing.T, dons []kslib.DonInfo, nops []keys
return req
}

func makeNopToNodes(t *testing.T, nops []keystone.NOP, nodes map[string]keystone.Node) map[kcr.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc {
func makeNopToNodes(t *testing.T, nops []keystone.NOP, nodes map[string]deployment.Node) map[kcr.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc {
nopToNodes := make(map[kcr.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc)

for _, nop := range nops {
// all chain configs are the same wrt admin address & node keys
// so we can just use the first one
crnop := kcr.CapabilitiesRegistryNodeOperator{
Name: nop.Name,
Admin: common.HexToAddress(nodes[nop.Nodes[0]].ChainConfigs[0].AdminAddress),
Admin: common.HexToAddress(nodes[nop.Nodes[0]].AdminAddr),
}
var signers []*internal.P2PSignerEnc
for _, nodeID := range nop.Nodes {
node := nodes[nodeID]
require.NotNil(t, node.PublicKey, "public key is nil %s", node.ID)
require.NotNil(t, node.CSAKey, "public key is nil %s", node.NodeID)
// all chain configs are the same wrt admin address & node keys
p, err := kscs.NewP2PSignerEncFromJD(node.ChainConfigs[0], *node.PublicKey)
require.NoError(t, err, "failed to make p2p signer enc from clo nod %s", node.ID)
p, err := kscs.NewP2PSignerEnc(&node, registryChain.Selector)
require.NoError(t, err, "failed to make p2p signer enc from clo nod %s", node.NodeID)
signers = append(signers, p)
}
nopToNodes[crnop] = signers
Expand All @@ -260,8 +274,8 @@ func makeP2PToCapabilities(t *testing.T, dons []kslib.DonInfo) map[p2pkey.PeerID
for _, don := range dons {
for _, node := range don.Nodes {
for _, cap := range don.Capabilities {
p, err := kscs.NewP2PSignerEncFromJD(node.ChainConfigs[0], *node.PublicKey)
require.NoError(t, err, "failed to make p2p signer enc from clo nod %s", node.ID)
p, err := kscs.NewP2PSignerEnc(&node, registryChain.Selector)
require.NoError(t, err, "failed to make p2p signer enc from clo nod %s", node.NodeID)
p2pToCapabilities[p.P2PKey] = append(p2pToCapabilities[p.P2PKey], cap)
}
}
Expand All @@ -282,8 +296,8 @@ func testDon(t *testing.T, don kslib.DonInfo) kstest.Don {
for _, node := range don.Nodes {
// all chain configs are the same wrt admin address & node keys
// so we can just use the first one
p, err := kscs.NewP2PSignerEncFromJD(node.ChainConfigs[0], *node.PublicKey)
require.NoError(t, err, "failed to make p2p signer enc from clo nod %s", node.ID)
p, err := kscs.NewP2PSignerEnc(&node, registryChain.Selector)
require.NoError(t, err, "failed to make p2p signer enc from clo nod %s", node.NodeID)
p2pids = append(p2pids, p.P2PKey)
}

Expand All @@ -299,11 +313,3 @@ func testDon(t *testing.T, don kslib.DonInfo) kstest.Don {
CapabilityConfigs: capabilityConfigs,
}
}

func newP2PSignerEnc(signer [32]byte, p2pkey p2pkey.PeerID, encryptionPublicKey [32]byte) *internal.P2PSignerEnc {
return &internal.P2PSignerEnc{
Signer: signer,
P2PKey: p2pkey,
EncryptionPublicKey: encryptionPublicKey,
}
}
Loading
Loading