diff --git a/deployment/ccip/state.go b/deployment/ccip/state.go index fa299d2260f..528d21700cf 100644 --- a/deployment/ccip/state.go +++ b/deployment/ccip/state.go @@ -157,7 +157,7 @@ func (c CCIPChainState) GenerateView() (view.ChainView, error) { chainView.RMNProxy[c.RMNProxy.Address().Hex()] = rmnProxyView } if c.CapabilityRegistry != nil { - capRegView, err := common_v1_0.GenerateCapRegView(c.CapabilityRegistry) + capRegView, err := common_v1_0.GenerateCapabilityRegistryView(c.CapabilityRegistry) if err != nil { return chainView, err } diff --git a/deployment/ccip/view/view.go b/deployment/ccip/view/view.go index 3fb2591573b..9ef8583bdf6 100644 --- a/deployment/ccip/view/view.go +++ b/deployment/ccip/view/view.go @@ -20,12 +20,12 @@ type ChainView struct { TokenAdminRegistry map[string]v1_5.TokenAdminRegistryView `json:"tokenAdminRegistry,omitempty"` CommitStore map[string]v1_5.CommitStoreView `json:"commitStore,omitempty"` // v1.6 - FeeQuoter map[string]v1_6.FeeQuoterView `json:"feeQuoter,omitempty"` - NonceManager map[string]v1_6.NonceManagerView `json:"nonceManager,omitempty"` - RMN map[string]v1_6.RMNRemoteView `json:"rmn,omitempty"` - OnRamp map[string]v1_6.OnRampView `json:"onRamp,omitempty"` - OffRamp map[string]v1_6.OffRampView `json:"offRamp,omitempty"` - CapabilityRegistry map[string]common_v1_0.CapRegView `json:"capabilityRegistry,omitempty"` + FeeQuoter map[string]v1_6.FeeQuoterView `json:"feeQuoter,omitempty"` + NonceManager map[string]v1_6.NonceManagerView `json:"nonceManager,omitempty"` + RMN map[string]v1_6.RMNRemoteView `json:"rmn,omitempty"` + OnRamp map[string]v1_6.OnRampView `json:"onRamp,omitempty"` + OffRamp map[string]v1_6.OffRampView `json:"offRamp,omitempty"` + CapabilityRegistry map[string]common_v1_0.CapabilityRegistryView `json:"capabilityRegistry,omitempty"` } func NewChain() ChainView { @@ -43,7 +43,7 @@ func NewChain() ChainView { RMN: make(map[string]v1_6.RMNRemoteView), OnRamp: make(map[string]v1_6.OnRampView), OffRamp: make(map[string]v1_6.OffRampView), - CapabilityRegistry: make(map[string]common_v1_0.CapRegView), + CapabilityRegistry: make(map[string]common_v1_0.CapabilityRegistryView), } } diff --git a/deployment/common/view/v1_0/capreg.go b/deployment/common/view/v1_0/capreg.go index b509fad0a04..92d44af5983 100644 --- a/deployment/common/view/v1_0/capreg.go +++ b/deployment/common/view/v1_0/capreg.go @@ -1,45 +1,375 @@ package v1_0 import ( - "github.com/ethereum/go-ethereum/common" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "math/big" + "slices" + "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink/deployment/common/view/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" ) -// CapRegView denotes a view of the capabilities registry contract. -// Note that the contract itself is 1.0.0 versioned, but we're releasing it first -// as part of 1.6 for CCIP. -type CapRegView struct { +// CapabilityRegistryView is a high-fidelity view of the capabilities registry contract. +type CapabilityRegistryView struct { types.ContractMetaData Capabilities []CapabilityView `json:"capabilities,omitempty"` + Nodes []NodeView `json:"nodes,omitempty"` + Nops []NopView `json:"nops,omitempty"` + Dons []DonView `json:"dons,omitempty"` } -type CapabilityView struct { - LabelledName string `json:"labelledName"` - Version string `json:"version"` - ConfigContract common.Address `json:"configContract"` +// MarshalJSON marshals the CapabilityRegistryView to JSON. It includes the Capabilities, Nodes, Nops, and Dons +// and a denormalized summary of the Dons with their associated Nodes and Capabilities, which is useful for a high-level view +func (v CapabilityRegistryView) MarshalJSON() ([]byte, error) { + // Alias to avoid recursive calls + type Alias struct { + types.ContractMetaData + Capabilities []CapabilityView `json:"capabilities,omitempty"` + Nodes []NodeView `json:"nodes,omitempty"` + Nops []NopView `json:"nops,omitempty"` + Dons []DonView `json:"dons,omitempty"` + DonCapabilities []DonDenormalizedView `json:"don_capabilities_summary,omitempty"` + } + a := Alias{ + ContractMetaData: v.ContractMetaData, + Capabilities: v.Capabilities, + Nodes: v.Nodes, + Nops: v.Nops, + Dons: v.Dons, + } + dc, err := v.DonDenormalizedView() + if err != nil { + return nil, err + } + a.DonCapabilities = dc + return json.MarshalIndent(&a, "", " ") } -func GenerateCapRegView(capReg *capabilities_registry.CapabilitiesRegistry) (CapRegView, error) { +// GenerateCapabilityRegistryView generates a CapRegView from a CapabilitiesRegistry contract. +func GenerateCapabilityRegistryView(capReg *capabilities_registry.CapabilitiesRegistry) (CapabilityRegistryView, error) { tv, err := types.NewContractMetaData(capReg, capReg.Address()) if err != nil { - return CapRegView{}, err + return CapabilityRegistryView{}, err } caps, err := capReg.GetCapabilities(nil) if err != nil { - return CapRegView{}, err + return CapabilityRegistryView{}, err } var capViews []CapabilityView for _, capability := range caps { - capViews = append(capViews, CapabilityView{ - LabelledName: capability.LabelledName, - Version: capability.Version, - ConfigContract: capability.ConfigurationContract, - }) + capViews = append(capViews, NewCapabilityView(capability)) + } + donInfos, err := capReg.GetDONs(nil) + if err != nil { + return CapabilityRegistryView{}, err + } + var donViews []DonView + for _, donInfo := range donInfos { + donViews = append(donViews, NewDonView(donInfo)) + } + + nodeInfos, err := capReg.GetNodes(nil) + if err != nil { + return CapabilityRegistryView{}, err + } + var nodeViews []NodeView + for _, nodeInfo := range nodeInfos { + nodeViews = append(nodeViews, NewNodeView(nodeInfo)) } - return CapRegView{ + + nopInfos, err := capReg.GetNodeOperators(nil) + if err != nil { + return CapabilityRegistryView{}, err + } + var nopViews []NopView + for _, nopInfo := range nopInfos { + nopViews = append(nopViews, NewNopView(nopInfo)) + } + + return CapabilityRegistryView{ ContractMetaData: tv, Capabilities: capViews, + Dons: donViews, + Nodes: nodeViews, + Nops: nopViews, }, nil } + +// DonDenormalizedView is a view of a Don with its associated Nodes and Capabilities. +type DonDenormalizedView struct { + Don DonUniversalMetadata `json:"don"` + Nodes []NodeDenormalizedView `json:"nodes"` + Capabilities []CapabilityView `json:"capabilities"` +} + +// DonDenormalizedView returns a list of DonDenormalizedView, which are Dons with their associated +// Nodes and Capabilities. This is a useful form of the CapabilityRegistryView, but it is not definitive. +// The full CapRegView should be used for the most accurate information as it can contain +// Capabilities and Nodes the are not associated with any Don. +func (v CapabilityRegistryView) DonDenormalizedView() ([]DonDenormalizedView, error) { + var out []DonDenormalizedView + for _, don := range v.Dons { + var nodes []NodeDenormalizedView + for _, node := range v.Nodes { + if don.hasNode(node) { + ndv, err := v.nodeDenormalizedView(node) + if err != nil { + return nil, err + } + nodes = append(nodes, ndv) + } + } + var capabilities []CapabilityView + for _, cap := range v.Capabilities { + if don.hasCapability(cap) { + capabilities = append(capabilities, cap) + } + } + out = append(out, DonDenormalizedView{ + Don: don.DonUniversalMetadata, + Nodes: nodes, + Capabilities: capabilities, + }) + } + return out, nil +} + +// CapabilityView is a serialization-friendly view of a capability in the capabilities registry. +type CapabilityView struct { + ID string `json:"id"` // hex 32 bytes + LabelledName string `json:"labelled_name"` + Version string `json:"version"` + CapabilityType uint8 `json:"capability_type"` + ResponseType uint8 `json:"response_type"` + ConfigurationContract common.Address `json:"configuration_contract,omitempty"` + IsDeprecated bool `json:"is_deprecated,omitempty"` +} + +// NewCapabilityView creates a CapabilityView from a CapabilitiesRegistryCapabilityInfo. +func NewCapabilityView(capInfo capabilities_registry.CapabilitiesRegistryCapabilityInfo) CapabilityView { + return CapabilityView{ + ID: hex.EncodeToString(capInfo.HashedId[:]), + LabelledName: capInfo.LabelledName, + Version: capInfo.Version, + CapabilityType: capInfo.CapabilityType, + ResponseType: capInfo.ResponseType, + ConfigurationContract: capInfo.ConfigurationContract, + IsDeprecated: capInfo.IsDeprecated, + } +} + +// Validate checks that the CapabilityView is valid. +func (cv CapabilityView) Validate() error { + id, err := hex.DecodeString(cv.ID) + if err != nil { + return err + } + if len(id) != 32 { + return errors.New("capability id must be 32 bytes") + } + return nil +} + +// DonView is a serialization-friendly view of a Don in the capabilities registry. +type DonView struct { + DonUniversalMetadata + NodeP2PIds []p2pkey.PeerID `json:"node_p2p_ids,omitempty"` + CapabilityConfigurations []CapabilitiesConfiguration `json:"capability_configurations,omitempty"` +} + +type DonUniversalMetadata struct { + ID uint32 `json:"id"` + ConfigCount uint32 `json:"config_count"` + F uint8 `json:"f"` + IsPublic bool `json:"is_public,omitempty"` + AcceptsWorkflows bool `json:"accepts_workflows,omitempty"` +} + +// NewDonView creates a DonView from a CapabilitiesRegistryDONInfo. +func NewDonView(d capabilities_registry.CapabilitiesRegistryDONInfo) DonView { + return DonView{ + DonUniversalMetadata: DonUniversalMetadata{ + ID: d.Id, + ConfigCount: d.ConfigCount, + F: d.F, + IsPublic: d.IsPublic, + AcceptsWorkflows: d.AcceptsWorkflows, + }, + NodeP2PIds: p2pIds(d.NodeP2PIds), + CapabilityConfigurations: NewCapabilityConfigurations(d.CapabilityConfigurations), + } +} + +func (dv DonView) Validate() error { + for i, cfg := range dv.CapabilityConfigurations { + if err := cfg.Validate(); err != nil { + return fmt.Errorf("capability configuration at index %d invalid:%w ", i, err) + } + } + return nil +} + +// CapabilitiesConfiguration is a serialization-friendly view of a capability configuration in the capabilities registry. +type CapabilitiesConfiguration struct { + ID string `json:"id"` // hex 32 bytes + Config string `json:"config"` // hex +} + +// NewCapabilityConfigurations creates a list of CapabilitiesConfiguration from a list of CapabilitiesRegistryCapabilityConfiguration. +func NewCapabilityConfigurations(cfgs []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration) []CapabilitiesConfiguration { + var out []CapabilitiesConfiguration + for _, cfg := range cfgs { + out = append(out, CapabilitiesConfiguration{ + ID: hex.EncodeToString(cfg.CapabilityId[:]), + Config: hex.EncodeToString(cfg.Config), + }) + } + return out +} + +func (cc CapabilitiesConfiguration) Validate() error { + id, err := hex.DecodeString(cc.ID) + if err != nil { + return errors.New("capability id must be hex encoded") + } + if len(id) != 32 { + return errors.New("capability id must be 32 bytes") + } + _, err = hex.DecodeString(cc.Config) + if err != nil { + return errors.New("config must be hex encoded") + } + return nil +} + +// NodeView is a serialization-friendly view of a node in the capabilities registry. +type NodeView struct { + NodeUniversalMetadata + NodeOperatorID uint32 `json:"node_operator_id"` + CapabilityIDs []string `json:"capability_ids,omitempty"` // hex 32 bytes + DONIDs []*big.Int `json:",don_ids, omitempty"` +} + +// NodeUniversalMetadata is a serialization-friendly view of the universal metadata of a node in the capabilities registry. +type NodeUniversalMetadata struct { + ConfigCount uint32 `json:"config_count"` + WorkflowDONID uint32 `json:"workflow_don_id"` + Signer string `json:"signer"` // hex 32 bytes + P2pId p2pkey.PeerID `json:"p2p_id"` + EncryptionPublicKey string `json:"encryption_public_key"` // hex 32 bytes +} + +// NewNodeView creates a NodeView from a CapabilitiesRegistryNodeInfoProviderNodeInfo. +func NewNodeView(n capabilities_registry.INodeInfoProviderNodeInfo) NodeView { + return NodeView{ + NodeUniversalMetadata: NodeUniversalMetadata{ + ConfigCount: n.ConfigCount, + WorkflowDONID: n.WorkflowDONId, + Signer: hex.EncodeToString(n.Signer[:]), + P2pId: p2pkey.PeerID(n.P2pId), + EncryptionPublicKey: hex.EncodeToString(n.EncryptionPublicKey[:]), + }, + NodeOperatorID: n.NodeOperatorId, + CapabilityIDs: hexIds(n.HashedCapabilityIds), + DONIDs: n.CapabilitiesDONIds, + } +} + +func (nv NodeView) Validate() error { + s, err := hex.DecodeString(nv.Signer) + if err != nil { + return errors.New("signer must be hex encoded") + } + if len(s) != 32 { + return errors.New("signer must be 32 bytes") + } + + e, err := hex.DecodeString(nv.EncryptionPublicKey) + if err != nil { + return errors.New("encryption public key must be hex encoded") + } + if len(e) != 32 { + return errors.New("encryption public key must be 32 bytes") + } + + for _, id := range nv.CapabilityIDs { + cid, err := hex.DecodeString(id) + if err != nil { + return errors.New("hashed capability id must be hex encoded") + } + if len(cid) != 32 { + return errors.New("hashed capability id must be 32 bytes") + } + } + return nil +} + +// NodeDenormalizedView is a serialization-friendly view of a node in the capabilities registry with its associated NOP. +type NodeDenormalizedView struct { + NodeUniversalMetadata + Nop NopView `json:"nop"` +} + +type NopView struct { + Admin common.Address `json:"admin"` + Name string `json:"name"` +} + +func NewNopView(nop capabilities_registry.CapabilitiesRegistryNodeOperator) NopView { + return NopView{ + Admin: nop.Admin, + Name: nop.Name, + } +} + +func (v CapabilityRegistryView) nodeDenormalizedView(n NodeView) (NodeDenormalizedView, error) { + nop, err := nodeNop(n, v.Nops) + if err != nil { + return NodeDenormalizedView{}, err + } + return NodeDenormalizedView{ + NodeUniversalMetadata: n.NodeUniversalMetadata, + Nop: nop, + }, nil +} + +func nodeNop(n NodeView, nops []NopView) (NopView, error) { + for i, nop := range nops { + // nops are 1-indexed. there is no natural key to match on, so we use the index. + idx := i + 1 + if n.NodeOperatorID == uint32(idx) { + return nop, nil + } + } + return NopView{}, fmt.Errorf("could not find nop for node %d", n.NodeOperatorID) +} + +func p2pIds(rawIds [][32]byte) []p2pkey.PeerID { + var out []p2pkey.PeerID + for _, id := range rawIds { + out = append(out, p2pkey.PeerID(id)) + } + return out +} + +func hexIds(ids [][32]byte) []string { + var out []string + for _, id := range ids { + out = append(out, hex.EncodeToString(id[:])) + } + return out +} + +func (v DonView) hasNode(node NodeView) bool { + donId := big.NewInt(int64(v.ID)) + return slices.ContainsFunc(node.DONIDs, func(elem *big.Int) bool { return elem.Cmp(donId) == 0 }) +} + +func (v DonView) hasCapability(candidate CapabilityView) bool { + return slices.ContainsFunc(v.CapabilityConfigurations, func(elem CapabilitiesConfiguration) bool { return elem.ID == candidate.ID }) +} diff --git a/deployment/common/view/v1_0/capreg_test.go b/deployment/common/view/v1_0/capreg_test.go new file mode 100644 index 00000000000..8ba7b880364 --- /dev/null +++ b/deployment/common/view/v1_0/capreg_test.go @@ -0,0 +1,267 @@ +package v1_0 + +import ( + "math/big" + "testing" + + "github.com/smartcontractkit/chainlink/deployment/common/view/types" + cr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + "github.com/stretchr/testify/assert" +) + +func TestCapRegView_Denormalize(t *testing.T) { + type fields struct { + ContractMetaData types.ContractMetaData + Capabilities []CapabilityView + Nodes []NodeView + Dons []DonView + Nops []NopView + } + tests := []struct { + name string + fields fields + want []DonDenormalizedView + wantErr bool + }{ + { + name: "empty", + fields: fields{}, + want: nil, + }, + { + name: "one don", + fields: fields{ + Dons: []DonView{ + NewDonView(cr.CapabilitiesRegistryDONInfo{ + Id: 1, + CapabilityConfigurations: []cr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: [32]byte{0: 1}, + }, + }, + }), + }, + Nodes: []NodeView{ + NewNodeView(cr.INodeInfoProviderNodeInfo{ + CapabilitiesDONIds: []*big.Int{big.NewInt(1)}, + NodeOperatorId: 1, // 1-based index + }), + }, + Nops: []NopView{ + {Name: "first nop"}, + }, + Capabilities: []CapabilityView{ + NewCapabilityView(cr.CapabilitiesRegistryCapabilityInfo{ + HashedId: [32]byte{0: 1}, + LabelledName: "cap1", + Version: "1.0.0", + }), + + NewCapabilityView(cr.CapabilitiesRegistryCapabilityInfo{ + HashedId: [32]byte{0: 2}, + LabelledName: "cap2", + Version: "1.0.0", + }), + }, + }, + want: []DonDenormalizedView{ + { + Don: NewDonView(cr.CapabilitiesRegistryDONInfo{ + Id: 1, + CapabilityConfigurations: []cr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: [32]byte{0: 1}, + }, + }, + }).DonUniversalMetadata, + Nodes: []NodeDenormalizedView{ + { + NodeUniversalMetadata: NewNodeView(cr.INodeInfoProviderNodeInfo{ + CapabilitiesDONIds: []*big.Int{big.NewInt(1)}, + NodeOperatorId: 1, // 1-based index + }).NodeUniversalMetadata, + Nop: NopView{Name: "first nop"}, + }, + }, + Capabilities: []CapabilityView{ + NewCapabilityView(cr.CapabilitiesRegistryCapabilityInfo{ + HashedId: [32]byte{0: 1}, + LabelledName: "cap1", + Version: "1.0.0", + }), + }, + }, + }, + }, + + { + name: "two dons, multiple capabilities", + fields: fields{ + Dons: []DonView{ + // don1 + NewDonView(cr.CapabilitiesRegistryDONInfo{ + Id: 1, + CapabilityConfigurations: []cr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: [32]byte{0: 1}, + }, + { + CapabilityId: [32]byte{1: 1}, + }, + }, + }), + + // don2 + NewDonView(cr.CapabilitiesRegistryDONInfo{ + Id: 2, + CapabilityConfigurations: []cr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: [32]byte{2: 2}, + }, + }, + }), + }, + Nodes: []NodeView{ + // nodes for don1 + NewNodeView(cr.INodeInfoProviderNodeInfo{ + P2pId: [32]byte{31: 1}, + CapabilitiesDONIds: []*big.Int{big.NewInt(1)}, // matches don ID 1 + NodeOperatorId: 1, // 1-based index + }), + + NewNodeView(cr.INodeInfoProviderNodeInfo{ + P2pId: [32]byte{31: 11}, + CapabilitiesDONIds: []*big.Int{big.NewInt(1)}, // matches don ID 1 + NodeOperatorId: 3, // 1-based index + }), + + // nodes for don2 + NewNodeView(cr.INodeInfoProviderNodeInfo{ + P2pId: [32]byte{31: 22}, + CapabilitiesDONIds: []*big.Int{big.NewInt(2)}, // matches don ID 2 + NodeOperatorId: 2, // 1-based index + }), + }, + Nops: []NopView{ + {Name: "first nop"}, + {Name: "second nop"}, + {Name: "third nop"}, + }, + Capabilities: []CapabilityView{ + //capabilities for don1 + NewCapabilityView(cr.CapabilitiesRegistryCapabilityInfo{ + HashedId: [32]byte{0: 1}, + LabelledName: "cap1", + Version: "1.0.0", + }), + NewCapabilityView(cr.CapabilitiesRegistryCapabilityInfo{ + HashedId: [32]byte{1: 1}, // matches don ID 1, capabitility ID 2 + LabelledName: "cap2", + Version: "1.0.0", + }), + + //capabilities for don2 + NewCapabilityView(cr.CapabilitiesRegistryCapabilityInfo{ + HashedId: [32]byte{2: 2}, // matches don ID 2, capabitility ID 1 + LabelledName: "other cap", + Version: "1.0.0", + }), + }, + }, + want: []DonDenormalizedView{ + { + Don: NewDonView(cr.CapabilitiesRegistryDONInfo{ + Id: 1, + CapabilityConfigurations: []cr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: [32]byte{0: 1}, + }, + { + CapabilityId: [32]byte{1: 1}, + }, + }, + }).DonUniversalMetadata, + Nodes: []NodeDenormalizedView{ + { + NodeUniversalMetadata: NewNodeView(cr.INodeInfoProviderNodeInfo{ + P2pId: [32]byte{31: 1}, + CapabilitiesDONIds: []*big.Int{big.NewInt(1)}, // matches don ID 1 + }).NodeUniversalMetadata, + Nop: NopView{Name: "first nop"}, + }, + { + NodeUniversalMetadata: NewNodeView(cr.INodeInfoProviderNodeInfo{ + P2pId: [32]byte{31: 11}, + CapabilitiesDONIds: []*big.Int{big.NewInt(1)}, // matches don ID 1 + }).NodeUniversalMetadata, + Nop: NopView{Name: "third nop"}, + }, + }, + Capabilities: []CapabilityView{ + NewCapabilityView(cr.CapabilitiesRegistryCapabilityInfo{ + HashedId: [32]byte{0: 1}, + LabelledName: "cap1", + Version: "1.0.0", + }), + + NewCapabilityView(cr.CapabilitiesRegistryCapabilityInfo{ + HashedId: [32]byte{1: 1}, + LabelledName: "cap2", + Version: "1.0.0", + }), + }, + }, + { + Don: NewDonView(cr.CapabilitiesRegistryDONInfo{ + Id: 2, + CapabilityConfigurations: []cr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: [32]byte{2: 2}, + }, + }, + }).DonUniversalMetadata, + Nodes: []NodeDenormalizedView{ + { + NodeUniversalMetadata: NewNodeView(cr.INodeInfoProviderNodeInfo{ + P2pId: [32]byte{31: 22}, + CapabilitiesDONIds: []*big.Int{big.NewInt(2)}, // matches don ID 2 + }).NodeUniversalMetadata, + Nop: NopView{Name: "second nop"}, + }, + }, + Capabilities: []CapabilityView{ + NewCapabilityView(cr.CapabilitiesRegistryCapabilityInfo{ + HashedId: [32]byte{2: 2}, // matches don ID 2, capabitility ID 1 + LabelledName: "other cap", + Version: "1.0.0", + }), + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := CapabilityRegistryView{ + ContractMetaData: tt.fields.ContractMetaData, + Capabilities: tt.fields.Capabilities, + Nodes: tt.fields.Nodes, + Dons: tt.fields.Dons, + Nops: tt.fields.Nops, + } + got, err := v.DonDenormalizedView() + if (err != nil) != tt.wantErr { + t.Errorf("CapRegView.Denormalize() error = %v, wantErr %v", err, tt.wantErr) + return + } + for i := range got { + assert.Equal(t, tt.want[i].Don, got[i].Don) + for j := range got[i].Nodes { + assert.Equal(t, tt.want[i].Nodes[j].NodeUniversalMetadata, got[i].Nodes[j].NodeUniversalMetadata, "NodeUniversalMetadata mismatch at index %d for don %d", j, i) + assert.Equal(t, tt.want[i].Nodes[j].Nop, got[i].Nodes[j].Nop, "Nop mismatch at index %d for don %d", j, i) + } + assert.Equal(t, tt.want[i].Capabilities, got[i].Capabilities) + } + }) + } +} diff --git a/deployment/keystone/state.go b/deployment/keystone/state.go index c9f94e5bb81..e226d3b4b91 100644 --- a/deployment/keystone/state.go +++ b/deployment/keystone/state.go @@ -31,7 +31,7 @@ type ContractSet struct { func (cs ContractSet) View() (view.KeystoneChainView, error) { out := view.NewKeystoneChainView() if cs.CapabilitiesRegistry != nil { - capRegView, err := common_v1_0.GenerateCapRegView(cs.CapabilitiesRegistry) + capRegView, err := common_v1_0.GenerateCapabilityRegistryView(cs.CapabilitiesRegistry) if err != nil { return view.KeystoneChainView{}, err } diff --git a/deployment/keystone/view/view.go b/deployment/keystone/view/view.go index ae3f374a01f..1320344b7ca 100644 --- a/deployment/keystone/view/view.go +++ b/deployment/keystone/view/view.go @@ -8,13 +8,13 @@ import ( ) type KeystoneChainView struct { - CapabilityRegistry map[string]common_v1_0.CapRegView `json:"capabilityRegistry,omitempty"` + CapabilityRegistry map[string]common_v1_0.CapabilityRegistryView `json:"capabilityRegistry,omitempty"` // TODO forwarders etc } func NewKeystoneChainView() KeystoneChainView { return KeystoneChainView{ - CapabilityRegistry: make(map[string]common_v1_0.CapRegView), + CapabilityRegistry: make(map[string]common_v1_0.CapabilityRegistryView), } }