From f5d228ea0b803cdc07a7351f2eec3cbd46b0bc06 Mon Sep 17 00:00:00 2001 From: Vyzaldy Sanchez Date: Mon, 25 Nov 2024 12:54:47 -0400 Subject: [PATCH] Allow to hydrate contract from the capability registry view (#15309) * Adds `Hydrate()` functionality to `CapabilityRegistryView` * Extracts hydration functionality into a `common` package * Add `UnmarshalJSON` functionality to `CapabilityRegistryView` * Adds test - WIP * Completes tests comparing capability registry views * Refactors to use existing deployment logic * Gets deployed contract from contract sets * Uses uint64 type annotation * Moves implementation to `keystone/test` --- deployment/common/view/v1_0/capreg.go | 123 ++++- deployment/common/view/v1_0/capreg_test.go | 3 +- .../keystone/capability_registry_deployer.go | 1 + .../keystone/changeset/deploy_registry.go | 8 +- .../changeset/deploy_registry_test.go | 1 + deployment/keystone/changeset/view_test.go | 1 + deployment/keystone/contract_set.go | 1 + deployment/keystone/state.go | 1 + .../test/changeset/capability_registry.go | 87 ++++ .../changeset/capability_registry_test.go | 46 ++ .../testdata/capability_registry_view.json | 455 ++++++++++++++++++ 11 files changed, 717 insertions(+), 10 deletions(-) create mode 100644 deployment/keystone/test/changeset/capability_registry.go create mode 100644 deployment/keystone/test/changeset/capability_registry_test.go create mode 100644 deployment/keystone/test/changeset/testdata/capability_registry_view.json diff --git a/deployment/common/view/v1_0/capreg.go b/deployment/common/view/v1_0/capreg.go index 2ddd5a13463..3274cc4e97f 100644 --- a/deployment/common/view/v1_0/capreg.go +++ b/deployment/common/view/v1_0/capreg.go @@ -26,7 +26,7 @@ type CapabilityRegistryView struct { // 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) { +func (v *CapabilityRegistryView) MarshalJSON() ([]byte, error) { // Alias to avoid recursive calls type Alias struct { types.ContractMetaData @@ -51,6 +51,36 @@ func (v CapabilityRegistryView) MarshalJSON() ([]byte, error) { return json.MarshalIndent(&a, "", " ") } +// UnmarshalJSON unmarshals the CapabilityRegistryView from JSON. Since the CapabilityRegistryView doesn't hold a DonCapabilities field, +// it is not unmarshaled. +func (v *CapabilityRegistryView) UnmarshalJSON(data []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, + } + if err := json.Unmarshal(data, &a); err != nil { + return err + } + v.ContractMetaData = a.ContractMetaData + v.Capabilities = a.Capabilities + v.Nodes = a.Nodes + v.Nops = a.Nops + v.Dons = a.Dons + return nil +} + // GenerateCapabilityRegistryView generates a CapRegView from a CapabilitiesRegistry contract. func GenerateCapabilityRegistryView(capReg *capabilities_registry.CapabilitiesRegistry) (CapabilityRegistryView, error) { tv, err := types.NewContractMetaData(capReg, capReg.Address()) @@ -112,7 +142,7 @@ type DonDenormalizedView struct { // 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) { +func (v *CapabilityRegistryView) DonDenormalizedView() ([]DonDenormalizedView, error) { var out []DonDenormalizedView for _, don := range v.Dons { var nodes []NodeDenormalizedView @@ -140,6 +170,91 @@ func (v CapabilityRegistryView) DonDenormalizedView() ([]DonDenormalizedView, er return out, nil } +func (v *CapabilityRegistryView) NodesToNodesParams() ([]capabilities_registry.CapabilitiesRegistryNodeParams, error) { + var nodesParams []capabilities_registry.CapabilitiesRegistryNodeParams + for _, node := range v.Nodes { + signer, err := hexTo32Bytes(node.Signer) + if err != nil { + return nil, err + } + encryptionPubKey, err := hexTo32Bytes(node.EncryptionPublicKey) + if err != nil { + return nil, err + } + capIDs := make([][32]byte, len(node.CapabilityIDs)) + for i, id := range node.CapabilityIDs { + cid, err := hexTo32Bytes(id) + if err != nil { + return nil, err + } + capIDs[i] = cid + } + nodesParams = append(nodesParams, capabilities_registry.CapabilitiesRegistryNodeParams{ + Signer: signer, + P2pId: node.P2pId, + EncryptionPublicKey: encryptionPubKey, + NodeOperatorId: node.NodeOperatorID, + HashedCapabilityIds: capIDs, + }) + } + + return nodesParams, nil +} + +func (v *CapabilityRegistryView) CapabilitiesToCapabilitiesParams() []capabilities_registry.CapabilitiesRegistryCapability { + var capabilitiesParams []capabilities_registry.CapabilitiesRegistryCapability + for _, capability := range v.Capabilities { + capabilitiesParams = append(capabilitiesParams, capabilities_registry.CapabilitiesRegistryCapability{ + LabelledName: capability.LabelledName, + Version: capability.Version, + CapabilityType: capability.CapabilityType, + ResponseType: capability.ResponseType, + ConfigurationContract: capability.ConfigurationContract, + }) + } + return capabilitiesParams +} + +func (v *CapabilityRegistryView) NopsToNopsParams() []capabilities_registry.CapabilitiesRegistryNodeOperator { + var nopsParams []capabilities_registry.CapabilitiesRegistryNodeOperator + for _, nop := range v.Nops { + nopsParams = append(nopsParams, capabilities_registry.CapabilitiesRegistryNodeOperator{ + Admin: nop.Admin, + Name: nop.Name, + }) + } + return nopsParams +} + +func (v *CapabilityRegistryView) CapabilityConfigToCapabilityConfigParams(don DonView) ([]capabilities_registry.CapabilitiesRegistryCapabilityConfiguration, error) { + var cfgs []capabilities_registry.CapabilitiesRegistryCapabilityConfiguration + for _, cfg := range don.CapabilityConfigurations { + cid, err := hexTo32Bytes(cfg.ID) + if err != nil { + return nil, err + } + config, err := hex.DecodeString(cfg.Config) + if err != nil { + return nil, err + } + cfgs = append(cfgs, capabilities_registry.CapabilitiesRegistryCapabilityConfiguration{ + CapabilityId: cid, + Config: config, + }) + } + return cfgs, nil +} + +func hexTo32Bytes(val string) ([32]byte, error) { + var out [32]byte + b, err := hex.DecodeString(val) + if err != nil { + return out, err + } + copy(out[:], b) + 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 @@ -272,7 +387,7 @@ func NewNodeView(n capabilities_registry.INodeInfoProviderNodeInfo) NodeView { ConfigCount: n.ConfigCount, WorkflowDONID: n.WorkflowDONId, Signer: hex.EncodeToString(n.Signer[:]), - P2pId: p2pkey.PeerID(n.P2pId), + P2pId: n.P2pId, EncryptionPublicKey: hex.EncodeToString(n.EncryptionPublicKey[:]), }, NodeOperatorID: n.NodeOperatorId, @@ -328,7 +443,7 @@ func NewNopView(nop capabilities_registry.CapabilitiesRegistryNodeOperator) NopV } } -func (v CapabilityRegistryView) nodeDenormalizedView(n NodeView) (NodeDenormalizedView, error) { +func (v *CapabilityRegistryView) nodeDenormalizedView(n NodeView) (NodeDenormalizedView, error) { nop, err := nodeNop(n, v.Nops) if err != nil { return NodeDenormalizedView{}, err diff --git a/deployment/common/view/v1_0/capreg_test.go b/deployment/common/view/v1_0/capreg_test.go index 8ba7b880364..2f58034a619 100644 --- a/deployment/common/view/v1_0/capreg_test.go +++ b/deployment/common/view/v1_0/capreg_test.go @@ -4,9 +4,10 @@ import ( "math/big" "testing" + "github.com/stretchr/testify/assert" + "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) { diff --git a/deployment/keystone/capability_registry_deployer.go b/deployment/keystone/capability_registry_deployer.go index efe1d23f11f..a4648c27905 100644 --- a/deployment/keystone/capability_registry_deployer.go +++ b/deployment/keystone/capability_registry_deployer.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" ) diff --git a/deployment/keystone/changeset/deploy_registry.go b/deployment/keystone/changeset/deploy_registry.go index 2c08e5ca8b7..e9b142812d7 100644 --- a/deployment/keystone/changeset/deploy_registry.go +++ b/deployment/keystone/changeset/deploy_registry.go @@ -7,11 +7,9 @@ import ( kslib "github.com/smartcontractkit/chainlink/deployment/keystone" ) -func DeployCapabilityRegistry(env deployment.Environment, config interface{}) (deployment.ChangesetOutput, error) { - registrySelector, ok := config.(uint64) - if !ok { - return deployment.ChangesetOutput{}, deployment.ErrInvalidConfig - } +var _ deployment.ChangeSet[uint64] = DeployCapabilityRegistry + +func DeployCapabilityRegistry(env deployment.Environment, registrySelector uint64) (deployment.ChangesetOutput, error) { chain, ok := env.Chains[registrySelector] if !ok { return deployment.ChangesetOutput{}, fmt.Errorf("chain not found in environment") diff --git a/deployment/keystone/changeset/deploy_registry_test.go b/deployment/keystone/changeset/deploy_registry_test.go index 6aa383ef68c..9abf357f2a8 100644 --- a/deployment/keystone/changeset/deploy_registry_test.go +++ b/deployment/keystone/changeset/deploy_registry_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment/environment/memory" "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" ) diff --git a/deployment/keystone/changeset/view_test.go b/deployment/keystone/changeset/view_test.go index 2d4569dfbec..023b4462549 100644 --- a/deployment/keystone/changeset/view_test.go +++ b/deployment/keystone/changeset/view_test.go @@ -7,6 +7,7 @@ import ( "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment/environment/memory" ) diff --git a/deployment/keystone/contract_set.go b/deployment/keystone/contract_set.go index a0446dcfce0..ccd89f6905f 100644 --- a/deployment/keystone/contract_set.go +++ b/deployment/keystone/contract_set.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment" ) diff --git a/deployment/keystone/state.go b/deployment/keystone/state.go index 33200a40e02..68f2ab97a5d 100644 --- a/deployment/keystone/state.go +++ b/deployment/keystone/state.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment" common_v1_0 "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" "github.com/smartcontractkit/chainlink/deployment/keystone/view" diff --git a/deployment/keystone/test/changeset/capability_registry.go b/deployment/keystone/test/changeset/capability_registry.go new file mode 100644 index 00000000000..28ee711dc75 --- /dev/null +++ b/deployment/keystone/test/changeset/capability_registry.go @@ -0,0 +1,87 @@ +package changeset + +import ( + "fmt" + "testing" + + chainsel "github.com/smartcontractkit/chain-selectors" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" + "github.com/smartcontractkit/chainlink/deployment/keystone" + "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" +) + +type HydrateConfig struct { + ChainID uint64 +} + +// HydrateCapabilityRegistry deploys a new capabilities registry contract and hydrates it with the provided data. +func HydrateCapabilityRegistry(t *testing.T, v v1_0.CapabilityRegistryView, env deployment.Environment, cfg HydrateConfig) (*capabilities_registry.CapabilitiesRegistry, error) { + t.Helper() + chainSelector, err := chainsel.SelectorFromChainId(cfg.ChainID) + if err != nil { + return nil, fmt.Errorf("failed to get chain selector from chain id: %w", err) + } + chain, ok := env.Chains[chainSelector] + if !ok { + return nil, fmt.Errorf("chain with id %d not found", cfg.ChainID) + } + changesetOutput, err := changeset.DeployCapabilityRegistry(env, chainSelector) + if err != nil { + return nil, fmt.Errorf("failed to deploy contract: %w", err) + } + + resp, err := keystone.GetContractSets(env.Logger, &keystone.GetContractSetsRequest{ + Chains: env.Chains, + AddressBook: changesetOutput.AddressBook, + }) + if err != nil { + return nil, fmt.Errorf("failed to get contract sets: %w", err) + } + cs, ok := resp.ContractSets[chainSelector] + if !ok { + return nil, fmt.Errorf("failed to get contract set for chain selector: %d, chain ID: %d", chainSelector, cfg.ChainID) + } + + deployedContract := cs.CapabilitiesRegistry + + nopsParams := v.NopsToNopsParams() + tx, err := deployedContract.AddNodeOperators(chain.DeployerKey, nopsParams) + if _, err = deployment.ConfirmIfNoError(chain, tx, keystone.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err)); err != nil { + return nil, fmt.Errorf("failed to add node operators: %w", err) + } + + capabilitiesParams := v.CapabilitiesToCapabilitiesParams() + tx, err = deployedContract.AddCapabilities(chain.DeployerKey, capabilitiesParams) + if _, err = deployment.ConfirmIfNoError(chain, tx, keystone.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err)); err != nil { + return nil, fmt.Errorf("failed to add capabilities: %w", err) + } + + nodesParams, err := v.NodesToNodesParams() + if err != nil { + return nil, fmt.Errorf("failed to convert nodes to nodes params: %w", err) + } + tx, err = deployedContract.AddNodes(chain.DeployerKey, nodesParams) + if _, err = deployment.ConfirmIfNoError(chain, tx, keystone.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err)); err != nil { + return nil, fmt.Errorf("failed to add nodes: %w", err) + } + + for _, don := range v.Dons { + cfgs, err := v.CapabilityConfigToCapabilityConfigParams(don) + if err != nil { + return nil, fmt.Errorf("failed to convert capability configurations to capability configuration params: %w", err) + } + var peerIds [][32]byte + for _, id := range don.NodeP2PIds { + peerIds = append(peerIds, id) + } + tx, err = deployedContract.AddDON(chain.DeployerKey, peerIds, cfgs, don.IsPublic, don.AcceptsWorkflows, don.F) + if _, err = deployment.ConfirmIfNoError(chain, tx, keystone.DecodeErr(capabilities_registry.CapabilitiesRegistryABI, err)); err != nil { + return nil, fmt.Errorf("failed to add don: %w", err) + } + } + + return deployedContract, nil +} diff --git a/deployment/keystone/test/changeset/capability_registry_test.go b/deployment/keystone/test/changeset/capability_registry_test.go new file mode 100644 index 00000000000..765c95ff294 --- /dev/null +++ b/deployment/keystone/test/changeset/capability_registry_test.go @@ -0,0 +1,46 @@ +package changeset + +import ( + "encoding/json" + "os" + "testing" + + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + + chainsel "github.com/smartcontractkit/chain-selectors" + + "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" + "github.com/smartcontractkit/chainlink/deployment/environment/memory" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func TestHydrateCapabilityRegistry(t *testing.T) { + b, err := os.ReadFile("testdata/capability_registry_view.json") + require.NoError(t, err) + require.NotEmpty(t, b) + var capabilityRegistryView v1_0.CapabilityRegistryView + require.NoError(t, json.Unmarshal(b, &capabilityRegistryView)) + + chainID := chainsel.TEST_90000001.EvmChainID + cfg := HydrateConfig{ChainID: chainID} + env := memory.NewMemoryEnvironment(t, logger.TestLogger(t), zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + Bootstraps: 1, + Chains: 1, + Nodes: 4, + }) + hydrated, err := HydrateCapabilityRegistry(t, capabilityRegistryView, env, cfg) + require.NoError(t, err) + require.NotNil(t, hydrated) + hydratedCapView, err := v1_0.GenerateCapabilityRegistryView(hydrated) + require.NoError(t, err) + + // Setting address/owner values to be the same in order to compare the views + hydratedCapView.Address = capabilityRegistryView.Address + hydratedCapView.Owner = capabilityRegistryView.Owner + b1, err := capabilityRegistryView.MarshalJSON() + require.NoError(t, err) + b2, err := hydratedCapView.MarshalJSON() + require.NoError(t, err) + require.Equal(t, string(b1), string(b2)) +} diff --git a/deployment/keystone/test/changeset/testdata/capability_registry_view.json b/deployment/keystone/test/changeset/testdata/capability_registry_view.json new file mode 100644 index 00000000000..fef22a7b4dc --- /dev/null +++ b/deployment/keystone/test/changeset/testdata/capability_registry_view.json @@ -0,0 +1,455 @@ +{ + "typeAndVersion": "CapabilitiesRegistry 1.1.0", + "address": "0x65a097a74769fe1c415993f45d9b56ffffb65807", + "owner": "0x61050aeba13a5f7ba0b06cac4e1aee625fe4a640", + "capabilities": [ + { + "id": "9f0f33d3737af0a2acae89e7f8c4638aeb886a67c1d84e13aee8532c70aa9483", + "labelled_name": "write_ethereum-testnet-sepolia", + "version": "1.0.0", + "capability_type": 3, + "response_type": 0, + "configuration_contract": "0x0000000000000000000000000000000000000000" + }, + { + "id": "1ea6e85dfb8ebb08b7cd82b42ab4655429a8ae116d513be0d68a7d7661667a8a", + "labelled_name": "streams-trigger", + "version": "1.0.0", + "capability_type": 0, + "response_type": 0, + "configuration_contract": "0x0000000000000000000000000000000000000000" + }, + { + "id": "578ebf7413c15e36dfd792396c5a4d75c43f0ee7bbabdb703f7c5acd0bb65a1b", + "labelled_name": "offchain_reporting", + "version": "1.0.0", + "capability_type": 2, + "response_type": 0, + "configuration_contract": "0x0000000000000000000000000000000000000000" + } + ], + "nodes": [ + { + "config_count": 1, + "workflow_don_id": 1, + "signer": "f36818345fb18549bd24305c79f2f28d80d25710000000000000000000000000", + "p2p_id": "p2p_12D3KooWGVWpMrCZDotqSYWj8xqb1i5kNecpoarczpQoSAHdqwj8", + "encryption_public_key": "986707930499b85ab01705aa8d5cd99dca40926db3fa1728e6b58d25fddfe46b", + "node_operator_id": 1, + "capability_ids": [ + "578ebf7413c15e36dfd792396c5a4d75c43f0ee7bbabdb703f7c5acd0bb65a1b" + ] + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "a0e11229b4b5d40d299f7f4b3348a1b16f5f3dd3000000000000000000000000", + "p2p_id": "p2p_12D3KooWPsGKEe34m9X8qiWVpa6VEcjdvhbygJRLKXdVEr3r96PH", + "encryption_public_key": "10c77049880fe0924f463763789985a0f76e09f82fc6f39943684b2dd05b98bf", + "node_operator_id": 2, + "capability_ids": [ + "9f0f33d3737af0a2acae89e7f8c4638aeb886a67c1d84e13aee8532c70aa9483" + ], + "capability_don_ids": [ + 2 + ] + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "3f871cd7c34e50320934b6ba9b90b249363fd378000000000000000000000000", + "p2p_id": "p2p_12D3KooWLjKKWLhYkr68PH9FaZZR93WbJuohWWcTfA9JeY1UPoJa", + "encryption_public_key": "5cf5abd0349b27cde43ad1a55aa3b02a5518aa6eed365bd65ca5e0e2cbfdaa5a", + "node_operator_id": 3, + "capability_ids": [ + "1ea6e85dfb8ebb08b7cd82b42ab4655429a8ae116d513be0d68a7d7661667a8a" + ], + "capability_don_ids": [ + 3 + ] + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "d7a432ce8f23c410302e7577de9973ccbca29576000000000000000000000000", + "p2p_id": "p2p_12D3KooWRG3rsPdQyWCPsQukUF5JqFLdPqVVXvEfG2PAPBbqdRXp", + "encryption_public_key": "c10cead34374ba20704a05ca0369018b41778a0297adb85554cdd6e5955bf4b6", + "node_operator_id": 3, + "capability_ids": [ + "1ea6e85dfb8ebb08b7cd82b42ab4655429a8ae116d513be0d68a7d7661667a8a" + ], + "capability_don_ids": [ + 3 + ] + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "f1d2b3d13c46b0c1ded96534aabd6bae61934e3f000000000000000000000000", + "p2p_id": "p2p_12D3KooWD1uj8Y9Phdr1yScfbBy1Q9ebedMVBjMUBLW4m4XnEzGF", + "encryption_public_key": "5fc3dae131da5b0ac5474ad029e3a6c547e17249a6266427d5eea1f529b6488b", + "node_operator_id": 3, + "capability_ids": [ + "1ea6e85dfb8ebb08b7cd82b42ab4655429a8ae116d513be0d68a7d7661667a8a" + ], + "capability_don_ids": [ + 3 + ] + }, + { + "config_count": 1, + "workflow_don_id": 1, + "signer": "afabd7685010d619ee6614e732b15e898c293426000000000000000000000000", + "p2p_id": "p2p_12D3KooWE8ymezL2wxXcZdhznnBkVNcBRbtxVfNx1vppu7Z7zfnd", + "encryption_public_key": "8a2aef988ccc85d985d12270c5263032b9a7c71a86430e7b3c30a2f493462aee", + "node_operator_id": 1, + "capability_ids": [ + "578ebf7413c15e36dfd792396c5a4d75c43f0ee7bbabdb703f7c5acd0bb65a1b" + ] + }, + { + "config_count": 1, + "workflow_don_id": 1, + "signer": "4bad6c33b85ba3bbbada74c93a7a719962456af8000000000000000000000000", + "p2p_id": "p2p_12D3KooWSEF7cqkbMrS6uCo6xzCsfcPgquN652mZijmay8e3pn5R", + "encryption_public_key": "cf8e0e8c830f733dd5992076d0adaeb34c97410dd5d8d04afccad88070b07f20", + "node_operator_id": 1, + "capability_ids": [ + "578ebf7413c15e36dfd792396c5a4d75c43f0ee7bbabdb703f7c5acd0bb65a1b" + ] + }, + { + "config_count": 1, + "workflow_don_id": 1, + "signer": "078cb3bca763a11a0c79df636b018311d9e36954000000000000000000000000", + "p2p_id": "p2p_12D3KooWAumheDZJdc3in1qiYGAQESTUm1nAWGA59eQPb3wuFHJa", + "encryption_public_key": "c8f45b74982037dd52eeb3df5295c7769757c72cde9a930fc82cb72caf0085ad", + "node_operator_id": 1, + "capability_ids": [ + "578ebf7413c15e36dfd792396c5a4d75c43f0ee7bbabdb703f7c5acd0bb65a1b" + ] + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "7621630d4f33e02f659445974060df375c126e82000000000000000000000000", + "p2p_id": "p2p_12D3KooWF1XmgvoLx45H3iGc9jD4mK7X1mctYmhZCqw5h1S1sXtA", + "encryption_public_key": "f8d7485910b56c9521dea1beb3d50151917bae18e131ad311ef90300318b8110", + "node_operator_id": 2, + "capability_ids": [ + "9f0f33d3737af0a2acae89e7f8c4638aeb886a67c1d84e13aee8532c70aa9483" + ], + "capability_don_ids": [ + 2 + ] + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "b5599a0a8aa99860997e42d650c3dc1ac767a4b1000000000000000000000000", + "p2p_id": "p2p_12D3KooWFBsQE8gCZndyefzCZG6KHyjoeii7o7ff171tQHsGYq1p", + "encryption_public_key": "4081aacba87f07ea54c32517a9b661963a5fed5fbfd2042ff0823293d8fbd523", + "node_operator_id": 2, + "capability_ids": [ + "9f0f33d3737af0a2acae89e7f8c4638aeb886a67c1d84e13aee8532c70aa9483" + ], + "capability_don_ids": [ + 2 + ] + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "2067cc4ec71978974265e578eb9b5c62cccc0d33000000000000000000000000", + "p2p_id": "p2p_12D3KooWKYQ51iqiS99MWfE8gwy7P1sN8oL4WboWLPxHRo8duc28", + "encryption_public_key": "533bfbd913615823cbb93251f27f3cbdd035a732c0a68ad49081b37a702fe4a1", + "node_operator_id": 2, + "capability_ids": [ + "9f0f33d3737af0a2acae89e7f8c4638aeb886a67c1d84e13aee8532c70aa9483" + ], + "capability_don_ids": [ + 2 + ] + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "cd08a4d5934c41a389c2bd7b6fadfb1dace5f110000000000000000000000000", + "p2p_id": "p2p_12D3KooWMQjP7pp8ecxMSMd3Y3NocDsHt7g64bd7iGJhCr35gkby", + "encryption_public_key": "9ee181448ef55e381217ec959375fb302eba62e61006e4c9467832836cc8640e", + "node_operator_id": 3, + "capability_ids": [ + "1ea6e85dfb8ebb08b7cd82b42ab4655429a8ae116d513be0d68a7d7661667a8a" + ], + "capability_don_ids": [ + 3 + ] + } + ], + "nops": [ + { + "admin": "0x528fdfde37119b52010a4920b09ef828249a3434", + "name": "nop 1" + }, + { + "admin": "0xe0967f1d0f36df4c53a34e491465c9d3f0095eb9", + "name": "nop 2" + }, + { + "admin": "0xe676a607b6f5c0b6d4c21aa616d727d5b0a47f35", + "name": "nop 3" + } + ], + "dons": [ + { + "id": 1, + "config_count": 1, + "f": 1, + "is_public": true, + "accepts_workflows": true, + "node_p2p_ids": [ + "p2p_12D3KooWAumheDZJdc3in1qiYGAQESTUm1nAWGA59eQPb3wuFHJa", + "p2p_12D3KooWE8ymezL2wxXcZdhznnBkVNcBRbtxVfNx1vppu7Z7zfnd", + "p2p_12D3KooWGVWpMrCZDotqSYWj8xqb1i5kNecpoarczpQoSAHdqwj8", + "p2p_12D3KooWSEF7cqkbMrS6uCo6xzCsfcPgquN652mZijmay8e3pn5R" + ], + "capability_configurations": [ + { + "id": "578ebf7413c15e36dfd792396c5a4d75c43f0ee7bbabdb703f7c5acd0bb65a1b", + "config": "0a00" + } + ] + }, + { + "id": 2, + "config_count": 1, + "f": 1, + "is_public": true, + "node_p2p_ids": [ + "p2p_12D3KooWF1XmgvoLx45H3iGc9jD4mK7X1mctYmhZCqw5h1S1sXtA", + "p2p_12D3KooWFBsQE8gCZndyefzCZG6KHyjoeii7o7ff171tQHsGYq1p", + "p2p_12D3KooWKYQ51iqiS99MWfE8gwy7P1sN8oL4WboWLPxHRo8duc28", + "p2p_12D3KooWPsGKEe34m9X8qiWVpa6VEcjdvhbygJRLKXdVEr3r96PH" + ], + "capability_configurations": [ + { + "id": "9f0f33d3737af0a2acae89e7f8c4638aeb886a67c1d84e13aee8532c70aa9483", + "config": "0a001a1a0a187369676e65645f7265706f72742e5369676e617475726573" + } + ] + }, + { + "id": 3, + "config_count": 1, + "f": 1, + "is_public": true, + "node_p2p_ids": [ + "p2p_12D3KooWD1uj8Y9Phdr1yScfbBy1Q9ebedMVBjMUBLW4m4XnEzGF", + "p2p_12D3KooWLjKKWLhYkr68PH9FaZZR93WbJuohWWcTfA9JeY1UPoJa", + "p2p_12D3KooWMQjP7pp8ecxMSMd3Y3NocDsHt7g64bd7iGJhCr35gkby", + "p2p_12D3KooWRG3rsPdQyWCPsQukUF5JqFLdPqVVXvEfG2PAPBbqdRXp" + ], + "capability_configurations": [ + { + "id": "1ea6e85dfb8ebb08b7cd82b42ab4655429a8ae116d513be0d68a7d7661667a8a", + "config": "0a00120a0a0208141202083c1802" + } + ] + } + ], + "don_capabilities_summary": [ + { + "don": { + "id": 1, + "config_count": 1, + "f": 1, + "is_public": true, + "accepts_workflows": true + }, + "nodes": [ + { + "config_count": 1, + "workflow_don_id": 1, + "signer": "f36818345fb18549bd24305c79f2f28d80d25710000000000000000000000000", + "p2p_id": "p2p_12D3KooWGVWpMrCZDotqSYWj8xqb1i5kNecpoarczpQoSAHdqwj8", + "encryption_public_key": "986707930499b85ab01705aa8d5cd99dca40926db3fa1728e6b58d25fddfe46b", + "nop": { + "admin": "0x528fdfde37119b52010a4920b09ef828249a3434", + "name": "nop 1" + } + }, + { + "config_count": 1, + "workflow_don_id": 1, + "signer": "afabd7685010d619ee6614e732b15e898c293426000000000000000000000000", + "p2p_id": "p2p_12D3KooWE8ymezL2wxXcZdhznnBkVNcBRbtxVfNx1vppu7Z7zfnd", + "encryption_public_key": "8a2aef988ccc85d985d12270c5263032b9a7c71a86430e7b3c30a2f493462aee", + "nop": { + "admin": "0x528fdfde37119b52010a4920b09ef828249a3434", + "name": "nop 1" + } + }, + { + "config_count": 1, + "workflow_don_id": 1, + "signer": "4bad6c33b85ba3bbbada74c93a7a719962456af8000000000000000000000000", + "p2p_id": "p2p_12D3KooWSEF7cqkbMrS6uCo6xzCsfcPgquN652mZijmay8e3pn5R", + "encryption_public_key": "cf8e0e8c830f733dd5992076d0adaeb34c97410dd5d8d04afccad88070b07f20", + "nop": { + "admin": "0x528fdfde37119b52010a4920b09ef828249a3434", + "name": "nop 1" + } + }, + { + "config_count": 1, + "workflow_don_id": 1, + "signer": "078cb3bca763a11a0c79df636b018311d9e36954000000000000000000000000", + "p2p_id": "p2p_12D3KooWAumheDZJdc3in1qiYGAQESTUm1nAWGA59eQPb3wuFHJa", + "encryption_public_key": "c8f45b74982037dd52eeb3df5295c7769757c72cde9a930fc82cb72caf0085ad", + "nop": { + "admin": "0x528fdfde37119b52010a4920b09ef828249a3434", + "name": "nop 1" + } + } + ], + "capabilities": [ + { + "id": "578ebf7413c15e36dfd792396c5a4d75c43f0ee7bbabdb703f7c5acd0bb65a1b", + "labelled_name": "offchain_reporting", + "version": "1.0.0", + "capability_type": 2, + "response_type": 0, + "configuration_contract": "0x0000000000000000000000000000000000000000" + } + ] + }, + { + "don": { + "id": 2, + "config_count": 1, + "f": 1, + "is_public": true + }, + "nodes": [ + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "a0e11229b4b5d40d299f7f4b3348a1b16f5f3dd3000000000000000000000000", + "p2p_id": "p2p_12D3KooWPsGKEe34m9X8qiWVpa6VEcjdvhbygJRLKXdVEr3r96PH", + "encryption_public_key": "10c77049880fe0924f463763789985a0f76e09f82fc6f39943684b2dd05b98bf", + "nop": { + "admin": "0xe0967f1d0f36df4c53a34e491465c9d3f0095eb9", + "name": "nop 2" + } + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "7621630d4f33e02f659445974060df375c126e82000000000000000000000000", + "p2p_id": "p2p_12D3KooWF1XmgvoLx45H3iGc9jD4mK7X1mctYmhZCqw5h1S1sXtA", + "encryption_public_key": "f8d7485910b56c9521dea1beb3d50151917bae18e131ad311ef90300318b8110", + "nop": { + "admin": "0xe0967f1d0f36df4c53a34e491465c9d3f0095eb9", + "name": "nop 2" + } + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "b5599a0a8aa99860997e42d650c3dc1ac767a4b1000000000000000000000000", + "p2p_id": "p2p_12D3KooWFBsQE8gCZndyefzCZG6KHyjoeii7o7ff171tQHsGYq1p", + "encryption_public_key": "4081aacba87f07ea54c32517a9b661963a5fed5fbfd2042ff0823293d8fbd523", + "nop": { + "admin": "0xe0967f1d0f36df4c53a34e491465c9d3f0095eb9", + "name": "nop 2" + } + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "2067cc4ec71978974265e578eb9b5c62cccc0d33000000000000000000000000", + "p2p_id": "p2p_12D3KooWKYQ51iqiS99MWfE8gwy7P1sN8oL4WboWLPxHRo8duc28", + "encryption_public_key": "533bfbd913615823cbb93251f27f3cbdd035a732c0a68ad49081b37a702fe4a1", + "nop": { + "admin": "0xe0967f1d0f36df4c53a34e491465c9d3f0095eb9", + "name": "nop 2" + } + } + ], + "capabilities": [ + { + "id": "9f0f33d3737af0a2acae89e7f8c4638aeb886a67c1d84e13aee8532c70aa9483", + "labelled_name": "write_ethereum-testnet-sepolia", + "version": "1.0.0", + "capability_type": 3, + "response_type": 0, + "configuration_contract": "0x0000000000000000000000000000000000000000" + } + ] + }, + { + "don": { + "id": 3, + "config_count": 1, + "f": 1, + "is_public": true + }, + "nodes": [ + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "3f871cd7c34e50320934b6ba9b90b249363fd378000000000000000000000000", + "p2p_id": "p2p_12D3KooWLjKKWLhYkr68PH9FaZZR93WbJuohWWcTfA9JeY1UPoJa", + "encryption_public_key": "5cf5abd0349b27cde43ad1a55aa3b02a5518aa6eed365bd65ca5e0e2cbfdaa5a", + "nop": { + "admin": "0xe676a607b6f5c0b6d4c21aa616d727d5b0a47f35", + "name": "nop 3" + } + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "d7a432ce8f23c410302e7577de9973ccbca29576000000000000000000000000", + "p2p_id": "p2p_12D3KooWRG3rsPdQyWCPsQukUF5JqFLdPqVVXvEfG2PAPBbqdRXp", + "encryption_public_key": "c10cead34374ba20704a05ca0369018b41778a0297adb85554cdd6e5955bf4b6", + "nop": { + "admin": "0xe676a607b6f5c0b6d4c21aa616d727d5b0a47f35", + "name": "nop 3" + } + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "f1d2b3d13c46b0c1ded96534aabd6bae61934e3f000000000000000000000000", + "p2p_id": "p2p_12D3KooWD1uj8Y9Phdr1yScfbBy1Q9ebedMVBjMUBLW4m4XnEzGF", + "encryption_public_key": "5fc3dae131da5b0ac5474ad029e3a6c547e17249a6266427d5eea1f529b6488b", + "nop": { + "admin": "0xe676a607b6f5c0b6d4c21aa616d727d5b0a47f35", + "name": "nop 3" + } + }, + { + "config_count": 1, + "workflow_don_id": 0, + "signer": "cd08a4d5934c41a389c2bd7b6fadfb1dace5f110000000000000000000000000", + "p2p_id": "p2p_12D3KooWMQjP7pp8ecxMSMd3Y3NocDsHt7g64bd7iGJhCr35gkby", + "encryption_public_key": "9ee181448ef55e381217ec959375fb302eba62e61006e4c9467832836cc8640e", + "nop": { + "admin": "0xe676a607b6f5c0b6d4c21aa616d727d5b0a47f35", + "name": "nop 3" + } + } + ], + "capabilities": [ + { + "id": "1ea6e85dfb8ebb08b7cd82b42ab4655429a8ae116d513be0d68a7d7661667a8a", + "labelled_name": "streams-trigger", + "version": "1.0.0", + "capability_type": 0, + "response_type": 0, + "configuration_contract": "0x0000000000000000000000000000000000000000" + } + ] + } + ] +} \ No newline at end of file