diff --git a/GNUmakefile b/GNUmakefile index e41a3240073..a7ced725619 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -111,7 +111,7 @@ abigen: ## Build & install abigen. .PHONY: generate generate: abigen codecgen mockery protoc gomods ## Execute all go:generate commands. ## Updating PATH makes sure that go:generate uses the version of protoc installed by the protoc make command. - export PATH=$(HOME)/.local/bin:$(PATH); gomods -w go generate -x ./... + export PATH="$(HOME)/.local/bin:$(PATH)"; gomods -w go generate -x ./... find . -type f -name .mockery.yaml -execdir mockery \; ## Execute mockery for all .mockery.yaml files .PHONY: rm-mocked diff --git a/deployment/keystone/changeset/internal/deploy.go b/deployment/keystone/changeset/internal/deploy.go index 8b67013aa15..be4c3a192b8 100644 --- a/deployment/keystone/changeset/internal/deploy.go +++ b/deployment/keystone/changeset/internal/deploy.go @@ -668,16 +668,21 @@ func RegisterNodes(lggr logger.Logger, req *RegisterNodesRequest) (*RegisterNode } nodeIDToParams := make(map[string]capabilities_registry.CapabilitiesRegistryNodeParams) + nodeIDToDon := make(map[string]string) for don, nodes := range req.DonToNodes { caps, ok := req.DonToCapabilities[don] if !ok { return nil, fmt.Errorf("capabilities not found for don %s", don) } - var hashedCapabilityIds [][32]byte + var ( + hashedCapabilityIDs [][32]byte + capIDs []string + ) for _, cap := range caps { - hashedCapabilityIds = append(hashedCapabilityIds, cap.ID) + hashedCapabilityIDs = append(hashedCapabilityIDs, cap.ID) + capIDs = append(capIDs, hex.EncodeToString(cap.ID[:])) } - lggr.Debugw("hashed capability ids", "don", don, "ids", hashedCapabilityIds) + lggr.Debugw("hashed capability ids", "don", don, "ids", capIDs) for _, n := range nodes { if n.IsBootstrap { // bootstraps are part of the DON but don't host capabilities @@ -703,37 +708,50 @@ func RegisterNodes(lggr logger.Logger, req *RegisterNodesRequest) (*RegisterNode Signer: signer, P2pId: n.PeerID, EncryptionPublicKey: csakey, - HashedCapabilityIds: hashedCapabilityIds, + HashedCapabilityIds: hashedCapabilityIDs, } } else { // when we have a node operator, we need to dedup capabilities against the existing ones - var newCapIds [][32]byte - for _, proposedCapId := range hashedCapabilityIds { + var newCapIDs [][32]byte + for _, proposedCapID := range hashedCapabilityIDs { shouldAdd := true - for _, existingCapId := range params.HashedCapabilityIds { - if existingCapId == proposedCapId { + for _, existingCapID := range params.HashedCapabilityIds { + if existingCapID == proposedCapID { shouldAdd = false break } } if shouldAdd { - newCapIds = append(newCapIds, proposedCapId) + newCapIDs = append(newCapIDs, proposedCapID) } } - params.HashedCapabilityIds = append(params.HashedCapabilityIds, newCapIds...) + params.HashedCapabilityIds = append(params.HashedCapabilityIds, newCapIDs...) } nodeIDToParams[n.NodeID] = params + nodeIDToDon[n.NodeID] = don } } - var uniqueNodeParams []capabilities_registry.CapabilitiesRegistryNodeParams - for _, v := range nodeIDToParams { - uniqueNodeParams = append(uniqueNodeParams, v) + lggr.Debugw("checking for existing nodes", "count", len(nodeIDToParams)) + + nodes2Add, err := getNodesToRegister(registry, nodeIDToParams) + if err != nil { + return nil, fmt.Errorf("failed to get nodes to register: %w", err) + } + + lggr.Debugf("found %d missing nodes", len(nodes2Add)) + + if len(nodes2Add) == 0 { + lggr.Debug("no new nodes to register") + return &RegisterNodesResponse{ + nodeIDToParams: nodeIDToParams, + }, nil } - lggr.Debugw("unique node params to add", "count", len(uniqueNodeParams), "params", uniqueNodeParams) + + lggr.Debugw("unique node params to add after deduplication", "count", len(nodes2Add), "params", nodes2Add) if req.UseMCMS { - ops, err := addNodesMCMSProposal(registry, uniqueNodeParams, registryChain) + ops, err := addNodesMCMSProposal(registry, nodes2Add, registryChain) if err != nil { return nil, fmt.Errorf("failed to generate proposal to add nodes: %w", err) } @@ -744,7 +762,7 @@ func RegisterNodes(lggr logger.Logger, req *RegisterNodesRequest) (*RegisterNode }, nil } - tx, err := registry.AddNodes(registryChain.DeployerKey, uniqueNodeParams) + tx, err := registry.AddNodes(registryChain.DeployerKey, nodes2Add) if err != nil { err = deployment.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err) // no typed errors in the abi, so we have to do string matching @@ -753,7 +771,7 @@ func RegisterNodes(lggr logger.Logger, req *RegisterNodesRequest) (*RegisterNode return nil, fmt.Errorf("failed to call AddNodes for bulk add nodes: %w", err) } lggr.Warn("nodes already exist, falling back to 1-by-1") - for _, singleNodeParams := range uniqueNodeParams { + for _, singleNodeParams := range nodes2Add { tx, err = registry.AddNodes(registryChain.DeployerKey, []capabilities_registry.CapabilitiesRegistryNodeParams{singleNodeParams}) if err != nil { err = deployment.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err) @@ -783,6 +801,34 @@ func RegisterNodes(lggr logger.Logger, req *RegisterNodesRequest) (*RegisterNode }, nil } +// getNodesToRegister returns the nodes that are not already registered in the registry +func getNodesToRegister( + registry *capabilities_registry.CapabilitiesRegistry, + nodeIDToParams map[string]capabilities_registry.CapabilitiesRegistryNodeParams, +) ([]capabilities_registry.CapabilitiesRegistryNodeParams, error) { + nodes2Add := make([]capabilities_registry.CapabilitiesRegistryNodeParams, 0) + for nodeID, nodeParams := range nodeIDToParams { + var ( + ni capabilities_registry.INodeInfoProviderNodeInfo + err error + ) + if ni, err = registry.GetNode(&bind.CallOpts{}, nodeParams.P2pId); err != nil { + if err = deployment.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err); strings.Contains(err.Error(), "NodeDoesNotExist") { + nodes2Add = append(nodes2Add, nodeParams) + continue + } + return nil, fmt.Errorf("failed to call GetNode for node %s: %w", nodeID, err) + } + + // if no error, but node info is empty, then the node does not exist and should be added. + if hex.EncodeToString(ni.P2pId[:]) != hex.EncodeToString(nodeParams.P2pId[:]) && hex.EncodeToString(ni.P2pId[:]) == "0000000000000000000000000000000000000000000000000000000000000000" { + nodes2Add = append(nodes2Add, nodeParams) + continue + } + } + return nodes2Add, nil +} + // addNodesMCMSProposal generates a single call to AddNodes for all the node params at once. func addNodesMCMSProposal(registry *capabilities_registry.CapabilitiesRegistry, params []capabilities_registry.CapabilitiesRegistryNodeParams, regChain deployment.Chain) (*timelock.BatchChainOperation, error) { tx, err := registry.AddNodes(deployment.SimTransactOpts(), params) @@ -857,7 +903,7 @@ func RegisterDons(lggr logger.Logger, req RegisterDonsRequest) (*RegisterDonsRes } lggr.Infow("fetched existing DONs...", "len", len(donInfos), "lenByNodesHash", len(existingDONs)) - mcmsOps := make([]mcms.Operation, 0, len(req.DonsToRegister)) + mcmsOps := make([]mcms.Operation, 0) for _, don := range req.DonsToRegister { var p2pIds [][32]byte for _, n := range don.Nodes { @@ -908,6 +954,8 @@ func RegisterDons(lggr logger.Logger, req RegisterDonsRequest) (*RegisterDonsRes txOpts = deployment.SimTransactOpts() } + lggr.Debugw("calling add don", "don", don.Name, "p2p sorted hash", p2pSortedHash, "cgs", cfgs, "wfSupported", wfSupported, "f", don.F, + "p2pids", p2pIds, "node count", len(p2pIds)) tx, err := registry.AddDON(txOpts, p2pIds, cfgs, true, wfSupported, don.F) if err != nil { err = deployment.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err) @@ -934,12 +982,15 @@ func RegisterDons(lggr logger.Logger, req RegisterDonsRequest) (*RegisterDonsRes } if req.UseMCMS { - return &RegisterDonsResponse{ - Ops: &timelock.BatchChainOperation{ - ChainIdentifier: mcms.ChainIdentifier(registryChain.Selector), - Batch: mcmsOps, - }, - }, nil + if len(mcmsOps) > 0 { + return &RegisterDonsResponse{ + Ops: &timelock.BatchChainOperation{ + ChainIdentifier: mcms.ChainIdentifier(registryChain.Selector), + Batch: mcmsOps, + }, + }, nil + } + return &RegisterDonsResponse{}, nil } lggr.Debugf("Registered all DONs (new=%d), waiting for registry to update", addedDons) diff --git a/deployment/keystone/changeset/internal/deploy_test.go b/deployment/keystone/changeset/internal/deploy_test.go index c059cd5a981..5b37b397026 100644 --- a/deployment/keystone/changeset/internal/deploy_test.go +++ b/deployment/keystone/changeset/internal/deploy_test.go @@ -6,12 +6,14 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/logger" + chain_selectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal" kstest "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal/test" kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" ) func Test_RegisterNOPS(t *testing.T) { @@ -86,13 +88,71 @@ func Test_AddCapabilities(t *testing.T) { func Test_RegisterNodes(t *testing.T) { var ( - useMCMS bool - lggr = logger.Test(t) - setupResp = kstest.SetupTestRegistry(t, lggr, &kstest.SetupTestRegistryRequest{}) - registry = setupResp.Registry - chain = setupResp.Chain + useMCMS bool + lggr = logger.Test(t) + existingNOP = testNop(t, "testNop") + initialp2pToCapabilities = map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability{ + testPeerID(t, "0x1"): { + { + LabelledName: "test", + Version: "1.0.0", + CapabilityType: 0, + }, + }, + } + nopToNodes = map[kcr.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc{ + existingNOP: { + { + Signer: [32]byte{0: 1}, + P2PKey: testPeerID(t, "0x1"), + EncryptionPublicKey: [32]byte{3: 16, 4: 2}, + }, + }, + } + + setupResp = kstest.SetupTestRegistry(t, lggr, &kstest.SetupTestRegistryRequest{ + P2pToCapabilities: initialp2pToCapabilities, + NopToNodes: nopToNodes, + }) + registry = setupResp.Registry + chain = setupResp.Chain + + registeredCapabilities = kstest.GetRegisteredCapabilities(t, lggr, initialp2pToCapabilities, setupResp.CapabilityCache) + + registeredNodeParams = kstest.ToNodeParams(t, nopToNodes, + kstest.ToP2PToCapabilities(t, initialp2pToCapabilities, registry, registeredCapabilities), + ) ) t.Run("success create add nodes mcms proposal", func(t *testing.T) { + var ( + nop2Add = testNop(t, "newNop") + caps2Add = map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability{ + testPeerID(t, "0x2"): { + { + LabelledName: "new-cap", + Version: "1.0.0", + CapabilityType: 0, + }, + }, + } + + nopToNodes = map[kcr.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc{ + nop2Add: { + { + Signer: [32]byte{0: 1}, + P2PKey: testPeerID(t, "0x2"), + EncryptionPublicKey: [32]byte{3: 16, 4: 2}, + }, + }, + } + + rc, _ = kstest.MustAddCapabilities(t, lggr, caps2Add, chain, registry) + + nps = kstest.ToNodeParams(t, nopToNodes, + kstest.ToP2PToCapabilities(t, caps2Add, registry, rc), + ) + ) + useMCMS = true env := &deployment.Environment{ Logger: lggr, @@ -112,11 +172,109 @@ func Test_RegisterNodes(t *testing.T) { Env: env, RegistryChainSelector: chain.Selector, UseMCMS: useMCMS, + DonToCapabilities: map[string][]internal.RegisteredCapability{ + "testDON": rc, + }, + NopToNodeIDs: map[kcr.CapabilitiesRegistryNodeOperator][]string{ + nop2Add: {"node-id"}, + }, + DonToNodes: map[string][]deployment.Node{ + "testDON": { + { + PeerID: nps[0].P2pId, + NodeID: "node-id", + SelToOCRConfig: map[chain_selectors.ChainDetails]deployment.OCRConfig{ + { + ChainSelector: chain.Selector, + }: {}, + }, + }, + }, + }, + Nops: []*kcr.CapabilitiesRegistryNodeOperatorAdded{{ + Name: nop2Add.Name, + Admin: nop2Add.Admin, + NodeOperatorId: 2, + }}, }) require.NoError(t, err) require.NotNil(t, resp.Ops) require.Len(t, resp.Ops.Batch, 1) }) + + t.Run("no ops in proposal if node already exists", func(t *testing.T) { + useMCMS = true + env := &deployment.Environment{ + Logger: lggr, + Chains: map[uint64]deployment.Chain{ + chain.Selector: chain, + }, + ExistingAddresses: deployment.NewMemoryAddressBookFromMap(map[uint64]map[string]deployment.TypeAndVersion{ + chain.Selector: { + registry.Address().String(): deployment.TypeAndVersion{ + Type: internal.CapabilitiesRegistry, + Version: deployment.Version1_0_0, + }, + }, + }), + } + resp, err := internal.RegisterNodes(lggr, &internal.RegisterNodesRequest{ + Env: env, + RegistryChainSelector: chain.Selector, + UseMCMS: useMCMS, + DonToCapabilities: map[string][]internal.RegisteredCapability{ + "testDON": registeredCapabilities, + }, + NopToNodeIDs: map[kcr.CapabilitiesRegistryNodeOperator][]string{ + existingNOP: {"node-id"}, + }, + DonToNodes: map[string][]deployment.Node{ + "testDON": { + { + PeerID: registeredNodeParams[0].P2pId, + NodeID: "node-id", + SelToOCRConfig: map[chain_selectors.ChainDetails]deployment.OCRConfig{ + { + ChainSelector: chain.Selector, + }: {}, + }, + }, + }, + }, + Nops: []*kcr.CapabilitiesRegistryNodeOperatorAdded{{ + Name: existingNOP.Name, + Admin: existingNOP.Admin, + NodeOperatorId: 1, + }}, + }) + require.NoError(t, err) + require.Nil(t, resp.Ops) + }) + + t.Run("no new nodes to add results in no mcms ops", func(t *testing.T) { + useMCMS = true + env := &deployment.Environment{ + Logger: lggr, + Chains: map[uint64]deployment.Chain{ + chain.Selector: chain, + }, + ExistingAddresses: deployment.NewMemoryAddressBookFromMap(map[uint64]map[string]deployment.TypeAndVersion{ + chain.Selector: { + registry.Address().String(): deployment.TypeAndVersion{ + Type: internal.CapabilitiesRegistry, + Version: deployment.Version1_0_0, + }, + }, + }), + } + resp, err := internal.RegisterNodes(lggr, &internal.RegisterNodesRequest{ + Env: env, + RegistryChainSelector: chain.Selector, + UseMCMS: useMCMS, + }) + require.NoError(t, err) + require.Nil(t, resp.Ops) + }) } func Test_RegisterDons(t *testing.T) { @@ -162,6 +320,98 @@ func Test_RegisterDons(t *testing.T) { require.Len(t, resp.Ops.Batch, 1) }) + t.Run("no new dons to add results in no mcms ops", func(t *testing.T) { + var ( + existingNOP = testNop(t, "testNop") + existingP2Pkey = testPeerID(t, "0x1") + initialp2pToCapabilities = map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability{ + existingP2Pkey: { + { + LabelledName: "test", + Version: "1.0.0", + CapabilityType: 0, + }, + }, + testPeerID(t, "0x2"): { + { + LabelledName: "test", + Version: "1.0.0", + CapabilityType: 0, + }, + }, + testPeerID(t, "0x3"): { + { + LabelledName: "test", + Version: "1.0.0", + CapabilityType: 0, + }, + }, + } + nopToNodes = map[kcr.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc{ + existingNOP: { + { + Signer: [32]byte{0: 1}, + P2PKey: existingP2Pkey, + EncryptionPublicKey: [32]byte{3: 16, 4: 2}, + }, + { + Signer: [32]byte{0: 1, 1: 1}, + P2PKey: testPeerID(t, "0x2"), + EncryptionPublicKey: [32]byte{3: 16, 4: 2}, + }, + { + Signer: [32]byte{0: 1, 1: 1, 2: 1}, + P2PKey: testPeerID(t, "0x3"), + EncryptionPublicKey: [32]byte{3: 16, 4: 2}, + }, + }, + } + + setupResp = kstest.SetupTestRegistry(t, lggr, &kstest.SetupTestRegistryRequest{ + P2pToCapabilities: initialp2pToCapabilities, + NopToNodes: nopToNodes, + Dons: []kstest.Don{ + { + Name: "test-don", + P2PIDs: []p2pkey.PeerID{existingP2Pkey, testPeerID(t, "0x2"), testPeerID(t, "0x3")}, + }, + }, + }) + regContract = setupResp.Registry + ) + + env := &deployment.Environment{ + Logger: lggr, + Chains: map[uint64]deployment.Chain{ + setupResp.Chain.Selector: setupResp.Chain, + }, + ExistingAddresses: deployment.NewMemoryAddressBookFromMap(map[uint64]map[string]deployment.TypeAndVersion{ + setupResp.Chain.Selector: { + regContract.Address().String(): deployment.TypeAndVersion{ + Type: internal.CapabilitiesRegistry, + Version: deployment.Version1_0_0, + }, + }, + }), + } + resp, err := internal.RegisterDons(lggr, internal.RegisterDonsRequest{ + Env: env, + RegistryChainSelector: setupResp.Chain.Selector, + DonToCapabilities: map[string][]internal.RegisteredCapability{ + "test-don": {}, + }, + DonsToRegister: []internal.DONToRegister{ + { + Name: "test-don", + F: 1, + }, + }, + UseMCMS: true, + }) + require.NoError(t, err) + require.Nil(t, resp.Ops) + }) + t.Run("success create add DONs mcms proposal with multiple DONs", func(t *testing.T) { useMCMS = true env := &deployment.Environment{ diff --git a/deployment/keystone/changeset/internal/test/utils.go b/deployment/keystone/changeset/internal/test/utils.go index 290d0ee2196..f5d078e7521 100644 --- a/deployment/keystone/changeset/internal/test/utils.go +++ b/deployment/keystone/changeset/internal/test/utils.go @@ -30,9 +30,14 @@ type Don struct { } type SetupTestRegistryRequest struct { + // P2pToCapabilities maps a node's p2pID to the capabilities it has P2pToCapabilities map[p2pkey.PeerID][]capabilities_registry.CapabilitiesRegistryCapability - NopToNodes map[capabilities_registry.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc - Dons []Don + + // NopToNodes maps a node operator to the nodes they operate + NopToNodes map[capabilities_registry.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc + + // Dons groups the p2pIDs of the nodes that comprise it and the capabilities they have + Dons []Don // TODO maybe add support for MCMS at this level } @@ -41,70 +46,157 @@ type SetupTestRegistryResponse struct { Chain deployment.Chain RegistrySelector uint64 ContractSet *internal.ContractSet + CapabilityCache *CapabilityCache } func SetupTestRegistry(t *testing.T, lggr logger.Logger, req *SetupTestRegistryRequest) *SetupTestRegistryResponse { chain := testChain(t) + // deploy the registry registry := deployCapReg(t, chain) + // convert req to nodeoperators + nops := ToNodeOps(t, req.NopToNodes) + addNopsResp := addNops(t, lggr, chain, registry, nops) + require.Len(t, addNopsResp.Nops, len(nops)) + + // add capabilities to registry + registeredCapabilities, capCache := MustAddCapabilities(t, lggr, req.P2pToCapabilities, chain, registry) + + // make the nodes and register node + nodeParams := ToNodeParams(t, + req.NopToNodes, + ToP2PToCapabilities(t, req.P2pToCapabilities, registry, registeredCapabilities), + ) + + AddNodes(t, lggr, chain, registry, nodeParams) + + // add the Dons + addDons(t, lggr, chain, registry, capCache, req.Dons) + + return &SetupTestRegistryResponse{ + Registry: registry, + Chain: chain, + RegistrySelector: chain.Selector, + ContractSet: &internal.ContractSet{ + CapabilitiesRegistry: registry, + }, + CapabilityCache: capCache, + } +} + +// ToNodeParams transforms a map of node operators to nops and a map of node p2pID to capabilities +// into a slice of node params required to register the nodes. The number of capabilities +// must match the number of nodes. +func ToNodeParams(t *testing.T, + nop2Nodes map[capabilities_registry.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc, + p2pToCapabilities map[p2pkey.PeerID][][32]byte, +) []capabilities_registry.CapabilitiesRegistryNodeParams { + t.Helper() + + var nodeParams []capabilities_registry.CapabilitiesRegistryNodeParams + var i uint32 + for _, p2pSignerEncs := range nop2Nodes { + for _, p2pSignerEnc := range p2pSignerEncs { + _, exists := p2pToCapabilities[p2pSignerEnc.P2PKey] + require.True(t, exists, "missing capabilities for p2pID %s", p2pSignerEnc.P2PKey) + + nodeParams = append(nodeParams, capabilities_registry.CapabilitiesRegistryNodeParams{ + Signer: p2pSignerEnc.Signer, + P2pId: p2pSignerEnc.P2PKey, + EncryptionPublicKey: p2pSignerEnc.EncryptionPublicKey, + HashedCapabilityIds: p2pToCapabilities[p2pSignerEnc.P2PKey], + NodeOperatorId: i + 1, + }) + } + i++ + } + + return nodeParams +} + +func ToNodeOps( + t *testing.T, + nop2Nodes map[capabilities_registry.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc, +) []capabilities_registry.CapabilitiesRegistryNodeOperator { + t.Helper() + nops := make([]capabilities_registry.CapabilitiesRegistryNodeOperator, 0) - for nop := range req.NopToNodes { + for nop := range nop2Nodes { nops = append(nops, nop) } + sort.Slice(nops, func(i, j int) bool { return nops[i].Name < nops[j].Name }) - addNopsResp := addNops(t, lggr, chain, registry, nops) - require.Len(t, addNopsResp.Nops, len(nops)) + return nops +} - // add capabilities to registry - capCache := NewCapabiltyCache(t) +// MustAddCapabilities adds the capabilities to the registry and returns the registered capabilities +// if the capability is already registered, this call will fail. +func MustAddCapabilities( + t *testing.T, + lggr logger.Logger, + in map[p2pkey.PeerID][]capabilities_registry.CapabilitiesRegistryCapability, + chain deployment.Chain, + registry *capabilities_registry.CapabilitiesRegistry, +) ([]internal.RegisteredCapability, *CapabilityCache) { + t.Helper() + cache := NewCapabiltyCache(t) var capabilities []capabilities_registry.CapabilitiesRegistryCapability - for _, caps := range req.P2pToCapabilities { + for _, caps := range in { capabilities = append(capabilities, caps...) } - registeredCapabilities := capCache.AddCapabilities(lggr, chain, registry, capabilities) + + registeredCapabilities := cache.AddCapabilities(lggr, chain, registry, capabilities) expectedDeduped := make(map[capabilities_registry.CapabilitiesRegistryCapability]struct{}) for _, cap := range capabilities { expectedDeduped[cap] = struct{}{} } require.Len(t, registeredCapabilities, len(expectedDeduped)) + return registeredCapabilities, cache +} - // make the nodes and register node - var nodeParams []capabilities_registry.CapabilitiesRegistryNodeParams - initialp2pToCapabilities := make(map[p2pkey.PeerID][][32]byte) - for p2pID := range req.P2pToCapabilities { - initialp2pToCapabilities[p2pID] = mustCapabilityIds(t, registry, registeredCapabilities) +// GetRegisteredCapabilities returns the registered capabilities for the given capabilities. Each +// capability must exist on the cache already. +func GetRegisteredCapabilities( + t *testing.T, + lggr logger.Logger, + in map[p2pkey.PeerID][]capabilities_registry.CapabilitiesRegistryCapability, + cache *CapabilityCache, +) []internal.RegisteredCapability { + t.Helper() + + var capabilities []capabilities_registry.CapabilitiesRegistryCapability + for _, caps := range in { + capabilities = append(capabilities, caps...) } - // create node with initial capabilities assigned to nop - for i, nop := range nops { - if _, exists := req.NopToNodes[nop]; !exists { - require.Fail(t, "missing nopToNodes for %s", nop.Name) - } - for _, p2pSignerEnc := range req.NopToNodes[nop] { - nodeParams = append(nodeParams, capabilities_registry.CapabilitiesRegistryNodeParams{ - Signer: p2pSignerEnc.Signer, - P2pId: p2pSignerEnc.P2PKey, - EncryptionPublicKey: p2pSignerEnc.EncryptionPublicKey, - HashedCapabilityIds: initialp2pToCapabilities[p2pSignerEnc.P2PKey], - NodeOperatorId: uint32(i + 1), // nopid in contract is 1-indexed - }) - } + + registeredCapabilities := make([]internal.RegisteredCapability, 0) + for _, cap := range capabilities { + id, exists := cache.Get(cap) + require.True(t, exists, "capability not found in cache %v", cap) + registeredCapabilities = append(registeredCapabilities, internal.RegisteredCapability{ + CapabilitiesRegistryCapability: cap, + ID: id, + }) } - addNodes(t, lggr, chain, registry, nodeParams) - // add the Dons - addDons(t, lggr, chain, registry, capCache, req.Dons) + return registeredCapabilities +} - return &SetupTestRegistryResponse{ - Registry: registry, - Chain: chain, - RegistrySelector: chain.Selector, - ContractSet: &internal.ContractSet{ - CapabilitiesRegistry: registry, - }, +func ToP2PToCapabilities( + t *testing.T, + in map[p2pkey.PeerID][]capabilities_registry.CapabilitiesRegistryCapability, + registry *capabilities_registry.CapabilitiesRegistry, + caps []internal.RegisteredCapability, +) map[p2pkey.PeerID][][32]byte { + t.Helper() + out := make(map[p2pkey.PeerID][][32]byte) + for p2pID := range in { + out[p2pID] = mustCapabilityIds(t, registry, caps) } + return out } func deployCapReg(t *testing.T, chain deployment.Chain) *capabilities_registry.CapabilitiesRegistry { @@ -139,7 +231,13 @@ func addNops(t *testing.T, lggr logger.Logger, chain deployment.Chain, registry return resp } -func addNodes(t *testing.T, lggr logger.Logger, chain deployment.Chain, registry *capabilities_registry.CapabilitiesRegistry, nodes []capabilities_registry.CapabilitiesRegistryNodeParams) { +func AddNodes( + t *testing.T, + lggr logger.Logger, + chain deployment.Chain, + registry *capabilities_registry.CapabilitiesRegistry, + nodes []capabilities_registry.CapabilitiesRegistryNodeParams, +) { tx, err := registry.AddNodes(chain.DeployerKey, nodes) if err != nil { err2 := deployment.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err) @@ -149,7 +247,14 @@ func addNodes(t *testing.T, lggr logger.Logger, chain deployment.Chain, registry require.NoError(t, err) } -func addDons(t *testing.T, lggr logger.Logger, chain deployment.Chain, registry *capabilities_registry.CapabilitiesRegistry, capCache *CapabilityCache, dons []Don) { +func addDons( + t *testing.T, + _ logger.Logger, + chain deployment.Chain, + registry *capabilities_registry.CapabilitiesRegistry, + capCache *CapabilityCache, + dons []Don, +) { for _, don := range dons { acceptsWorkflows := false // lookup the capabilities diff --git a/deployment/keystone/changeset/internal/update_nodes_test.go b/deployment/keystone/changeset/internal/update_nodes_test.go index 642eaebbe4d..fc565c699b2 100644 --- a/deployment/keystone/changeset/internal/update_nodes_test.go +++ b/deployment/keystone/changeset/internal/update_nodes_test.go @@ -276,12 +276,13 @@ func TestUpdateNodes(t *testing.T) { wantErr: false, }, { - name: "twos node, different capabilities", + name: "twos nodes with different capabilities", args: args{ lggr: lggr, req: &internal.UpdateNodesRequest{ P2pToUpdates: map[p2pkey.PeerID]internal.NodeUpdate{ - testPeerID(t, "peerID_1"): internal.NodeUpdate{ + testPeerID(t, "peerID_1"): { + NodeOperatorID: 1, Capabilities: []kcr.CapabilitiesRegistryCapability{ { LabelledName: "cap1", @@ -290,7 +291,8 @@ func TestUpdateNodes(t *testing.T) { }, }, }, - testPeerID(t, "peerID_2"): internal.NodeUpdate{ + testPeerID(t, "peerID_2"): { + NodeOperatorID: 2, Capabilities: []kcr.CapabilitiesRegistryCapability{ { LabelledName: "cap2", @@ -303,14 +305,14 @@ func TestUpdateNodes(t *testing.T) { Chain: chain, }, nopsToNodes: map[kcr.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc{ - testNop(t, "nopA"): []*internal.P2PSignerEnc{ + testNop(t, "nopA"): { { P2PKey: testPeerID(t, "peerID_1"), Signer: [32]byte{0: 1, 31: 1}, EncryptionPublicKey: [32]byte{0: 7, 1: 7}, }, }, - testNop(t, "nopB"): []*internal.P2PSignerEnc{ + testNop(t, "nopB"): { { P2PKey: testPeerID(t, "peerID_2"), Signer: [32]byte{0: 2, 31: 2}, @@ -496,12 +498,12 @@ func TestUpdateNodes(t *testing.T) { t.Errorf("UpdateNodes() error = %v, wantErr %v", err, tt.wantErr) return } - for i, p := range got.NodeParams { - expected := tt.want.NodeParams[i] - require.Equal(t, expected.NodeOperatorId, p.NodeOperatorId) - require.Equal(t, expected.P2pId, p.P2pId) - require.Equal(t, expected.Signer, p.Signer) - require.Equal(t, expected.EncryptionPublicKey, p.EncryptionPublicKey) + + for _, p := range got.NodeParams { + // check the node params + expected := findNodeParams(t, tt.want.NodeParams, p.P2pId) + assertNodeParams(t, expected, p) + // check the capabilities expectedCaps := expectedUpdatedCaps[p.P2pId] var wantHashedIDs [][32]byte @@ -703,3 +705,21 @@ func testNop(t *testing.T, name string) kcr.CapabilitiesRegistryNodeOperator { Name: name, } } + +func findNodeParams(t *testing.T, nodes []kcr.CapabilitiesRegistryNodeParams, p2p p2pkey.PeerID) kcr.CapabilitiesRegistryNodeParams { + for _, n := range nodes { + if n.P2pId == p2p { + return n + } + } + require.Failf(t, "could not find node %s", p2p.String()) + return kcr.CapabilitiesRegistryNodeParams{} +} + +func assertNodeParams(t *testing.T, expected, got kcr.CapabilitiesRegistryNodeParams) { + t.Helper() + assert.Equal(t, expected.P2pId, got.P2pId, "p2p ID failed : expected %v, got %v", expected.P2pId, got.P2pId) + assert.Equal(t, expected.NodeOperatorId, got.NodeOperatorId, "nop ID failed : expected %d, got %d", expected.NodeOperatorId, got.NodeOperatorId) + assert.Equal(t, expected.Signer, got.Signer, "signer failed : expected %v, got %v", expected.Signer, got.Signer) + assert.Equal(t, expected.EncryptionPublicKey, got.EncryptionPublicKey, "encryption key failed : expected %v, got %v", expected.EncryptionPublicKey, got.EncryptionPublicKey) +}