diff --git a/db/migrations/postgres/000091_add_namespace_to_identity_indexes.down.sql b/db/migrations/postgres/000091_add_namespace_to_identity_indexes.down.sql new file mode 100644 index 000000000..37fed11db --- /dev/null +++ b/db/migrations/postgres/000091_add_namespace_to_identity_indexes.down.sql @@ -0,0 +1,7 @@ +BEGIN; +DROP INDEX identities_did; +CREATE UNIQUE INDEX identities_did ON identities(did); + +DROP INDEX verifiers_value; +CREATE UNIQUE INDEX verifiers_value ON verifiers(vtype, value); +COMMIT; diff --git a/db/migrations/postgres/000091_add_namespace_to_identity_indexes.up.sql b/db/migrations/postgres/000091_add_namespace_to_identity_indexes.up.sql new file mode 100644 index 000000000..e35cd424e --- /dev/null +++ b/db/migrations/postgres/000091_add_namespace_to_identity_indexes.up.sql @@ -0,0 +1,7 @@ +BEGIN; +DROP INDEX identities_did; +CREATE UNIQUE INDEX identities_did ON identities(namespace, did); + +DROP INDEX verifiers_value; +CREATE UNIQUE INDEX verifiers_value ON verifiers(namespace, vtype, value); +COMMIT; diff --git a/db/migrations/sqlite/000091_add_namespace_to_identity_indexes.down.sql b/db/migrations/sqlite/000091_add_namespace_to_identity_indexes.down.sql new file mode 100644 index 000000000..61e611754 --- /dev/null +++ b/db/migrations/sqlite/000091_add_namespace_to_identity_indexes.down.sql @@ -0,0 +1,5 @@ +DROP INDEX identities_did; +CREATE UNIQUE INDEX identities_did ON identities(did); + +DROP INDEX verifiers_value; +CREATE UNIQUE INDEX verifiers_value ON verifiers(vtype, value); diff --git a/db/migrations/sqlite/000091_add_namespace_to_identity_indexes.up.sql b/db/migrations/sqlite/000091_add_namespace_to_identity_indexes.up.sql new file mode 100644 index 000000000..e2dbcc5a3 --- /dev/null +++ b/db/migrations/sqlite/000091_add_namespace_to_identity_indexes.up.sql @@ -0,0 +1,5 @@ +DROP INDEX identities_did; +CREATE UNIQUE INDEX identities_did ON identities(namespace, did); + +DROP INDEX verifiers_value; +CREATE UNIQUE INDEX verifiers_value ON verifiers(namespace, vtype, value); diff --git a/docs/reference/config.md b/docs/reference/config.md index f876009c1..5abb8c3df 100644 --- a/docs/reference/config.md +++ b/docs/reference/config.md @@ -637,12 +637,26 @@ nav_order: 2 |Key|Description|Type|Default Value| |---|-----------|----|-------------| +|defaultKey|A default signing key for blockchain transactions within this namespace|`string`|`` |description|A description for the namespace|`string`|`` -|mode|The namespace mode. Valid values: gateway, multiparty|`string`|`` |name|The name of the namespace (must be unique)|`string`|`` |plugins|The list of plugins for this namespace|`string`|`` |remoteName|The namespace name to be sent in plugin calls, if it differs from namespace name|`string`|`` +## namespaces.predefined[].multiparty + +|Key|Description|Type|Default Value| +|---|-----------|----|-------------| +|enabled|Enables multi-party mode for this namespace (defaults to true if an org name or key is configured, either here or at the root level)|`boolean`|`` + +## namespaces.predefined[].multiparty.org + +|Key|Description|Type|Default Value| +|---|-----------|----|-------------| +|description|A description for the local root organization within this namespace|`string`|`` +|key|The signing key allocated to the root organization within this namespace|`string`|`` +|name|A short name for the local root organization within this namespace|`string`|`` + ## node |Key|Description|Type|Default Value| @@ -677,10 +691,9 @@ nav_order: 2 |Key|Description|Type|Default Value| |---|-----------|----|-------------| -|description|A description of the organization to which this FireFly node belongs|`string`|`` -|identity|`DEPRECATED` Please use `org.key` instead|`string`|`` -|key|The signing identity allocated to the organization (can be the same as the nodes)|`string`|`` -|name|The name of the organization to which this FireFly node belongs|`string`|`` +|description|A description of the organization to which this FireFly node belongs (deprecated - should be set on each multi-party namespace instead)|`string`|`` +|key|The signing key allocated to the organization (deprecated - should be set on each multi-party namespace instead)|`string`|`` +|name|The name of the organization to which this FireFly node belongs (deprecated - should be set on each multi-party namespace instead)|`string`|`` ## plugins diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 5f5247a7c..400c43c3b 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -5558,7 +5558,8 @@ paths: - Default Namespace /identities: get: - description: Gets a list of identities that have been registered in the network + description: Gets a list of all identities that have been registered in the + namespace operationId: getIdentities parameters: - description: When set, the API will return the verifier for this identity @@ -5981,6 +5982,131 @@ paths: description: "" tags: - Default Namespace + /identities/{did}: + get: + description: Gets an identity by its DID + operationId: getIdentityByDID + parameters: + - description: The identity DID + in: path + name: did + required: true + schema: + type: string + - description: When set, the API will return the verifier for this identity + in: query + name: fetchverifiers + schema: + example: "true" + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + responses: + "200": + content: + application/json: + schema: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and node + identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root organization + identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + verifiers: + description: The verifiers, such as blockchain signing keys, that + have been bound to this identity and can be used to prove data + orignates from that identity + items: + description: The verifiers, such as blockchain signing keys, + that have been bound to this identity and can be used to prove + data orignates from that identity + properties: + type: + description: The type of the verifier + enum: + - ethereum_address + - fabric_msp_id + - dx_peer_id + type: string + value: + description: The verifier string, such as an Ethereum address, + or Fabric MSP identifier + type: string + type: object + type: array + type: object + description: Success + default: + description: "" + tags: + - Default Namespace /identities/{iid}: get: description: Gets an identity by its ID @@ -8333,15 +8459,17 @@ paths: description: "" tags: - Global - post: - description: Creates and broadcasts a new namespace - operationId: postNewNamespace + /namespaces/{ns}: + get: + description: Gets a namespace + operationId: getNamespace parameters: - - description: When true the HTTP request blocks until the message is confirmed - in: query - name: confirm + - description: The namespace which scopes this request + in: path + name: ns + required: true schema: - example: "true" + example: default type: string - description: Server-side request timeout (millseconds, or set a custom suffix like 10s) @@ -8350,18 +8478,6 @@ paths: schema: default: 120s type: string - requestBody: - content: - application/json: - schema: - properties: - description: - description: A description of the namespace - type: string - name: - description: The namespace name - type: string - type: object responses: "200": content: @@ -8444,200 +8560,14 @@ paths: type: string type: object description: Success - "202": - content: - application/json: - schema: - properties: - created: - description: The time the namespace was created - format: date-time - type: string - description: - description: A description of the namespace - type: string - fireflyContract: - description: Info on the FireFly smart contract configured for - this namespace - properties: - active: - description: The currently active FireFly smart contract - properties: - finalEvent: - description: The identifier for the final blockchain event - received from this contract before termination - type: string - index: - description: The index of this contract in the config - file - type: integer - info: - additionalProperties: - description: Blockchain-specific info on the contract, - such as its location on chain - description: Blockchain-specific info on the contract, - such as its location on chain - type: object - type: object - terminated: - description: Previously-terminated FireFly smart contracts - items: - description: Previously-terminated FireFly smart contracts - properties: - finalEvent: - description: The identifier for the final blockchain - event received from this contract before termination - type: string - index: - description: The index of this contract in the config - file - type: integer - info: - additionalProperties: - description: Blockchain-specific info on the contract, - such as its location on chain - description: Blockchain-specific info on the contract, - such as its location on chain - type: object - type: object - type: array - type: object - id: - description: The UUID of the namespace. For locally established - namespaces will be different on each node in the network. For - broadcast namespaces, will be the same on every node - format: uuid - type: string - message: - description: The UUID of broadcast message used to establish the - namespace. Unset for local namespaces - format: uuid - type: string - name: - description: The namespace name - type: string - type: - description: The type of the namespace - enum: - - local - - broadcast - - system - type: string - type: object - description: Success default: description: "" tags: - Global - /namespaces/{ns}: + /namespaces/{ns}/apis: get: - description: Gets a namespace - operationId: getNamespace - parameters: - - description: The namespace which scopes this request - in: path - name: ns - required: true - schema: - example: default - type: string - - description: Server-side request timeout (millseconds, or set a custom suffix - like 10s) - in: header - name: Request-Timeout - schema: - default: 120s - type: string - responses: - "200": - content: - application/json: - schema: - properties: - created: - description: The time the namespace was created - format: date-time - type: string - description: - description: A description of the namespace - type: string - fireflyContract: - description: Info on the FireFly smart contract configured for - this namespace - properties: - active: - description: The currently active FireFly smart contract - properties: - finalEvent: - description: The identifier for the final blockchain event - received from this contract before termination - type: string - index: - description: The index of this contract in the config - file - type: integer - info: - additionalProperties: - description: Blockchain-specific info on the contract, - such as its location on chain - description: Blockchain-specific info on the contract, - such as its location on chain - type: object - type: object - terminated: - description: Previously-terminated FireFly smart contracts - items: - description: Previously-terminated FireFly smart contracts - properties: - finalEvent: - description: The identifier for the final blockchain - event received from this contract before termination - type: string - index: - description: The index of this contract in the config - file - type: integer - info: - additionalProperties: - description: Blockchain-specific info on the contract, - such as its location on chain - description: Blockchain-specific info on the contract, - such as its location on chain - type: object - type: object - type: array - type: object - id: - description: The UUID of the namespace. For locally established - namespaces will be different on each node in the network. For - broadcast namespaces, will be the same on every node - format: uuid - type: string - message: - description: The UUID of broadcast message used to establish the - namespace. Unset for local namespaces - format: uuid - type: string - name: - description: The namespace name - type: string - type: - description: The type of the namespace - enum: - - local - - broadcast - - system - type: string - type: object - description: Success - default: - description: "" - tags: - - Global - /namespaces/{ns}/apis: - get: - description: Gets a list of contract APIs that have been published - operationId: getContractAPIsNamespace + description: Gets a list of contract APIs that have been published + operationId: getContractAPIsNamespace parameters: - description: The namespace which scopes this request in: path @@ -14607,7 +14537,8 @@ paths: - Non-Default Namespace /namespaces/{ns}/identities: get: - description: Gets a list of identities that have been registered in the network + description: Gets a list of all identities that have been registered in the + namespace operationId: getIdentitiesNamespace parameters: - description: The namespace which scopes this request @@ -15044,6 +14975,138 @@ paths: description: "" tags: - Non-Default Namespace + /namespaces/{ns}/identities/{did}: + get: + description: Gets an identity by its DID + operationId: getIdentityByDIDNamespace + parameters: + - description: The identity DID + in: path + name: did + required: true + schema: + type: string + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: When set, the API will return the verifier for this identity + in: query + name: fetchverifiers + schema: + example: "true" + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + responses: + "200": + content: + application/json: + schema: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and node + identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root organization + identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + verifiers: + description: The verifiers, such as blockchain signing keys, that + have been bound to this identity and can be used to prove data + orignates from that identity + items: + description: The verifiers, such as blockchain signing keys, + that have been bound to this identity and can be used to prove + data orignates from that identity + properties: + type: + description: The type of the verifier + enum: + - ethereum_address + - fabric_msp_id + - dx_peer_id + type: string + value: + description: The verifier string, such as an Ethereum address, + or Fabric MSP identifier + type: string + type: object + type: array + type: object + description: Success + default: + description: "" + tags: + - Non-Default Namespace /namespaces/{ns}/identities/{iid}: get: description: Gets an identity by its ID @@ -17330,23 +17393,1699 @@ paths: - transfer_private type: string type: object - pins: - description: For private messages, a unique pin hash:nonce is - assigned for each topic - items: - description: For private messages, a unique pin hash:nonce is - assigned for each topic - type: string - type: array - state: - description: The current state of the message + pins: + description: For private messages, a unique pin hash:nonce is + assigned for each topic + items: + description: For private messages, a unique pin hash:nonce is + assigned for each topic + type: string + type: array + state: + description: The current state of the message + enum: + - staged + - ready + - sent + - pending + - confirmed + - rejected + type: string + type: object + description: Success + default: + description: "" + tags: + - Non-Default Namespace + /namespaces/{ns}/network/action: + post: + description: Notify all nodes in the network of a new governance action + operationId: postNetworkActionNamespace + parameters: + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + requestBody: + content: + application/json: + schema: + properties: + type: + description: The action to be performed + enum: + - terminate + type: string + type: object + responses: + "202": + content: + application/json: + schema: + properties: + type: + description: The action to be performed + enum: + - terminate + type: string + type: object + description: Success + default: + description: "" + tags: + - Non-Default Namespace + /namespaces/{ns}/network/diddocs/{did}: + get: + description: Gets a DID document by its DID + operationId: getNetworkDIDDocByDIDNamespace + parameters: + - description: The identity DID + in: path + name: did + required: true + schema: + type: string + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + responses: + "200": + content: + application/json: + schema: + properties: + '@context': + description: See https://www.w3.org/TR/did-core/#json-ld + items: + description: See https://www.w3.org/TR/did-core/#json-ld + type: string + type: array + authentication: + description: See https://www.w3.org/TR/did-core/#did-document-properties + items: + description: See https://www.w3.org/TR/did-core/#did-document-properties + type: string + type: array + id: + description: See https://www.w3.org/TR/did-core/#did-document-properties + type: string + verificationMethod: + description: See https://www.w3.org/TR/did-core/#did-document-properties + items: + description: See https://www.w3.org/TR/did-core/#did-document-properties + properties: + blockchainAcountId: + description: For blockchains like Ethereum that represent + signing identities directly by their public key summarized + in an account string + type: string + controller: + description: See https://www.w3.org/TR/did-core/#service-properties + type: string + dataExchangePeerID: + description: A string provided by your Data Exchange plugin, + that it uses a technology specific mechanism to validate + against when messages arrive from this identity + type: string + id: + description: See https://www.w3.org/TR/did-core/#service-properties + type: string + mspIdentityString: + description: For Hyperledger Fabric where the signing identity + is represented by an MSP identifier (containing X509 certificate + DN strings) that were validated by your local MSP + type: string + type: + description: See https://www.w3.org/TR/did-core/#service-properties + type: string + type: object + type: array + type: object + description: Success + default: + description: "" + tags: + - Non-Default Namespace + /namespaces/{ns}/network/identities: + get: + deprecated: true + description: Gets the list of identities in the network (deprecated - use /identities + instead of /network/identities + operationId: getNetworkIdentitiesNamespace + parameters: + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: When set, the API will return the verifier for this identity + in: query + name: fetchverifiers + schema: + example: "true" + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: created + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: description + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: did + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: id + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: messages.claim + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: messages.update + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: messages.verification + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: name + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: namespace + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: parent + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: profile + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: type + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: updated + schema: + type: string + - description: Sort field. For multi-field sort use comma separated values (or + multiple query values) with '-' prefix for descending + in: query + name: sort + schema: + type: string + - description: Ascending sort order (overrides all fields in a multi-field sort) + in: query + name: ascending + schema: + type: string + - description: Descending sort order (overrides all fields in a multi-field + sort) + in: query + name: descending + schema: + type: string + - description: 'The number of records to skip (max: 1,000). Unsuitable for bulk + operations' + in: query + name: skip + schema: + type: string + - description: 'The maximum number of records to return (max: 1,000)' + in: query + name: limit + schema: + example: "25" + type: string + - description: Return a total count as well as items (adds extra database processing) + in: query + name: count + schema: + type: string + responses: + "200": + content: + application/json: + schema: + items: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and + node identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root + organization identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + verifiers: + description: The verifiers, such as blockchain signing keys, + that have been bound to this identity and can be used to prove + data orignates from that identity + items: + description: The verifiers, such as blockchain signing keys, + that have been bound to this identity and can be used to + prove data orignates from that identity + properties: + type: + description: The type of the verifier + enum: + - ethereum_address + - fabric_msp_id + - dx_peer_id + type: string + value: + description: The verifier string, such as an Ethereum + address, or Fabric MSP identifier + type: string + type: object + type: array + type: object + type: array + description: Success + default: + description: "" + tags: + - Non-Default Namespace + /namespaces/{ns}/network/identities/{did}: + get: + deprecated: true + description: Gets an identity by its DID (deprecated - use /identities/{did} + instead of /network/identities/{did}) + operationId: getNetworkIdentityByDIDNamespace + parameters: + - description: The identity DID + in: path + name: did + required: true + schema: + type: string + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: When set, the API will return the verifier for this identity + in: query + name: fetchverifiers + schema: + example: "true" + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + responses: + "200": + content: + application/json: + schema: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and node + identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root organization + identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + verifiers: + description: The verifiers, such as blockchain signing keys, that + have been bound to this identity and can be used to prove data + orignates from that identity + items: + description: The verifiers, such as blockchain signing keys, + that have been bound to this identity and can be used to prove + data orignates from that identity + properties: + type: + description: The type of the verifier + enum: + - ethereum_address + - fabric_msp_id + - dx_peer_id + type: string + value: + description: The verifier string, such as an Ethereum address, + or Fabric MSP identifier + type: string + type: object + type: array + type: object + description: Success + default: + description: "" + tags: + - Non-Default Namespace + /namespaces/{ns}/network/nodes: + get: + description: Gets a list of nodes in the network + operationId: getNetworkNodesNamespace + parameters: + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: created + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: description + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: did + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: id + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: messages.claim + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: messages.update + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: messages.verification + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: name + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: namespace + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: parent + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: profile + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: type + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: updated + schema: + type: string + - description: Sort field. For multi-field sort use comma separated values (or + multiple query values) with '-' prefix for descending + in: query + name: sort + schema: + type: string + - description: Ascending sort order (overrides all fields in a multi-field sort) + in: query + name: ascending + schema: + type: string + - description: Descending sort order (overrides all fields in a multi-field + sort) + in: query + name: descending + schema: + type: string + - description: 'The number of records to skip (max: 1,000). Unsuitable for bulk + operations' + in: query + name: skip + schema: + type: string + - description: 'The maximum number of records to return (max: 1,000)' + in: query + name: limit + schema: + example: "25" + type: string + - description: Return a total count as well as items (adds extra database processing) + in: query + name: count + schema: + type: string + responses: + "200": + content: + application/json: + schema: + items: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and + node identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root + organization identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + type: object + type: array + description: Success + default: + description: "" + tags: + - Non-Default Namespace + /namespaces/{ns}/network/nodes/{nameOrId}: + get: + description: Gets information about a specific node in the network + operationId: getNetworkNodeNamespace + parameters: + - description: The name or ID of the node + in: path + name: nameOrId + required: true + schema: + type: string + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + responses: + "200": + content: + application/json: + schema: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and node + identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root organization + identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + type: object + description: Success + default: + description: "" + tags: + - Non-Default Namespace + /namespaces/{ns}/network/nodes/self: + post: + description: Instructs this FireFly node to register itself on the network + operationId: postNodesSelfNamespace + parameters: + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: When true the HTTP request blocks until the message is confirmed + in: query + name: confirm + schema: + example: "true" + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + requestBody: + content: + application/json: + schema: {} + responses: + "200": + content: + application/json: + schema: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and node + identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root organization + identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + type: object + description: Success + "202": + content: + application/json: + schema: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and node + identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root organization + identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + type: object + description: Success + default: + description: "" + tags: + - Non-Default Namespace + /namespaces/{ns}/network/organizations: + get: + description: Gets a list of orgs in the network + operationId: getNetworkOrgsNamespace + parameters: + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: created + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: description + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: did + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: id + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: messages.claim + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: messages.update + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: messages.verification + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: name + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: namespace + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: parent + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: profile + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: type + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: updated + schema: + type: string + - description: Sort field. For multi-field sort use comma separated values (or + multiple query values) with '-' prefix for descending + in: query + name: sort + schema: + type: string + - description: Ascending sort order (overrides all fields in a multi-field sort) + in: query + name: ascending + schema: + type: string + - description: Descending sort order (overrides all fields in a multi-field + sort) + in: query + name: descending + schema: + type: string + - description: 'The number of records to skip (max: 1,000). Unsuitable for bulk + operations' + in: query + name: skip + schema: + type: string + - description: 'The maximum number of records to return (max: 1,000)' + in: query + name: limit + schema: + example: "25" + type: string + - description: Return a total count as well as items (adds extra database processing) + in: query + name: count + schema: + type: string + responses: + "200": + content: + application/json: + schema: + items: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and + node identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root + organization identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + type: object + type: array + description: Success + default: + description: "" + tags: + - Non-Default Namespace + post: + description: Registers a new org in the network + operationId: postNewOrganizationNamespace + parameters: + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: When true the HTTP request blocks until the message is confirmed + in: query + name: confirm + schema: + example: "true" + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + requestBody: + content: + application/json: + schema: + properties: + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + key: + description: The blockchain signing key to use to make the claim + to the identity. Must be available to the local node to sign the + identity claim. Will become a verifier on the established identity + type: string + name: + description: The name of the identity. The name must be unique within + the type and namespace + type: string + parent: + description: On input the parent can be specified directly as the + UUID of and existing identity, or as a DID to resolve to that + identity, or an organization name. The parent must already have + been registered, and its blockchain signing key must be available + to the local node to sign the verification + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + type: object + type: + description: The type of the identity + type: string + type: object + responses: + "200": + content: + application/json: + schema: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and node + identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root organization + identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + type: object + description: Success + "202": + content: + application/json: + schema: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and node + identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root organization + identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + type: object + description: Success + default: + description: "" + tags: + - Non-Default Namespace + /namespaces/{ns}/network/organizations/{nameOrId}: + get: + description: Gets information about a specifc org in the network + operationId: getNetworkOrgNamespace + parameters: + - description: The name or ID of the org + in: path + name: nameOrId + required: true + schema: + type: string + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + responses: + "200": + content: + application/json: + schema: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and node + identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root organization + identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + type: object + description: Success + default: + description: "" + tags: + - Non-Default Namespace + /namespaces/{ns}/network/organizations/self: + post: + description: Instructs this FireFly node to register its org on the network + operationId: postNewOrganizationSelfNamespace + parameters: + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: When true the HTTP request blocks until the message is confirmed + in: query + name: confirm + schema: + example: "true" + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + requestBody: + content: + application/json: + schema: {} + responses: + "200": + content: + application/json: + schema: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and node + identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root organization + identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + type: object + type: + description: The type of the identity + enum: + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time + type: string + type: object + description: Success + "202": + content: + application/json: + schema: + properties: + created: + description: The creation time of the identity + format: date-time + type: string + description: + description: A description of the identity. Part of the updatable + profile information of an identity + type: string + did: + description: The DID of the identity. Unique across namespaces + within a FireFly network + type: string + id: + description: The UUID of the identity + format: uuid + type: string + messages: + description: References to the broadcast messages that established + this identity and proved ownership of the associated verifiers + (keys) + properties: + claim: + description: The UUID of claim message + format: uuid + type: string + update: + description: The UUID of the most recently applied update + message. Unset if no updates have been confirmed + format: uuid + type: string + verification: + description: The UUID of claim message. Unset for root organization + identities + format: uuid + type: string + type: object + name: + description: The name of the identity. The name must be unique + within the type and namespace + type: string + namespace: + description: The namespace of the identity. Organization and node + identities are always defined in the ff_system namespace + type: string + parent: + description: The UUID of the parent identity. Unset for root organization + identities + format: uuid + type: string + profile: + additionalProperties: + description: A set of metadata for the identity. Part of the + updatable profile information of an identity + description: A set of metadata for the identity. Part of the updatable + profile information of an identity + type: object + type: + description: The type of the identity enum: - - staged - - ready - - sent - - pending - - confirmed - - rejected + - org + - node + - custom + type: string + updated: + description: The last update time of the identity profile + format: date-time type: string type: object description: Success @@ -17751,6 +19490,190 @@ paths: description: "" tags: - Non-Default Namespace + /namespaces/{ns}/status: + get: + description: Gets the status of this node + operationId: getStatusNamespace + parameters: + - description: The namespace which scopes this request + in: path + name: ns + required: true + schema: + example: default + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + responses: + "200": + content: + application/json: + schema: + properties: + namespace: + description: The namespace that this status applies to + type: string + node: + description: Details of the local node + properties: + id: + description: The UUID of the node, if registered + format: uuid + type: string + name: + description: The name of this node, as specified in the local + configuration + type: string + registered: + description: Whether the node has been successfully registered + type: boolean + type: object + org: + description: Details of the organization identity that operates + this node + properties: + did: + description: The DID of the organization identity, if registered + type: string + id: + description: The UUID of the organization, if registered + format: uuid + type: string + name: + description: The name of the node operator organization, as + specified in the local configuration + type: string + registered: + description: Whether the organization has been successfully + registered + type: boolean + verifiers: + description: Array of verifiers (blockchain keys) owned by + this identity + items: + description: Array of verifiers (blockchain keys) owned + by this identity + properties: + type: + description: The type of the verifier + enum: + - ethereum_address + - fabric_msp_id + - dx_peer_id + type: string + value: + description: The verifier string, such as an Ethereum + address, or Fabric MSP identifier + type: string + type: object + type: array + type: object + plugins: + description: Information about plugins configured on this node + properties: + blockchain: + description: The blockchain plugins on this node + items: + description: The blockchain plugins on this node + properties: + name: + description: The name of the plugin + type: string + pluginType: + description: The type of the plugin + type: string + type: object + type: array + dataExchange: + description: The data exchange plugins on this node + items: + description: The data exchange plugins on this node + properties: + name: + description: The name of the plugin + type: string + pluginType: + description: The type of the plugin + type: string + type: object + type: array + database: + description: The database plugins on this node + items: + description: The database plugins on this node + properties: + name: + description: The name of the plugin + type: string + pluginType: + description: The type of the plugin + type: string + type: object + type: array + events: + description: The event plugins on this node + items: + description: The event plugins on this node + properties: + name: + description: The name of the plugin + type: string + pluginType: + description: The type of the plugin + type: string + type: object + type: array + identity: + description: The identity plugins on this node + items: + description: The identity plugins on this node + properties: + name: + description: The name of the plugin + type: string + pluginType: + description: The type of the plugin + type: string + type: object + type: array + sharedStorage: + description: The shared storage plugins on this node + items: + description: The shared storage plugins on this node + properties: + name: + description: The name of the plugin + type: string + pluginType: + description: The type of the plugin + type: string + type: object + type: array + tokens: + description: The token plugins on this node + items: + description: The token plugins on this node + properties: + name: + description: The name of the plugin + type: string + pluginType: + description: The type of the plugin + type: string + type: object + type: array + type: object + type: object + description: Success + default: + description: "" + tags: + - Non-Default Namespace /namespaces/{ns}/subscriptions: get: description: Gets a list of subscriptions @@ -22881,11 +24804,11 @@ paths: default: description: "" tags: - - Global + - Default Namespace /network/diddocs/{did}: get: description: Gets a DID document by its DID - operationId: getDIDDocByDID + operationId: getNetworkDIDDocByDID parameters: - description: The identity DID in: path @@ -22957,10 +24880,12 @@ paths: default: description: "" tags: - - Global + - Default Namespace /network/identities: get: - description: Gets the list of identities in the network + deprecated: true + description: Gets the list of identities in the network (deprecated - use /identities + instead of /network/identities operationId: getNetworkIdentities parameters: - description: When set, the API will return the verifier for this identity @@ -23177,11 +25102,13 @@ paths: default: description: "" tags: - - Global + - Default Namespace /network/identities/{did}: get: - description: Gets an identity by its DID - operationId: getIdentityByDID + deprecated: true + description: Gets an identity by its DID (deprecated - use /identities/{did} + instead of /network/identities/{did}) + operationId: getNetworkIdentityByDID parameters: - description: The identity DID in: path @@ -23302,7 +25229,7 @@ paths: default: description: "" tags: - - Global + - Default Namespace /network/nodes: get: description: Gets a list of nodes in the network @@ -23494,7 +25421,7 @@ paths: default: description: "" tags: - - Global + - Default Namespace /network/nodes/{nameOrId}: get: description: Gets information about a specific node in the network @@ -23591,7 +25518,7 @@ paths: default: description: "" tags: - - Global + - Default Namespace /network/nodes/self: post: description: Instructs this FireFly node to register itself on the network @@ -23766,7 +25693,7 @@ paths: default: description: "" tags: - - Global + - Default Namespace /network/organizations: get: description: Gets a list of orgs in the network @@ -23958,7 +25885,7 @@ paths: default: description: "" tags: - - Global + - Default Namespace post: description: Registers a new org in the network operationId: postNewOrganization @@ -24164,7 +26091,7 @@ paths: default: description: "" tags: - - Global + - Default Namespace /network/organizations/{nameOrId}: get: description: Gets information about a specifc org in the network @@ -24261,7 +26188,7 @@ paths: default: description: "" tags: - - Global + - Default Namespace /network/organizations/self: post: description: Instructs this FireFly node to register its org on the network @@ -24436,7 +26363,7 @@ paths: default: description: "" tags: - - Global + - Default Namespace /operations: get: description: Gets a a list of operations @@ -24831,14 +26758,9 @@ paths: application/json: schema: properties: - defaults: - description: Information about defaults configured on this node - that appplications might need to query on startup - properties: - namespace: - description: The default namespace on this node - type: string - type: object + namespace: + description: The namespace that this status applies to + type: string node: description: Details of the local node properties: @@ -24994,7 +26916,7 @@ paths: default: description: "" tags: - - Global + - Default Namespace /status/batchmanager: get: description: Gets the status of the batch manager diff --git a/internal/apiserver/route_post_new_namespace.go b/internal/apiserver/route_get_identity_by_did.go similarity index 55% rename from internal/apiserver/route_post_new_namespace.go rename to internal/apiserver/route_get_identity_by_did.go index 2a2d6ec8d..8fbf9858d 100644 --- a/internal/apiserver/route_post_new_namespace.go +++ b/internal/apiserver/route_get_identity_by_did.go @@ -25,22 +25,25 @@ import ( "github.com/hyperledger/firefly/pkg/core" ) -var postNewNamespace = &oapispec.Route{ - Name: "postNewNamespace", - Path: "namespaces", - Method: http.MethodPost, +var getIdentityByDID = &oapispec.Route{ + Name: "getIdentityByDID", + Path: "identities/{did:did:.+}", + Method: http.MethodGet, QueryParams: []*oapispec.QueryParam{ - {Name: "confirm", Description: coremsgs.APIConfirmQueryParam, IsBool: true, Example: "true"}, + {Name: "fetchverifiers", Example: "true", Description: coremsgs.APIParamsFetchVerifiers, IsBool: true}, + }, + PathParams: []*oapispec.PathParam{ + {Name: "did", Description: coremsgs.APIParamsDID}, }, FilterFactory: nil, - Description: coremsgs.APIEndpointsPostNewNamespace, - JSONInputValue: func() interface{} { return &core.Namespace{} }, - JSONOutputValue: func() interface{} { return &core.Namespace{} }, - JSONOutputCodes: []int{http.StatusAccepted, http.StatusOK}, + Description: coremsgs.APIEndpointsGetIdentityByDID, + JSONInputValue: nil, + JSONOutputValue: func() interface{} { return &core.IdentityWithVerifiers{} }, + JSONOutputCodes: []int{http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { - waitConfirm := strings.EqualFold(r.QP["confirm"], "true") - r.SuccessStatus = syncRetcode(waitConfirm) - _, err = getOr(r.Ctx).Broadcast().BroadcastNamespace(r.Ctx, r.Input.(*core.Namespace), waitConfirm) - return r.Input, err + if strings.EqualFold(r.QP["fetchverifiers"], "true") { + return getOr(r.Ctx).NetworkMap().GetIdentityByDIDWithVerifiers(r.Ctx, extractNamespace(r.PP), r.PP["did"]) + } + return getOr(r.Ctx).NetworkMap().GetIdentityByDID(r.Ctx, extractNamespace(r.PP), r.PP["did"]) }, } diff --git a/internal/apiserver/route_post_new_namespace_test.go b/internal/apiserver/route_get_identity_by_did_test.go similarity index 56% rename from internal/apiserver/route_post_new_namespace_test.go rename to internal/apiserver/route_get_identity_by_did_test.go index 64a07e87c..48fb10bd9 100644 --- a/internal/apiserver/route_post_new_namespace_test.go +++ b/internal/apiserver/route_get_identity_by_did_test.go @@ -17,48 +17,40 @@ package apiserver import ( - "bytes" - "encoding/json" "net/http/httptest" "testing" - "github.com/hyperledger/firefly/mocks/broadcastmocks" + "github.com/hyperledger/firefly/mocks/networkmapmocks" "github.com/hyperledger/firefly/pkg/core" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) -func TestPostNewNamespace(t *testing.T) { +func TestGetIdentityByDID(t *testing.T) { o, r := newTestAPIServer() - mbm := &broadcastmocks.Manager{} - o.On("Broadcast").Return(mbm) - input := core.Namespace{} - var buf bytes.Buffer - json.NewEncoder(&buf).Encode(&input) - req := httptest.NewRequest("POST", "/api/v1/namespaces", &buf) + nmn := &networkmapmocks.Manager{} + o.On("NetworkMap").Return(nmn) + req := httptest.NewRequest("GET", "/api/v1/identities/did:firefly:org/org_1", nil) req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mbm.On("BroadcastNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), false). - Return(&core.Message{}, nil) + nmn.On("GetIdentityByDID", mock.Anything, "default", "did:firefly:org/org_1"). + Return(&core.Identity{}, nil) r.ServeHTTP(res, req) - assert.Equal(t, 202, res.Result().StatusCode) + assert.Equal(t, 200, res.Result().StatusCode) } -func TestPostNewNamespaceSync(t *testing.T) { +func TestGetIdentityByDIDWithVerifiers(t *testing.T) { o, r := newTestAPIServer() - mbm := &broadcastmocks.Manager{} - o.On("Broadcast").Return(mbm) - input := core.Namespace{} - var buf bytes.Buffer - json.NewEncoder(&buf).Encode(&input) - req := httptest.NewRequest("POST", "/api/v1/namespaces?confirm", &buf) + nmn := &networkmapmocks.Manager{} + o.On("NetworkMap").Return(nmn) + req := httptest.NewRequest("GET", "/api/v1/identities/did:firefly:org/org_1?fetchverifiers", nil) req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mbm.On("BroadcastNamespace", mock.Anything, mock.AnythingOfType("*core.Namespace"), true). - Return(&core.Message{}, nil) + nmn.On("GetIdentityByDIDWithVerifiers", mock.Anything, "default", "did:firefly:org/org_1"). + Return(&core.IdentityWithVerifiers{}, nil) r.ServeHTTP(res, req) assert.Equal(t, 200, res.Result().StatusCode) diff --git a/internal/apiserver/route_get_net_did.go b/internal/apiserver/route_get_net_did.go index 8b88f0d65..7e6e20057 100644 --- a/internal/apiserver/route_get_net_did.go +++ b/internal/apiserver/route_get_net_did.go @@ -25,8 +25,8 @@ import ( "github.com/hyperledger/firefly/pkg/core" ) -var getIdentityByDID = &oapispec.Route{ - Name: "getIdentityByDID", +var getNetworkIdentityByDID = &oapispec.Route{ + Name: "getNetworkIdentityByDID", Path: "network/identities/{did:.+}", Method: http.MethodGet, QueryParams: []*oapispec.QueryParam{ @@ -36,14 +36,15 @@ var getIdentityByDID = &oapispec.Route{ {Name: "did", Description: coremsgs.APIParamsDID}, }, FilterFactory: nil, - Description: coremsgs.APIEndpointsGetIdentityByDID, + Description: coremsgs.APIEndpointsGetNetworkIdentityByDID, + Deprecated: true, // use getIdentityByDID instead JSONInputValue: nil, JSONOutputValue: func() interface{} { return &core.IdentityWithVerifiers{} }, JSONOutputCodes: []int{http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { if strings.EqualFold(r.QP["fetchverifiers"], "true") { - return getOr(r.Ctx).NetworkMap().GetIdentityByDIDWithVerifiers(r.Ctx, r.PP["did"]) + return getOr(r.Ctx).NetworkMap().GetIdentityByDIDWithVerifiers(r.Ctx, extractNamespace(r.PP), r.PP["did"]) } - return getOr(r.Ctx).NetworkMap().GetIdentityByDID(r.Ctx, r.PP["did"]) + return getOr(r.Ctx).NetworkMap().GetIdentityByDID(r.Ctx, extractNamespace(r.PP), r.PP["did"]) }, } diff --git a/internal/apiserver/route_get_net_did_test.go b/internal/apiserver/route_get_net_did_test.go index c14db9c6c..fe9680644 100644 --- a/internal/apiserver/route_get_net_did_test.go +++ b/internal/apiserver/route_get_net_did_test.go @@ -26,7 +26,7 @@ import ( "github.com/stretchr/testify/mock" ) -func TestGetIdentityByDID(t *testing.T) { +func TestGetNetIdentityByDID(t *testing.T) { o, r := newTestAPIServer() nmn := &networkmapmocks.Manager{} o.On("NetworkMap").Return(nmn) @@ -34,14 +34,14 @@ func TestGetIdentityByDID(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - nmn.On("GetIdentityByDID", mock.Anything, "did:firefly:org/org_1"). + nmn.On("GetIdentityByDID", mock.Anything, "default", "did:firefly:org/org_1"). Return(&core.Identity{}, nil) r.ServeHTTP(res, req) assert.Equal(t, 200, res.Result().StatusCode) } -func TestGetIdentityByDIDWithVerifiers(t *testing.T) { +func TestGetNetIdentityByDIDWithVerifiers(t *testing.T) { o, r := newTestAPIServer() nmn := &networkmapmocks.Manager{} o.On("NetworkMap").Return(nmn) @@ -49,7 +49,7 @@ func TestGetIdentityByDIDWithVerifiers(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - nmn.On("GetIdentityByDIDWithVerifiers", mock.Anything, "did:firefly:org/org_1"). + nmn.On("GetIdentityByDIDWithVerifiers", mock.Anything, "default", "did:firefly:org/org_1"). Return(&core.IdentityWithVerifiers{}, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/route_get_net_diddoc.go b/internal/apiserver/route_get_net_diddoc.go index 5efb0d3aa..0752d385c 100644 --- a/internal/apiserver/route_get_net_diddoc.go +++ b/internal/apiserver/route_get_net_diddoc.go @@ -24,8 +24,8 @@ import ( "github.com/hyperledger/firefly/internal/oapispec" ) -var getDIDDocByDID = &oapispec.Route{ - Name: "getDIDDocByDID", +var getNetworkDIDDocByDID = &oapispec.Route{ + Name: "getNetworkDIDDocByDID", Path: "network/diddocs/{did:.+}", Method: http.MethodGet, PathParams: []*oapispec.PathParam{ @@ -37,6 +37,6 @@ var getDIDDocByDID = &oapispec.Route{ JSONOutputValue: func() interface{} { return &networkmap.DIDDocument{} }, JSONOutputCodes: []int{http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { - return getOr(r.Ctx).NetworkMap().GetDIDDocForIndentityByDID(r.Ctx, r.PP["did"]) + return getOr(r.Ctx).NetworkMap().GetDIDDocForIndentityByDID(r.Ctx, extractNamespace(r.PP), r.PP["did"]) }, } diff --git a/internal/apiserver/route_get_net_diddoc_test.go b/internal/apiserver/route_get_net_diddoc_test.go index a836d9f34..46ee15ceb 100644 --- a/internal/apiserver/route_get_net_diddoc_test.go +++ b/internal/apiserver/route_get_net_diddoc_test.go @@ -34,7 +34,7 @@ func TestGetDIDDocByDID(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - nmn.On("GetDIDDocForIndentityByDID", mock.Anything, "did:firefly:org/org_1"). + nmn.On("GetDIDDocForIndentityByDID", mock.Anything, "default", "did:firefly:org/org_1"). Return(&networkmap.DIDDocument{}, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/route_get_net_identities.go b/internal/apiserver/route_get_net_identities.go index 254f04e15..fbc712acf 100644 --- a/internal/apiserver/route_get_net_identities.go +++ b/internal/apiserver/route_get_net_identities.go @@ -35,13 +35,14 @@ var getNetworkIdentities = &oapispec.Route{ }, FilterFactory: database.IdentityQueryFactory, Description: coremsgs.APIEndpointsGetNetworkIdentities, + Deprecated: true, // use getIdentities instead JSONInputValue: nil, JSONOutputValue: func() interface{} { return &[]*core.IdentityWithVerifiers{} }, JSONOutputCodes: []int{http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { if strings.EqualFold(r.QP["fetchverifiers"], "true") { - return filterResult(getOr(r.Ctx).NetworkMap().GetIdentitiesWithVerifiersGlobal(r.Ctx, r.Filter)) + return filterResult(getOr(r.Ctx).NetworkMap().GetIdentitiesWithVerifiers(r.Ctx, extractNamespace(r.PP), r.Filter)) } - return filterResult(getOr(r.Ctx).NetworkMap().GetIdentitiesGlobal(r.Ctx, r.Filter)) + return filterResult(getOr(r.Ctx).NetworkMap().GetIdentities(r.Ctx, extractNamespace(r.PP), r.Filter)) }, } diff --git a/internal/apiserver/route_get_net_identities_test.go b/internal/apiserver/route_get_net_identities_test.go index 01d4cf9ec..2f769b138 100644 --- a/internal/apiserver/route_get_net_identities_test.go +++ b/internal/apiserver/route_get_net_identities_test.go @@ -34,7 +34,7 @@ func TestGetNetIdentities(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mnm.On("GetIdentitiesGlobal", mock.Anything, mock.Anything).Return([]*core.Identity{}, nil, nil) + mnm.On("GetIdentities", mock.Anything, "default", mock.Anything).Return([]*core.Identity{}, nil, nil) r.ServeHTTP(res, req) assert.Equal(t, 200, res.Result().StatusCode) @@ -48,7 +48,7 @@ func TestGetNetIdentitiesWithVerifiers(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mnm.On("GetIdentitiesWithVerifiersGlobal", mock.Anything, mock.Anything).Return([]*core.IdentityWithVerifiers{}, nil, nil) + mnm.On("GetIdentitiesWithVerifiers", mock.Anything, "default", mock.Anything).Return([]*core.IdentityWithVerifiers{}, nil, nil) r.ServeHTTP(res, req) assert.Equal(t, 200, res.Result().StatusCode) diff --git a/internal/apiserver/route_get_net_node.go b/internal/apiserver/route_get_net_node.go index cc4a8e563..bbd756161 100644 --- a/internal/apiserver/route_get_net_node.go +++ b/internal/apiserver/route_get_net_node.go @@ -38,7 +38,7 @@ var getNetworkNode = &oapispec.Route{ JSONOutputValue: func() interface{} { return &core.Identity{} }, JSONOutputCodes: []int{http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { - output, err = getOr(r.Ctx).NetworkMap().GetNodeByNameOrID(r.Ctx, r.PP["nameOrId"]) + output, err = getOr(r.Ctx).NetworkMap().GetNodeByNameOrID(r.Ctx, extractNamespace(r.PP), r.PP["nameOrId"]) return output, err }, } diff --git a/internal/apiserver/route_get_net_node_test.go b/internal/apiserver/route_get_net_node_test.go index c3768dc01..6c3e49565 100644 --- a/internal/apiserver/route_get_net_node_test.go +++ b/internal/apiserver/route_get_net_node_test.go @@ -34,7 +34,7 @@ func TestGetNode(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - nmn.On("GetNodeByNameOrID", mock.Anything, "node12345"). + nmn.On("GetNodeByNameOrID", mock.Anything, "default", "node12345"). Return(&core.Identity{}, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/route_get_net_nodes.go b/internal/apiserver/route_get_net_nodes.go index c11aad137..cc6f6c5a3 100644 --- a/internal/apiserver/route_get_net_nodes.go +++ b/internal/apiserver/route_get_net_nodes.go @@ -37,6 +37,6 @@ var getNetworkNodes = &oapispec.Route{ JSONOutputValue: func() interface{} { return []*core.Identity{} }, JSONOutputCodes: []int{http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { - return filterResult(getOr(r.Ctx).NetworkMap().GetNodes(r.Ctx, r.Filter)) + return filterResult(getOr(r.Ctx).NetworkMap().GetNodes(r.Ctx, extractNamespace(r.PP), r.Filter)) }, } diff --git a/internal/apiserver/route_get_net_nodes_test.go b/internal/apiserver/route_get_net_nodes_test.go index 63ac9f591..cdade72c9 100644 --- a/internal/apiserver/route_get_net_nodes_test.go +++ b/internal/apiserver/route_get_net_nodes_test.go @@ -34,7 +34,7 @@ func TestGetNodess(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mnm.On("GetNodes", mock.Anything, mock.Anything). + mnm.On("GetNodes", mock.Anything, "default", mock.Anything). Return([]*core.Identity{}, nil, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/route_get_net_org.go b/internal/apiserver/route_get_net_org.go index 0cbff3149..0283686d4 100644 --- a/internal/apiserver/route_get_net_org.go +++ b/internal/apiserver/route_get_net_org.go @@ -38,7 +38,7 @@ var getNetworkOrg = &oapispec.Route{ JSONOutputValue: func() interface{} { return &core.Identity{} }, JSONOutputCodes: []int{http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { - output, err = getOr(r.Ctx).NetworkMap().GetOrganizationByNameOrID(r.Ctx, r.PP["nameOrId"]) + output, err = getOr(r.Ctx).NetworkMap().GetOrganizationByNameOrID(r.Ctx, extractNamespace(r.PP), r.PP["nameOrId"]) return output, err }, } diff --git a/internal/apiserver/route_get_net_org_test.go b/internal/apiserver/route_get_net_org_test.go index bea14bb6f..eabf1e51c 100644 --- a/internal/apiserver/route_get_net_org_test.go +++ b/internal/apiserver/route_get_net_org_test.go @@ -34,7 +34,7 @@ func TestGetOrg(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - nmn.On("GetOrganizationByNameOrID", mock.Anything, "org12345"). + nmn.On("GetOrganizationByNameOrID", mock.Anything, "default", "org12345"). Return(&core.Identity{}, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/route_get_net_orgs.go b/internal/apiserver/route_get_net_orgs.go index 57710ab12..528a48e2b 100644 --- a/internal/apiserver/route_get_net_orgs.go +++ b/internal/apiserver/route_get_net_orgs.go @@ -37,6 +37,6 @@ var getNetworkOrgs = &oapispec.Route{ JSONOutputValue: func() interface{} { return []*core.Identity{} }, JSONOutputCodes: []int{http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { - return filterResult(getOr(r.Ctx).NetworkMap().GetOrganizations(r.Ctx, r.Filter)) + return filterResult(getOr(r.Ctx).NetworkMap().GetOrganizations(r.Ctx, extractNamespace(r.PP), r.Filter)) }, } diff --git a/internal/apiserver/route_get_net_orgs_test.go b/internal/apiserver/route_get_net_orgs_test.go index e995b30fb..f86ece85a 100644 --- a/internal/apiserver/route_get_net_orgs_test.go +++ b/internal/apiserver/route_get_net_orgs_test.go @@ -34,7 +34,7 @@ func TestGetOrganizations(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mnm.On("GetOrganizations", mock.Anything, mock.Anything). + mnm.On("GetOrganizations", mock.Anything, "default", mock.Anything). Return([]*core.Identity{}, nil, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/route_get_status.go b/internal/apiserver/route_get_status.go index c980eef27..b8f327962 100644 --- a/internal/apiserver/route_get_status.go +++ b/internal/apiserver/route_get_status.go @@ -36,7 +36,7 @@ var getStatus = &oapispec.Route{ JSONOutputValue: func() interface{} { return &core.NodeStatus{} }, JSONOutputCodes: []int{http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { - output, err = getOr(r.Ctx).GetStatus(r.Ctx) + output, err = getOr(r.Ctx).GetStatus(r.Ctx, extractNamespace(r.PP)) return output, err }, } diff --git a/internal/apiserver/route_get_status_test.go b/internal/apiserver/route_get_status_test.go index 9dd35e014..a8ef1cd97 100644 --- a/internal/apiserver/route_get_status_test.go +++ b/internal/apiserver/route_get_status_test.go @@ -31,7 +31,7 @@ func TestGetStatus(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - o.On("GetStatus", mock.Anything, mock.Anything). + o.On("GetStatus", mock.Anything, "default"). Return(&core.NodeStatus{}, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/route_post_network_action.go b/internal/apiserver/route_post_network_action.go index 02b22e320..a9e5577a7 100644 --- a/internal/apiserver/route_post_network_action.go +++ b/internal/apiserver/route_post_network_action.go @@ -36,7 +36,7 @@ var postNetworkAction = &oapispec.Route{ JSONOutputValue: func() interface{} { return &core.NetworkAction{} }, JSONOutputCodes: []int{http.StatusAccepted}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { - err = getOr(r.Ctx).SubmitNetworkAction(r.Ctx, r.Input.(*core.NetworkAction)) + err = getOr(r.Ctx).SubmitNetworkAction(r.Ctx, extractNamespace(r.PP), r.Input.(*core.NetworkAction)) return r.Input, err }, } diff --git a/internal/apiserver/route_post_network_action_test.go b/internal/apiserver/route_post_network_action_test.go index fe401d258..08be427ce 100644 --- a/internal/apiserver/route_post_network_action_test.go +++ b/internal/apiserver/route_post_network_action_test.go @@ -36,7 +36,7 @@ func TestPostNetworkAction(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - o.On("SubmitNetworkAction", mock.Anything, mock.AnythingOfType("*core.NetworkAction")).Return(nil) + o.On("SubmitNetworkAction", mock.Anything, "default", mock.AnythingOfType("*core.NetworkAction")).Return(nil) r.ServeHTTP(res, req) assert.Equal(t, 202, res.Result().StatusCode) diff --git a/internal/apiserver/route_post_new_node_self.go b/internal/apiserver/route_post_new_node_self.go index 008da7e70..281f03c97 100644 --- a/internal/apiserver/route_post_new_node_self.go +++ b/internal/apiserver/route_post_new_node_self.go @@ -41,7 +41,7 @@ var postNodesSelf = &oapispec.Route{ JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { waitConfirm := strings.EqualFold(r.QP["confirm"], "true") r.SuccessStatus = syncRetcode(waitConfirm) - node, err := getOr(r.Ctx).NetworkMap().RegisterNode(r.Ctx, waitConfirm) + node, err := getOr(r.Ctx).NetworkMap().RegisterNode(r.Ctx, extractNamespace(r.PP), waitConfirm) return node, err }, } diff --git a/internal/apiserver/route_post_new_node_self_test.go b/internal/apiserver/route_post_new_node_self_test.go index aa0ab0f69..acdc0c5c9 100644 --- a/internal/apiserver/route_post_new_node_self_test.go +++ b/internal/apiserver/route_post_new_node_self_test.go @@ -39,7 +39,7 @@ func TestPostNewNodeSelf(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mnm.On("RegisterNode", mock.Anything, false). + mnm.On("RegisterNode", mock.Anything, "default", false). Return(&core.Identity{}, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/route_post_new_organization.go b/internal/apiserver/route_post_new_organization.go index 394c24da0..8926dc727 100644 --- a/internal/apiserver/route_post_new_organization.go +++ b/internal/apiserver/route_post_new_organization.go @@ -41,7 +41,7 @@ var postNewOrganization = &oapispec.Route{ JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { waitConfirm := strings.EqualFold(r.QP["confirm"], "true") r.SuccessStatus = syncRetcode(waitConfirm) - _, err = getOr(r.Ctx).NetworkMap().RegisterOrganization(r.Ctx, r.Input.(*core.IdentityCreateDTO), waitConfirm) + _, err = getOr(r.Ctx).NetworkMap().RegisterOrganization(r.Ctx, extractNamespace(r.PP), r.Input.(*core.IdentityCreateDTO), waitConfirm) return r.Input, err }, } diff --git a/internal/apiserver/route_post_new_organization_self.go b/internal/apiserver/route_post_new_organization_self.go index 2b212e62a..50aa94676 100644 --- a/internal/apiserver/route_post_new_organization_self.go +++ b/internal/apiserver/route_post_new_organization_self.go @@ -41,7 +41,7 @@ var postNewOrganizationSelf = &oapispec.Route{ JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { waitConfirm := strings.EqualFold(r.QP["confirm"], "true") r.SuccessStatus = syncRetcode(waitConfirm) - org, err := getOr(r.Ctx).NetworkMap().RegisterNodeOrganization(r.Ctx, waitConfirm) + org, err := getOr(r.Ctx).NetworkMap().RegisterNodeOrganization(r.Ctx, extractNamespace(r.PP), waitConfirm) return org, err }, } diff --git a/internal/apiserver/route_post_new_organization_self_test.go b/internal/apiserver/route_post_new_organization_self_test.go index e9900f1f8..7a7353ec3 100644 --- a/internal/apiserver/route_post_new_organization_self_test.go +++ b/internal/apiserver/route_post_new_organization_self_test.go @@ -39,7 +39,7 @@ func TestNewOrganizationSelf(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mnm.On("RegisterNodeOrganization", mock.Anything, false). + mnm.On("RegisterNodeOrganization", mock.Anything, "default", false). Return(&core.Identity{}, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/route_post_new_organization_test.go b/internal/apiserver/route_post_new_organization_test.go index bdcaaadd7..e859e5901 100644 --- a/internal/apiserver/route_post_new_organization_test.go +++ b/internal/apiserver/route_post_new_organization_test.go @@ -39,7 +39,7 @@ func TestNewOrganization(t *testing.T) { req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mnm.On("RegisterOrganization", mock.Anything, mock.AnythingOfType("*core.IdentityCreateDTO"), false). + mnm.On("RegisterOrganization", mock.Anything, "default", mock.AnythingOfType("*core.IdentityCreateDTO"), false). Return(&core.Identity{}, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/routes.go b/internal/apiserver/routes.go index cc7e18eff..03e5f9b15 100644 --- a/internal/apiserver/routes.go +++ b/internal/apiserver/routes.go @@ -25,24 +25,11 @@ import ( var routes = append( globalRoutes([]*oapispec.Route{ - getDIDDocByDID, - getIdentityByDID, getNamespace, getNamespaces, - getNetworkIdentities, - getNetworkNode, - getNetworkNodes, - getNetworkOrg, - getNetworkOrgs, - getStatus, getStatusBatchManager, getStatusPins, getStatusWebSockets, - postNetworkAction, - postNewNamespace, - postNewOrganization, - postNewOrganizationSelf, - postNodesSelf, }), namespacedRoutes([]*oapispec.Route{ deleteContractListener, @@ -72,6 +59,7 @@ var routes = append( getGroupByHash, getGroups, getIdentities, + getIdentityByDID, getIdentityByID, getIdentityDID, getIdentityVerifiers, @@ -80,8 +68,16 @@ var routes = append( getMsgEvents, getMsgs, getMsgTxn, + getNetworkDIDDocByDID, + getNetworkIdentities, + getNetworkIdentityByDID, + getNetworkNode, + getNetworkNodes, + getNetworkOrg, + getNetworkOrgs, getOpByID, getOps, + getStatus, getSubscriptionByID, getSubscriptions, getTokenAccountPools, @@ -108,6 +104,7 @@ var routes = append( postContractInvoke, postContractQuery, postData, + postNetworkAction, postNewContractAPI, postNewContractInterface, postNewContractListener, @@ -117,6 +114,9 @@ var routes = append( postNewMessagePrivate, postNewMessageRequestReply, postNewSubscription, + postNewOrganization, + postNewOrganizationSelf, + postNodesSelf, postOpRetry, postTokenApproval, postTokenBurn, diff --git a/internal/assets/token_approval.go b/internal/assets/token_approval.go index 42d3bb093..26679935c 100644 --- a/internal/assets/token_approval.go +++ b/internal/assets/token_approval.go @@ -145,6 +145,6 @@ func (am *assetManager) validateApproval(ctx context.Context, ns string, approva if pool.State != core.TokenPoolStateConfirmed { return nil, i18n.NewError(ctx, coremsgs.MsgTokenPoolNotConfirmed) } - approval.Key, err = am.identity.NormalizeSigningKey(ctx, approval.Key, am.keyNormalization) + approval.Key, err = am.identity.NormalizeSigningKey(ctx, ns, approval.Key, am.keyNormalization) return pool, err } diff --git a/internal/assets/token_approval_test.go b/internal/assets/token_approval_test.go index 52a045fcd..04e6aef1c 100644 --- a/internal/assets/token_approval_test.go +++ b/internal/assets/token_approval_test.go @@ -69,7 +69,7 @@ func TestTokenApprovalSuccess(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenApproval).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -108,7 +108,7 @@ func TestTokenApprovalSuccessUnknownIdentity(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenApproval).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -165,7 +165,7 @@ func TestApprovalBadConnector(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) _, err := am.TokenApproval(context.Background(), "ns1", approval, false) @@ -206,7 +206,7 @@ func TestApprovalDefaultPoolSuccess(t *testing.T) { filterResult := &database.FilterResult{ TotalCount: &totalCount, } - mim.On("NormalizeSigningKey", context.Background(), "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPools", context.Background(), mock.MatchedBy((func(f database.AndFilter) bool { info, _ := f.Finalize() return info.Count && info.Limit == 1 @@ -272,7 +272,7 @@ func TestApprovalBadPool(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(nil, fmt.Errorf("pop")) _, err := am.TokenApproval(context.Background(), "ns1", approval, false) @@ -324,7 +324,7 @@ func TestApprovalIdentityFail(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) _, err := am.TokenApproval(context.Background(), "ns1", approval, false) @@ -356,7 +356,7 @@ func TestApprovalFail(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenApproval).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -394,7 +394,7 @@ func TestApprovalTransactionFail(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenApproval).Return(nil, fmt.Errorf("pop")) @@ -427,7 +427,7 @@ func TestApprovalOperationsFail(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) - mim.On("NormalizeSigningKey", context.Background(), "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenApproval).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(fmt.Errorf("pop")) @@ -460,7 +460,7 @@ func TestTokenApprovalConfirm(t *testing.T) { msa := am.syncasync.(*syncasyncmocks.Bridge) mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenApproval).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -508,7 +508,7 @@ func TestApprovalPrepare(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "key", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) err := sender.Prepare(context.Background()) diff --git a/internal/assets/token_pool.go b/internal/assets/token_pool.go index 67c670710..2134d4f82 100644 --- a/internal/assets/token_pool.go +++ b/internal/assets/token_pool.go @@ -52,7 +52,7 @@ func (am *assetManager) CreateTokenPool(ctx context.Context, ns string, pool *co } var err error - pool.Key, err = am.identity.NormalizeSigningKey(ctx, pool.Key, am.keyNormalization) + pool.Key, err = am.identity.NormalizeSigningKey(ctx, ns, pool.Key, am.keyNormalization) if err != nil { return nil, err } diff --git a/internal/assets/token_pool_test.go b/internal/assets/token_pool_test.go index 569d59631..1df083b87 100644 --- a/internal/assets/token_pool_test.go +++ b/internal/assets/token_pool_test.go @@ -100,7 +100,7 @@ func TestCreateTokenPoolDefaultConnectorSuccess(t *testing.T) { mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) mdi.On("GetTokenPool", context.Background(), "ns1", "testpool").Return(nil, nil) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("resolved-key", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("resolved-key", nil) mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenPool).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -215,7 +215,7 @@ func TestCreateTokenPoolIdentityFail(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mdi.On("GetTokenPool", context.Background(), "ns1", "testpool").Return(nil, nil) mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) _, err := am.CreateTokenPool(context.Background(), "ns1", pool, false) assert.EqualError(t, err, "pop") @@ -238,7 +238,7 @@ func TestCreateTokenPoolWrongConnector(t *testing.T) { mdm := am.data.(*datamocks.Manager) mim := am.identity.(*identitymanagermocks.Manager) mdi.On("GetTokenPool", context.Background(), "ns1", "testpool").Return(nil, nil) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) _, err := am.CreateTokenPool(context.Background(), "ns1", pool, false) @@ -264,7 +264,7 @@ func TestCreateTokenPoolFail(t *testing.T) { mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) mdi.On("GetTokenPool", context.Background(), "ns1", "testpool").Return(nil, nil) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenPool).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -297,7 +297,7 @@ func TestCreateTokenPoolTransactionFail(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) mdi.On("GetTokenPool", context.Background(), "ns1", "testpool").Return(nil, nil) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenPool).Return(nil, fmt.Errorf("pop")) @@ -324,7 +324,7 @@ func TestCreateTokenPoolOpInsertFail(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) mdi.On("GetTokenPool", context.Background(), "ns1", "testpool").Return(nil, nil) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenPool).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(fmt.Errorf("pop")) @@ -353,7 +353,7 @@ func TestCreateTokenPoolSyncSuccess(t *testing.T) { mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) mdi.On("GetTokenPool", context.Background(), "ns1", "testpool").Return(nil, nil) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenPool).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -387,7 +387,7 @@ func TestCreateTokenPoolAsyncSuccess(t *testing.T) { mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) mdi.On("GetTokenPool", context.Background(), "ns1", "testpool").Return(nil, nil) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenPool).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -423,7 +423,7 @@ func TestCreateTokenPoolConfirm(t *testing.T) { mom := am.operations.(*operationmocks.Manager) mdi.On("GetTokenPool", context.Background(), "ns1", "testpool").Return(nil, nil) mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenPool).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) msa.On("WaitForTokenPool", context.Background(), "ns1", mock.Anything, mock.Anything). diff --git a/internal/assets/token_transfer.go b/internal/assets/token_transfer.go index 5fb2ac0fc..79b9d8e26 100644 --- a/internal/assets/token_transfer.go +++ b/internal/assets/token_transfer.go @@ -106,7 +106,7 @@ func (am *assetManager) validateTransfer(ctx context.Context, ns string, transfe if pool.State != core.TokenPoolStateConfirmed { return nil, i18n.NewError(ctx, coremsgs.MsgTokenPoolNotConfirmed) } - if transfer.Key, err = am.identity.NormalizeSigningKey(ctx, transfer.Key, am.keyNormalization); err != nil { + if transfer.Key, err = am.identity.NormalizeSigningKey(ctx, ns, transfer.Key, am.keyNormalization); err != nil { return nil, err } if transfer.From == "" { diff --git a/internal/assets/token_transfer_test.go b/internal/assets/token_transfer_test.go index 3faed2845..3e9af842e 100644 --- a/internal/assets/token_transfer_test.go +++ b/internal/assets/token_transfer_test.go @@ -91,7 +91,7 @@ func TestMintTokensSuccess(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -126,7 +126,7 @@ func TestMintTokensBadConnector(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) _, err := am.MintTokens(context.Background(), "ns1", mint, false) @@ -179,7 +179,7 @@ func TestMintTokenDefaultPoolSuccess(t *testing.T) { filterResult := &database.FilterResult{ TotalCount: &totalCount, } - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPools", context.Background(), mock.MatchedBy((func(f database.AndFilter) bool { info, _ := f.Finalize() return info.Count && info.Limit == 1 @@ -337,7 +337,7 @@ func TestMintTokensIdentityFail(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) _, err := am.MintTokens(context.Background(), "ns1", mint, false) @@ -366,7 +366,7 @@ func TestMintTokensFail(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -403,7 +403,7 @@ func TestMintTokensOperationFail(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(fmt.Errorf("pop")) @@ -437,7 +437,7 @@ func TestMintTokensConfirm(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -480,7 +480,7 @@ func TestBurnTokensSuccess(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -515,7 +515,7 @@ func TestBurnTokensIdentityFail(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) _, err := am.BurnTokens(context.Background(), "ns1", burn, false) @@ -546,7 +546,7 @@ func TestBurnTokensConfirm(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -592,7 +592,7 @@ func TestTransferTokensSuccess(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -656,7 +656,7 @@ func TestTransferTokensIdentityFail(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) @@ -680,7 +680,7 @@ func TestTransferTokensNoFromOrTo(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) @@ -711,7 +711,7 @@ func TestTransferTokensTransactionFail(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(nil, fmt.Errorf("pop")) @@ -761,7 +761,7 @@ func TestTransferTokensWithBroadcastMessage(t *testing.T) { mms := &sysmessagingmocks.MessageSender{} mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -824,7 +824,7 @@ func TestTransferTokensWithBroadcastMessageSendFail(t *testing.T) { mms := &sysmessagingmocks.MessageSender{} mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -916,7 +916,7 @@ func TestTransferTokensWithPrivateMessage(t *testing.T) { mms := &sysmessagingmocks.MessageSender{} mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -993,7 +993,7 @@ func TestTransferTokensConfirm(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) @@ -1058,7 +1058,7 @@ func TestTransferTokensWithBroadcastConfirm(t *testing.T) { msa := am.syncasync.(*syncasyncmocks.Bridge) mth := am.txHelper.(*txcommonmocks.Helper) mom := am.operations.(*operationmocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) mdi.On("InsertOperation", context.Background(), mock.Anything).Return(nil) mth.On("SubmitNewTransaction", context.Background(), "ns1", core.TransactionTypeTokenTransfer).Return(fftypes.NewUUID(), nil) @@ -1139,7 +1139,7 @@ func TestTransferPrepare(t *testing.T) { mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) - mim.On("NormalizeSigningKey", context.Background(), "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) + mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("0x12345", nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(pool, nil) err := sender.Prepare(context.Background()) diff --git a/internal/batch/batch_manager_test.go b/internal/batch/batch_manager_test.go index 51e3cd952..a77d0b656 100644 --- a/internal/batch/batch_manager_test.go +++ b/internal/batch/batch_manager_test.go @@ -60,7 +60,7 @@ func TestE2EDispatchBroadcast(t *testing.T) { mdm := &datamocks.Manager{} mni := &sysmessagingmocks.LocalNodeInfo{} txHelper := txcommon.NewTransactionHelper(mdi, mdm) - mni.On("GetNodeUUID", mock.Anything).Return(fftypes.NewUUID()) + mni.On("GetNodeUUID", mock.Anything, "ns1").Return(fftypes.NewUUID()) readyForDispatch := make(chan bool) waitForDispatch := make(chan *DispatchState) handler := func(ctx context.Context, state *DispatchState) error { @@ -176,7 +176,7 @@ func TestE2EDispatchPrivateUnpinned(t *testing.T) { mdm := &datamocks.Manager{} mni := &sysmessagingmocks.LocalNodeInfo{} txHelper := txcommon.NewTransactionHelper(mdi, mdm) - mni.On("GetNodeUUID", mock.Anything).Return(fftypes.NewUUID()) + mni.On("GetNodeUUID", mock.Anything, "ns1").Return(fftypes.NewUUID()) readyForDispatch := make(chan bool) waitForDispatch := make(chan *DispatchState) var groupID fftypes.Bytes32 @@ -386,7 +386,7 @@ func TestMessageSequencerUpdateMessagesFail(t *testing.T) { mdm := &datamocks.Manager{} mni := &sysmessagingmocks.LocalNodeInfo{} txHelper := txcommon.NewTransactionHelper(mdi, mdm) - mni.On("GetNodeUUID", mock.Anything).Return(fftypes.NewUUID()) + mni.On("GetNodeUUID", mock.Anything, "ns1").Return(fftypes.NewUUID()) ctx, cancelCtx := context.WithCancel(context.Background()) bm, _ := NewBatchManager(ctx, mni, mdi, mdm, txHelper) bm.RegisterDispatcher("utdispatcher", core.TransactionTypeBatchPin, []core.MessageType{core.MessageTypeBroadcast}, @@ -440,7 +440,7 @@ func TestMessageSequencerDispatchFail(t *testing.T) { mdi := &databasemocks.Plugin{} mdm := &datamocks.Manager{} mni := &sysmessagingmocks.LocalNodeInfo{} - mni.On("GetNodeUUID", mock.Anything).Return(fftypes.NewUUID()) + mni.On("GetNodeUUID", mock.Anything, "ns1").Return(fftypes.NewUUID()) txHelper := txcommon.NewTransactionHelper(mdi, mdm) ctx, cancelCtx := context.WithCancel(context.Background()) bm, _ := NewBatchManager(ctx, mni, mdi, mdm, txHelper) @@ -480,7 +480,7 @@ func TestMessageSequencerUpdateBatchFail(t *testing.T) { mdi := &databasemocks.Plugin{} mdm := &datamocks.Manager{} mni := &sysmessagingmocks.LocalNodeInfo{} - mni.On("GetNodeUUID", mock.Anything).Return(fftypes.NewUUID()) + mni.On("GetNodeUUID", mock.Anything, "ns1").Return(fftypes.NewUUID()) ctx, cancelCtx := context.WithCancel(context.Background()) txHelper := txcommon.NewTransactionHelper(mdi, mdm) bm, _ := NewBatchManager(ctx, mni, mdi, mdm, txHelper) diff --git a/internal/batch/batch_processor.go b/internal/batch/batch_processor.go index c8f1243f5..f970cb3ef 100644 --- a/internal/batch/batch_processor.go +++ b/internal/batch/batch_processor.go @@ -383,7 +383,7 @@ func (bp *batchProcessor) initFlushState(id *fftypes.UUID, flushWork []*batchWor SignerRef: bp.conf.signer, Group: bp.conf.group, Created: fftypes.Now(), - Node: bp.ni.GetNodeUUID(bp.ctx), + Node: bp.ni.GetNodeUUID(bp.ctx, bp.conf.namespace), }, }, } diff --git a/internal/batch/batch_processor_test.go b/internal/batch/batch_processor_test.go index 15ad633af..5bbdafdb9 100644 --- a/internal/batch/batch_processor_test.go +++ b/internal/batch/batch_processor_test.go @@ -42,7 +42,7 @@ func newTestBatchProcessor(t *testing.T, dispatch DispatchHandler) (func(), *dat mni := bm.ni.(*sysmessagingmocks.LocalNodeInfo) mdm := bm.data.(*datamocks.Manager) txHelper := txcommon.NewTransactionHelper(mdi, mdm) - mni.On("GetNodeUUID", mock.Anything).Return(fftypes.NewUUID()).Maybe() + mni.On("GetNodeUUID", mock.Anything, "ns1").Return(fftypes.NewUUID()).Maybe() bp := newBatchProcessor(bm, &batchProcessorConf{ namespace: "ns1", txType: core.TransactionTypeBatchPin, diff --git a/internal/blockchain/ethereum/abi_definitions.go b/internal/blockchain/ethereum/abi_definitions.go index 1c7e5dc19..a0849f00a 100644 --- a/internal/blockchain/ethereum/abi_definitions.go +++ b/internal/blockchain/ethereum/abi_definitions.go @@ -96,3 +96,16 @@ var batchPinEventABI = ABIElementMarshaling{ }, }, } + +var networkVersionMethodABI = ABIElementMarshaling{ + Name: "networkVersion", + Type: "function", + StateMutability: "pure", + Inputs: []ABIArgumentMarshaling{}, + Outputs: []ABIArgumentMarshaling{ + { + InternalType: "uint8", + Type: "uint8", + }, + }, +} diff --git a/internal/blockchain/ethereum/ethereum.go b/internal/blockchain/ethereum/ethereum.go index 25627e061..2b7791e9c 100644 --- a/internal/blockchain/ethereum/ethereum.go +++ b/internal/blockchain/ethereum/ethereum.go @@ -22,6 +22,7 @@ import ( "encoding/json" "fmt" "regexp" + "strconv" "strings" "sync" @@ -49,21 +50,22 @@ const ( ) type Ethereum struct { - ctx context.Context - topic string - fireflyContract string - fireflyFromBlock string - fireflyMux sync.Mutex - prefixShort string - prefixLong string - capabilities *blockchain.Capabilities - callbacks blockchain.Callbacks - client *resty.Client - fftmClient *resty.Client - streams *streamManager - initInfo struct { - stream *eventStream - sub *subscription + ctx context.Context + topic string + prefixShort string + prefixLong string + capabilities *blockchain.Capabilities + callbacks blockchain.Callbacks + client *resty.Client + fftmClient *resty.Client + streams *streamManager + streamID string + fireflyContract struct { + mux sync.Mutex + address string + fromBlock string + networkVersion int + subscription string } wsconn wsclient.WSClient closed chan struct{} @@ -199,10 +201,12 @@ func (e *Ethereum) Init(ctx context.Context, config config.Section, callbacks bl e.streams = &streamManager{client: e.client} batchSize := ethconnectConf.GetUint(EthconnectConfigBatchSize) batchTimeout := uint(ethconnectConf.GetDuration(EthconnectConfigBatchTimeout).Milliseconds()) - if e.initInfo.stream, err = e.streams.ensureEventStream(e.ctx, e.topic, batchSize, batchTimeout); err != nil { + stream, err := e.streams.ensureEventStream(e.ctx, e.topic, batchSize, batchTimeout) + if err != nil { return err } - log.L(e.ctx).Infof("Event stream: %s (topic=%s)", e.initInfo.stream.ID, e.topic) + e.streamID = stream.ID + log.L(e.ctx).Infof("Event stream: %s (topic=%s)", e.streamID, e.topic) e.closed = make(chan struct{}) go e.eventLoop() @@ -267,16 +271,18 @@ func (e *Ethereum) ConfigureContract(ctx context.Context, contracts *core.FireFl return err } - e.initInfo.sub, err = e.streams.ensureFireFlySubscription(ctx, address, fromBlock, e.initInfo.stream.ID, batchPinEventABI) + sub, err := e.streams.ensureFireFlySubscription(ctx, address, fromBlock, e.streamID, batchPinEventABI) if err == nil { - e.fireflyMux.Lock() - e.fireflyContract = address - e.fireflyFromBlock = fromBlock - e.fireflyMux.Unlock() + e.fireflyContract.mux.Lock() + e.fireflyContract.address = address + e.fireflyContract.fromBlock = fromBlock + e.fireflyContract.networkVersion = 0 + e.fireflyContract.subscription = sub.ID + e.fireflyContract.mux.Unlock() contracts.Active.Info = fftypes.JSONObject{ "address": address, "fromBlock": fromBlock, - "subscription": e.initInfo.sub.ID, + "subscription": sub.ID, } } return err @@ -288,13 +294,13 @@ func (e *Ethereum) TerminateContract(ctx context.Context, contracts *core.FireFl if err != nil { return err } - e.fireflyMux.Lock() - if address != e.fireflyContract { - log.L(ctx).Warnf("Ignoring termination request from address %s, which differs from active address %s", address, e.fireflyContract) - e.fireflyMux.Unlock() + e.fireflyContract.mux.Lock() + if address != e.fireflyContract.address { + log.L(ctx).Warnf("Ignoring termination request from address %s, which differs from active address %s", address, e.fireflyContract.address) + e.fireflyContract.mux.Unlock() return nil } - e.fireflyMux.Unlock() + e.fireflyContract.mux.Unlock() log.L(ctx).Infof("Processing termination request from address %s", address) contracts.Active.FinalEvent = termination.ProtocolID @@ -499,7 +505,11 @@ func (e *Ethereum) handleMessageBatch(ctx context.Context, messages []interface{ l1.Infof("Received '%s' message", signature) l1.Tracef("Message: %+v", msgJSON) - if sub == e.initInfo.sub.ID { + e.fireflyContract.mux.Lock() + fireflySub := e.fireflyContract.subscription + e.fireflyContract.mux.Unlock() + + if sub == fireflySub { // Matches the active FireFly BatchPin subscription switch signature { case broadcastBatchEventSignature: @@ -655,10 +665,16 @@ func (e *Ethereum) queryContractMethod(ctx context.Context, address string, abi if err != nil { return nil, err } - return e.client.R(). + var resErr ethError + res, err := e.client.R(). SetContext(ctx). SetBody(body). + SetError(&resErr). Post("/") + if err != nil || !res.IsSuccess() { + return res, wrapError(ctx, &resErr, res, err) + } + return res, nil } func (e *Ethereum) SubmitBatchPin(ctx context.Context, operationID *fftypes.UUID, signingKey string, batch *blockchain.BatchPin) error { @@ -676,9 +692,9 @@ func (e *Ethereum) SubmitBatchPin(ctx context.Context, operationID *fftypes.UUID batch.BatchPayloadRef, ethHashes, } - e.fireflyMux.Lock() - address := e.fireflyContract - e.fireflyMux.Unlock() + e.fireflyContract.mux.Lock() + address := e.fireflyContract.address + e.fireflyContract.mux.Unlock() return e.invokeContractMethod(ctx, address, signingKey, batchPinMethodABI, operationID.String(), input, nil) } @@ -690,9 +706,9 @@ func (e *Ethereum) SubmitNetworkAction(ctx context.Context, operationID *fftypes "", []string{}, } - e.fireflyMux.Lock() - address := e.fireflyContract - e.fireflyMux.Unlock() + e.fireflyContract.mux.Lock() + address := e.fireflyContract.address + e.fireflyContract.mux.Unlock() return e.invokeContractMethod(ctx, address, signingKey, batchPinMethodABI, operationID.String(), input, nil) } @@ -719,7 +735,7 @@ func (e *Ethereum) QueryContract(ctx context.Context, location *fftypes.JSONAny, } res, err := e.queryContractMethod(ctx, ethereumLocation.Address, abi, orderedInput, options) if err != nil || !res.IsSuccess() { - return nil, ffresty.WrapRestErr(ctx, res, err, coremsgs.MsgEthconnectRESTErr) + return nil, err } output := &queryOutput{} if err = json.Unmarshal(res.Body(), output); err != nil { @@ -766,7 +782,7 @@ func (e *Ethereum) AddContractListener(ctx context.Context, listener *core.Contr } subName := fmt.Sprintf("ff-sub-%s", listener.ID) - result, err := e.streams.createSubscription(ctx, location, e.initInfo.stream.ID, subName, listener.Options.FirstEvent, abi) + result, err := e.streams.createSubscription(ctx, location, e.streamID, subName, listener.Options.FirstEvent, abi) if err != nil { return err } @@ -1060,3 +1076,33 @@ func (e *Ethereum) getFFIType(solitidyType string) string { } return "" } + +func (e *Ethereum) getNetworkVersion(ctx context.Context, address string) (int, error) { + res, err := e.queryContractMethod(ctx, address, networkVersionMethodABI, []interface{}{}, nil) + if err != nil || !res.IsSuccess() { + // "Call failed" is interpreted as "method does not exist, default to version 1" + if strings.Contains(err.Error(), "FFEC100148") { + return 1, nil + } + return 0, err + } + output := &queryOutput{} + if err = json.Unmarshal(res.Body(), output); err != nil { + return 0, err + } + return strconv.Atoi(output.Output.(string)) +} + +func (e *Ethereum) NetworkVersion(ctx context.Context) (int, error) { + e.fireflyContract.mux.Lock() + defer e.fireflyContract.mux.Unlock() + if e.fireflyContract.networkVersion > 0 { + return e.fireflyContract.networkVersion, nil + } + address := e.fireflyContract.address + version, err := e.getNetworkVersion(ctx, address) + if err == nil { + e.fireflyContract.networkVersion = version + } + return version, err +} diff --git a/internal/blockchain/ethereum/ethereum_test.go b/internal/blockchain/ethereum/ethereum_test.go index 9ef512015..ada6a21eb 100644 --- a/internal/blockchain/ethereum/ethereum_test.go +++ b/internal/blockchain/ethereum/ethereum_test.go @@ -82,16 +82,16 @@ func newTestEthereum() (*Ethereum, func()) { mm.On("BlockchainTransaction", mock.Anything, mock.Anything).Return(nil) mm.On("BlockchainQuery", mock.Anything, mock.Anything).Return(nil) e := &Ethereum{ - ctx: ctx, - client: resty.New().SetBaseURL("http://localhost:12345"), - fireflyContract: "/instances/0x12345", - topic: "topic1", - prefixShort: defaultPrefixShort, - prefixLong: defaultPrefixLong, - callbacks: em, - wsconn: wsm, - metrics: mm, - } + ctx: ctx, + client: resty.New().SetBaseURL("http://localhost:12345"), + topic: "topic1", + prefixShort: defaultPrefixShort, + prefixLong: defaultPrefixLong, + callbacks: em, + wsconn: wsm, + metrics: mm, + } + e.fireflyContract.address = "/instances/0x12345" return e, func() { cancel() if e.closed != nil { @@ -178,8 +178,8 @@ func TestInitAndStartWithFFTM(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 4, httpmock.GetTotalCallCount()) - assert.Equal(t, "es12345", e.initInfo.stream.ID) - assert.Equal(t, "sub12345", e.initInfo.sub.ID) + assert.Equal(t, "es12345", e.streamID) + assert.Equal(t, "sub12345", e.fireflyContract.subscription) assert.NotNil(t, e.Capabilities()) err = e.Start() @@ -271,8 +271,8 @@ func TestInitAllExistingStreams(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 3, httpmock.GetTotalCallCount()) - assert.Equal(t, "es12345", e.initInfo.stream.ID) - assert.Equal(t, "sub12345", e.initInfo.sub.ID) + assert.Equal(t, "es12345", e.streamID) + assert.Equal(t, "sub12345", e.fireflyContract.subscription) } @@ -320,7 +320,7 @@ func TestInitOldInstancePathContracts(t *testing.T) { err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{}) assert.NoError(t, err) - assert.Equal(t, e.fireflyContract, "0x71c7656ec7ab88b098defb751b7401b5f6d8976f") + assert.Equal(t, e.fireflyContract.address, "0x71c7656ec7ab88b098defb751b7401b5f6d8976f") } func TestInitOldInstancePathInstances(t *testing.T) { @@ -357,7 +357,7 @@ func TestInitOldInstancePathInstances(t *testing.T) { err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{}) assert.NoError(t, err) - assert.Equal(t, e.fireflyContract, "0x71c7656ec7ab88b098defb751b7401b5f6d8976f") + assert.Equal(t, e.fireflyContract.address, "0x71c7656ec7ab88b098defb751b7401b5f6d8976f") } func TestInitOldInstancePathError(t *testing.T) { @@ -427,7 +427,7 @@ func TestInitNewConfig(t *testing.T) { err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{}) assert.NoError(t, err) - assert.Equal(t, e.fireflyContract, "0x71c7656ec7ab88b098defb751b7401b5f6d8976f") + assert.Equal(t, e.fireflyContract.address, "0x71c7656ec7ab88b098defb751b7401b5f6d8976f") } func TestInitNewConfigError(t *testing.T) { @@ -974,9 +974,7 @@ func TestHandleMessageBatchPinOK(t *testing.T) { e := &Ethereum{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" expectedSigningKeyRef := &core.VerifierRef{ Type: core.VerifierTypeEthAddress, @@ -1061,9 +1059,7 @@ func TestHandleMessageBatchPinMissingAuthor(t *testing.T) { e := &Ethereum{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" var events []interface{} err := json.Unmarshal(data.Bytes(), &events) @@ -1105,9 +1101,7 @@ func TestHandleMessageEmptyPayloadRef(t *testing.T) { e := &Ethereum{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" expectedSigningKeyRef := &core.VerifierRef{ Type: core.VerifierTypeEthAddress, @@ -1168,9 +1162,7 @@ func TestHandleMessageBatchPinExit(t *testing.T) { e := &Ethereum{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" em.On("BatchPinComplete", mock.Anything, expectedSigningKeyRef, mock.Anything).Return(fmt.Errorf("pop")) @@ -1185,9 +1177,7 @@ func TestHandleMessageBatchPinExit(t *testing.T) { func TestHandleMessageBatchPinEmpty(t *testing.T) { em := &blockchainmocks.Callbacks{} e := &Ethereum{callbacks: em} - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" var events []interface{} err := json.Unmarshal([]byte(` @@ -1206,9 +1196,7 @@ func TestHandleMessageBatchPinEmpty(t *testing.T) { func TestHandleMessageBatchMissingData(t *testing.T) { em := &blockchainmocks.Callbacks{} e := &Ethereum{callbacks: em} - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" var events []interface{} err := json.Unmarshal([]byte(` @@ -1228,9 +1216,7 @@ func TestHandleMessageBatchMissingData(t *testing.T) { func TestHandleMessageBatchPinBadTransactionID(t *testing.T) { em := &blockchainmocks.Callbacks{} e := &Ethereum{callbacks: em} - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" data := fftypes.JSONAnyPtr(`[{ "address": "0x1C197604587F046FD40684A8f21f4609FB811A7b", "subId": "sb-b5b97a4e-a317-4053-6400-1474650efcb5", @@ -1262,9 +1248,7 @@ func TestHandleMessageBatchPinBadTransactionID(t *testing.T) { func TestHandleMessageBatchPinBadIDentity(t *testing.T) { em := &blockchainmocks.Callbacks{} e := &Ethereum{callbacks: em} - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" data := fftypes.JSONAnyPtr(`[{ "address": "0x1C197604587F046FD40684A8f21f4609FB811A7b", "subId": "sb-b5b97a4e-a317-4053-6400-1474650efcb5", @@ -1296,9 +1280,7 @@ func TestHandleMessageBatchPinBadIDentity(t *testing.T) { func TestHandleMessageBatchPinBadBatchHash(t *testing.T) { em := &blockchainmocks.Callbacks{} e := &Ethereum{callbacks: em} - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" data := fftypes.JSONAnyPtr(`[{ "address": "0x1C197604587F046FD40684A8f21f4609FB811A7b", "subId": "sb-b5b97a4e-a317-4053-6400-1474650efcb5", @@ -1330,9 +1312,7 @@ func TestHandleMessageBatchPinBadBatchHash(t *testing.T) { func TestHandleMessageBatchPinBadPin(t *testing.T) { em := &blockchainmocks.Callbacks{} e := &Ethereum{callbacks: em} - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" data := fftypes.JSONAnyPtr(`[{ "address": "0x1C197604587F046FD40684A8f21f4609FB811A7b", "subId": "sb-b5b97a4e-a317-4053-6400-1474650efcb5", @@ -1553,9 +1533,7 @@ func TestAddSubscription(t *testing.T) { defer cancel() httpmock.ActivateNonDefault(e.client.GetClient()) defer httpmock.DeactivateAndReset() - e.initInfo.stream = &eventStream{ - ID: "es-1", - } + e.streamID = "es-1" e.streams = &streamManager{ client: e.client, } @@ -1595,9 +1573,7 @@ func TestAddSubscriptionBadParamDetails(t *testing.T) { defer cancel() httpmock.ActivateNonDefault(e.client.GetClient()) defer httpmock.DeactivateAndReset() - e.initInfo.stream = &eventStream{ - ID: "es-1", - } + e.streamID = "es-1" e.streams = &streamManager{ client: e.client, } @@ -1635,9 +1611,7 @@ func TestAddSubscriptionBadLocation(t *testing.T) { httpmock.ActivateNonDefault(e.client.GetClient()) defer httpmock.DeactivateAndReset() - e.initInfo.stream = &eventStream{ - ID: "es-1", - } + e.streamID = "es-1" e.streams = &streamManager{ client: e.client, } @@ -1660,9 +1634,7 @@ func TestAddSubscriptionFail(t *testing.T) { httpmock.ActivateNonDefault(e.client.GetClient()) defer httpmock.DeactivateAndReset() - e.initInfo.stream = &eventStream{ - ID: "es-1", - } + e.streamID = "es-1" e.streams = &streamManager{ client: e.client, } @@ -1694,9 +1666,7 @@ func TestDeleteSubscription(t *testing.T) { httpmock.ActivateNonDefault(e.client.GetClient()) defer httpmock.DeactivateAndReset() - e.initInfo.stream = &eventStream{ - ID: "es-1", - } + e.streamID = "es-1" e.streams = &streamManager{ client: e.client, } @@ -1719,9 +1689,7 @@ func TestDeleteSubscriptionFail(t *testing.T) { httpmock.ActivateNonDefault(e.client.GetClient()) defer httpmock.DeactivateAndReset() - e.initInfo.stream = &eventStream{ - ID: "es-1", - } + e.streamID = "es-1" e.streams = &streamManager{ client: e.client, } @@ -1761,9 +1729,7 @@ func TestHandleMessageContractEvent(t *testing.T) { e := &Ethereum{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" em.On("BlockchainEvent", mock.MatchedBy(func(e *blockchain.EventWithSubscription) bool { assert.Equal(t, "0xc26df2bf1a733e9249372d61eb11bd8662d26c8129df76890b1beb2f6fa72628", e.BlockchainTXID) @@ -1825,9 +1791,7 @@ func TestHandleMessageContractEventError(t *testing.T) { e := &Ethereum{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" em.On("BlockchainEvent", mock.Anything).Return(fmt.Errorf("pop")) @@ -2909,9 +2873,7 @@ func TestHandleNetworkAction(t *testing.T) { e := &Ethereum{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" expectedSigningKeyRef := &core.VerifierRef{ Type: core.VerifierTypeEthAddress, @@ -2929,3 +2891,89 @@ func TestHandleNetworkAction(t *testing.T) { em.AssertExpectations(t) } + +func TestNetworkVersion(t *testing.T) { + e, _ := newTestEthereum() + httpmock.ActivateNonDefault(e.client.GetClient()) + defer httpmock.DeactivateAndReset() + httpmock.RegisterResponder("POST", `http://localhost:12345/`, + func(req *http.Request) (*http.Response, error) { + var body map[string]interface{} + json.NewDecoder(req.Body).Decode(&body) + headers := body["headers"].(map[string]interface{}) + assert.Equal(t, "Query", headers["type"]) + method := body["method"].(map[string]interface{}) + assert.Equal(t, "networkVersion", method["name"]) + return httpmock.NewJsonResponderOrPanic(200, queryOutput{Output: "2"})(req) + }) + + version, err := e.NetworkVersion(context.Background()) + assert.NoError(t, err) + assert.Equal(t, 2, version) +} + +func TestNetworkVersionCached(t *testing.T) { + e, _ := newTestEthereum() + e.fireflyContract.networkVersion = 2 + version, err := e.NetworkVersion(context.Background()) + assert.NoError(t, err) + assert.Equal(t, 2, version) +} + +func TestNetworkVersionNotFound(t *testing.T) { + e, _ := newTestEthereum() + httpmock.ActivateNonDefault(e.client.GetClient()) + defer httpmock.DeactivateAndReset() + httpmock.RegisterResponder("POST", `http://localhost:12345/`, + func(req *http.Request) (*http.Response, error) { + var body map[string]interface{} + json.NewDecoder(req.Body).Decode(&body) + headers := body["headers"].(map[string]interface{}) + assert.Equal(t, "Query", headers["type"]) + method := body["method"].(map[string]interface{}) + assert.Equal(t, "networkVersion", method["name"]) + return httpmock.NewJsonResponderOrPanic(500, ethError{Error: "FFEC100148"})(req) + }) + + version, err := e.NetworkVersion(context.Background()) + assert.NoError(t, err) + assert.Equal(t, 1, version) +} + +func TestNetworkVersionError(t *testing.T) { + e, _ := newTestEthereum() + httpmock.ActivateNonDefault(e.client.GetClient()) + defer httpmock.DeactivateAndReset() + httpmock.RegisterResponder("POST", `http://localhost:12345/`, + func(req *http.Request) (*http.Response, error) { + var body map[string]interface{} + json.NewDecoder(req.Body).Decode(&body) + headers := body["headers"].(map[string]interface{}) + assert.Equal(t, "Query", headers["type"]) + method := body["method"].(map[string]interface{}) + assert.Equal(t, "networkVersion", method["name"]) + return httpmock.NewJsonResponderOrPanic(500, ethError{Error: "Unknown"})(req) + }) + + _, err := e.NetworkVersion(context.Background()) + assert.Regexp(t, "FF10111", err) +} + +func TestNetworkVersionBadResponse(t *testing.T) { + e, _ := newTestEthereum() + httpmock.ActivateNonDefault(e.client.GetClient()) + defer httpmock.DeactivateAndReset() + httpmock.RegisterResponder("POST", `http://localhost:12345/`, + func(req *http.Request) (*http.Response, error) { + var body map[string]interface{} + json.NewDecoder(req.Body).Decode(&body) + headers := body["headers"].(map[string]interface{}) + assert.Equal(t, "Query", headers["type"]) + method := body["method"].(map[string]interface{}) + assert.Equal(t, "networkVersion", method["name"]) + return httpmock.NewJsonResponderOrPanic(200, "")(req) + }) + + _, err := e.NetworkVersion(context.Background()) + assert.Regexp(t, "json: cannot unmarshal", err) +} diff --git a/internal/blockchain/fabric/fabric.go b/internal/blockchain/fabric/fabric.go index edd7dfd28..c83c93684 100644 --- a/internal/blockchain/fabric/fabric.go +++ b/internal/blockchain/fabric/fabric.go @@ -44,22 +44,23 @@ const ( ) type Fabric struct { - ctx context.Context - topic string - defaultChannel string - fireflyChaincode string - fireflyFromBlock string - fireflyMux sync.Mutex - signer string - prefixShort string - prefixLong string - capabilities *blockchain.Capabilities - callbacks blockchain.Callbacks - client *resty.Client - streams *streamManager - initInfo struct { - stream *eventStream - sub *subscription + ctx context.Context + topic string + defaultChannel string + signer string + prefixShort string + prefixLong string + capabilities *blockchain.Capabilities + callbacks blockchain.Callbacks + client *resty.Client + streams *streamManager + streamID string + fireflyContract struct { + mux sync.Mutex + chaincode string + fromBlock string + networkVersion int + subscription string } idCache map[string]*fabIdentity wsconn wsclient.WSClient @@ -146,6 +147,7 @@ var batchPinPrefixItems = []*PrefixItem{ Type: "string", }, } +var networkVersionMethodName = "NetworkVersion" var fullIdentityPattern = regexp.MustCompile(".+::x509::(.+)::.+") @@ -196,10 +198,12 @@ func (f *Fabric) Init(ctx context.Context, config config.Section, callbacks bloc f.streams = &streamManager{client: f.client, signer: f.signer} batchSize := f.fabconnectConf.GetUint(FabconnectConfigBatchSize) batchTimeout := uint(f.fabconnectConf.GetDuration(FabconnectConfigBatchTimeout).Milliseconds()) - if f.initInfo.stream, err = f.streams.ensureEventStream(f.ctx, f.topic, batchSize, batchTimeout); err != nil { + stream, err := f.streams.ensureEventStream(f.ctx, f.topic, batchSize, batchTimeout) + if err != nil { return err } - log.L(f.ctx).Infof("Event stream: %s", f.initInfo.stream.ID) + f.streamID = stream.ID + log.L(f.ctx).Infof("Event stream: %s", f.streamID) f.closed = make(chan struct{}) go f.eventLoop() @@ -216,7 +220,7 @@ func (f *Fabric) resolveFireFlyContract(ctx context.Context, contractIndex int) } chaincode = f.contractConf.ArrayEntry(contractIndex).GetString(FireFlyContractChaincode) if chaincode == "" { - return "", "", i18n.NewError(ctx, coremsgs.MsgMissingPluginConfig, "address", "blockchain.fabric.fireflyContract") + return "", "", i18n.NewError(ctx, coremsgs.MsgMissingPluginConfig, "chaincode", "blockchain.fabric.fireflyContract") } fromBlock = f.contractConf.ArrayEntry(contractIndex).GetString(FireFlyContractFromBlock) } else { @@ -243,16 +247,18 @@ func (f *Fabric) ConfigureContract(ctx context.Context, contracts *core.FireFlyC } location := &Location{Channel: f.defaultChannel, Chaincode: chaincode} - f.initInfo.sub, err = f.streams.ensureFireFlySubscription(ctx, location, fromBlock, f.initInfo.stream.ID, batchPinEvent) + sub, err := f.streams.ensureFireFlySubscription(ctx, location, fromBlock, f.streamID, batchPinEvent) if err == nil { - f.fireflyMux.Lock() - f.fireflyChaincode = chaincode - f.fireflyFromBlock = fromBlock - f.fireflyMux.Unlock() + f.fireflyContract.mux.Lock() + f.fireflyContract.chaincode = chaincode + f.fireflyContract.fromBlock = fromBlock + f.fireflyContract.networkVersion = 0 + f.fireflyContract.subscription = sub.ID + f.fireflyContract.mux.Unlock() contracts.Active.Info = fftypes.JSONObject{ "chaincode": chaincode, "fromBlock": fromBlock, - "subscription": f.initInfo.sub.ID, + "subscription": sub.ID, } } return err @@ -261,13 +267,13 @@ func (f *Fabric) ConfigureContract(ctx context.Context, contracts *core.FireFlyC func (f *Fabric) TerminateContract(ctx context.Context, contracts *core.FireFlyContracts, termination *blockchain.Event) (err error) { chaincode := termination.Info.GetString("chaincodeId") - f.fireflyMux.Lock() - if chaincode != f.fireflyChaincode { - log.L(ctx).Warnf("Ignoring termination request from chaincode %s, which differs from active chaincode %s", chaincode, f.fireflyChaincode) - f.fireflyMux.Unlock() + f.fireflyContract.mux.Lock() + if chaincode != f.fireflyContract.chaincode { + log.L(ctx).Warnf("Ignoring termination request from chaincode %s, which differs from active chaincode %s", chaincode, f.fireflyContract.chaincode) + f.fireflyContract.mux.Unlock() return nil } - f.fireflyMux.Unlock() + f.fireflyContract.mux.Unlock() log.L(ctx).Infof("Processing termination request from chaincode %s", chaincode) contracts.Active.FinalEvent = termination.ProtocolID @@ -468,7 +474,11 @@ func (f *Fabric) handleMessageBatch(ctx context.Context, messages []interface{}) l1.Infof("Received '%s' message", eventName) l1.Tracef("Message: %+v", msgJSON) - if sub == f.initInfo.sub.ID { + f.fireflyContract.mux.Lock() + fireflySub := f.fireflyContract.subscription + f.fireflyContract.mux.Unlock() + + if sub == fireflySub { // Matches the active FireFly BatchPin subscription switch eventName { case broadcastBatchEventName: @@ -590,6 +600,23 @@ func (f *Fabric) invokeContractMethod(ctx context.Context, channel, chaincode, m return nil } +func (f *Fabric) queryContractMethod(ctx context.Context, channel, chaincode, methodName, signingKey, requestID string, prefixItems []*PrefixItem, input map[string]interface{}, options map[string]interface{}) (*resty.Response, error) { + body, err := f.buildFabconnectRequestBody(ctx, channel, chaincode, methodName, signingKey, requestID, prefixItems, input, options) + if err != nil { + return nil, err + } + var resErr fabError + res, err := f.client.R(). + SetContext(ctx). + SetBody(body). + SetError(&resErr). + Post("/query") + if err != nil || !res.IsSuccess() { + return res, wrapError(ctx, &resErr, res, err) + } + return res, nil +} + func getUserName(fullIDString string) string { matches := fullIdentityPattern.FindStringSubmatch(fullIDString) if len(matches) == 0 { @@ -625,9 +652,9 @@ func (f *Fabric) SubmitBatchPin(ctx context.Context, operationID *fftypes.UUID, "contexts": hashes, } input, _ := jsonEncodeInput(pinInput) - f.fireflyMux.Lock() - chaincode := f.fireflyChaincode - f.fireflyMux.Unlock() + f.fireflyContract.mux.Lock() + chaincode := f.fireflyContract.chaincode + f.fireflyContract.mux.Unlock() return f.invokeContractMethod(ctx, f.defaultChannel, chaincode, batchPinMethodName, signingKey, operationID.String(), batchPinPrefixItems, input, nil) } @@ -640,9 +667,9 @@ func (f *Fabric) SubmitNetworkAction(ctx context.Context, operationID *fftypes.U "contexts": []string{}, } input, _ := jsonEncodeInput(pinInput) - f.fireflyMux.Lock() - chaincode := f.fireflyChaincode - f.fireflyMux.Unlock() + f.fireflyContract.mux.Lock() + chaincode := f.fireflyContract.chaincode + f.fireflyContract.mux.Unlock() return f.invokeContractMethod(ctx, f.defaultChannel, chaincode, batchPinMethodName, signingKey, operationID.String(), batchPinPrefixItems, input, nil) } @@ -715,18 +742,10 @@ func (f *Fabric) QueryContract(ctx context.Context, location *fftypes.JSONAny, m } } - body, err := f.buildFabconnectRequestBody(ctx, fabricOnChainLocation.Channel, fabricOnChainLocation.Chaincode, method.Name, f.signer, "", prefixItems, input, options) + res, err := f.queryContractMethod(ctx, fabricOnChainLocation.Channel, fabricOnChainLocation.Chaincode, method.Name, f.signer, "", prefixItems, input, options) if err != nil { return nil, err } - res, err := f.client.R(). - SetContext(ctx). - SetBody(body). - Post("/query") - - if err != nil || !res.IsSuccess() { - return nil, ffresty.WrapRestErr(ctx, res, err, coremsgs.MsgFabconnectRESTErr) - } output := &fabQueryNamedOutput{} if err = json.Unmarshal(res.Body(), output); err != nil { return nil, err @@ -783,7 +802,7 @@ func (f *Fabric) AddContractListener(ctx context.Context, listener *core.Contrac if err != nil { return err } - result, err := f.streams.createSubscription(ctx, location, f.initInfo.stream.ID, "", listener.Event.Name, listener.Options.FirstEvent) + result, err := f.streams.createSubscription(ctx, location, f.streamID, "", listener.Event.Name, listener.Options.FirstEvent) if err != nil { return err } @@ -807,3 +826,34 @@ func (f *Fabric) GenerateFFI(ctx context.Context, generationRequest *core.FFIGen func (f *Fabric) GenerateEventSignature(ctx context.Context, event *core.FFIEventDefinition) string { return event.Name } + +func (f *Fabric) getNetworkVersion(ctx context.Context, chaincode string) (int, error) { + res, err := f.queryContractMethod(ctx, f.defaultChannel, chaincode, networkVersionMethodName, f.signer, "", []*PrefixItem{}, map[string]interface{}{}, nil) + if err != nil || !res.IsSuccess() { + // "Function not found" is interpreted as "default to version 1" + notFoundError := fmt.Sprintf("Function %s not found", networkVersionMethodName) + if strings.Contains(err.Error(), notFoundError) { + return 1, nil + } + return 0, err + } + output := &fabQueryNamedOutput{} + if err = json.Unmarshal(res.Body(), output); err != nil { + return 0, err + } + return int(output.Result.(float64)), nil +} + +func (f *Fabric) NetworkVersion(ctx context.Context) (int, error) { + f.fireflyContract.mux.Lock() + defer f.fireflyContract.mux.Unlock() + if f.fireflyContract.networkVersion > 0 { + return f.fireflyContract.networkVersion, nil + } + chaincode := f.fireflyContract.chaincode + version, err := f.getNetworkVersion(ctx, chaincode) + if err == nil { + f.fireflyContract.networkVersion = version + } + return version, err +} diff --git a/internal/blockchain/fabric/fabric_test.go b/internal/blockchain/fabric/fabric_test.go index e16f94415..ddfc303fc 100644 --- a/internal/blockchain/fabric/fabric_test.go +++ b/internal/blockchain/fabric/fabric_test.go @@ -56,16 +56,16 @@ func newTestFabric() (*Fabric, func()) { em := &blockchainmocks.Callbacks{} wsm := &wsmocks.WSClient{} e := &Fabric{ - ctx: ctx, - client: resty.New().SetBaseURL("http://localhost:12345"), - defaultChannel: "firefly", - fireflyChaincode: "firefly", - topic: "topic1", - prefixShort: defaultPrefixShort, - prefixLong: defaultPrefixLong, - callbacks: em, - wsconn: wsm, - } + ctx: ctx, + client: resty.New().SetBaseURL("http://localhost:12345"), + defaultChannel: "firefly", + topic: "topic1", + prefixShort: defaultPrefixShort, + prefixLong: defaultPrefixLong, + callbacks: em, + wsconn: wsm, + } + e.fireflyContract.chaincode = "firefly" return e, func() { cancel() if e.closed != nil { @@ -172,8 +172,8 @@ func TestInitAllNewStreamsAndWSEvent(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 4, httpmock.GetTotalCallCount()) - assert.Equal(t, "es12345", e.initInfo.stream.ID) - assert.Equal(t, "sub12345", e.initInfo.sub.ID) + assert.Equal(t, "es12345", e.streamID) + assert.Equal(t, "sub12345", e.fireflyContract.subscription) assert.NotNil(t, e.Capabilities()) startupMessage := <-toServer @@ -262,8 +262,8 @@ func TestInitAllExistingStreams(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 2, httpmock.GetTotalCallCount()) - assert.Equal(t, "es12345", e.initInfo.stream.ID) - assert.Equal(t, "sub12345", e.initInfo.sub.ID) + assert.Equal(t, "es12345", e.streamID) + assert.Equal(t, "sub12345", e.fireflyContract.subscription) } @@ -296,8 +296,8 @@ func TestInitNewConfig(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 2, httpmock.GetTotalCallCount()) - assert.Equal(t, "es12345", e.initInfo.stream.ID) - assert.Equal(t, "sub12345", e.initInfo.sub.ID) + assert.Equal(t, "es12345", e.streamID) + assert.Equal(t, "sub12345", e.fireflyContract.subscription) } @@ -824,9 +824,7 @@ func TestHandleMessageBatchPinOK(t *testing.T) { e := &Fabric{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", - } + e.fireflyContract.subscription = "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e" expectedSigningKeyRef := &core.VerifierRef{ Type: core.VerifierTypeMSPIdentity, @@ -873,9 +871,7 @@ func TestHandleMessageEmptyPayloadRef(t *testing.T) { e := &Fabric{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", - } + e.fireflyContract.subscription = "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e" expectedSigningKeyRef := &core.VerifierRef{ Type: core.VerifierTypeMSPIdentity, @@ -922,9 +918,7 @@ func TestHandleMessageBatchPinExit(t *testing.T) { e := &Fabric{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", - } + e.fireflyContract.subscription = "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e" expectedSigningKeyRef := &core.VerifierRef{ Type: core.VerifierTypeMSPIdentity, @@ -955,9 +949,7 @@ func TestHandleMessageBatchPinEmpty(t *testing.T) { em := &blockchainmocks.Callbacks{} e := &Fabric{callbacks: em} - e.initInfo.sub = &subscription{ - ID: "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", - } + e.fireflyContract.subscription = "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e" var events []interface{} err := json.Unmarshal(data, &events) @@ -981,9 +973,7 @@ func TestHandleMessageUnknownEventName(t *testing.T) { em := &blockchainmocks.Callbacks{} e := &Fabric{callbacks: em} - e.initInfo.sub = &subscription{ - ID: "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", - } + e.fireflyContract.subscription = "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e" var events []interface{} err := json.Unmarshal(data, &events) @@ -996,9 +986,7 @@ func TestHandleMessageUnknownEventName(t *testing.T) { func TestHandleMessageBatchPinBadBatchHash(t *testing.T) { em := &blockchainmocks.Callbacks{} e := &Fabric{callbacks: em} - e.initInfo.sub = &subscription{ - ID: "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", - } + e.fireflyContract.subscription = "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e" data := []byte(`[{ "chaincodeId": "firefly", "blockNumber": 91, @@ -1018,9 +1006,7 @@ func TestHandleMessageBatchPinBadBatchHash(t *testing.T) { func TestHandleMessageBatchPinBadPin(t *testing.T) { em := &blockchainmocks.Callbacks{} e := &Fabric{callbacks: em} - e.initInfo.sub = &subscription{ - ID: "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", - } + e.fireflyContract.subscription = "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e" data := []byte(`[{ "chaincodeId": "firefly", "blockNumber": 91, @@ -1040,9 +1026,7 @@ func TestHandleMessageBatchPinBadPin(t *testing.T) { func TestHandleMessageBatchPinBadPayloadEncoding(t *testing.T) { em := &blockchainmocks.Callbacks{} e := &Fabric{callbacks: em} - e.initInfo.sub = &subscription{ - ID: "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", - } + e.fireflyContract.subscription = "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e" data := []byte(`[{ "chaincodeId": "firefly", "blockNumber": 91, @@ -1062,9 +1046,7 @@ func TestHandleMessageBatchPinBadPayloadEncoding(t *testing.T) { func TestHandleMessageBatchPinBadPayloadUUIDs(t *testing.T) { em := &blockchainmocks.Callbacks{} e := &Fabric{callbacks: em} - e.initInfo.sub = &subscription{ - ID: "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", - } + e.fireflyContract.subscription = "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e" data := []byte(`[{ "chaincodeId": "firefly", "blockNumber": 91, @@ -1307,9 +1289,7 @@ func TestAddSubscription(t *testing.T) { httpmock.ActivateNonDefault(e.client.GetClient()) defer httpmock.DeactivateAndReset() - e.initInfo.stream = &eventStream{ - ID: "es-1", - } + e.streamID = "es-1" e.streams = &streamManager{ client: e.client, } @@ -1346,9 +1326,7 @@ func TestAddSubscriptionBadLocation(t *testing.T) { httpmock.ActivateNonDefault(e.client.GetClient()) defer httpmock.DeactivateAndReset() - e.initInfo.stream = &eventStream{ - ID: "es-1", - } + e.streamID = "es-1" e.streams = &streamManager{ client: e.client, } @@ -1371,9 +1349,7 @@ func TestAddSubscriptionFail(t *testing.T) { httpmock.ActivateNonDefault(e.client.GetClient()) defer httpmock.DeactivateAndReset() - e.initInfo.stream = &eventStream{ - ID: "es-1", - } + e.streamID = "es-1" e.streams = &streamManager{ client: e.client, } @@ -1405,9 +1381,7 @@ func TestDeleteSubscription(t *testing.T) { httpmock.ActivateNonDefault(e.client.GetClient()) defer httpmock.DeactivateAndReset() - e.initInfo.stream = &eventStream{ - ID: "es-1", - } + e.streamID = "es-1" e.streams = &streamManager{ client: e.client, } @@ -1430,9 +1404,7 @@ func TestDeleteSubscriptionFail(t *testing.T) { httpmock.ActivateNonDefault(e.client.GetClient()) defer httpmock.DeactivateAndReset() - e.initInfo.stream = &eventStream{ - ID: "es-1", - } + e.streamID = "es-1" e.streams = &streamManager{ client: e.client, } @@ -1468,9 +1440,7 @@ func TestHandleMessageContractEvent(t *testing.T) { e := &Fabric{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" em.On("BlockchainEvent", mock.MatchedBy(func(e *blockchain.EventWithSubscription) bool { assert.Equal(t, "4763a0c50e3bba7cef1a7ba35dd3f9f3426bb04d0156f326e84ec99387c4746d", e.BlockchainTXID) @@ -1528,9 +1498,7 @@ func TestHandleMessageContractEventBadPayload(t *testing.T) { e := &Fabric{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" var events []interface{} err := json.Unmarshal(data, &events) @@ -1558,9 +1526,7 @@ func TestHandleMessageContractEventError(t *testing.T) { e := &Fabric{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-b5b97a4e-a317-4053-6400-1474650efcb5", - } + e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5" em.On("BlockchainEvent", mock.Anything).Return(fmt.Errorf("pop")) @@ -1959,9 +1925,7 @@ func TestHandleNetworkAction(t *testing.T) { e := &Fabric{ callbacks: em, } - e.initInfo.sub = &subscription{ - ID: "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e", - } + e.fireflyContract.subscription = "sb-0910f6a8-7bd6-4ced-453e-2db68149ce8e" expectedSigningKeyRef := &core.VerifierRef{ Type: core.VerifierTypeMSPIdentity, @@ -1979,3 +1943,77 @@ func TestHandleNetworkAction(t *testing.T) { em.AssertExpectations(t) } + +func TestNetworkVersion(t *testing.T) { + e, _ := newTestFabric() + httpmock.ActivateNonDefault(e.client.GetClient()) + defer httpmock.DeactivateAndReset() + httpmock.RegisterResponder("POST", `http://localhost:12345/query`, + func(req *http.Request) (*http.Response, error) { + var body map[string]interface{} + json.NewDecoder(req.Body).Decode(&body) + assert.Equal(t, "NetworkVersion", body["func"]) + return httpmock.NewJsonResponderOrPanic(200, fabQueryNamedOutput{Result: 2})(req) + }) + + version, err := e.NetworkVersion(context.Background()) + assert.NoError(t, err) + assert.Equal(t, 2, version) +} + +func TestNetworkVersionCached(t *testing.T) { + e, _ := newTestFabric() + e.fireflyContract.networkVersion = 2 + version, err := e.NetworkVersion(context.Background()) + assert.NoError(t, err) + assert.Equal(t, 2, version) +} + +func TestNetworkVersionNotFound(t *testing.T) { + e, _ := newTestFabric() + httpmock.ActivateNonDefault(e.client.GetClient()) + defer httpmock.DeactivateAndReset() + httpmock.RegisterResponder("POST", `http://localhost:12345/query`, + func(req *http.Request) (*http.Response, error) { + var body map[string]interface{} + json.NewDecoder(req.Body).Decode(&body) + assert.Equal(t, "NetworkVersion", body["func"]) + return httpmock.NewJsonResponderOrPanic(500, fabError{Error: "Function NetworkVersion not found"})(req) + }) + + version, err := e.NetworkVersion(context.Background()) + assert.NoError(t, err) + assert.Equal(t, 1, version) +} + +func TestNetworkVersionError(t *testing.T) { + e, _ := newTestFabric() + httpmock.ActivateNonDefault(e.client.GetClient()) + defer httpmock.DeactivateAndReset() + httpmock.RegisterResponder("POST", `http://localhost:12345/query`, + func(req *http.Request) (*http.Response, error) { + var body map[string]interface{} + json.NewDecoder(req.Body).Decode(&body) + assert.Equal(t, "NetworkVersion", body["func"]) + return httpmock.NewJsonResponderOrPanic(500, fabError{Error: "Unknown"})(req) + }) + + _, err := e.NetworkVersion(context.Background()) + assert.Regexp(t, "FF10284", err) +} + +func TestNetworkVersionBadResponse(t *testing.T) { + e, _ := newTestFabric() + httpmock.ActivateNonDefault(e.client.GetClient()) + defer httpmock.DeactivateAndReset() + httpmock.RegisterResponder("POST", `http://localhost:12345/query`, + func(req *http.Request) (*http.Response, error) { + var body map[string]interface{} + json.NewDecoder(req.Body).Decode(&body) + assert.Equal(t, "NetworkVersion", body["func"]) + return httpmock.NewJsonResponderOrPanic(200, "")(req) + }) + + _, err := e.NetworkVersion(context.Background()) + assert.Regexp(t, "json: cannot unmarshal", err) +} diff --git a/internal/broadcast/definition.go b/internal/broadcast/definition.go index 96d72a7c1..1373a6312 100644 --- a/internal/broadcast/definition.go +++ b/internal/broadcast/definition.go @@ -46,7 +46,7 @@ func (bm *broadcastManager) BroadcastDefinition(ctx context.Context, ns string, // The blockchain "key" will be normalized, but the "author" will pass through unchecked func (bm *broadcastManager) BroadcastIdentityClaim(ctx context.Context, ns string, def *core.IdentityClaim, signingIdentity *core.SignerRef, tag string, waitConfirm bool) (msg *core.Message, err error) { - signingIdentity.Key, err = bm.identity.NormalizeSigningKey(ctx, signingIdentity.Key, identity.KeyNormalizationBlockchainPlugin) + signingIdentity.Key, err = bm.identity.NormalizeSigningKey(ctx, ns, signingIdentity.Key, identity.KeyNormalizationBlockchainPlugin) if err != nil { return nil, err } diff --git a/internal/broadcast/definition_test.go b/internal/broadcast/definition_test.go index 4de1fd78a..8004c7a84 100644 --- a/internal/broadcast/definition_test.go +++ b/internal/broadcast/definition_test.go @@ -35,10 +35,10 @@ func TestBroadcastDefinitionAsNodeConfirm(t *testing.T) { msa := bm.syncasync.(*syncasyncmocks.Bridge) mim := bm.identity.(*identitymanagermocks.Manager) - mim.On("ResolveInputSigningIdentity", mock.Anything, "ff_system", mock.Anything).Return(nil) - msa.On("WaitForMessage", bm.ctx, "ff_system", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) + mim.On("ResolveInputSigningIdentity", mock.Anything, "ns1", mock.Anything).Return(nil) + msa.On("WaitForMessage", bm.ctx, "ns1", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) - _, err := bm.BroadcastDefinitionAsNode(bm.ctx, core.SystemNamespace, &core.Namespace{}, core.SystemTagDefineNamespace, true) + _, err := bm.BroadcastDefinitionAsNode(bm.ctx, "ns1", &core.Namespace{}, core.SystemTagDefineNamespace, true) assert.EqualError(t, err, "pop") msa.AssertExpectations(t) @@ -52,10 +52,10 @@ func TestBroadcastIdentityClaim(t *testing.T) { msa := bm.syncasync.(*syncasyncmocks.Bridge) mim := bm.identity.(*identitymanagermocks.Manager) - mim.On("NormalizeSigningKey", mock.Anything, "0x1234", identity.KeyNormalizationBlockchainPlugin).Return("", nil) - msa.On("WaitForMessage", bm.ctx, "ff_system", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "0x1234", identity.KeyNormalizationBlockchainPlugin).Return("", nil) + msa.On("WaitForMessage", bm.ctx, "ns1", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) - _, err := bm.BroadcastIdentityClaim(bm.ctx, core.SystemNamespace, &core.IdentityClaim{ + _, err := bm.BroadcastIdentityClaim(bm.ctx, "ns1", &core.IdentityClaim{ Identity: &core.Identity{}, }, &core.SignerRef{ Key: "0x1234", @@ -72,9 +72,9 @@ func TestBroadcastIdentityClaimFail(t *testing.T) { mim := bm.identity.(*identitymanagermocks.Manager) - mim.On("NormalizeSigningKey", mock.Anything, "0x1234", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "0x1234", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) - _, err := bm.BroadcastIdentityClaim(bm.ctx, core.SystemNamespace, &core.IdentityClaim{ + _, err := bm.BroadcastIdentityClaim(bm.ctx, "ns1", &core.IdentityClaim{ Identity: &core.Identity{}, }, &core.SignerRef{ Key: "0x1234", @@ -107,8 +107,8 @@ func TestBroadcastDefinitionBadIdentity(t *testing.T) { defer cancel() mim := bm.identity.(*identitymanagermocks.Manager) - mim.On("ResolveInputSigningIdentity", mock.Anything, core.SystemNamespace, mock.Anything).Return(fmt.Errorf("pop")) - _, err := bm.BroadcastDefinition(bm.ctx, core.SystemNamespace, &core.Namespace{}, &core.SignerRef{ + mim.On("ResolveInputSigningIdentity", mock.Anything, "ns1", mock.Anything).Return(fmt.Errorf("pop")) + _, err := bm.BroadcastDefinition(bm.ctx, "ns1", &core.Namespace{}, &core.SignerRef{ Author: "wrong", Key: "wrong", }, core.SystemTagDefineNamespace, false) diff --git a/internal/broadcast/manager.go b/internal/broadcast/manager.go index 6da0bcaca..8f424a564 100644 --- a/internal/broadcast/manager.go +++ b/internal/broadcast/manager.go @@ -47,7 +47,6 @@ type Manager interface { NewBroadcast(ns string, in *core.MessageInOut) sysmessaging.MessageSender BroadcastDatatype(ctx context.Context, ns string, datatype *core.Datatype, waitConfirm bool) (msg *core.Message, err error) - BroadcastNamespace(ctx context.Context, ns *core.Namespace, waitConfirm bool) (msg *core.Message, err error) BroadcastMessage(ctx context.Context, ns string, in *core.MessageInOut, waitConfirm bool) (out *core.Message, err error) BroadcastDefinitionAsNode(ctx context.Context, ns string, def core.Definition, tag string, waitConfirm bool) (msg *core.Message, err error) BroadcastDefinition(ctx context.Context, ns string, def core.Definition, signingIdentity *core.SignerRef, tag string, waitConfirm bool) (msg *core.Message, err error) diff --git a/internal/broadcast/namespace.go b/internal/broadcast/namespace.go deleted file mode 100644 index bad1f1da1..000000000 --- a/internal/broadcast/namespace.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright © 2022 Kaleido, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package broadcast - -import ( - "context" - - "github.com/hyperledger/firefly-common/pkg/fftypes" - "github.com/hyperledger/firefly/pkg/core" -) - -func (bm *broadcastManager) BroadcastNamespace(ctx context.Context, ns *core.Namespace, waitConfirm bool) (*core.Message, error) { - - // Validate the input data definition data - ns.ID = fftypes.NewUUID() - ns.Created = fftypes.Now() - ns.Type = core.NamespaceTypeBroadcast - if err := ns.Validate(ctx, false); err != nil { - return nil, err - } - msg, err := bm.BroadcastDefinitionAsNode(ctx, core.SystemNamespace, ns, core.SystemTagDefineNamespace, waitConfirm) - if msg != nil { - ns.Message = msg.Header.ID - } - return msg, err -} diff --git a/internal/broadcast/namespace_test.go b/internal/broadcast/namespace_test.go deleted file mode 100644 index 6592fdc8b..000000000 --- a/internal/broadcast/namespace_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © 2021 Kaleido, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package broadcast - -import ( - "context" - "strings" - "testing" - - "github.com/hyperledger/firefly/mocks/databasemocks" - "github.com/hyperledger/firefly/mocks/datamocks" - "github.com/hyperledger/firefly/mocks/identitymanagermocks" - "github.com/hyperledger/firefly/pkg/core" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func TestBroadcastNamespaceBadName(t *testing.T) { - bm, cancel := newTestBroadcast(t) - defer cancel() - mdi := bm.database.(*databasemocks.Plugin) - - mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(&core.Namespace{Name: "ns1"}, nil) - _, err := bm.BroadcastNamespace(context.Background(), &core.Namespace{ - Name: "!ns", - }, false) - assert.Regexp(t, "FF00140.*name", err) -} - -func TestBroadcastNamespaceDescriptionTooLong(t *testing.T) { - bm, cancel := newTestBroadcast(t) - defer cancel() - mdi := bm.database.(*databasemocks.Plugin) - - mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(&core.Namespace{Name: "ns1"}, nil) - buff := strings.Builder{} - buff.Grow(4097) - for i := 0; i < 4097; i++ { - buff.WriteByte(byte('a' + i%26)) - } - _, err := bm.BroadcastNamespace(context.Background(), &core.Namespace{ - Name: "ns1", - Description: buff.String(), - }, false) - assert.Regexp(t, "FF00135.*description", err) -} - -func TestBroadcastNamespaceBroadcastOk(t *testing.T) { - bm, cancel := newTestBroadcast(t) - defer cancel() - mdi := bm.database.(*databasemocks.Plugin) - mdm := bm.data.(*datamocks.Manager) - mim := bm.identity.(*identitymanagermocks.Manager) - - mim.On("ResolveInputSigningIdentity", mock.Anything, core.SystemNamespace, mock.Anything).Return(nil) - mdi.On("GetNamespace", mock.Anything, mock.Anything).Return(&core.Namespace{Name: "ns1"}, nil) - mdm.On("CheckDatatype", mock.Anything, "ns1", mock.Anything).Return(nil) - mdm.On("UpdateMessageCache", mock.Anything, mock.Anything).Return() - mdm.On("WriteNewMessage", mock.Anything, mock.Anything).Return(nil) - buff := strings.Builder{} - buff.Grow(4097) - for i := 0; i < 4097; i++ { - buff.WriteByte(byte('a' + i%26)) - } - _, err := bm.BroadcastNamespace(context.Background(), &core.Namespace{ - Name: "ns1", - Description: "my namespace", - }, false) - assert.NoError(t, err) -} diff --git a/internal/contracts/manager.go b/internal/contracts/manager.go index 892f44c81..7ad99275b 100644 --- a/internal/contracts/manager.go +++ b/internal/contracts/manager.go @@ -218,7 +218,7 @@ func (cm *contractManager) writeInvokeTransaction(ctx context.Context, ns string } func (cm *contractManager) InvokeContract(ctx context.Context, ns string, req *core.ContractCallRequest, waitConfirm bool) (res interface{}, err error) { - req.Key, err = cm.identity.NormalizeSigningKey(ctx, req.Key, identity.KeyNormalizationBlockchainPlugin) + req.Key, err = cm.identity.NormalizeSigningKey(ctx, ns, req.Key, identity.KeyNormalizationBlockchainPlugin) if err != nil { return nil, err } diff --git a/internal/contracts/manager_test.go b/internal/contracts/manager_test.go index 0727d6763..3dfce5df2 100644 --- a/internal/contracts/manager_test.go +++ b/internal/contracts/manager_test.go @@ -1278,7 +1278,7 @@ func TestInvokeContract(t *testing.T) { } mth.On("SubmitNewTransaction", mock.Anything, "ns1", core.TransactionTypeContractInvoke).Return(fftypes.NewUUID(), nil) - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) mdi.On("InsertOperation", mock.Anything, mock.MatchedBy(func(op *core.Operation) bool { return op.Namespace == "ns1" && op.Type == core.OpTypeBlockchainInvoke && op.Plugin == "mockblockchain" })).Return(nil) @@ -1318,7 +1318,7 @@ func TestInvokeContractConfirm(t *testing.T) { } mth.On("SubmitNewTransaction", mock.Anything, "ns1", core.TransactionTypeContractInvoke).Return(fftypes.NewUUID(), nil) - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) mdi.On("InsertOperation", mock.Anything, mock.MatchedBy(func(op *core.Operation) bool { return op.Namespace == "ns1" && op.Type == core.OpTypeBlockchainInvoke && op.Plugin == "mockblockchain" })).Return(nil) @@ -1364,7 +1364,7 @@ func TestInvokeContractFail(t *testing.T) { } mth.On("SubmitNewTransaction", mock.Anything, "ns1", core.TransactionTypeContractInvoke).Return(fftypes.NewUUID(), nil) - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) mdi.On("InsertOperation", mock.Anything, mock.MatchedBy(func(op *core.Operation) bool { return op.Namespace == "ns1" && op.Type == core.OpTypeBlockchainInvoke && op.Plugin == "mockblockchain" })).Return(nil) @@ -1393,7 +1393,7 @@ func TestInvokeContractFailNormalizeSigningKey(t *testing.T) { Location: fftypes.JSONAnyPtr(""), } - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) _, err := cm.InvokeContract(context.Background(), "ns1", req, false) @@ -1411,7 +1411,7 @@ func TestInvokeContractFailResolve(t *testing.T) { Location: fftypes.JSONAnyPtr(""), } - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) mbi.On("InvokeContract", mock.Anything, mock.AnythingOfType("*fftypes.UUID"), "key-resolved", req.Location, req.Method, req.Input).Return(nil) _, err := cm.InvokeContract(context.Background(), "ns1", req, false) @@ -1436,7 +1436,7 @@ func TestInvokeContractTXFail(t *testing.T) { }, } - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) mth.On("SubmitNewTransaction", mock.Anything, "ns1", core.TransactionTypeContractInvoke).Return(nil, fmt.Errorf("pop")) _, err := cm.InvokeContract(context.Background(), "ns1", req, false) @@ -1456,7 +1456,7 @@ func TestInvokeContractMethodNotFound(t *testing.T) { MethodPath: "set", } - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) mdb.On("GetFFIMethod", mock.Anything, "ns1", req.Interface, req.MethodPath).Return(nil, fmt.Errorf("pop")) _, err := cm.InvokeContract(context.Background(), "ns1", req, false) @@ -1493,7 +1493,7 @@ func TestInvokeContractMethodBadInput(t *testing.T) { }, }, } - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) _, err := cm.InvokeContract(context.Background(), "ns1", req, false) assert.Regexp(t, "FF10304", err) @@ -1518,7 +1518,7 @@ func TestQueryContract(t *testing.T) { }, } - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) mth.On("SubmitNewTransaction", mock.Anything, "ns1", core.TransactionTypeContractInvoke).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", mock.Anything, mock.MatchedBy(func(op *core.Operation) bool { return op.Namespace == "ns1" && op.Type == core.OpTypeBlockchainInvoke && op.Plugin == "mockblockchain" @@ -1547,7 +1547,7 @@ func TestCallContractInvalidType(t *testing.T) { }, } - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) mth.On("SubmitNewTransaction", mock.Anything, "ns1", core.TransactionTypeContractInvoke).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", mock.Anything, mock.MatchedBy(func(op *core.Operation) bool { return op.Namespace == "ns1" && op.Type == core.OpTypeBlockchainInvoke && op.Plugin == "mockblockchain" @@ -1782,7 +1782,7 @@ func TestInvokeContractAPI(t *testing.T) { Location: fftypes.JSONAnyPtr(""), } - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) mdb.On("GetContractAPIByName", mock.Anything, "ns1", "banana").Return(api, nil) mth.On("SubmitNewTransaction", mock.Anything, "ns1", core.TransactionTypeContractInvoke).Return(fftypes.NewUUID(), nil) mdi.On("InsertOperation", mock.Anything, mock.MatchedBy(func(op *core.Operation) bool { @@ -1817,7 +1817,7 @@ func TestInvokeContractAPIFailContractLookup(t *testing.T) { }, } - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) mdb.On("GetContractAPIByName", mock.Anything, "ns1", "banana").Return(nil, fmt.Errorf("pop")) _, err := cm.InvokeContractAPI(context.Background(), "ns1", "banana", "peel", req, false) @@ -1838,7 +1838,7 @@ func TestInvokeContractAPIContractNotFound(t *testing.T) { }, } - mim.On("NormalizeSigningKey", mock.Anything, "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) + mim.On("NormalizeSigningKey", mock.Anything, "ns1", "", identity.KeyNormalizationBlockchainPlugin).Return("key-resolved", nil) mdb.On("GetContractAPIByName", mock.Anything, "ns1", "banana").Return(nil, nil) _, err := cm.InvokeContractAPI(context.Background(), "ns1", "banana", "peel", req, false) diff --git a/internal/coreconfig/coreconfig.go b/internal/coreconfig/coreconfig.go index c5599b4a2..88361d7e9 100644 --- a/internal/coreconfig/coreconfig.go +++ b/internal/coreconfig/coreconfig.go @@ -29,16 +29,24 @@ const ( PluginConfigName = "name" // PluginConfigType is the type of the plugin to be loaded PluginConfigType = "type" - // NamespaceMode is the configured mode of a namespace - NamespaceMode = "mode" - // NamespaceRemoteName is the namespace name to be sent in plugin calls - NamespaceRemoteName = "remoteName" - // NamespacePlugins is the list of namespace plugins - NamespacePlugins = "plugins" // NamespaceName is the short name for a pre-defined namespace NamespaceName = "name" // NamespaceName is the long description for a pre-defined namespace NamespaceDescription = "description" + // NamespaceRemoteName is the namespace name to be sent in plugin calls + NamespaceRemoteName = "remoteName" + // NamespacePlugins is the list of namespace plugins + NamespacePlugins = "plugins" + // NamespaceDefaultKey is the default signing key for blockchain transactions within this namespace + NamespaceDefaultKey = "defaultKey" + // NamespaceMultipartyEnabled specifies if multi-party mode is enabled for a namespace + NamespaceMultipartyEnabled = "multiparty.enabled" + // NamespaceMultipartyOrgName is a short name for the local root org within a namespace + NamespaceMultipartyOrgName = "multiparty.org.name" + // NamespaceMultipartyOrgDescription is a description for the local root org within a namespace + NamespaceMultipartyOrgDescription = "multiparty.org.description" + // NamespaceMultipartyOrgKey is the signing key allocated to the local root org within a namespace + NamespaceMultipartyOrgKey = "multiparty.org.key" ) // The following keys can be access from the root configuration. @@ -222,7 +230,7 @@ var ( NamespaceCacheSize = ffc("namespaces.cache.size") // NamespaceCacheTTL cache time-to-live for namespaces NamespaceCacheTTL = ffc("namespaces.cache.ttl") - // NodeName is a description for the node + // NodeName is the short name for the node NodeName = ffc("node.name") // NodeDescription is a description for the node NodeDescription = ffc("node.description") @@ -240,10 +248,8 @@ var ( OpUpdateWorkerBatchMaxInserts = ffc("opupdate.worker.batchMaxInserts") // OpUpdateWorkerQueueLength OpUpdateWorkerQueueLength = ffc("opupdate.worker.queueLength") - // OrgName is the short name o the org + // OrgName is the short name for the org OrgName = ffc("org.name") - // OrgIdentityDeprecated deprecated synonym to org.key - OrgIdentityDeprecated = ffc("org.identity") // OrgKey is the signing identity allocated to the organization (can be the same as the nodes) OrgKey = ffc("org.key") // OrgDescription is a description for the org diff --git a/internal/coremsgs/en_api_translations.go b/internal/coremsgs/en_api_translations.go index ff53723e3..8961ce3e6 100644 --- a/internal/coremsgs/en_api_translations.go +++ b/internal/coremsgs/en_api_translations.go @@ -18,7 +18,7 @@ package coremsgs //revive:disable var ( - CoreSystemNSDescription = ffm("core.systemNSDescription", "FireFly system namespace") + CoreSystemNSDescription = ffm("core.systemNSDescription", "FireFly system namespace (legacy - no longer used by newer versions)") APIParamsConfigRecordKeyUpdate = ffm("api.params.configRecordKey.update", "The configuration key to update. This should use dot notation to reference a key documented in https://hyperledger.github.io/firefly/reference/config.html") APIParamsConfigRecordKeyGet = ffm("api.params.configRecordKey.get", "The configuration key to get. This should use dot notation to reference a key documented in https://hyperledger.github.io/firefly/reference/config.html") @@ -95,7 +95,7 @@ var ( APIEndpointsGetEvents = ffm("api.endpoints.getEvents", "Gets a list of events") APIEndpointsGetGroupByHash = ffm("api.endpoints.getGroupByHash", "Gets a group by its ID (hash)") APIEndpointsGetGroups = ffm("api.endpoints.getGroups", "Gets a list of groups") - APIEndpointsGetIdentities = ffm("api.endpoints.getIdentities", "Gets a list of identities that have been registered in the network") + APIEndpointsGetIdentities = ffm("api.endpoints.getIdentities", "Gets a list of all identities that have been registered in the namespace") APIEndpointsGetIdentityByID = ffm("api.endpoints.getIdentityByID", "Gets an identity by its ID") APIEndpointsGetIdentityDID = ffm("api.endpoints.getIdentityDID", "Gets the DID for an identity based on its ID") APIEndpointsGetIdentityVerifiers = ffm("api.endpoints.getIdentityVerifiers", "Gets the verifiers for an identity") @@ -106,9 +106,10 @@ var ( APIEndpointsGetMsgs = ffm("api.endpoints.getMsgs", "Gets a list of messages") APIEndpointsGetNamespace = ffm("api.endpoints.getNamespace", "Gets a namespace") APIEndpointsGetNamespaces = ffm("api.endpoints.getNamespaces", "Gets a list of namespaces") + APIEndpointsGetNetworkIdentityByDID = ffm("api.endpoints.getNetworkIdentityByDID", "Gets an identity by its DID (deprecated - use /identities/{did} instead of /network/identities/{did})") APIEndpointsGetIdentityByDID = ffm("api.endpoints.getIdentityByDID", "Gets an identity by its DID") APIEndpointsGetDIDDocByDID = ffm("api.endpoints.getDIDDocByDID", "Gets a DID document by its DID") - APIEndpointsGetNetworkIdentities = ffm("api.endpoints.getNetworkIdentities", "Gets the list of identities in the network") + APIEndpointsGetNetworkIdentities = ffm("api.endpoints.getNetworkIdentities", "Gets the list of identities in the network (deprecated - use /identities instead of /network/identities") APIEndpointsGetNetworkNode = ffm("api.endpoints.getNetworkNode", "Gets information about a specific node in the network") APIEndpointsGetNetworkNodes = ffm("api.endpoints.getNetworkNodes", "Gets a list of nodes in the network") APIEndpointsGetNetworkOrg = ffm("api.endpoints.getNetworkOrg", "Gets information about a specifc org in the network") diff --git a/internal/coremsgs/en_config_descriptions.go b/internal/coremsgs/en_config_descriptions.go index 6200ae7c2..c7e9c4ae8 100644 --- a/internal/coremsgs/en_config_descriptions.go +++ b/internal/coremsgs/en_config_descriptions.go @@ -233,7 +233,8 @@ var ( ConfigPluginIdentityType = ffc("config.plugins.identity[].type", "The type of a configured Identity plugin", i18n.StringType) ConfigPluginIdentityName = ffc("config.plugins.identity[].name", "The name of a configured Identity plugin", i18n.StringType) - ConfigIdentityManagerCacheLimit = ffc("config.identity.manager.cache.limit", "The identity manager cache limit in count of items", i18n.IntType) + ConfigIdentityManagerCacheLimit = ffc("config.identity.manager.cache.limit", "The identity manager cache limit in count of items", i18n.IntType) + ConfigIdentityManagerLegacySystemIdentitites = ffc("config.identity.manager.legacySystemIdentities", "Whether the identity manager should resolve legacy identities registered on the ff_system namespace", i18n.BooleanType) ConfigLogCompress = ffc("config.log.compress", "Determines if the rotated log files should be compressed using gzip", i18n.BooleanType) ConfigLogFilename = ffc("config.log.filename", "Filename is the file to write logs to. Backup log files will be retained in the same directory", i18n.StringType) @@ -261,10 +262,14 @@ var ( ConfigNamespacesDefault = ffc("config.namespaces.default", "The default namespace - must be in the predefined list", i18n.StringType) ConfigNamespacesPredefined = ffc("config.namespaces.predefined", "A list of namespaces to ensure exists, without requiring a broadcast from the network", "List "+i18n.StringType) ConfigNamespacesPredefinedName = ffc("config.namespaces.predefined[].name", "The name of the namespace (must be unique)", i18n.StringType) + ConfigNamespacesPredefinedDescription = ffc("config.namespaces.predefined[].description", "A description for the namespace", i18n.StringType) ConfigNamespacesPredefinedPlugins = ffc("config.namespaces.predefined[].plugins", "The list of plugins for this namespace", i18n.StringType) - ConfigNamespacesPredefinedMode = ffc("config.namespaces.predefined[].mode", "The namespace mode. Valid values: gateway, multiparty", i18n.StringType) ConfigNamespacesPredefinedRemoteName = ffc("config.namespaces.predefined[].remoteName", "The namespace name to be sent in plugin calls, if it differs from namespace name", i18n.StringType) - ConfigNamespacesPredefinedDescription = ffc("config.namespaces.predefined[].description", "A description for the namespace", i18n.StringType) + ConfigNamespacesPredefinedDefaultKey = ffc("config.namespaces.predefined[].defaultKey", "A default signing key for blockchain transactions within this namespace", i18n.StringType) + ConfigNamespacesMultipartyEnabled = ffc("config.namespaces.predefined[].multiparty.enabled", "Enables multi-party mode for this namespace (defaults to true if an org name or key is configured, either here or at the root level)", i18n.BooleanType) + ConfigNamespacesMultipartyOrgName = ffc("config.namespaces.predefined[].multiparty.org.name", "A short name for the local root organization within this namespace", i18n.StringType) + ConfigNamespacesMultipartyOrgDesc = ffc("config.namespaces.predefined[].multiparty.org.description", "A description for the local root organization within this namespace", i18n.StringType) + ConfigNamespacesMultipartyOrgKey = ffc("config.namespaces.predefined[].multiparty.org.key", "The signing key allocated to the root organization within this namespace", i18n.StringType) ConfigNodeDescription = ffc("config.node.description", "The description of this FireFly node", i18n.StringType) ConfigNodeName = ffc("config.node.name", "The name of this FireFly node", i18n.StringType) @@ -276,10 +281,9 @@ var ( ConfigOrchestratorStartupAttempts = ffc("config.orchestrator.startupAttempts", "The number of times to attempt to connect to core infrastructure on startup", i18n.StringType) - ConfigOrgDescription = ffc("config.org.description", "A description of the organization to which this FireFly node belongs", i18n.StringType) - ConfigOrgIdentity = ffc("config.org.identity", "`DEPRECATED` Please use `org.key` instead", i18n.StringType) - ConfigOrgKey = ffc("config.org.key", "The signing identity allocated to the organization (can be the same as the nodes)", i18n.StringType) - ConfigOrgName = ffc("config.org.name", "The name of the organization to which this FireFly node belongs", i18n.StringType) + ConfigOrgDescription = ffc("config.org.description", "A description of the organization to which this FireFly node belongs (deprecated - should be set on each multi-party namespace instead)", i18n.StringType) + ConfigOrgKey = ffc("config.org.key", "The signing key allocated to the organization (deprecated - should be set on each multi-party namespace instead)", i18n.StringType) + ConfigOrgName = ffc("config.org.name", "The name of the organization to which this FireFly node belongs (deprecated - should be set on each multi-party namespace instead)", i18n.StringType) ConfigPrivatemessagingBatchAgentTimeout = ffc("config.privatemessaging.batch.agentTimeout", "How long to keep around a batching agent for a sending identity before disposal", i18n.TimeDurationType) ConfigPrivatemessagingBatchPayloadLimit = ffc("config.privatemessaging.batch.payloadLimit", "The maximum payload size of a private message Data Exchange payload", i18n.ByteSizeType) diff --git a/internal/coremsgs/en_error_messages.go b/internal/coremsgs/en_error_messages.go index c1dc5fa3c..4eb8e68ee 100644 --- a/internal/coremsgs/en_error_messages.go +++ b/internal/coremsgs/en_error_messages.go @@ -113,7 +113,7 @@ var ( MsgUnknownDataExchangePlugin = ffe("FF10213", "Unknown Data Exchange plugin '%s'") MsgParentIdentityNotFound = ffe("FF10214", "Identity '%s' not found in identity chain for %s '%s'") MsgInvalidSigningIdentity = ffe("FF10215", "Invalid signing identity") - MsgNodeAndOrgIDMustBeSet = ffe("FF10216", "node.name, org.name and org.identity must be configured first", 409) + MsgNodeAndOrgIDMustBeSet = ffe("FF10216", "node.name, org.name and org.key must be configured first", 409) MsgBlobStreamingFailed = ffe("FF10217", "Blob streaming terminated with error", 500) MsgMultiPartFormReadError = ffe("FF10218", "Error reading multi-part form input", 400) MsgNodeNotFound = ffe("FF10224", "Node with name or identity '%s' not found", 400) @@ -207,7 +207,7 @@ var ( MsgIdentityNotNode = ffe("FF10351", "Identity '%s' with DID '%s' is not a node", 400) MsgBlockchainKeyNotSet = ffe("FF10352", "No blockchain key specified", 400) MsgNoVerifierForIdentity = ffe("FF10353", "No %s verifier registered for identity %s", 400) - MsgNodeMissingBlockchainKey = ffe("FF10354", "No organization signing key configured on node", 400) + MsgNodeMissingBlockchainKey = ffe("FF10354", "No default signing key or organization signing key configured for this namespace", 400) MsgAuthorRegistrationMismatch = ffe("FF10355", "Verifier '%s' cannot be used for signing with author '%s'. Verifier registered to '%s'", 400) MsgAuthorMissingForKey = ffe("FF10356", "Key '%s' has not been registered by any identity, and a separate 'author' was not supplied", 404) MsgAuthorIncorrectForRootReg = ffe("FF10357", "Author namespace '%s' and DID '%s' combination invalid for root organization registration", 400) @@ -245,4 +245,5 @@ var ( MsgInvalidFireFlyContractIndex = ffe("FF10396", "No configuration found for FireFly contract at %s") MsgUnrecognizedNetworkAction = ffe("FF10397", "Unrecognized network action: %s", 400) MsgOverrideExistingFieldCustomOption = ffe("FF10398", "Cannot override existing field with custom option named '%s'", 400) + MsgTerminateNotSupported = ffe("FF10399", "The 'terminate' operation to mark a switchover of smart contracts is not supported on namespace %s", 400) ) diff --git a/internal/coremsgs/en_struct_descriptions.go b/internal/coremsgs/en_struct_descriptions.go index 21ad60f2d..b2fe603d5 100644 --- a/internal/coremsgs/en_struct_descriptions.go +++ b/internal/coremsgs/en_struct_descriptions.go @@ -386,9 +386,9 @@ var ( NetworkActionType = ffm("NetworkAction.type", "The action to be performed") // NodeStatus field descriptions + NodeNamespace = ffm("NodeStatus.namespace", "The namespace that this status applies to") NodeStatusNode = ffm("NodeStatus.node", "Details of the local node") NodeStatusOrg = ffm("NodeStatus.org", "Details of the organization identity that operates this node") - NodeDefaults = ffm("NodeStatus.defaults", "Information about defaults configured on this node that appplications might need to query on startup") NodePlugins = ffm("NodeStatus.plugins", "Information about plugins configured on this node") // NodeStatusNode field descriptions diff --git a/internal/definitions/definition_handler_datatype_test.go b/internal/definitions/definition_handler_datatype_test.go index ea465292b..ec3ee2980 100644 --- a/internal/definitions/definition_handler_datatype_test.go +++ b/internal/definitions/definition_handler_datatype_test.go @@ -212,7 +212,7 @@ func TestHandleDefinitionBroadcastDatatypeLookupFail(t *testing.T) { mbi.On("GetDatatypeByName", mock.Anything, "ns1", "name1", "ver1").Return(nil, fmt.Errorf("pop")) action, err := dh.HandleDefinitionBroadcast(context.Background(), bs, &core.Message{ Header: core.MessageHeader{ - Namespace: core.SystemNamespace, + Namespace: "ns1", Tag: core.SystemTagDefineDatatype, }, }, core.DataArray{data}, fftypes.NewUUID()) diff --git a/internal/definitions/definition_handler_identity_claim_test.go b/internal/definitions/definition_handler_identity_claim_test.go index c46fbea0f..eee45737f 100644 --- a/internal/definitions/definition_handler_identity_claim_test.go +++ b/internal/definitions/definition_handler_identity_claim_test.go @@ -37,7 +37,7 @@ func testOrgIdentity(t *testing.T, name string) *core.Identity { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), Type: core.IdentityTypeOrg, - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: name, }, IdentityProfile: core.IdentityProfile{ diff --git a/internal/definitions/definition_handler_network_node.go b/internal/definitions/definition_handler_network_node.go index 4405d4243..10587d74d 100644 --- a/internal/definitions/definition_handler_network_node.go +++ b/internal/definitions/definition_handler_network_node.go @@ -32,7 +32,7 @@ func (dh *definitionHandlers) handleDeprecatedNodeBroadcast(ctx context.Context, return HandlerResult{Action: ActionReject}, nil } - owner, err := dh.identity.FindIdentityForVerifier(ctx, []core.IdentityType{core.IdentityTypeOrg}, core.SystemNamespace, &core.VerifierRef{ + owner, err := dh.identity.FindIdentityForVerifier(ctx, []core.IdentityType{core.IdentityTypeOrg}, core.LegacySystemNamespace, &core.VerifierRef{ Type: dh.blockchain.VerifierType(), Value: nodeOld.Owner, }) diff --git a/internal/definitions/definition_handler_network_node_test.go b/internal/definitions/definition_handler_network_node_test.go index 8620de31b..93588a0d8 100644 --- a/internal/definitions/definition_handler_network_node_test.go +++ b/internal/definitions/definition_handler_network_node_test.go @@ -103,16 +103,16 @@ func TestHandleDeprecatedNodeDefinitionOK(t *testing.T) { parent, _, _ := testDeprecatedRootOrg(t) mim := dh.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", ctx, []core.IdentityType{core.IdentityTypeOrg}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", ctx, []core.IdentityType{core.IdentityTypeOrg}, core.LegacySystemNamespace, &core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: node.Owner, }).Return(parent.Migrated().Identity, nil) mim.On("VerifyIdentityChain", ctx, mock.Anything).Return(parent.Migrated().Identity, false, nil) mdi := dh.database.(*databasemocks.Plugin) - mdi.On("GetIdentityByName", ctx, core.IdentityTypeNode, core.SystemNamespace, node.Name).Return(nil, nil) + mdi.On("GetIdentityByName", ctx, core.IdentityTypeNode, core.LegacySystemNamespace, node.Name).Return(nil, nil) mdi.On("GetIdentityByID", ctx, node.ID).Return(nil, nil) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeFFDXPeerID, core.SystemNamespace, "member_0").Return(nil, nil) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeFFDXPeerID, core.LegacySystemNamespace, "member_0").Return(nil, nil) mdi.On("UpsertIdentity", ctx, mock.MatchedBy(func(identity *core.Identity) bool { assert.Equal(t, *msg.Header.ID, *identity.Messages.Claim) return true @@ -163,7 +163,7 @@ func TestHandleDeprecatedNodeDefinitionFailOrgLookup(t *testing.T) { node, msg, data := testDeprecatedRootNode(t) mim := dh.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", ctx, []core.IdentityType{core.IdentityTypeOrg}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", ctx, []core.IdentityType{core.IdentityTypeOrg}, core.LegacySystemNamespace, &core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: node.Owner, }).Return(nil, fmt.Errorf("pop")) @@ -184,7 +184,7 @@ func TestHandleDeprecatedNodeDefinitionOrgNotFound(t *testing.T) { node, msg, data := testDeprecatedRootNode(t) mim := dh.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", ctx, []core.IdentityType{core.IdentityTypeOrg}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", ctx, []core.IdentityType{core.IdentityTypeOrg}, core.LegacySystemNamespace, &core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: node.Owner, }).Return(nil, nil) diff --git a/internal/definitions/definition_handler_network_org_test.go b/internal/definitions/definition_handler_network_org_test.go index a510b42b7..f11323a3c 100644 --- a/internal/definitions/definition_handler_network_org_test.go +++ b/internal/definitions/definition_handler_network_org_test.go @@ -93,9 +93,9 @@ func TestHandleDeprecatedOrgDefinitionOK(t *testing.T) { mim.On("VerifyIdentityChain", ctx, mock.Anything).Return(nil, false, nil) mdi := dh.database.(*databasemocks.Plugin) - mdi.On("GetIdentityByName", ctx, core.IdentityTypeOrg, core.SystemNamespace, org.Name).Return(nil, nil) + mdi.On("GetIdentityByName", ctx, core.IdentityTypeOrg, core.LegacySystemNamespace, org.Name).Return(nil, nil) mdi.On("GetIdentityByID", ctx, org.ID).Return(nil, nil) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, msg.Header.Key).Return(nil, nil) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.LegacySystemNamespace, msg.Header.Key).Return(nil, nil) mdi.On("UpsertIdentity", ctx, mock.MatchedBy(func(identity *core.Identity) bool { assert.Equal(t, *msg.Header.ID, *identity.Messages.Claim) return true diff --git a/internal/events/aggregator.go b/internal/events/aggregator.go index d3b2f5cd7..611c131da 100644 --- a/internal/events/aggregator.go +++ b/internal/events/aggregator.go @@ -94,7 +94,7 @@ func newAggregator(ctx context.Context, di database.Plugin, bi blockchain.Plugin Factor: config.GetFloat64(coreconfig.EventAggregatorRetryFactor), }, firstEvent: &firstEvent, - namespace: core.SystemNamespace, + namespace: "pins", // not a real namespace (used only for logging) offsetType: core.OffsetTypeAggregator, offsetName: aggregatorOffsetName, newEventsHandler: ag.processPinsEventsHandler, diff --git a/internal/events/dx_callbacks.go b/internal/events/dx_callbacks.go index 77394d4b7..1b91174bc 100644 --- a/internal/events/dx_callbacks.go +++ b/internal/events/dx_callbacks.go @@ -30,11 +30,11 @@ import ( // Check data exchange peer the data came from, has been registered to the org listed in the batch. // Note the on-chain identity check is performed separately by the aggregator (across broadcast and private consistently). -func (em *eventManager) checkReceivedOffchainIdentity(ctx context.Context, peerID, author string) (node *core.Identity, err error) { +func (em *eventManager) checkReceivedOffchainIdentity(ctx context.Context, ns, peerID, author string) (node *core.Identity, err error) { l := log.L(em.ctx) // Resolve the node for the peer ID - node, err = em.identity.FindIdentityForVerifier(ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + node, err = em.identity.FindIdentityForVerifier(ctx, []core.IdentityType{core.IdentityTypeNode}, ns, &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: peerID, }) @@ -43,7 +43,7 @@ func (em *eventManager) checkReceivedOffchainIdentity(ctx context.Context, peerI } // Find the identity in the mesage - org, retryable, err := em.identity.CachedIdentityLookupMustExist(ctx, author) + org, retryable, err := em.identity.CachedIdentityLookupMustExist(ctx, ns, author) if err != nil && retryable { l.Errorf("Failed to retrieve org: %v", err) return nil, err // retryable error @@ -95,7 +95,7 @@ func (em *eventManager) privateBatchReceived(peerID string, batch *core.Batch, w } } - node, err := em.checkReceivedOffchainIdentity(ctx, peerID, batch.Author) + node, err := em.checkReceivedOffchainIdentity(ctx, batch.Namespace, peerID, batch.Author) if err != nil { return err } diff --git a/internal/events/dx_callbacks_test.go b/internal/events/dx_callbacks_test.go index 9ac96e286..d64c32346 100644 --- a/internal/events/dx_callbacks_test.go +++ b/internal/events/dx_callbacks_test.go @@ -52,7 +52,7 @@ func newTestOrg(name string) *core.Identity { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), Type: core.IdentityTypeOrg, - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: name, Parent: nil, }, @@ -66,7 +66,7 @@ func newTestNode(name string, owner *core.Identity) *core.Identity { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), Type: core.IdentityTypeNode, - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: name, Parent: owner.ID, }, @@ -145,11 +145,11 @@ func TestPinnedReceiveOK(t *testing.T) { mdi := em.database.(*databasemocks.Plugin) mdx := &dataexchangemocks.Plugin{} mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(org1, false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(org1, false, nil) mdi.On("UpsertBatch", em.ctx, mock.Anything).Return(nil, nil) mdi.On("InsertDataArray", em.ctx, mock.Anything).Return(nil, nil) mdi.On("InsertMessages", em.ctx, mock.Anything, mock.AnythingOfType("database.PostCompletionHook")).Return(nil, nil).Run(func(args mock.Arguments) { @@ -192,11 +192,11 @@ func TestMessageReceiveOkBadBatchIgnored(t *testing.T) { mdx := &dataexchangemocks.Plugin{} mdx.On("Name").Return("utdx") mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(org1, false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(org1, false, nil) mde := newMessageReceived("peer1", b, "") em.messageReceived(mdx, mde) @@ -218,11 +218,11 @@ func TestMessageReceivePersistBatchError(t *testing.T) { mdx := &dataexchangemocks.Plugin{} mdx.On("Name").Return("utdx") mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(org1, false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(org1, false, nil) mdi.On("UpsertBatch", em.ctx, mock.Anything).Return(fmt.Errorf("pop")) // no ack as we are simulating termination mid retry @@ -314,13 +314,17 @@ func TestMessageReceiveNodeLookupError(t *testing.T) { em, cancel := newTestEventManager(t) cancel() // to stop retry - batch := &core.Batch{} + batch := &core.Batch{ + BatchHeader: core.BatchHeader{ + Namespace: "ns1", + }, + } b, _ := json.Marshal(&core.TransportWrapper{ Batch: batch, }) mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(nil, fmt.Errorf("pop")) @@ -347,11 +351,11 @@ func TestMessageReceiveGetCandidateOrgFail(t *testing.T) { mdx := &dataexchangemocks.Plugin{} mdx.On("Name").Return("utdx") mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(nil, true, fmt.Errorf("pop")) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(nil, true, fmt.Errorf("pop")) // no ack as we are simulating termination mid retry mde := newMessageReceivedNoAck("peer1", b) @@ -374,11 +378,11 @@ func TestMessageReceiveGetCandidateOrgNotFound(t *testing.T) { mdx := &dataexchangemocks.Plugin{} mdx.On("Name").Return("utdx") mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(nil, false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(nil, false, nil) mde := newMessageReceived("peer1", b, "") em.messageReceived(mdx, mde) @@ -399,11 +403,11 @@ func TestMessageReceiveGetCandidateOrgNotMatch(t *testing.T) { org1 := newTestOrg("org1") node1 := newTestNode("node1", org1) mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(newTestOrg("org2"), false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(newTestOrg("org2"), false, nil) mde := newMessageReceived("peer1", b, "") em.messageReceived(mdx, mde) @@ -504,11 +508,11 @@ func TestMessageReceiveMessageIdentityFail(t *testing.T) { mdx.On("Name").Return("utdx") mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(org2, false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(org2, false, nil) mim.On("CachedIdentityLookupByID", em.ctx, org2.Parent).Return(nil, fmt.Errorf("pop")) // no ack as we are simulating termination mid retry @@ -534,11 +538,11 @@ func TestMessageReceiveMessageIdentityParentNotFound(t *testing.T) { mdx.On("Name").Return("utdx") mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(org2, false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(org2, false, nil) mim.On("CachedIdentityLookupByID", em.ctx, org2.Parent).Return(nil, nil) mde := newMessageReceived("peer1", b, "") @@ -564,11 +568,11 @@ func TestMessageReceiveMessageIdentityIncorrect(t *testing.T) { mdx.On("Name").Return("utdx") mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(org2, false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(org2, false, nil) mim.On("CachedIdentityLookupByID", em.ctx, org2.Parent).Return(org3, nil) mde := newMessageReceived("peer1", b, "") @@ -595,11 +599,11 @@ func TestMessageReceiveMessagePersistMessageFail(t *testing.T) { org1 := newTestOrg("org1") node1 := newTestNode("node1", org1) mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(org1, false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(org1, false, nil) mdi.On("UpsertBatch", em.ctx, mock.Anything).Return(nil, nil) mdi.On("InsertDataArray", em.ctx, mock.Anything).Return(nil) mdi.On("InsertMessages", em.ctx, mock.Anything, mock.AnythingOfType("database.PostCompletionHook")).Return(fmt.Errorf("optimization fail")) @@ -631,11 +635,11 @@ func TestMessageReceiveMessagePersistDataFail(t *testing.T) { org1 := newTestOrg("org1") node1 := newTestNode("node1", org1) mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(org1, false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(org1, false, nil) mdi.On("UpsertBatch", em.ctx, mock.Anything).Return(nil, nil) mdi.On("InsertDataArray", em.ctx, mock.Anything).Return(fmt.Errorf("optimization miss")) mdi.On("UpsertData", em.ctx, mock.Anything, database.UpsertOptimizationExisting).Return(fmt.Errorf("pop")) @@ -666,11 +670,11 @@ func TestMessageReceiveUnpinnedBatchOk(t *testing.T) { mpm := em.messaging.(*privatemessagingmocks.Manager) mpm.On("EnsureLocalGroup", em.ctx, mock.Anything).Return(true, nil) mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(org1, false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(org1, false, nil) mdi.On("UpsertBatch", em.ctx, mock.Anything).Return(nil, nil) mdi.On("InsertDataArray", em.ctx, mock.Anything).Return(nil) mdi.On("InsertMessages", em.ctx, mock.Anything, mock.AnythingOfType("database.PostCompletionHook")).Return(nil, nil).Run(func(args mock.Arguments) { @@ -707,11 +711,11 @@ func TestMessageReceiveUnpinnedBatchConfirmMessagesFail(t *testing.T) { mpm := em.messaging.(*privatemessagingmocks.Manager) mpm.On("EnsureLocalGroup", em.ctx, mock.Anything).Return(true, nil) mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(org1, false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(org1, false, nil) mdi.On("UpsertBatch", em.ctx, mock.Anything).Return(nil, nil) mdi.On("InsertDataArray", em.ctx, mock.Anything).Return(nil) mdi.On("InsertMessages", em.ctx, mock.Anything, mock.AnythingOfType("database.PostCompletionHook")).Return(nil, nil).Run(func(args mock.Arguments) { @@ -748,11 +752,11 @@ func TestMessageReceiveUnpinnedBatchPersistEventFail(t *testing.T) { mpm := em.messaging.(*privatemessagingmocks.Manager) mpm.On("EnsureLocalGroup", em.ctx, mock.Anything).Return(true, nil) mim := em.identity.(*identitymanagermocks.Manager) - mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, core.SystemNamespace, &core.VerifierRef{ + mim.On("FindIdentityForVerifier", em.ctx, []core.IdentityType{core.IdentityTypeNode}, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }).Return(node1, nil) - mim.On("CachedIdentityLookupMustExist", em.ctx, "signingOrg").Return(org1, false, nil) + mim.On("CachedIdentityLookupMustExist", em.ctx, "ns1", "signingOrg").Return(org1, false, nil) mdi.On("UpsertBatch", em.ctx, mock.Anything).Return(nil, nil) mdi.On("InsertDataArray", em.ctx, mock.Anything).Return(nil) mdi.On("InsertMessages", em.ctx, mock.Anything, mock.AnythingOfType("database.PostCompletionHook")).Return(nil, nil).Run(func(args mock.Arguments) { diff --git a/internal/events/event_manager_test.go b/internal/events/event_manager_test.go index 88d8a028f..655c0ffa4 100644 --- a/internal/events/event_manager_test.go +++ b/internal/events/event_manager_test.go @@ -86,7 +86,7 @@ func newTestEventManagerCommon(t *testing.T, metrics, dbconcurrency bool) (*even if metrics { mmi.On("TransferConfirmed", mock.Anything) } - mni.On("GetNodeUUID", mock.Anything).Return(testNodeID).Maybe() + mni.On("GetNodeUUID", mock.Anything, "ns1").Return(testNodeID).Maybe() met.On("Name").Return("ut").Maybe() mbi.On("VerifierType").Return(core.VerifierTypeEthAddress).Maybe() mdi.On("Capabilities").Return(&database.Capabilities{Concurrency: dbconcurrency}).Maybe() diff --git a/internal/events/network_action.go b/internal/events/network_action.go index 205465f77..957314580 100644 --- a/internal/events/network_action.go +++ b/internal/events/network_action.go @@ -26,7 +26,7 @@ import ( func (em *eventManager) actionTerminate(bi blockchain.Plugin, event *blockchain.Event) error { return em.database.RunAsGroup(em.ctx, func(ctx context.Context) error { - ns, err := em.database.GetNamespace(ctx, core.SystemNamespace) + ns, err := em.database.GetNamespace(ctx, core.LegacySystemNamespace) if err != nil { return err } @@ -40,7 +40,7 @@ func (em *eventManager) actionTerminate(bi blockchain.Plugin, event *blockchain. func (em *eventManager) BlockchainNetworkAction(bi blockchain.Plugin, action string, event *blockchain.Event, signingKey *core.VerifierRef) error { return em.retry.Do(em.ctx, "handle network action", func(attempt int) (retry bool, err error) { // Verify that the action came from a registered root org - resolvedAuthor, err := em.identity.FindIdentityForVerifier(em.ctx, []core.IdentityType{core.IdentityTypeOrg}, core.SystemNamespace, signingKey) + resolvedAuthor, err := em.identity.FindIdentityForVerifier(em.ctx, []core.IdentityType{core.IdentityTypeOrg}, core.LegacySystemNamespace, signingKey) if err != nil { return true, err } @@ -61,7 +61,7 @@ func (em *eventManager) BlockchainNetworkAction(bi blockchain.Plugin, action str } if err == nil { - chainEvent := buildBlockchainEvent(core.SystemNamespace, nil, event, &core.BlockchainTransactionRef{ + chainEvent := buildBlockchainEvent(core.LegacySystemNamespace, nil, event, &core.BlockchainTransactionRef{ BlockchainID: event.BlockchainTXID, }) err = em.maybePersistBlockchainEvent(em.ctx, chainEvent) diff --git a/internal/events/persist_batch.go b/internal/events/persist_batch.go index 7ba921147..1fa6553b6 100644 --- a/internal/events/persist_batch.go +++ b/internal/events/persist_batch.go @@ -211,7 +211,7 @@ func (em *eventManager) validateBatchMessage(ctx context.Context, batch *core.Ba } func (em *eventManager) sentByUs(ctx context.Context, batch *core.Batch) bool { - localNode := em.ni.GetNodeUUID(ctx) + localNode := em.ni.GetNodeUUID(ctx, batch.Namespace) if batch.Node == nil { // This is from a node that hasn't yet completed registration, so we can't optimize return false diff --git a/internal/identity/identitymanager.go b/internal/identity/identitymanager.go index a61e666b5..d481c60ad 100644 --- a/internal/identity/identitymanager.go +++ b/internal/identity/identitymanager.go @@ -29,6 +29,7 @@ import ( "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/internal/coremsgs" "github.com/hyperledger/firefly/internal/data" + "github.com/hyperledger/firefly/internal/namespace" "github.com/hyperledger/firefly/pkg/blockchain" "github.com/hyperledger/firefly/pkg/core" "github.com/hyperledger/firefly/pkg/database" @@ -42,17 +43,16 @@ const ( ) type Manager interface { - ResolveInputSigningIdentity(ctx context.Context, namespace string, msgSignerRef *core.SignerRef) (err error) - ResolveNodeOwnerSigningIdentity(ctx context.Context, msgSignerRef *core.SignerRef) (err error) - NormalizeSigningKey(ctx context.Context, namespace string, keyNormalizationMode int) (signingKey string, err error) + ResolveInputSigningIdentity(ctx context.Context, namespace string, signerRef *core.SignerRef) (err error) + NormalizeSigningKey(ctx context.Context, namespace, inputKey string, keyNormalizationMode int) (signingKey string, err error) FindIdentityForVerifier(ctx context.Context, iTypes []core.IdentityType, namespace string, verifier *core.VerifierRef) (identity *core.Identity, err error) ResolveIdentitySigner(ctx context.Context, identity *core.Identity) (parentSigner *core.SignerRef, err error) CachedIdentityLookupByID(ctx context.Context, id *fftypes.UUID) (identity *core.Identity, err error) - CachedIdentityLookupMustExist(ctx context.Context, did string) (identity *core.Identity, retryable bool, err error) - CachedIdentityLookupNilOK(ctx context.Context, did string) (identity *core.Identity, retryable bool, err error) - CachedVerifierLookup(ctx context.Context, vType core.VerifierType, ns, value string) (verifier *core.Verifier, err error) - GetNodeOwnerBlockchainKey(ctx context.Context) (*core.VerifierRef, error) - GetNodeOwnerOrg(ctx context.Context) (*core.Identity, error) + CachedIdentityLookupMustExist(ctx context.Context, namespace, did string) (identity *core.Identity, retryable bool, err error) + CachedIdentityLookupNilOK(ctx context.Context, namespace, did string) (identity *core.Identity, retryable bool, err error) + CachedVerifierLookup(ctx context.Context, vType core.VerifierType, namespace, value string) (verifier *core.Verifier, err error) + GetMultipartyRootVerifier(ctx context.Context, namespace string) (*core.VerifierRef, error) + GetMultipartyRootOrg(ctx context.Context, namespace string) (*core.Identity, error) VerifyIdentityChain(ctx context.Context, identity *core.Identity) (immediateParent *core.Identity, retryable bool, err error) } @@ -61,26 +61,30 @@ type identityManager struct { plugin map[string]identity.Plugin blockchain blockchain.Plugin data data.Manager + namespace namespace.Manager - nodeOwnerBlockchainKey *core.VerifierRef - nodeOwningOrgIdentity *core.Identity + multipartyRootVerifier map[string]*core.VerifierRef + multipartyRootOrg map[string]*core.Identity identityCacheTTL time.Duration identityCache *ccache.Cache signingKeyCacheTTL time.Duration signingKeyCache *ccache.Cache } -func NewIdentityManager(ctx context.Context, di database.Plugin, ii map[string]identity.Plugin, bi blockchain.Plugin, dm data.Manager) (Manager, error) { - if di == nil || ii == nil || bi == nil { +func NewIdentityManager(ctx context.Context, di database.Plugin, ii map[string]identity.Plugin, bi blockchain.Plugin, dm data.Manager, nm namespace.Manager) (Manager, error) { + if di == nil || ii == nil || bi == nil || nm == nil { return nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "IdentityManager") } im := &identityManager{ - database: di, - plugin: ii, - blockchain: bi, - data: dm, - identityCacheTTL: config.GetDuration(coreconfig.IdentityManagerCacheTTL), - signingKeyCacheTTL: config.GetDuration(coreconfig.IdentityManagerCacheTTL), + database: di, + plugin: ii, + blockchain: bi, + data: dm, + namespace: nm, + multipartyRootVerifier: make(map[string]*core.VerifierRef), + multipartyRootOrg: make(map[string]*core.Identity), + identityCacheTTL: config.GetDuration(coreconfig.IdentityManagerCacheTTL), + signingKeyCacheTTL: config.GetDuration(coreconfig.IdentityManagerCacheTTL), } // For the identity and signingkey caches, we just treat them all equally sized and the max items im.identityCache = ccache.New( @@ -102,19 +106,19 @@ func ParseKeyNormalizationConfig(strConfigVal string) int { } } -// NormalizeSigningKey is for cases where there is no "author" field alongside the "key" in the input (custom contracts, tokens), -// or the author is known by the caller and should not / cannot be confirmed prior to sending (identity claims) -func (im *identityManager) NormalizeSigningKey(ctx context.Context, inputKey string, keyNormalizationMode int) (signingKey string, err error) { +// NormalizeSigningKey takes in only a "key" (which may be empty to use the default) to be normalized and returned. +// This is for cases where keys are used directly without an "author" field alongside them (custom contracts, tokens), +// or when the author is known by the caller and should not / cannot be confirmed prior to sending (identity claims) +func (im *identityManager) NormalizeSigningKey(ctx context.Context, namespace, inputKey string, keyNormalizationMode int) (signingKey string, err error) { if inputKey == "" { - msgSignerRef := &core.SignerRef{} - err = im.ResolveNodeOwnerSigningIdentity(ctx, msgSignerRef) + verifierRef, err := im.getDefaultVerifier(ctx, namespace) if err != nil { return "", err } - return msgSignerRef.Key, nil + return verifierRef.Value, nil } // If the caller is not confident that the blockchain plugin/connector should be used to resolve, - // for example it might be a different blockchain (Eth vs Fabric etc.), or it has it's own + // for example it might be a different blockchain (Eth vs Fabric etc.), or it has its own // verification/management of keys, it should set `assets.keyNormalization: "none"` in the config. if keyNormalizationMode != KeyNormalizationBlockchainPlugin { return inputKey, nil @@ -126,65 +130,69 @@ func (im *identityManager) NormalizeSigningKey(ctx context.Context, inputKey str return signer.Value, nil } -// ResolveInputIdentity takes in blockchain signing input information from an API call, -// and resolves the final information that should be written in the message etc.. -func (im *identityManager) ResolveInputSigningIdentity(ctx context.Context, namespace string, msgSignerRef *core.SignerRef) (err error) { - log.L(ctx).Debugf("Resolving identity input: key='%s' author='%s'", msgSignerRef.Key, msgSignerRef.Author) +// ResolveInputIdentity takes in blockchain signing input information from an API call (which may +// include author or key or both), and updates it with fully resolved and normalized values +func (im *identityManager) ResolveInputSigningIdentity(ctx context.Context, namespace string, signerRef *core.SignerRef) (err error) { + log.L(ctx).Debugf("Resolving identity input: key='%s' author='%s'", signerRef.Key, signerRef.Author) var verifier *core.VerifierRef switch { - case msgSignerRef.Author == "" && msgSignerRef.Key == "": - err = im.ResolveNodeOwnerSigningIdentity(ctx, msgSignerRef) + case signerRef.Author == "" && signerRef.Key == "": + // Nothing specified: use the default node identity + err = im.resolveDefaultSigningIdentity(ctx, namespace, signerRef) if err != nil { return err } - case msgSignerRef.Key != "": - if verifier, err = im.normalizeKeyViaBlockchainPlugin(ctx, msgSignerRef.Key); err != nil { + + case signerRef.Key != "": + // Key specified: normalize it, then check it against author (if specified) + if verifier, err = im.normalizeKeyViaBlockchainPlugin(ctx, signerRef.Key); err != nil { return err } - msgSignerRef.Key = verifier.Value - // Fill in or verify the author DID based on the verfier, if it's been registered + signerRef.Key = verifier.Value + identity, err := im.FindIdentityForVerifier(ctx, []core.IdentityType{ core.IdentityTypeOrg, core.IdentityTypeCustom, }, namespace, verifier) - if err != nil { - return err - } switch { + case err != nil: + return err case identity != nil: - if msgSignerRef.Author == identity.Name || msgSignerRef.Author == "" { - // Switch to full DID automatically - msgSignerRef.Author = identity.DID + // Key matches a registered verifier: author must be unspecified OR must match verifier identity + if signerRef.Author == identity.Name || signerRef.Author == "" { + // Resolve author to DID (if blank or bare name) + signerRef.Author = identity.DID } - if msgSignerRef.Author != identity.DID { - return i18n.NewError(ctx, coremsgs.MsgAuthorRegistrationMismatch, verifier.Value, msgSignerRef.Author, identity.DID) + if signerRef.Author != identity.DID { + return i18n.NewError(ctx, coremsgs.MsgAuthorRegistrationMismatch, verifier.Value, signerRef.Author, identity.DID) } - case msgSignerRef.Author != "": - identity, _, err := im.CachedIdentityLookupMustExist(ctx, msgSignerRef.Author) + case signerRef.Author != "": + // Key is unrecognized, but an author was specified: use the key and resolve author to DID + identity, _, err := im.CachedIdentityLookupMustExist(ctx, namespace, signerRef.Author) if err != nil { return err } - msgSignerRef.Author = identity.DID + signerRef.Author = identity.DID default: - return i18n.NewError(ctx, coremsgs.MsgAuthorMissingForKey, msgSignerRef.Key) + return i18n.NewError(ctx, coremsgs.MsgAuthorMissingForKey, signerRef.Key) } - case msgSignerRef.Author != "": - // Author must be non-empty (see above), so we want to find that identity and then - // use the first blockchain key that's associated with it. - identity, _, err := im.CachedIdentityLookupMustExist(ctx, msgSignerRef.Author) + + case signerRef.Author != "": + // Author specified (without key): use the first blockchain key associated with it + identity, _, err := im.CachedIdentityLookupMustExist(ctx, namespace, signerRef.Author) if err != nil { return err } - msgSignerRef.Author = identity.DID verifier, _, err = im.firstVerifierForIdentity(ctx, im.blockchain.VerifierType(), identity) if err != nil { return err } - msgSignerRef.Key = verifier.Value + signerRef.Author = identity.DID + signerRef.Key = verifier.Value } - log.L(ctx).Debugf("Resolved identity: key='%s' author='%s'", msgSignerRef.Key, msgSignerRef.Author) + log.L(ctx).Debugf("Resolved identity: key='%s' author='%s'", signerRef.Key, signerRef.Author) return nil } @@ -206,34 +214,37 @@ func (im *identityManager) firstVerifierForIdentity(ctx context.Context, vType c return &verifiers[0].VerifierRef, false, nil } -// ResolveNodeOwnerSigningIdentity add the node owner identity into a message -func (im *identityManager) ResolveNodeOwnerSigningIdentity(ctx context.Context, msgSignerRef *core.SignerRef) (err error) { - verifierRef, err := im.GetNodeOwnerBlockchainKey(ctx) +// resolveDefaultSigningIdentity adds the default signing identity into a message +func (im *identityManager) resolveDefaultSigningIdentity(ctx context.Context, namespace string, signerRef *core.SignerRef) (err error) { + verifierRef, err := im.getDefaultVerifier(ctx, namespace) if err != nil { return err } - identity, err := im.GetNodeOwnerOrg(ctx) + identity, err := im.GetMultipartyRootOrg(ctx, namespace) if err != nil { return err } - msgSignerRef.Author = identity.DID - msgSignerRef.Key = verifierRef.Value + signerRef.Author = identity.DID + signerRef.Key = verifierRef.Value return nil } -// GetNodeOwnerBlockchainKey gets the blockchain key of the node owner, from the configuration -func (im *identityManager) GetNodeOwnerBlockchainKey(ctx context.Context) (*core.VerifierRef, error) { - if im.nodeOwnerBlockchainKey != nil { - return im.nodeOwnerBlockchainKey, nil +// getDefaultVerifier gets the default blockchain verifier via the configuration +func (im *identityManager) getDefaultVerifier(ctx context.Context, namespace string) (verifier *core.VerifierRef, err error) { + defaultKey := im.namespace.GetDefaultKey(namespace) + if defaultKey != "" { + return im.normalizeKeyViaBlockchainPlugin(ctx, defaultKey) } + return im.GetMultipartyRootVerifier(ctx, namespace) +} - orgKey := config.GetString(coreconfig.OrgKey) - if orgKey == "" { - orgKey = config.GetString(coreconfig.OrgIdentityDeprecated) - if orgKey != "" { - log.L(ctx).Warnf("The %s config key has been deprecated. Please use %s instead", coreconfig.OrgIdentityDeprecated, coreconfig.OrgKey) - } +// GetMultipartyRootVerifier gets the blockchain verifier of the root org via the configuration +func (im *identityManager) GetMultipartyRootVerifier(ctx context.Context, namespace string) (*core.VerifierRef, error) { + if key, ok := im.multipartyRootVerifier[namespace]; ok { + return key, nil } + + orgKey := im.namespace.GetMultipartyConfig(namespace, coreconfig.OrgKey) if orgKey == "" { return nil, i18n.NewError(ctx, coremsgs.MsgNodeMissingBlockchainKey) } @@ -242,8 +253,8 @@ func (im *identityManager) GetNodeOwnerBlockchainKey(ctx context.Context) (*core if err != nil { return nil, err } - im.nodeOwnerBlockchainKey = verifier - return im.nodeOwnerBlockchainKey, nil + im.multipartyRootVerifier[namespace] = verifier + return verifier, nil } // normalizeKeyViaBlockchainPlugin does a cached lookup of the fully qualified key, associated with a key reference string @@ -267,34 +278,27 @@ func (im *identityManager) normalizeKeyViaBlockchainPlugin(ctx context.Context, return verifier, nil } -// FindIdentityForVerifier is a reverse lookup function to look up an identity registered as owner of the specified verifier. -// Each of the supplied identity types will be checked in order. Returns nil if not found +// FindIdentityForVerifier is a reverse lookup function to look up an identity registered as owner of the specified verifier func (im *identityManager) FindIdentityForVerifier(ctx context.Context, iTypes []core.IdentityType, namespace string, verifier *core.VerifierRef) (identity *core.Identity, err error) { - for _, iType := range iTypes { - verifierNS := namespace - if iType != core.IdentityTypeCustom { - // Non-custom identity types are always in the system namespace - verifierNS = core.SystemNamespace - } - identity, err = im.cachedIdentityLookupByVerifierRef(ctx, verifierNS, verifier) - if err != nil || identity != nil { - return identity, err - } + identity, err = im.cachedIdentityLookupByVerifierRef(ctx, namespace, verifier) + if err != nil || identity != nil { + return identity, err } return nil, nil } -// GetNodeOwnerOrg returns the identity of the organization that owns the node, if fully registered -func (im *identityManager) GetNodeOwnerOrg(ctx context.Context) (*core.Identity, error) { - if im.nodeOwningOrgIdentity != nil { - return im.nodeOwningOrgIdentity, nil +// GetMultipartyRootOrg returns the identity of the organization that owns the node, if fully registered within the given namespace +func (im *identityManager) GetMultipartyRootOrg(ctx context.Context, namespace string) (*core.Identity, error) { + if id, ok := im.multipartyRootOrg[namespace]; ok { + return id, nil } - verifierRef, err := im.GetNodeOwnerBlockchainKey(ctx) + verifierRef, err := im.GetMultipartyRootVerifier(ctx, namespace) if err != nil { return nil, err } - orgName := config.GetString(coreconfig.OrgName) - identity, err := im.cachedIdentityLookupByVerifierRef(ctx, core.SystemNamespace, verifierRef) + + orgName := im.namespace.GetMultipartyConfig(namespace, coreconfig.OrgName) + identity, err := im.cachedIdentityLookupByVerifierRef(ctx, namespace, verifierRef) if err != nil || identity == nil { return nil, i18n.WrapError(ctx, err, coremsgs.MsgLocalOrgLookupFailed, orgName, verifierRef.Value) } @@ -302,8 +306,8 @@ func (im *identityManager) GetNodeOwnerOrg(ctx context.Context) (*core.Identity, if identity.Type != core.IdentityTypeOrg || identity.Name != orgName { return nil, i18n.NewError(ctx, coremsgs.MsgLocalOrgLookupFailed, orgName, verifierRef.Value) } - im.nodeOwningOrgIdentity = identity - return im.nodeOwningOrgIdentity, nil + im.multipartyRootOrg[namespace] = identity + return identity, nil } func (im *identityManager) VerifyIdentityChain(ctx context.Context, checkIdentity *core.Identity) (immediateParent *core.Identity, retryable bool, err error) { @@ -384,7 +388,17 @@ func (im *identityManager) cachedIdentityLookupByVerifierRef(ctx context.Context return cached.Value().(*core.Identity), nil } verifier, err := im.database.GetVerifierByValue(ctx, verifierRef.Type, namespace, verifierRef.Value) - if err != nil || verifier == nil { + if err != nil { + return nil, err + } else if verifier == nil { + if namespace != core.LegacySystemNamespace { + if version, err := im.blockchain.NetworkVersion(ctx); err != nil { + return nil, err + } else if version == 1 { + // For V1 networks, fall back to SystemNamespace for looking up identities + return im.cachedIdentityLookupByVerifierRef(ctx, core.LegacySystemNamespace, verifierRef) + } + } return nil, err } identity, err := im.database.GetIdentityByID(ctx, verifier.Identity) @@ -399,7 +413,7 @@ func (im *identityManager) cachedIdentityLookupByVerifierRef(ctx context.Context return identity, nil } -func (im *identityManager) CachedIdentityLookupNilOK(ctx context.Context, didLookupStr string) (identity *core.Identity, retryable bool, err error) { +func (im *identityManager) CachedIdentityLookupNilOK(ctx context.Context, namespace, didLookupStr string) (identity *core.Identity, retryable bool, err error) { // Use an LRU cache for the author identity, as it's likely for the same identity to be re-used over and over cacheKey := fmt.Sprintf("did=%s", didLookupStr) defer func() { @@ -434,7 +448,7 @@ func (im *identityManager) CachedIdentityLookupNilOK(ctx context.Context, didLoo } } else { // If there is just a name in there, then it could be an Org type identity (from the very original usage of the field) - if identity, err = im.database.GetIdentityByName(ctx, core.IdentityTypeOrg, core.SystemNamespace, didLookupStr); err != nil { + if identity, err = im.database.GetIdentityByName(ctx, core.IdentityTypeOrg, namespace, didLookupStr); err != nil { return nil, true /* DB Error */, err } } @@ -447,8 +461,8 @@ func (im *identityManager) CachedIdentityLookupNilOK(ctx context.Context, didLoo return identity, false, nil } -func (im *identityManager) CachedIdentityLookupMustExist(ctx context.Context, didLookupStr string) (identity *core.Identity, retryable bool, err error) { - identity, retryable, err = im.CachedIdentityLookupNilOK(ctx, didLookupStr) +func (im *identityManager) CachedIdentityLookupMustExist(ctx context.Context, namespace, didLookupStr string) (identity *core.Identity, retryable bool, err error) { + identity, retryable, err = im.CachedIdentityLookupNilOK(ctx, namespace, didLookupStr) if err != nil { return nil, retryable, err } @@ -475,14 +489,14 @@ func (im *identityManager) CachedIdentityLookupByID(ctx context.Context, id *fft return identity, nil } -func (im *identityManager) CachedVerifierLookup(ctx context.Context, vType core.VerifierType, ns, value string) (verifier *core.Verifier, err error) { +func (im *identityManager) CachedVerifierLookup(ctx context.Context, vType core.VerifierType, namespace, value string) (verifier *core.Verifier, err error) { // Use an LRU cache for the author identity, as it's likely for the same identity to be re-used over and over - cacheKey := fmt.Sprintf("v=%s|%s|%s", vType, ns, value) + cacheKey := fmt.Sprintf("v=%s|%s|%s", vType, namespace, value) if cached := im.identityCache.Get(cacheKey); cached != nil { cached.Extend(im.identityCacheTTL) verifier = cached.Value().(*core.Verifier) } else { - verifier, err = im.database.GetVerifierByValue(ctx, vType, ns, value) + verifier, err = im.database.GetVerifierByValue(ctx, vType, namespace, value) if err != nil || verifier == nil { return verifier, err } diff --git a/internal/identity/identitymanager_test.go b/internal/identity/identitymanager_test.go index 6b120f825..942ee862e 100644 --- a/internal/identity/identitymanager_test.go +++ b/internal/identity/identitymanager_test.go @@ -21,13 +21,13 @@ import ( "fmt" "testing" - "github.com/hyperledger/firefly-common/pkg/config" "github.com/hyperledger/firefly-common/pkg/fftypes" "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/mocks/blockchainmocks" "github.com/hyperledger/firefly/mocks/databasemocks" "github.com/hyperledger/firefly/mocks/datamocks" "github.com/hyperledger/firefly/mocks/identitymocks" + "github.com/hyperledger/firefly/mocks/namespacemocks" "github.com/hyperledger/firefly/pkg/core" "github.com/hyperledger/firefly/pkg/identity" "github.com/stretchr/testify/assert" @@ -35,44 +35,53 @@ import ( ) func newTestIdentityManager(t *testing.T) (context.Context, *identityManager) { + coreconfig.Reset() mdi := &databasemocks.Plugin{} mii := &identitymocks.Plugin{} mbi := &blockchainmocks.Plugin{} mdm := &datamocks.Manager{} - - coreconfig.Reset() + mns := &namespacemocks.Manager{} mbi.On("VerifierType").Return(core.VerifierTypeEthAddress).Maybe() plugins := map[string]identity.Plugin{"tbd": mii} ctx := context.Background() - im, err := NewIdentityManager(ctx, mdi, plugins, mbi, mdm) + im, err := NewIdentityManager(ctx, mdi, plugins, mbi, mdm, mns) assert.NoError(t, err) return ctx, im.(*identityManager) } func TestNewIdentityManagerMissingDeps(t *testing.T) { - _, err := NewIdentityManager(context.Background(), nil, nil, nil, nil) + _, err := NewIdentityManager(context.Background(), nil, nil, nil, nil, nil) assert.Regexp(t, "FF10128", err) } -func TestResolveInputSigningIdentityNoOrgKey(t *testing.T) { +func TestResolveInputSigningIdentityNoKey(t *testing.T) { ctx, im := newTestIdentityManager(t) + mns := im.namespace.(*namespacemocks.Manager) + mns.On("GetDefaultKey", "ns1").Return("") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgKey).Return("") + msgIdentity := &core.SignerRef{} err := im.ResolveInputSigningIdentity(ctx, "ns1", msgIdentity) assert.Regexp(t, "FF10354", err) + mns.AssertExpectations(t) + } func TestResolveInputSigningIdentityOrgFallbackOk(t *testing.T) { ctx, im := newTestIdentityManager(t) - config.Set(coreconfig.OrgKey, "key123") - config.Set(coreconfig.OrgName, "org1") + + mns := im.namespace.(*namespacemocks.Manager) + mns.On("GetDefaultKey", "ns1").Return("") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgName).Return("org1") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgKey).Return("key123") mbi := im.blockchain.(*blockchainmocks.Plugin) mbi.On("NormalizeSigningKey", ctx, "key123").Return("fullkey123", nil) @@ -80,10 +89,10 @@ func TestResolveInputSigningIdentityOrgFallbackOk(t *testing.T) { orgID := fftypes.NewUUID() mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "fullkey123"). + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123"). Return((&core.Verifier{ Identity: orgID, - Namespace: core.SystemNamespace, + Namespace: "ns1", VerifierRef: core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: "fullkey123", @@ -94,7 +103,7 @@ func TestResolveInputSigningIdentityOrgFallbackOk(t *testing.T) { IdentityBase: core.IdentityBase{ ID: orgID, DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org1", Type: core.IdentityTypeOrg, }, @@ -108,6 +117,7 @@ func TestResolveInputSigningIdentityOrgFallbackOk(t *testing.T) { mbi.AssertExpectations(t) mdi.AssertExpectations(t) + mns.AssertExpectations(t) } @@ -121,7 +131,7 @@ func TestResolveInputSigningIdentityByKeyOk(t *testing.T) { idID := fftypes.NewUUID() mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "fullkey123"). + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123"). Return((&core.Verifier{ Identity: idID, Namespace: "ns1", @@ -135,7 +145,7 @@ func TestResolveInputSigningIdentityByKeyOk(t *testing.T) { IdentityBase: core.IdentityBase{ ID: idID, DID: "did:firefly:ns/ns1/myid", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "myid", Type: core.IdentityTypeCustom, }, @@ -160,18 +170,20 @@ func TestResolveInputSigningIdentityAnonymousKeyWithAuthorOk(t *testing.T) { mbi := im.blockchain.(*blockchainmocks.Plugin) mbi.On("NormalizeSigningKey", ctx, "mykey123").Return("fullkey123", nil) + mbi.On("NetworkVersion", ctx).Return(1, nil) idID := fftypes.NewUUID() mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "fullkey123").Return(nil, nil) mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123").Return(nil, nil) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123").Return(nil, nil) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.LegacySystemNamespace, "fullkey123").Return(nil, nil) mdi.On("GetIdentityByDID", ctx, "did:firefly:ns/ns1/myid"). Return(&core.Identity{ IdentityBase: core.IdentityBase{ ID: idID, DID: "did:firefly:ns/ns1/myid", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "myid", Type: core.IdentityTypeCustom, }, @@ -197,10 +209,12 @@ func TestResolveInputSigningIdentityKeyWithNoAuthorFail(t *testing.T) { mbi := im.blockchain.(*blockchainmocks.Plugin) mbi.On("NormalizeSigningKey", ctx, "mykey123").Return("fullkey123", nil) + mbi.On("NetworkVersion", ctx).Return(1, nil) mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "fullkey123").Return(nil, nil) mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123").Return(nil, nil) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123").Return(nil, nil) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.LegacySystemNamespace, "fullkey123").Return(nil, nil) msgIdentity := &core.SignerRef{ Key: "mykey123", @@ -223,7 +237,7 @@ func TestResolveInputSigningIdentityByKeyDIDMismatch(t *testing.T) { idID := fftypes.NewUUID() mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "fullkey123"). + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123"). Return((&core.Verifier{ Identity: idID, Namespace: "ns1", @@ -261,12 +275,15 @@ func TestResolveInputSigningIdentityByKeyNotFound(t *testing.T) { mbi := im.blockchain.(*blockchainmocks.Plugin) mbi.On("NormalizeSigningKey", ctx, "mykey123").Return("fullkey123", nil) + mbi.On("NetworkVersion", ctx).Return(1, nil) mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "fullkey123"). + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123"). Return(nil, nil) mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123"). Return(nil, nil) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.LegacySystemNamespace, "fullkey123"). + Return(nil, nil) mdi.On("GetIdentityByDID", ctx, "did:firefly:ns/ns1/unknown"). Return(nil, nil) @@ -290,7 +307,7 @@ func TestResolveInputSigningIdentityByKeyFail(t *testing.T) { mbi.On("NormalizeSigningKey", ctx, "mykey123").Return("fullkey123", nil) mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "fullkey123"). + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123"). Return(nil, fmt.Errorf("pop")) msgIdentity := &core.SignerRef{ @@ -327,12 +344,12 @@ func TestResolveInputSigningIdentityByOrgNameOk(t *testing.T) { idID := fftypes.NewUUID() mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetIdentityByName", ctx, core.IdentityTypeOrg, core.SystemNamespace, "org1"). + mdi.On("GetIdentityByName", ctx, core.IdentityTypeOrg, "ns1", "org1"). Return(&core.Identity{ IdentityBase: core.IdentityBase{ ID: idID, DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "myid", Type: core.IdentityTypeOrg, }, @@ -366,7 +383,7 @@ func TestResolveInputSigningIdentityByOrgLookkupNotFound(t *testing.T) { ctx, im := newTestIdentityManager(t) mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetIdentityByName", ctx, core.IdentityTypeOrg, core.SystemNamespace, "org1"). + mdi.On("GetIdentityByName", ctx, core.IdentityTypeOrg, "ns1", "org1"). Return(nil, nil) msgIdentity := &core.SignerRef{ @@ -384,7 +401,7 @@ func TestResolveInputSigningIdentityByOrgLookkupFail(t *testing.T) { ctx, im := newTestIdentityManager(t) mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetIdentityByName", ctx, core.IdentityTypeOrg, core.SystemNamespace, "org1"). + mdi.On("GetIdentityByName", ctx, core.IdentityTypeOrg, "ns1", "org1"). Return(nil, fmt.Errorf("pop")) msgIdentity := &core.SignerRef{ @@ -404,12 +421,12 @@ func TestResolveInputSigningIdentityByOrgVerifierFail(t *testing.T) { idID := fftypes.NewUUID() mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetIdentityByName", ctx, core.IdentityTypeOrg, core.SystemNamespace, "org1"). + mdi.On("GetIdentityByName", ctx, core.IdentityTypeOrg, "ns1", "org1"). Return(&core.Identity{ IdentityBase: core.IdentityBase{ ID: idID, DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "myid", Type: core.IdentityTypeOrg, }, @@ -426,78 +443,72 @@ func TestResolveInputSigningIdentityByOrgVerifierFail(t *testing.T) { } -func TestNormalizeSigningKeyOrgFallbackOk(t *testing.T) { +func TestNormalizeSigningKeyDefault(t *testing.T) { ctx, im := newTestIdentityManager(t) - config.Set(coreconfig.OrgKey, "key123") - config.Set(coreconfig.OrgName, "org1") + + mns := im.namespace.(*namespacemocks.Manager) + mns.On("GetDefaultKey", "ns1").Return("key123") mbi := im.blockchain.(*blockchainmocks.Plugin) mbi.On("NormalizeSigningKey", ctx, "key123").Return("fullkey123", nil) - orgID := fftypes.NewUUID() + resolvedKey, err := im.NormalizeSigningKey(ctx, "ns1", "", KeyNormalizationBlockchainPlugin) + assert.NoError(t, err) + assert.Equal(t, "fullkey123", resolvedKey) - mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "fullkey123"). - Return((&core.Verifier{ - Identity: orgID, - Namespace: core.SystemNamespace, - VerifierRef: core.VerifierRef{ - Type: core.VerifierTypeEthAddress, - Value: "fullkey123", - }, - }).Seal(), nil) - mdi.On("GetIdentityByID", ctx, orgID). - Return(&core.Identity{ - IdentityBase: core.IdentityBase{ - ID: orgID, - DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, - Name: "org1", - Type: core.IdentityTypeOrg, - }, - }, nil) + mbi.AssertExpectations(t) + mns.AssertExpectations(t) + +} - resolvedKey, err := im.NormalizeSigningKey(ctx, "", KeyNormalizationBlockchainPlugin) +func TestNormalizeSigningKeyOrgFallbackOk(t *testing.T) { + + ctx, im := newTestIdentityManager(t) + + mns := im.namespace.(*namespacemocks.Manager) + mns.On("GetDefaultKey", "ns1").Return("") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgKey).Return("key123") + + mbi := im.blockchain.(*blockchainmocks.Plugin) + mbi.On("NormalizeSigningKey", ctx, "key123").Return("fullkey123", nil) + + resolvedKey, err := im.NormalizeSigningKey(ctx, "ns1", "", KeyNormalizationBlockchainPlugin) assert.NoError(t, err) assert.Equal(t, "fullkey123", resolvedKey) mbi.AssertExpectations(t) - mdi.AssertExpectations(t) + mns.AssertExpectations(t) } func TestNormalizeSigningKeyOrgFallbackErr(t *testing.T) { ctx, im := newTestIdentityManager(t) - config.Set(coreconfig.OrgKey, "key123") - config.Set(coreconfig.OrgName, "org1") - mbi := im.blockchain.(*blockchainmocks.Plugin) - mbi.On("NormalizeSigningKey", ctx, "key123").Return("fullkey123", nil) + mns := im.namespace.(*namespacemocks.Manager) + mns.On("GetDefaultKey", "ns1").Return("") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgKey).Return("key123") - mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "fullkey123"). - Return(nil, fmt.Errorf("pop")) + mbi := im.blockchain.(*blockchainmocks.Plugin) + mbi.On("NormalizeSigningKey", ctx, "key123").Return("fullkey123", fmt.Errorf("pop")) - _, err := im.NormalizeSigningKey(ctx, "", KeyNormalizationBlockchainPlugin) + _, err := im.NormalizeSigningKey(ctx, "ns1", "", KeyNormalizationBlockchainPlugin) assert.Regexp(t, "pop", err) mbi.AssertExpectations(t) - mdi.AssertExpectations(t) + mns.AssertExpectations(t) } func TestResolveInputSigningKeyOk(t *testing.T) { ctx, im := newTestIdentityManager(t) - config.Set(coreconfig.OrgKey, "key123") - config.Set(coreconfig.OrgName, "org1") mbi := im.blockchain.(*blockchainmocks.Plugin) mbi.On("NormalizeSigningKey", ctx, "key123").Return("fullkey123", nil) - resolvedKey, err := im.NormalizeSigningKey(ctx, "key123", KeyNormalizationBlockchainPlugin) + resolvedKey, err := im.NormalizeSigningKey(ctx, "ns1", "key123", KeyNormalizationBlockchainPlugin) assert.NoError(t, err) assert.Equal(t, "fullkey123", resolvedKey) @@ -507,13 +518,11 @@ func TestResolveInputSigningKeyOk(t *testing.T) { func TestResolveInputSigningKeyFail(t *testing.T) { ctx, im := newTestIdentityManager(t) - config.Set(coreconfig.OrgKey, "key123") - config.Set(coreconfig.OrgName, "org1") mbi := im.blockchain.(*blockchainmocks.Plugin) mbi.On("NormalizeSigningKey", ctx, "key123").Return("", fmt.Errorf("pop")) - _, err := im.NormalizeSigningKey(ctx, "key123", KeyNormalizationBlockchainPlugin) + _, err := im.NormalizeSigningKey(ctx, "ns1", "key123", KeyNormalizationBlockchainPlugin) assert.Regexp(t, "pop", err) mbi.AssertExpectations(t) @@ -522,10 +531,8 @@ func TestResolveInputSigningKeyFail(t *testing.T) { func TestResolveInputSigningKeyBypass(t *testing.T) { ctx, im := newTestIdentityManager(t) - config.Set(coreconfig.OrgKey, "key123") - config.Set(coreconfig.OrgName, "org1") - key, err := im.NormalizeSigningKey(ctx, "different-type-of-key", KeyNormalizationNone) + key, err := im.NormalizeSigningKey(ctx, "ns1", "different-type-of-key", KeyNormalizationNone) assert.NoError(t, err) assert.Equal(t, "different-type-of-key", key) } @@ -538,7 +545,7 @@ func TestFirstVerifierForIdentityNotFound(t *testing.T) { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "myid", Type: core.IdentityTypeOrg, }, @@ -555,34 +562,124 @@ func TestFirstVerifierForIdentityNotFound(t *testing.T) { } -func TestResolveNodeOwnerSigningIdentityNotFound(t *testing.T) { +func TestResolveDefaultSigningIdentityNotFound(t *testing.T) { + + ctx, im := newTestIdentityManager(t) + im.multipartyRootVerifier["ns1"] = &core.VerifierRef{ + Type: core.VerifierTypeEthAddress, + Value: "key12345", + } + + mbi := im.blockchain.(*blockchainmocks.Plugin) + mbi.On("NetworkVersion", ctx).Return(1, nil) + + mdi := im.database.(*databasemocks.Plugin) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "key12345").Return(nil, nil) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.LegacySystemNamespace, "key12345").Return(nil, nil) + + mns := im.namespace.(*namespacemocks.Manager) + mns.On("GetDefaultKey", "ns1").Return("") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgName).Return("org1") + + err := im.resolveDefaultSigningIdentity(ctx, "ns1", &core.SignerRef{}) + assert.Regexp(t, "FF10281", err) + + mbi.AssertExpectations(t) + mdi.AssertExpectations(t) + mns.AssertExpectations(t) + +} + +func TestResolveDefaultSigningIdentityVersionError(t *testing.T) { ctx, im := newTestIdentityManager(t) - im.nodeOwnerBlockchainKey = &core.VerifierRef{ + im.multipartyRootVerifier["ns1"] = &core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: "key12345", } - config.Set(coreconfig.OrgName, "org1") + + mbi := im.blockchain.(*blockchainmocks.Plugin) + mbi.On("NetworkVersion", ctx).Return(1, fmt.Errorf("pop")) mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "key12345").Return(nil, nil) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "key12345").Return(nil, nil) + + mns := im.namespace.(*namespacemocks.Manager) + mns.On("GetDefaultKey", "ns1").Return("") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgName).Return("org1") - err := im.ResolveNodeOwnerSigningIdentity(ctx, &core.SignerRef{}) + err := im.resolveDefaultSigningIdentity(ctx, "ns1", &core.SignerRef{}) assert.Regexp(t, "FF10281", err) + mbi.AssertExpectations(t) mdi.AssertExpectations(t) + mns.AssertExpectations(t) } -func TestGetNodeOwnerBlockchainKeyDeprecatedKeyResolveFailed(t *testing.T) { +func TestResolveDefaultSigningIdentitySystemFallback(t *testing.T) { ctx, im := newTestIdentityManager(t) - config.Set(coreconfig.OrgIdentityDeprecated, "0x12345") + im.multipartyRootVerifier["ns1"] = &core.VerifierRef{ + Type: core.VerifierTypeEthAddress, + Value: "key12345", + } + + id := &core.Identity{ + IdentityBase: core.IdentityBase{ + ID: fftypes.NewUUID(), + DID: "did:firefly:org/org1", + Namespace: "ns1", + Name: "org1", + Type: core.IdentityTypeOrg, + }, + } + verifier := &core.Verifier{ + Identity: id.ID, + VerifierRef: core.VerifierRef{ + Value: "key12345", + }, + } + + mbi := im.blockchain.(*blockchainmocks.Plugin) + mbi.On("NetworkVersion", ctx).Return(1, nil) + + mdi := im.database.(*databasemocks.Plugin) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "key12345").Return(nil, nil) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.LegacySystemNamespace, "key12345").Return(verifier, nil) + mdi.On("GetIdentityByID", ctx, id.ID).Return(id, nil) + + mns := im.namespace.(*namespacemocks.Manager) + mns.On("GetDefaultKey", "ns1").Return("") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgName).Return("org1") + + ref := &core.SignerRef{} + err := im.resolveDefaultSigningIdentity(ctx, "ns1", ref) + assert.NoError(t, err) + assert.Equal(t, "did:firefly:org/org1", ref.Author) + assert.Equal(t, "key12345", ref.Key) + + mbi.AssertExpectations(t) + mdi.AssertExpectations(t) + mns.AssertExpectations(t) + +} + +func TestGetMultipartyRootVerifierResolveFailed(t *testing.T) { + + ctx, im := newTestIdentityManager(t) + + mnm := im.namespace.(*namespacemocks.Manager) + mnm.On("GetConfigWithFallback", "ns1", coreconfig.OrgKey).Return("") mbi := im.blockchain.(*blockchainmocks.Plugin) mbi.On("NormalizeSigningKey", ctx, "0x12345").Return("", fmt.Errorf("pop")) - _, err := im.GetNodeOwnerBlockchainKey(ctx) + mns := im.namespace.(*namespacemocks.Manager) + mns.On("GetDefaultKey", "ns1").Return("") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgKey).Return("0x12345") + + _, err := im.GetMultipartyRootVerifier(ctx, "ns1") assert.Regexp(t, "pop", err) mbi.AssertExpectations(t) @@ -618,41 +715,46 @@ func TestNormalizeKeyViaBlockchainPluginCached(t *testing.T) { } -func TestGetNodeOwnerOrgCached(t *testing.T) { +func TestGetMultipartyRootOrgCached(t *testing.T) { ctx, im := newTestIdentityManager(t) - im.nodeOwningOrgIdentity = &core.Identity{} + im.multipartyRootOrg = map[string]*core.Identity{ + "ns1": {}, + } - id, err := im.GetNodeOwnerOrg(ctx) + id, err := im.GetMultipartyRootOrg(ctx, "ns1") assert.NoError(t, err) assert.NotNil(t, id) } -func TestGetNodeOwnerOrgKeyNotSet(t *testing.T) { +func TestGetMultipartyRootVerifierNotSet(t *testing.T) { ctx, im := newTestIdentityManager(t) - _, err := im.GetNodeOwnerOrg(ctx) + mns := im.namespace.(*namespacemocks.Manager) + mns.On("GetDefaultKey", "ns1").Return("") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgKey).Return("") + + _, err := im.GetMultipartyRootOrg(ctx, "ns1") assert.Regexp(t, "FF10354", err) } -func TestGetNodeOwnerOrgMismatch(t *testing.T) { +func TestGetMultipartyRootOrgMismatch(t *testing.T) { ctx, im := newTestIdentityManager(t) - im.nodeOwnerBlockchainKey = &core.VerifierRef{ + im.multipartyRootVerifier["ns1"] = &core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: "fullkey123", } - config.Set(coreconfig.OrgName, "org1") orgID := fftypes.NewUUID() mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "fullkey123"). + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123"). Return((&core.Verifier{ Identity: orgID, - Namespace: core.SystemNamespace, + Namespace: "ns1", VerifierRef: core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: "fullkey123", @@ -663,13 +765,17 @@ func TestGetNodeOwnerOrgMismatch(t *testing.T) { IdentityBase: core.IdentityBase{ ID: orgID, DID: "did:firefly:org/org2", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org2", Type: core.IdentityTypeOrg, }, }, nil) - _, err := im.GetNodeOwnerOrg(ctx) + mns := im.namespace.(*namespacemocks.Manager) + mns.On("GetDefaultKey", "ns1").Return("") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgName).Return("org1") + + _, err := im.GetMultipartyRootOrg(ctx, "ns1") assert.Regexp(t, "FF10281", err) } @@ -682,16 +788,16 @@ func TestCachedIdentityLookupByVerifierRefCaching(t *testing.T) { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), DID: "did:firefly:node/peer1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "peer1", Type: core.IdentityTypeOrg, }, } mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeFFDXPeerID, core.SystemNamespace, "peer1"). + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeFFDXPeerID, "ns1", "peer1"). Return((&core.Verifier{ Identity: id.ID, - Namespace: core.SystemNamespace, + Namespace: "ns1", VerifierRef: core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", @@ -700,14 +806,14 @@ func TestCachedIdentityLookupByVerifierRefCaching(t *testing.T) { mdi.On("GetIdentityByID", ctx, id.ID). Return(id, nil) - v1, err := im.cachedIdentityLookupByVerifierRef(ctx, core.SystemNamespace, &core.VerifierRef{ + v1, err := im.cachedIdentityLookupByVerifierRef(ctx, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }) assert.NoError(t, err) assert.Equal(t, id, v1) - v2, err := im.cachedIdentityLookupByVerifierRef(ctx, core.SystemNamespace, &core.VerifierRef{ + v2, err := im.cachedIdentityLookupByVerifierRef(ctx, "ns1", &core.VerifierRef{ Type: core.VerifierTypeFFDXPeerID, Value: "peer1", }) @@ -724,16 +830,16 @@ func TestCachedIdentityLookupByVerifierRefError(t *testing.T) { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), DID: "did:firefly:node/peer1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "peer1", Type: core.IdentityTypeOrg, }, } mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "peer1"). + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "peer1"). Return((&core.Verifier{ Identity: id.ID, - Namespace: core.SystemNamespace, + Namespace: "ns1", VerifierRef: core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: "peer1", @@ -741,7 +847,7 @@ func TestCachedIdentityLookupByVerifierRefError(t *testing.T) { }).Seal(), nil) mdi.On("GetIdentityByID", ctx, id.ID).Return(nil, fmt.Errorf("pop")) - _, err := im.cachedIdentityLookupByVerifierRef(ctx, core.SystemNamespace, &core.VerifierRef{ + _, err := im.cachedIdentityLookupByVerifierRef(ctx, "ns1", &core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: "peer1", }) @@ -757,16 +863,16 @@ func TestCachedIdentityLookupByVerifierRefNotFound(t *testing.T) { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), DID: "did:firefly:node/peer1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "peer1", Type: core.IdentityTypeOrg, }, } mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, core.SystemNamespace, "0x12345"). + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "0x12345"). Return((&core.Verifier{ Identity: id.ID, - Namespace: core.SystemNamespace, + Namespace: "ns1", VerifierRef: core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: "peer1", @@ -774,7 +880,7 @@ func TestCachedIdentityLookupByVerifierRefNotFound(t *testing.T) { }).Seal(), nil) mdi.On("GetIdentityByID", ctx, id.ID).Return(nil, nil) - _, err := im.cachedIdentityLookupByVerifierRef(ctx, core.SystemNamespace, &core.VerifierRef{ + _, err := im.cachedIdentityLookupByVerifierRef(ctx, "ns1", &core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: "0x12345", }) @@ -790,7 +896,7 @@ func TestCachedIdentityLookupMustExistCaching(t *testing.T) { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), DID: "did:firefly:node/peer1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "peer1", Type: core.IdentityTypeOrg, }, @@ -798,11 +904,11 @@ func TestCachedIdentityLookupMustExistCaching(t *testing.T) { mdi := im.database.(*databasemocks.Plugin) mdi.On("GetIdentityByDID", ctx, "did:firefly:node/peer1").Return(id, nil).Once() - v1, _, err := im.CachedIdentityLookupMustExist(ctx, "did:firefly:node/peer1") + v1, _, err := im.CachedIdentityLookupMustExist(ctx, "ns1", "did:firefly:node/peer1") assert.NoError(t, err) assert.Equal(t, id, v1) - v2, _, err := im.CachedIdentityLookupMustExist(ctx, "did:firefly:node/peer1") + v2, _, err := im.CachedIdentityLookupMustExist(ctx, "ns1", "did:firefly:node/peer1") assert.NoError(t, err) assert.Equal(t, id, v2) } @@ -811,7 +917,7 @@ func TestCachedIdentityLookupMustExistUnknownResolver(t *testing.T) { ctx, im := newTestIdentityManager(t) - _, retryable, err := im.CachedIdentityLookupMustExist(ctx, "did:random:anything") + _, retryable, err := im.CachedIdentityLookupMustExist(ctx, "ns1", "did:random:anything") assert.Regexp(t, "FF10349", err) assert.False(t, retryable) @@ -824,7 +930,7 @@ func TestCachedIdentityLookupMustExistGetIDFail(t *testing.T) { mdi := im.database.(*databasemocks.Plugin) mdi.On("GetIdentityByDID", ctx, "did:firefly:node/peer1").Return(nil, fmt.Errorf("pop")) - _, retryable, err := im.CachedIdentityLookupMustExist(ctx, "did:firefly:node/peer1") + _, retryable, err := im.CachedIdentityLookupMustExist(ctx, "ns1", "did:firefly:node/peer1") assert.Regexp(t, "pop", err) assert.True(t, retryable) @@ -843,7 +949,7 @@ func TestCachedIdentityLookupByVerifierByOldDIDFail(t *testing.T) { return uuid.Equals(orgUUID) })).Return(nil, fmt.Errorf("pop")) - _, retryable, err := im.CachedIdentityLookupMustExist(ctx, did) + _, retryable, err := im.CachedIdentityLookupMustExist(ctx, "ns1", did) assert.Regexp(t, "pop", err) assert.True(t, retryable) @@ -857,7 +963,7 @@ func TestCachedIdentityLookupByIDCaching(t *testing.T) { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), DID: "did:firefly:node/peer1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "peer1", Type: core.IdentityTypeOrg, }, @@ -884,7 +990,7 @@ func TestVerifyIdentityChainCustomOrgOrgOk(t *testing.T) { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org1", Type: core.IdentityTypeOrg, }, @@ -897,7 +1003,7 @@ func TestVerifyIdentityChainCustomOrgOrgOk(t *testing.T) { ID: fftypes.NewUUID(), Parent: idRoot.ID, DID: "did:firefly:org/org2", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org2", Type: core.IdentityTypeOrg, }, @@ -965,7 +1071,7 @@ func TestVerifyIdentityChainLoop(t *testing.T) { ID: idID1, Parent: idID2, DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org1", Type: core.IdentityTypeOrg, }, @@ -978,7 +1084,7 @@ func TestVerifyIdentityChainLoop(t *testing.T) { ID: idID2, Parent: idID1, DID: "did:firefly:org/org2", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org2", Type: core.IdentityTypeOrg, }, @@ -1008,7 +1114,7 @@ func TestVerifyIdentityChainBadParent(t *testing.T) { ID: idID1, Parent: idID2, DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org1", Type: core.IdentityTypeOrg, }, @@ -1017,7 +1123,7 @@ func TestVerifyIdentityChainBadParent(t *testing.T) { IdentityBase: core.IdentityBase{ ID: idID2, DID: "did:firefly:org/org2", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org2", Type: core.IdentityTypeOrg, }, @@ -1043,7 +1149,7 @@ func TestVerifyIdentityChainErr(t *testing.T) { ID: fftypes.NewUUID(), Parent: idID2, DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org1", Type: core.IdentityTypeOrg, }, @@ -1069,7 +1175,7 @@ func TestVerifyIdentityChainNotFound(t *testing.T) { ID: fftypes.NewUUID(), Parent: idID2, DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org1", Type: core.IdentityTypeOrg, }, @@ -1104,7 +1210,7 @@ func TestVerifyIdentityChainInvalidParent(t *testing.T) { ID: fftypes.NewUUID(), Parent: id1.ID, DID: "did:firefly:org/org2", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org2", Type: core.IdentityTypeOrg, }, @@ -1165,7 +1271,7 @@ func TestCachedVerifierLookupCaching(t *testing.T) { ctx, im := newTestIdentityManager(t) verifier := (&core.Verifier{ - Namespace: core.SystemNamespace, + Namespace: "ns1", VerifierRef: core.VerifierRef{ Value: "peer1", Type: core.VerifierTypeFFDXPeerID, @@ -1174,11 +1280,11 @@ func TestCachedVerifierLookupCaching(t *testing.T) { mdi := im.database.(*databasemocks.Plugin) mdi.On("GetVerifierByValue", ctx, verifier.Type, verifier.Namespace, verifier.Value).Return(verifier, nil).Once() - v1, err := im.CachedVerifierLookup(ctx, core.VerifierTypeFFDXPeerID, core.SystemNamespace, "peer1") + v1, err := im.CachedVerifierLookup(ctx, core.VerifierTypeFFDXPeerID, "ns1", "peer1") assert.NoError(t, err) assert.Equal(t, verifier, v1) - v2, err := im.CachedVerifierLookup(ctx, core.VerifierTypeFFDXPeerID, core.SystemNamespace, "peer1") + v2, err := im.CachedVerifierLookup(ctx, core.VerifierTypeFFDXPeerID, "ns1", "peer1") assert.NoError(t, err) assert.Equal(t, verifier, v2) @@ -1190,9 +1296,9 @@ func TestCachedVerifierLookupError(t *testing.T) { ctx, im := newTestIdentityManager(t) mdi := im.database.(*databasemocks.Plugin) - mdi.On("GetVerifierByValue", ctx, core.VerifierTypeFFDXPeerID, core.SystemNamespace, "peer1").Return(nil, fmt.Errorf("pop")) + mdi.On("GetVerifierByValue", ctx, core.VerifierTypeFFDXPeerID, "ns1", "peer1").Return(nil, fmt.Errorf("pop")) - _, err := im.CachedVerifierLookup(ctx, core.VerifierTypeFFDXPeerID, core.SystemNamespace, "peer1") + _, err := im.CachedVerifierLookup(ctx, core.VerifierTypeFFDXPeerID, "ns1", "peer1") assert.Regexp(t, "pop", err) mdi.AssertExpectations(t) @@ -1215,7 +1321,7 @@ func TestResolveIdentitySignerOk(t *testing.T) { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org1", Type: core.IdentityTypeOrg, }, @@ -1240,7 +1346,7 @@ func TestResolveIdentitySignerFail(t *testing.T) { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org1", Type: core.IdentityTypeOrg, }, @@ -1264,7 +1370,7 @@ func TestResolveIdentitySignerNotFound(t *testing.T) { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), DID: "did:firefly:org/org1", - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: "org1", Type: core.IdentityTypeOrg, }, diff --git a/internal/namespace/config.go b/internal/namespace/config.go index c6b5e7385..762f71aca 100644 --- a/internal/namespace/config.go +++ b/internal/namespace/config.go @@ -36,7 +36,11 @@ func InitConfig(withDefaults bool) { namespacePredefined.AddKnownKey(coreconfig.NamespaceDescription) namespacePredefined.AddKnownKey(coreconfig.NamespaceRemoteName) namespacePredefined.AddKnownKey(coreconfig.NamespacePlugins) - namespacePredefined.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + namespacePredefined.AddKnownKey(coreconfig.NamespaceDefaultKey) + namespacePredefined.AddKnownKey(coreconfig.NamespaceMultipartyEnabled) + namespacePredefined.AddKnownKey(coreconfig.NamespaceMultipartyOrgName) + namespacePredefined.AddKnownKey(coreconfig.NamespaceMultipartyOrgDescription) + namespacePredefined.AddKnownKey(coreconfig.NamespaceMultipartyOrgKey) if withDefaults { namespaceConfig.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceName, "default") namespaceConfig.AddKnownKey(NamespacePredefined+".0."+coreconfig.NamespaceDescription, "Default predefined namespace") diff --git a/internal/namespace/manager.go b/internal/namespace/manager.go index 4d7c4514c..1a12d3e6d 100644 --- a/internal/namespace/manager.go +++ b/internal/namespace/manager.go @@ -37,6 +37,10 @@ import ( type Manager interface { // Init initializes the manager Init(ctx context.Context, di database.Plugin) error + // GetDefaultKey retrieves the default blockchain key for this namespace + GetDefaultKey(ns string) string + // GetMultipartyConfig retrieves a config string from the namespace's multiparty config or the (deprecated) root config + GetMultipartyConfig(ns string, key config.RootKey) string } type namespaceManager struct { @@ -65,7 +69,8 @@ func NewNamespaceManager(ctx context.Context, bc map[string]blockchain.Plugin, d func buildNamespaceMap(ctx context.Context) map[string]config.Section { conf := namespacePredefined namespaces := make(map[string]config.Section, conf.ArraySize()) - for i := 0; i < conf.ArraySize(); i++ { + size := conf.ArraySize() + for i := 0; i < size; i++ { nsConfig := conf.ArrayEntry(i) name := nsConfig.GetString(coreconfig.NamespaceName) if name != "" { @@ -86,7 +91,7 @@ func (nm *namespaceManager) getPredefinedNamespaces(ctx context.Context) ([]*cor defaultNS := config.GetString(coreconfig.NamespacesDefault) namespaces := []*core.Namespace{ { - Name: core.SystemNamespace, + Name: core.LegacySystemNamespace, Type: core.NamespaceTypeSystem, Description: i18n.Expand(ctx, coremsgs.CoreSystemNSDescription), }, @@ -139,19 +144,40 @@ func (nm *namespaceManager) initNamespaces(ctx context.Context, di database.Plug return nil } +func (nm *namespaceManager) GetDefaultKey(ns string) string { + if nsConfig, ok := nm.nsConfig[ns]; ok { + return nsConfig.GetString(coreconfig.NamespaceDefaultKey) + } + return "" +} + +func (nm *namespaceManager) GetMultipartyConfig(ns string, key config.RootKey) string { + if nsConfig, ok := nm.nsConfig[ns]; ok { + val := nsConfig.SubSection("multiparty").GetString(string(key)) + if val != "" { + return val + } + } + return config.GetString(key) +} + func (nm *namespaceManager) validateNamespaceConfig(ctx context.Context, name string, index int, conf config.Section) error { if err := core.ValidateFFNameField(ctx, name, fmt.Sprintf("namespaces.predefined[%d].name", index)); err != nil { return err } - if name == core.SystemNamespace || conf.GetString(coreconfig.NamespaceRemoteName) == core.SystemNamespace { - return i18n.NewError(ctx, coremsgs.MsgFFSystemReservedName, core.SystemNamespace) + if name == core.LegacySystemNamespace || conf.GetString(coreconfig.NamespaceRemoteName) == core.LegacySystemNamespace { + return i18n.NewError(ctx, coremsgs.MsgFFSystemReservedName, core.LegacySystemNamespace) } - mode := conf.GetString(coreconfig.NamespaceMode) - plugins := conf.GetStringSlice(coreconfig.NamespacePlugins) + // If any multiparty org information is configured (here or at the root), assume multiparty mode by default + multiparty := conf.Get(coreconfig.NamespaceMultipartyEnabled) + if multiparty == nil { + multiparty = nm.GetMultipartyConfig(name, coreconfig.OrgName) != "" || nm.GetMultipartyConfig(name, coreconfig.OrgKey) != "" + } - // If no plugins are found when querying the config, assume older config file + // If no plugins are listed under this namespace, use all defined plugins by default + plugins := conf.GetStringSlice(coreconfig.NamespacePlugins) if len(plugins) == 0 { for plugin := range nm.bcPlugins { plugins = append(plugins, plugin) @@ -170,20 +196,10 @@ func (nm *namespaceManager) validateNamespaceConfig(ctx context.Context, name st } } - switch mode { - // Multiparty is the default mode when none is provided - case "multiparty": - if err := nm.validateMultiPartyConfig(ctx, name, plugins); err != nil { - return err - } - case "gateway": - if err := nm.validateGatewayConfig(ctx, name, plugins); err != nil { - return err - } - default: - return i18n.NewError(ctx, coremsgs.MsgInvalidNamespaceMode, name) + if multiparty.(bool) { + return nm.validateMultiPartyConfig(ctx, name, plugins) } - return nil + return nm.validateGatewayConfig(ctx, name, plugins) } func (nm *namespaceManager) validateMultiPartyConfig(ctx context.Context, name string, plugins []string) error { diff --git a/internal/namespace/manager_test.go b/internal/namespace/manager_test.go index f6b35a66b..32e316b22 100644 --- a/internal/namespace/manager_test.go +++ b/internal/namespace/manager_test.go @@ -102,7 +102,7 @@ func TestInitNamespacesBadName(t *testing.T) { utTestConfig.AddKnownKey(coreconfig.NamespaceName, "!Badness") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "test") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, true) utTestConfig.AddKnownKey(coreconfig.NamespaceDescription, "test description") utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "ffdx", "ipfs", "erc721"}) nm.nsConfig = map[string]config.Section{"!Badness": utTestConfig} @@ -120,7 +120,7 @@ func TestInitNamespacesReservedName(t *testing.T) { utTestConfig.AddKnownKey(coreconfig.NamespaceName, "ff_system") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "test") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, true) utTestConfig.AddKnownKey(coreconfig.NamespaceDescription, "test description") utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "ffdx", "ipfs", "erc721"}) nm.nsConfig = map[string]config.Section{"ff_system": utTestConfig} @@ -136,7 +136,7 @@ func TestInitNamespacesGetFail(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, true) utTestConfig.AddKnownKey(coreconfig.NamespaceDescription, "test description") utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "ffdx", "ipfs", "erc721"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -153,7 +153,7 @@ func TestInitNamespacesUpsertFail(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, true) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "ffdx", "ipfs", "erc721"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -170,7 +170,7 @@ func TestInitNamespacesMultipartyUnknownPlugin(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, true) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "ffdx", "ipfs", "erc721", "bad_unknown_plugin"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -185,7 +185,7 @@ func TestInitNamespacesMultipartyMultipleBlockchains(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, true) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "ffdx", "ipfs", "erc721", "fabric"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -200,7 +200,7 @@ func TestInitNamespacesMultipartyMultipleDX(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, true) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "ffdx", "ipfs", "erc721", "ffdx2"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -215,7 +215,7 @@ func TestInitNamespacesMultipartyMultipleSS(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, true) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "ffdx", "ipfs", "erc721", "ipfs2"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -230,7 +230,7 @@ func TestInitNamespacesMultipartyMultipleDB(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, true) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "ffdx", "ipfs", "erc721", "sqlite3"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -245,7 +245,7 @@ func TestInitNamespacesDeprecatedConfigMultipleBlockchains(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, true) utTestConfig.AddKnownKey(coreconfig.NamespaceDescription) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -264,7 +264,7 @@ func TestInitNamespacesGatewayMultipleDB(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "gateway") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, false) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "sqlite3"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -279,7 +279,7 @@ func TestInitNamespacesGatewayMultipleBlockchains(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "gateway") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, false) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "fabric"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -294,7 +294,7 @@ func TestInitNamespacesMultipartyMissingPlugins(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, true) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -309,7 +309,7 @@ func TestInitNamespacesGatewayWithDX(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "gateway") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, false) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "ffdx"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -324,7 +324,7 @@ func TestInitNamespacesGatewayWithSharedStorage(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "gateway") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, false) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "ipfs"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -339,7 +339,7 @@ func TestInitNamespacesGatewayUnknownPlugin(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "gateway") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, false) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "bad_unknown_plugin"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -354,7 +354,7 @@ func TestInitNamespacesGatewayNoDB(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "gateway") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, false) utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -362,21 +362,6 @@ func TestInitNamespacesGatewayNoDB(t *testing.T) { assert.Regexp(t, "FF10392", err) } -func TestInitNamespacesUnknownMode(t *testing.T) { - nm := newTestNamespaceManager(true) - defer nm.cleanup(t) - - utTestConfig := config.RootSection("default") - utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "not a real mode") - utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "ffdx", "ipfs", "erc721"}) - nm.nsConfig = map[string]config.Section{"default": utTestConfig} - - err := nm.initNamespaces(context.Background(), nm.mdi) - assert.Regexp(t, "FF10389", err) -} - func TestInitNamespacesUpsertNotNeeded(t *testing.T) { nm := newTestNamespaceManager(true) defer nm.cleanup(t) @@ -384,7 +369,7 @@ func TestInitNamespacesUpsertNotNeeded(t *testing.T) { utTestConfig := config.RootSection("default") utTestConfig.AddKnownKey(coreconfig.NamespaceName, "default") utTestConfig.AddKnownKey(coreconfig.NamespaceRemoteName, "default") - utTestConfig.AddKnownKey(coreconfig.NamespaceMode, "multiparty") + utTestConfig.AddKnownKey(coreconfig.NamespaceMultipartyEnabled, true) utTestConfig.AddKnownKey(coreconfig.NamespaceDescription, "test description") utTestConfig.AddKnownKey(coreconfig.NamespacePlugins, []string{"ethereum", "postgres", "ffdx", "ipfs", "erc721"}) nm.nsConfig = map[string]config.Section{"default": utTestConfig} @@ -418,27 +403,15 @@ func TestInitNamespacesDupName(t *testing.T) { predefined: - name: ns1 remoteName: ns1 - mode: gateway - org: - key: 0x123456 - plugins: - - sqlite3 - - ethereum - - erc721 + plugins: [sqlite3, ethereum, erc721] - name: ns2 remoteName: ns2 - mode: gateway - org: - key: 0x223456 plugins: - sqlite3 - ethereum - erc721 - name: ns2 remoteName: ns2 - mode: gateway - org: - key: 0x223456 plugins: - sqlite3 - ethereum @@ -456,7 +429,53 @@ func TestInitNamespacesDupName(t *testing.T) { for i, ns := range nsList { names[i] = ns.Name } - assert.Contains(t, names, core.SystemNamespace) + assert.Contains(t, names, core.LegacySystemNamespace) assert.Contains(t, names, "ns1") assert.Contains(t, names, "ns2") } + +func TestGetConfig(t *testing.T) { + coreconfig.Reset() + namespaceConfig.AddKnownKey("predefined.0."+coreconfig.NamespaceName, "ns1") + namespaceConfig.AddKnownKey("predefined.0."+coreconfig.NamespaceMultipartyOrgKey, "0x12345") + + nm := newTestNamespaceManager(false) + defer nm.cleanup(t) + + key := nm.GetMultipartyConfig("ns1", coreconfig.OrgKey) + assert.Equal(t, "0x12345", key) +} + +func TestGetConfigFallback(t *testing.T) { + coreconfig.Reset() + namespaceConfig.AddKnownKey("predefined.0."+coreconfig.NamespaceName, "ns1") + config.Set(coreconfig.OrgKey, "0x123") + + nm := newTestNamespaceManager(false) + defer nm.cleanup(t) + + key := nm.GetMultipartyConfig("ns1", coreconfig.OrgKey) + assert.Equal(t, "0x123", key) +} + +func TestGetDefaultKey(t *testing.T) { + coreconfig.Reset() + namespaceConfig.AddKnownKey("predefined.0."+coreconfig.NamespaceName, "ns1") + namespaceConfig.AddKnownKey("predefined.0."+coreconfig.NamespaceDefaultKey, "0x12345") + + nm := newTestNamespaceManager(false) + defer nm.cleanup(t) + + key := nm.GetDefaultKey("ns1") + assert.Equal(t, "0x12345", key) +} + +func TestGetDefaultKeyNotFound(t *testing.T) { + coreconfig.Reset() + + nm := newTestNamespaceManager(false) + defer nm.cleanup(t) + + key := nm.GetDefaultKey("ns1") + assert.Equal(t, "", key) +} diff --git a/internal/networkmap/data_query.go b/internal/networkmap/data_query.go index 5c1937e74..5d488155e 100644 --- a/internal/networkmap/data_query.go +++ b/internal/networkmap/data_query.go @@ -28,13 +28,13 @@ import ( "github.com/hyperledger/firefly/pkg/database" ) -func (nm *networkMap) GetOrganizationByNameOrID(ctx context.Context, nameOrID string) (org *core.Identity, err error) { +func (nm *networkMap) GetOrganizationByNameOrID(ctx context.Context, ns, nameOrID string) (org *core.Identity, err error) { u, err := fftypes.ParseUUID(ctx, nameOrID) if err != nil { if err := core.ValidateFFNameField(ctx, nameOrID, "name"); err != nil { return nil, err } - if org, err = nm.database.GetIdentityByName(ctx, core.IdentityTypeOrg, core.SystemNamespace, nameOrID); err != nil { + if org, err = nm.database.GetIdentityByName(ctx, core.IdentityTypeOrg, ns, nameOrID); err != nil { return nil, err } } else if org, err = nm.database.GetIdentityByID(ctx, u); err != nil { @@ -50,23 +50,23 @@ func (nm *networkMap) GetOrganizationByNameOrID(ctx context.Context, nameOrID st return org, nil } -func (nm *networkMap) GetOrganizations(ctx context.Context, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) { +func (nm *networkMap) GetOrganizations(ctx context.Context, ns string, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) { filter.Condition(filter.Builder().Eq("type", core.IdentityTypeOrg)) - return nm.GetIdentities(ctx, core.SystemNamespace, filter) + return nm.GetIdentities(ctx, ns, filter) } -func (nm *networkMap) GetOrganizationsWithVerifiers(ctx context.Context, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) { +func (nm *networkMap) GetOrganizationsWithVerifiers(ctx context.Context, ns string, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) { filter.Condition(filter.Builder().Eq("type", core.IdentityTypeOrg)) - return nm.GetIdentitiesWithVerifiers(ctx, core.SystemNamespace, filter) + return nm.GetIdentitiesWithVerifiers(ctx, ns, filter) } -func (nm *networkMap) GetNodeByNameOrID(ctx context.Context, nameOrID string) (node *core.Identity, err error) { +func (nm *networkMap) GetNodeByNameOrID(ctx context.Context, ns, nameOrID string) (node *core.Identity, err error) { u, err := fftypes.ParseUUID(ctx, nameOrID) if err != nil { if err := core.ValidateFFNameField(ctx, nameOrID, "name"); err != nil { return nil, err } - if node, err = nm.database.GetIdentityByName(ctx, core.IdentityTypeNode, core.SystemNamespace, nameOrID); err != nil { + if node, err = nm.database.GetIdentityByName(ctx, core.IdentityTypeNode, ns, nameOrID); err != nil { return nil, err } } else if node, err = nm.database.GetIdentityByID(ctx, u); err != nil { @@ -82,9 +82,9 @@ func (nm *networkMap) GetNodeByNameOrID(ctx context.Context, nameOrID string) (n return node, nil } -func (nm *networkMap) GetNodes(ctx context.Context, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) { +func (nm *networkMap) GetNodes(ctx context.Context, ns string, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) { filter.Condition(filter.Builder().Eq("type", core.IdentityTypeNode)) - filter.Condition(filter.Builder().Eq("namespace", core.SystemNamespace)) + filter.Condition(filter.Builder().Eq("namespace", ns)) return nm.database.GetIdentities(ctx, filter) } @@ -131,16 +131,16 @@ func (nm *networkMap) GetIdentityByIDWithVerifiers(ctx context.Context, ns, id s return nm.withVerifiers(ctx, identity) } -func (nm *networkMap) GetIdentityByDID(ctx context.Context, did string) (*core.Identity, error) { - identity, _, err := nm.identity.CachedIdentityLookupMustExist(ctx, did) +func (nm *networkMap) GetIdentityByDID(ctx context.Context, ns, did string) (*core.Identity, error) { + identity, _, err := nm.identity.CachedIdentityLookupMustExist(ctx, ns, did) if err != nil { return nil, err } return identity, nil } -func (nm *networkMap) GetIdentityByDIDWithVerifiers(ctx context.Context, did string) (*core.IdentityWithVerifiers, error) { - identity, _, err := nm.identity.CachedIdentityLookupMustExist(ctx, did) +func (nm *networkMap) GetIdentityByDIDWithVerifiers(ctx context.Context, ns, did string) (*core.IdentityWithVerifiers, error) { + identity, _, err := nm.identity.CachedIdentityLookupMustExist(ctx, ns, did) if err != nil { return nil, err } @@ -152,16 +152,12 @@ func (nm *networkMap) GetIdentities(ctx context.Context, ns string, filter datab return nm.database.GetIdentities(ctx, filter) } -func (nm *networkMap) GetIdentitiesGlobal(ctx context.Context, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) { - return nm.database.GetIdentities(ctx, filter) -} - func (nm *networkMap) GetIdentitiesWithVerifiers(ctx context.Context, ns string, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) { filter.Condition(filter.Builder().Eq("namespace", ns)) - return nm.GetIdentitiesWithVerifiersGlobal(ctx, filter) + return nm.getIdentitiesWithVerifiersGlobal(ctx, filter) } -func (nm *networkMap) GetIdentitiesWithVerifiersGlobal(ctx context.Context, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) { +func (nm *networkMap) getIdentitiesWithVerifiersGlobal(ctx context.Context, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) { identities, res, err := nm.database.GetIdentities(ctx, filter) if err != nil { return nil, nil, err @@ -210,8 +206,8 @@ func (nm *networkMap) GetDIDDocForIndentityByID(ctx context.Context, ns, id stri return nm.generateDIDDocument(ctx, identity) } -func (nm *networkMap) GetDIDDocForIndentityByDID(ctx context.Context, did string) (*DIDDocument, error) { - identity, err := nm.GetIdentityByDID(ctx, did) +func (nm *networkMap) GetDIDDocForIndentityByDID(ctx context.Context, ns, did string) (*DIDDocument, error) { + identity, err := nm.GetIdentityByDID(ctx, ns, did) if err != nil { return nil, err } diff --git a/internal/networkmap/data_query_test.go b/internal/networkmap/data_query_test.go index 3751f2db3..c307a4f08 100644 --- a/internal/networkmap/data_query_test.go +++ b/internal/networkmap/data_query_test.go @@ -35,7 +35,7 @@ func TestGetOrganizationByNameOrIDOk(t *testing.T) { id := fftypes.NewUUID() nm.database.(*databasemocks.Plugin).On("GetIdentityByID", nm.ctx, id). Return(&core.Identity{IdentityBase: core.IdentityBase{ID: id, Type: core.IdentityTypeOrg}}, nil) - res, err := nm.GetOrganizationByNameOrID(nm.ctx, id.String()) + res, err := nm.GetOrganizationByNameOrID(nm.ctx, "ns", id.String()) assert.NoError(t, err) assert.Equal(t, *id, *res.ID) } @@ -46,7 +46,7 @@ func TestGetOrganizationByNameOrIDNotOrg(t *testing.T) { id := fftypes.NewUUID() nm.database.(*databasemocks.Plugin).On("GetIdentityByID", nm.ctx, id). Return(&core.Identity{IdentityBase: core.IdentityBase{ID: id, Type: core.IdentityTypeNode}}, nil) - res, err := nm.GetOrganizationByNameOrID(nm.ctx, id.String()) + res, err := nm.GetOrganizationByNameOrID(nm.ctx, "ns", id.String()) assert.NoError(t, err) assert.Nil(t, res) } @@ -56,7 +56,7 @@ func TestGetOrganizationByNameOrIDNotFound(t *testing.T) { defer cancel() id := fftypes.NewUUID() nm.database.(*databasemocks.Plugin).On("GetIdentityByID", nm.ctx, id).Return(nil, nil) - _, err := nm.GetOrganizationByNameOrID(nm.ctx, id.String()) + _, err := nm.GetOrganizationByNameOrID(nm.ctx, "ns", id.String()) assert.Regexp(t, "FF10109", err) } @@ -65,22 +65,22 @@ func TestGetOrganizationByNameOrIDError(t *testing.T) { defer cancel() id := fftypes.NewUUID() nm.database.(*databasemocks.Plugin).On("GetIdentityByID", nm.ctx, id).Return(nil, fmt.Errorf("pop")) - _, err := nm.GetOrganizationByNameOrID(nm.ctx, id.String()) + _, err := nm.GetOrganizationByNameOrID(nm.ctx, "ns", id.String()) assert.Regexp(t, "pop", err) } func TestGetOrganizationByNameBadName(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() - _, err := nm.GetOrganizationByNameOrID(nm.ctx, "!bad") + _, err := nm.GetOrganizationByNameOrID(nm.ctx, "ns", "!bad") assert.Regexp(t, "FF00140", err) } func TestGetOrganizationByNameError(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() - nm.database.(*databasemocks.Plugin).On("GetIdentityByName", nm.ctx, core.IdentityTypeOrg, core.SystemNamespace, "bad").Return(nil, fmt.Errorf("pop")) - _, err := nm.GetOrganizationByNameOrID(nm.ctx, "bad") + nm.database.(*databasemocks.Plugin).On("GetIdentityByName", nm.ctx, core.IdentityTypeOrg, "ns", "bad").Return(nil, fmt.Errorf("pop")) + _, err := nm.GetOrganizationByNameOrID(nm.ctx, "ns", "bad") assert.Regexp(t, "pop", err) } @@ -90,7 +90,7 @@ func TestGetNodeByNameOrIDOk(t *testing.T) { id := fftypes.NewUUID() nm.database.(*databasemocks.Plugin).On("GetIdentityByID", nm.ctx, id). Return(&core.Identity{IdentityBase: core.IdentityBase{ID: id, Type: core.IdentityTypeNode}}, nil) - res, err := nm.GetNodeByNameOrID(nm.ctx, id.String()) + res, err := nm.GetNodeByNameOrID(nm.ctx, "ns", id.String()) assert.NoError(t, err) assert.Equal(t, *id, *res.ID) } @@ -101,7 +101,7 @@ func TestGetNodeByNameOrIDWrongType(t *testing.T) { id := fftypes.NewUUID() nm.database.(*databasemocks.Plugin).On("GetIdentityByID", nm.ctx, id). Return(&core.Identity{IdentityBase: core.IdentityBase{ID: id, Type: core.IdentityTypeOrg}}, nil) - res, err := nm.GetNodeByNameOrID(nm.ctx, id.String()) + res, err := nm.GetNodeByNameOrID(nm.ctx, "ns", id.String()) assert.NoError(t, err) assert.Nil(t, res) } @@ -111,7 +111,7 @@ func TestGetNodeByNameOrIDNotFound(t *testing.T) { defer cancel() id := fftypes.NewUUID() nm.database.(*databasemocks.Plugin).On("GetIdentityByID", nm.ctx, id).Return(nil, nil) - _, err := nm.GetNodeByNameOrID(nm.ctx, id.String()) + _, err := nm.GetNodeByNameOrID(nm.ctx, "ns", id.String()) assert.Regexp(t, "FF10109", err) } @@ -120,22 +120,22 @@ func TestGetNodeByNameOrIDError(t *testing.T) { defer cancel() id := fftypes.NewUUID() nm.database.(*databasemocks.Plugin).On("GetIdentityByID", nm.ctx, id).Return(nil, fmt.Errorf("pop")) - _, err := nm.GetNodeByNameOrID(nm.ctx, id.String()) + _, err := nm.GetNodeByNameOrID(nm.ctx, "ns", id.String()) assert.Regexp(t, "pop", err) } func TestGetNodeByNameBadName(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() - _, err := nm.GetNodeByNameOrID(nm.ctx, "!bad") + _, err := nm.GetNodeByNameOrID(nm.ctx, "ns", "!bad") assert.Regexp(t, "FF00140", err) } func TestGetNodeByNameError(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() - nm.database.(*databasemocks.Plugin).On("GetIdentityByName", nm.ctx, core.IdentityTypeNode, core.SystemNamespace, "bad").Return(nil, fmt.Errorf("pop")) - _, err := nm.GetNodeByNameOrID(nm.ctx, "bad") + nm.database.(*databasemocks.Plugin).On("GetIdentityByName", nm.ctx, core.IdentityTypeNode, "ns", "bad").Return(nil, fmt.Errorf("pop")) + _, err := nm.GetNodeByNameOrID(nm.ctx, "ns", "bad") assert.Regexp(t, "pop", err) } @@ -143,7 +143,7 @@ func TestGetOrganizations(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() nm.database.(*databasemocks.Plugin).On("GetIdentities", nm.ctx, mock.Anything).Return([]*core.Identity{}, nil, nil) - res, _, err := nm.GetOrganizations(nm.ctx, database.IdentityQueryFactory.NewFilter(nm.ctx).And()) + res, _, err := nm.GetOrganizations(nm.ctx, "ns", database.IdentityQueryFactory.NewFilter(nm.ctx).And()) assert.NoError(t, err) assert.Empty(t, res) } @@ -152,7 +152,7 @@ func TestGetNodes(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() nm.database.(*databasemocks.Plugin).On("GetIdentities", nm.ctx, mock.Anything).Return([]*core.Identity{}, nil, nil) - res, _, err := nm.GetNodes(nm.ctx, database.IdentityQueryFactory.NewFilter(nm.ctx).And()) + res, _, err := nm.GetNodes(nm.ctx, "ns", database.IdentityQueryFactory.NewFilter(nm.ctx).And()) assert.NoError(t, err) assert.Empty(t, res) } @@ -249,15 +249,6 @@ func TestGetIdentities(t *testing.T) { assert.Empty(t, res) } -func TestGetIdentitiesGlobal(t *testing.T) { - nm, cancel := newTestNetworkmap(t) - defer cancel() - nm.database.(*databasemocks.Plugin).On("GetIdentities", nm.ctx, mock.Anything).Return([]*core.Identity{}, nil, nil) - res, _, err := nm.GetIdentitiesGlobal(nm.ctx, database.IdentityQueryFactory.NewFilter(nm.ctx).And()) - assert.NoError(t, err) - assert.Empty(t, res) -} - func TestGetIdentityVerifiers(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() @@ -338,9 +329,9 @@ func TestGetVerifierByHashBadUUID(t *testing.T) { func TestGetVerifierByDIDOk(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() - nm.identity.(*identitymanagermocks.Manager).On("CachedIdentityLookupMustExist", nm.ctx, "did:firefly:org/abc"). + nm.identity.(*identitymanagermocks.Manager).On("CachedIdentityLookupMustExist", nm.ctx, "ns", "did:firefly:org/abc"). Return(testOrg("abc"), true, nil) - id, err := nm.GetIdentityByDID(nm.ctx, "did:firefly:org/abc") + id, err := nm.GetIdentityByDID(nm.ctx, "ns", "did:firefly:org/abc") assert.NoError(t, err) assert.Equal(t, "did:firefly:org/abc", id.DID) } @@ -348,7 +339,7 @@ func TestGetVerifierByDIDOk(t *testing.T) { func TestGetVerifierByDIDWithVerifiersOk(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() - nm.identity.(*identitymanagermocks.Manager).On("CachedIdentityLookupMustExist", nm.ctx, "did:firefly:org/abc"). + nm.identity.(*identitymanagermocks.Manager).On("CachedIdentityLookupMustExist", nm.ctx, "ns", "did:firefly:org/abc"). Return(testOrg("abc"), true, nil) nm.database.(*databasemocks.Plugin).On("GetVerifiers", nm.ctx, mock.Anything).Return([]*core.Verifier{ {Hash: fftypes.NewRandB32(), VerifierRef: core.VerifierRef{ @@ -356,7 +347,7 @@ func TestGetVerifierByDIDWithVerifiersOk(t *testing.T) { Value: "0x12345", }}, }, nil, nil) - id, err := nm.GetIdentityByDIDWithVerifiers(nm.ctx, "did:firefly:org/abc") + id, err := nm.GetIdentityByDIDWithVerifiers(nm.ctx, "ns", "did:firefly:org/abc") assert.NoError(t, err) assert.Equal(t, "did:firefly:org/abc", id.DID) assert.Equal(t, "0x12345", id.Verifiers[0].Value) @@ -365,18 +356,18 @@ func TestGetVerifierByDIDWithVerifiersOk(t *testing.T) { func TestGetVerifierByDIDWithVerifiersError(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() - nm.identity.(*identitymanagermocks.Manager).On("CachedIdentityLookupMustExist", nm.ctx, "did:firefly:org/abc"). + nm.identity.(*identitymanagermocks.Manager).On("CachedIdentityLookupMustExist", nm.ctx, "ns", "did:firefly:org/abc"). Return(nil, true, fmt.Errorf("pop")) - _, err := nm.GetIdentityByDIDWithVerifiers(nm.ctx, "did:firefly:org/abc") + _, err := nm.GetIdentityByDIDWithVerifiers(nm.ctx, "ns", "did:firefly:org/abc") assert.Regexp(t, "pop", err) } func TestGetVerifierByDIDNotErr(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() - nm.identity.(*identitymanagermocks.Manager).On("CachedIdentityLookupMustExist", nm.ctx, "did:firefly:org/abc"). + nm.identity.(*identitymanagermocks.Manager).On("CachedIdentityLookupMustExist", nm.ctx, "ns", "did:firefly:org/abc"). Return(nil, true, fmt.Errorf("pop")) - id, err := nm.GetIdentityByDID(nm.ctx, "did:firefly:org/abc") + id, err := nm.GetIdentityByDID(nm.ctx, "ns", "did:firefly:org/abc") assert.Regexp(t, "pop", err) assert.Nil(t, id) } @@ -396,7 +387,7 @@ func TestGetOrganizationsWithVerifiers(t *testing.T) { Value: "0x12345", }}, }, nil, nil) - res, _, err := nm.GetOrganizationsWithVerifiers(nm.ctx, database.IdentityQueryFactory.NewFilter(nm.ctx).And()) + res, _, err := nm.GetOrganizationsWithVerifiers(nm.ctx, "ns", database.IdentityQueryFactory.NewFilter(nm.ctx).And()) assert.NoError(t, err) assert.Len(t, res, 1) assert.Equal(t, "0x12345", res[0].Verifiers[0].Value) @@ -406,7 +397,7 @@ func TestGetOrganizationsWithVerifiersFailLookup(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() nm.database.(*databasemocks.Plugin).On("GetIdentities", nm.ctx, mock.Anything).Return(nil, nil, fmt.Errorf("pop")) - _, _, err := nm.GetOrganizationsWithVerifiers(nm.ctx, database.IdentityQueryFactory.NewFilter(nm.ctx).And()) + _, _, err := nm.GetOrganizationsWithVerifiers(nm.ctx, "ns", database.IdentityQueryFactory.NewFilter(nm.ctx).And()) assert.Regexp(t, "pop", err) } @@ -420,6 +411,6 @@ func TestGetIdentitiesWithVerifiersFailEnrich(t *testing.T) { }}, }, nil, nil) nm.database.(*databasemocks.Plugin).On("GetVerifiers", nm.ctx, mock.Anything).Return(nil, nil, fmt.Errorf("pop")) - _, _, err := nm.GetIdentitiesWithVerifiersGlobal(nm.ctx, database.IdentityQueryFactory.NewFilter(nm.ctx).And()) + _, _, err := nm.GetOrganizationsWithVerifiers(nm.ctx, "ns", database.IdentityQueryFactory.NewFilter(nm.ctx).And()) assert.Regexp(t, "pop", err) } diff --git a/internal/networkmap/did_test.go b/internal/networkmap/did_test.go index 7b23b5386..ed36361d5 100644 --- a/internal/networkmap/did_test.go +++ b/internal/networkmap/did_test.go @@ -152,9 +152,9 @@ func TestDIDGenerationGetIdentityByDIDFail(t *testing.T) { org1 := testOrg("org1") mii := nm.identity.(*identitymanagermocks.Manager) - mii.On("CachedIdentityLookupMustExist", nm.ctx, mock.Anything).Return(nil, false, fmt.Errorf("pop")) + mii.On("CachedIdentityLookupMustExist", nm.ctx, "ns", mock.Anything).Return(nil, false, fmt.Errorf("pop")) - _, err := nm.GetDIDDocForIndentityByDID(nm.ctx, org1.DID) + _, err := nm.GetDIDDocForIndentityByDID(nm.ctx, "ns", org1.DID) assert.Regexp(t, "pop", err) } @@ -165,7 +165,7 @@ func TestDIDGenerationGetIdentityByDIDFailVerifiers(t *testing.T) { org1 := testOrg("org1") mii := nm.identity.(*identitymanagermocks.Manager) - mii.On("CachedIdentityLookupMustExist", nm.ctx, mock.Anything).Return(&core.Identity{ + mii.On("CachedIdentityLookupMustExist", nm.ctx, "ns", mock.Anything).Return(&core.Identity{ IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), }, @@ -173,6 +173,6 @@ func TestDIDGenerationGetIdentityByDIDFailVerifiers(t *testing.T) { mdi := nm.database.(*databasemocks.Plugin) mdi.On("GetVerifiers", nm.ctx, mock.Anything).Return(nil, nil, fmt.Errorf("pop")) - _, err := nm.GetDIDDocForIndentityByDID(nm.ctx, org1.DID) + _, err := nm.GetDIDDocForIndentityByDID(nm.ctx, "ns", org1.DID) assert.Regexp(t, "pop", err) } diff --git a/internal/networkmap/manager.go b/internal/networkmap/manager.go index 759886014..2bbba05da 100644 --- a/internal/networkmap/manager.go +++ b/internal/networkmap/manager.go @@ -22,7 +22,9 @@ import ( "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly/internal/broadcast" "github.com/hyperledger/firefly/internal/coremsgs" + "github.com/hyperledger/firefly/internal/data" "github.com/hyperledger/firefly/internal/identity" + "github.com/hyperledger/firefly/internal/namespace" "github.com/hyperledger/firefly/internal/syncasync" "github.com/hyperledger/firefly/pkg/core" "github.com/hyperledger/firefly/pkg/database" @@ -30,53 +32,55 @@ import ( ) type Manager interface { - RegisterOrganization(ctx context.Context, org *core.IdentityCreateDTO, waitConfirm bool) (identity *core.Identity, err error) - RegisterNode(ctx context.Context, waitConfirm bool) (node *core.Identity, err error) - RegisterNodeOrganization(ctx context.Context, waitConfirm bool) (org *core.Identity, err error) + RegisterOrganization(ctx context.Context, ns string, org *core.IdentityCreateDTO, waitConfirm bool) (identity *core.Identity, err error) + RegisterNode(ctx context.Context, ns string, waitConfirm bool) (node *core.Identity, err error) + RegisterNodeOrganization(ctx context.Context, ns string, waitConfirm bool) (org *core.Identity, err error) RegisterIdentity(ctx context.Context, ns string, dto *core.IdentityCreateDTO, waitConfirm bool) (identity *core.Identity, err error) UpdateIdentity(ctx context.Context, ns string, id string, dto *core.IdentityUpdateDTO, waitConfirm bool) (identity *core.Identity, err error) - GetOrganizationByNameOrID(ctx context.Context, nameOrID string) (*core.Identity, error) - GetOrganizations(ctx context.Context, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) - GetOrganizationsWithVerifiers(ctx context.Context, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) - GetNodeByNameOrID(ctx context.Context, nameOrID string) (*core.Identity, error) - GetNodes(ctx context.Context, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) + GetOrganizationByNameOrID(ctx context.Context, ns, nameOrID string) (*core.Identity, error) + GetOrganizations(ctx context.Context, ns string, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) + GetOrganizationsWithVerifiers(ctx context.Context, ns string, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) + GetNodeByNameOrID(ctx context.Context, ns, nameOrID string) (*core.Identity, error) + GetNodes(ctx context.Context, ns string, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) GetIdentityByID(ctx context.Context, ns string, id string) (*core.Identity, error) GetIdentityByIDWithVerifiers(ctx context.Context, ns, id string) (*core.IdentityWithVerifiers, error) - GetIdentityByDID(ctx context.Context, did string) (*core.Identity, error) - GetIdentityByDIDWithVerifiers(ctx context.Context, did string) (*core.IdentityWithVerifiers, error) + GetIdentityByDID(ctx context.Context, ns, did string) (*core.Identity, error) + GetIdentityByDIDWithVerifiers(ctx context.Context, ns, did string) (*core.IdentityWithVerifiers, error) GetIdentities(ctx context.Context, ns string, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) - GetIdentitiesGlobal(ctx context.Context, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) GetIdentitiesWithVerifiers(ctx context.Context, ns string, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) - GetIdentitiesWithVerifiersGlobal(ctx context.Context, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) GetIdentityVerifiers(ctx context.Context, ns, id string, filter database.AndFilter) ([]*core.Verifier, *database.FilterResult, error) GetVerifiers(ctx context.Context, ns string, filter database.AndFilter) ([]*core.Verifier, *database.FilterResult, error) GetVerifierByHash(ctx context.Context, ns, hash string) (*core.Verifier, error) GetDIDDocForIndentityByID(ctx context.Context, ns, id string) (*DIDDocument, error) - GetDIDDocForIndentityByDID(ctx context.Context, did string) (*DIDDocument, error) + GetDIDDocForIndentityByDID(ctx context.Context, ns, did string) (*DIDDocument, error) } type networkMap struct { ctx context.Context database database.Plugin + data data.Manager broadcast broadcast.Manager exchange dataexchange.Plugin identity identity.Manager syncasync syncasync.Bridge + namespace namespace.Manager } -func NewNetworkMap(ctx context.Context, di database.Plugin, bm broadcast.Manager, dx dataexchange.Plugin, im identity.Manager, sa syncasync.Bridge) (Manager, error) { - if di == nil || bm == nil || dx == nil || im == nil { +func NewNetworkMap(ctx context.Context, di database.Plugin, dm data.Manager, bm broadcast.Manager, dx dataexchange.Plugin, im identity.Manager, sa syncasync.Bridge, ns namespace.Manager) (Manager, error) { + if di == nil || dm == nil || bm == nil || dx == nil || im == nil || ns == nil { return nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "NetworkMap") } nm := &networkMap{ ctx: ctx, database: di, + data: dm, broadcast: bm, exchange: dx, identity: im, syncasync: sa, + namespace: ns, } return nm, nil } diff --git a/internal/networkmap/manager_test.go b/internal/networkmap/manager_test.go index 273ac1a05..166463311 100644 --- a/internal/networkmap/manager_test.go +++ b/internal/networkmap/manager_test.go @@ -24,7 +24,9 @@ import ( "github.com/hyperledger/firefly/mocks/broadcastmocks" "github.com/hyperledger/firefly/mocks/databasemocks" "github.com/hyperledger/firefly/mocks/dataexchangemocks" + "github.com/hyperledger/firefly/mocks/datamocks" "github.com/hyperledger/firefly/mocks/identitymanagermocks" + "github.com/hyperledger/firefly/mocks/namespacemocks" "github.com/hyperledger/firefly/mocks/syncasyncmocks" "github.com/stretchr/testify/assert" ) @@ -33,17 +35,19 @@ func newTestNetworkmap(t *testing.T) (*networkMap, func()) { coreconfig.Reset() ctx, cancel := context.WithCancel(context.Background()) mdi := &databasemocks.Plugin{} + mdm := &datamocks.Manager{} mbm := &broadcastmocks.Manager{} mdx := &dataexchangemocks.Plugin{} mim := &identitymanagermocks.Manager{} msa := &syncasyncmocks.Bridge{} - nm, err := NewNetworkMap(ctx, mdi, mbm, mdx, mim, msa) + mns := &namespacemocks.Manager{} + nm, err := NewNetworkMap(ctx, mdi, mdm, mbm, mdx, mim, msa, mns) assert.NoError(t, err) return nm.(*networkMap), cancel } func TestNewNetworkMapMissingDep(t *testing.T) { - _, err := NewNetworkMap(context.Background(), nil, nil, nil, nil, nil) + _, err := NewNetworkMap(context.Background(), nil, nil, nil, nil, nil, nil, nil) assert.Regexp(t, "FF10128", err) } diff --git a/internal/networkmap/register_identity.go b/internal/networkmap/register_identity.go index 7c4af3d4e..0661071e7 100644 --- a/internal/networkmap/register_identity.go +++ b/internal/networkmap/register_identity.go @@ -33,7 +33,7 @@ func (nm *networkMap) RegisterIdentity(ctx context.Context, ns string, dto *core parent, err = fftypes.ParseUUID(ctx, dto.Parent) if err != nil { // Or a DID - parentIdentity, _, err := nm.identity.CachedIdentityLookupMustExist(ctx, dto.Parent) + parentIdentity, _, err := nm.identity.CachedIdentityLookupMustExist(ctx, ns, dto.Parent) if err != nil { return nil, err } @@ -56,13 +56,12 @@ func (nm *networkMap) RegisterIdentity(ctx context.Context, ns string, dto *core }, } + if err := nm.data.VerifyNamespaceExists(ctx, ns); err != nil { + return nil, err + } + // Set defaults - if identity.Namespace == core.SystemNamespace || identity.Namespace == "" { - identity.Namespace = core.SystemNamespace - if identity.Type == "" { - identity.Type = core.IdentityTypeOrg - } - } else if identity.Type == "" { + if identity.Type == "" { identity.Type = core.IdentityTypeCustom } diff --git a/internal/networkmap/register_identity_test.go b/internal/networkmap/register_identity_test.go index 570afe269..b08dcc1d5 100644 --- a/internal/networkmap/register_identity_test.go +++ b/internal/networkmap/register_identity_test.go @@ -24,6 +24,7 @@ import ( "github.com/hyperledger/firefly-common/pkg/fftypes" "github.com/hyperledger/firefly/internal/syncasync" "github.com/hyperledger/firefly/mocks/broadcastmocks" + "github.com/hyperledger/firefly/mocks/datamocks" "github.com/hyperledger/firefly/mocks/identitymanagermocks" "github.com/hyperledger/firefly/mocks/syncasyncmocks" "github.com/hyperledger/firefly/pkg/core" @@ -44,12 +45,15 @@ func TestRegisterIdentityOrgWithParentOk(t *testing.T) { Key: "0x23456", }, nil) + mdm := nm.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", nm.ctx, "ns").Return(nil) + mockMsg1 := &core.Message{Header: core.MessageHeader{ID: fftypes.NewUUID()}} mockMsg2 := &core.Message{Header: core.MessageHeader{ID: fftypes.NewUUID()}} mbm := nm.broadcast.(*broadcastmocks.Manager) mbm.On("BroadcastIdentityClaim", nm.ctx, - core.SystemNamespace, + "ns", mock.AnythingOfType("*core.IdentityClaim"), mock.MatchedBy(func(sr *core.SignerRef) bool { return sr.Key == "0x12345" @@ -57,14 +61,14 @@ func TestRegisterIdentityOrgWithParentOk(t *testing.T) { core.SystemTagIdentityClaim, false).Return(mockMsg1, nil) mbm.On("BroadcastDefinition", nm.ctx, - core.SystemNamespace, + "ns", mock.AnythingOfType("*core.IdentityVerification"), mock.MatchedBy(func(sr *core.SignerRef) bool { return sr.Key == "0x23456" }), core.SystemTagIdentityVerification, false).Return(mockMsg2, nil) - org, err := nm.RegisterIdentity(nm.ctx, core.SystemNamespace, &core.IdentityCreateDTO{ + org, err := nm.RegisterIdentity(nm.ctx, "ns", &core.IdentityCreateDTO{ Name: "child1", Key: "0x12345", Parent: fftypes.NewUUID().String(), @@ -75,6 +79,7 @@ func TestRegisterIdentityOrgWithParentOk(t *testing.T) { mim.AssertExpectations(t) mbm.AssertExpectations(t) + mdm.AssertExpectations(t) } func TestRegisterIdentityOrgWithParentWaitConfirmOk(t *testing.T) { @@ -102,12 +107,15 @@ func TestRegisterIdentityOrgWithParentWaitConfirmOk(t *testing.T) { assert.NoError(t, err) }).Return(nil, nil) + mdm := nm.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", nm.ctx, "ns1").Return(nil) + mockMsg1 := &core.Message{Header: core.MessageHeader{ID: fftypes.NewUUID()}} mockMsg2 := &core.Message{Header: core.MessageHeader{ID: fftypes.NewUUID()}} mbm := nm.broadcast.(*broadcastmocks.Manager) mbm.On("BroadcastIdentityClaim", nm.ctx, - core.SystemNamespace, + "ns1", mock.AnythingOfType("*core.IdentityClaim"), mock.MatchedBy(func(sr *core.SignerRef) bool { return sr.Key == "0x12345" @@ -115,14 +123,14 @@ func TestRegisterIdentityOrgWithParentWaitConfirmOk(t *testing.T) { core.SystemTagIdentityClaim, false).Return(mockMsg1, nil) mbm.On("BroadcastDefinition", nm.ctx, - core.SystemNamespace, + "ns1", mock.AnythingOfType("*core.IdentityVerification"), mock.MatchedBy(func(sr *core.SignerRef) bool { return sr.Key == "0x23456" }), core.SystemTagIdentityVerification, false).Return(mockMsg2, nil) - _, err := nm.RegisterIdentity(nm.ctx, core.SystemNamespace, &core.IdentityCreateDTO{ + _, err := nm.RegisterIdentity(nm.ctx, "ns1", &core.IdentityCreateDTO{ Name: "child1", Key: "0x12345", Parent: fftypes.NewUUID().String(), @@ -132,6 +140,34 @@ func TestRegisterIdentityOrgWithParentWaitConfirmOk(t *testing.T) { mim.AssertExpectations(t) mbm.AssertExpectations(t) msa.AssertExpectations(t) + mdm.AssertExpectations(t) +} + +func TestRegisterIdentityCustomBadNS(t *testing.T) { + + nm, cancel := newTestNetworkmap(t) + defer cancel() + + mim := nm.identity.(*identitymanagermocks.Manager) + mim.On("CachedIdentityLookupMustExist", nm.ctx, "ns1", "did:firefly:org/parent1").Return(&core.Identity{ + IdentityBase: core.IdentityBase{ + ID: fftypes.NewUUID(), + DID: "did:firefly:org/parent1", + }, + }, false, nil) + + mdm := nm.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", nm.ctx, "ns1").Return(fmt.Errorf("pop")) + + _, err := nm.RegisterIdentity(nm.ctx, "ns1", &core.IdentityCreateDTO{ + Name: "custom1", + Key: "0x12345", + Parent: "did:firefly:org/parent1", + }, false) + assert.Regexp(t, "pop", err) + + mim.AssertExpectations(t) + mdm.AssertExpectations(t) } func TestRegisterIdentityCustomWithParentFail(t *testing.T) { @@ -143,7 +179,7 @@ func TestRegisterIdentityCustomWithParentFail(t *testing.T) { mim := nm.identity.(*identitymanagermocks.Manager) mim.On("VerifyIdentityChain", nm.ctx, mock.AnythingOfType("*core.Identity")).Return(parentIdentity, false, nil) - mim.On("CachedIdentityLookupMustExist", nm.ctx, "did:firefly:org/parent1").Return(&core.Identity{ + mim.On("CachedIdentityLookupMustExist", nm.ctx, "ns1", "did:firefly:org/parent1").Return(&core.Identity{ IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), DID: "did:firefly:org/parent1", @@ -153,6 +189,9 @@ func TestRegisterIdentityCustomWithParentFail(t *testing.T) { Key: "0x23456", }, nil) + mdm := nm.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", nm.ctx, "ns1").Return(nil) + mockMsg := &core.Message{Header: core.MessageHeader{ID: fftypes.NewUUID()}} mbm := nm.broadcast.(*broadcastmocks.Manager) @@ -181,6 +220,7 @@ func TestRegisterIdentityCustomWithParentFail(t *testing.T) { mim.AssertExpectations(t) mbm.AssertExpectations(t) + mdm.AssertExpectations(t) } func TestRegisterIdentityGetParentMsgFail(t *testing.T) { @@ -194,6 +234,9 @@ func TestRegisterIdentityGetParentMsgFail(t *testing.T) { mim.On("VerifyIdentityChain", nm.ctx, mock.AnythingOfType("*core.Identity")).Return(parentIdentity, false, nil) mim.On("ResolveIdentitySigner", nm.ctx, parentIdentity).Return(nil, fmt.Errorf("pop")) + mdm := nm.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", nm.ctx, "ns1").Return(nil) + _, err := nm.RegisterIdentity(nm.ctx, "ns1", &core.IdentityCreateDTO{ Name: "custom1", Key: "0x12345", @@ -202,6 +245,7 @@ func TestRegisterIdentityGetParentMsgFail(t *testing.T) { assert.Regexp(t, "pop", err) mim.AssertExpectations(t) + mdm.AssertExpectations(t) } func TestRegisterIdentityRootBroadcastFail(t *testing.T) { @@ -212,6 +256,9 @@ func TestRegisterIdentityRootBroadcastFail(t *testing.T) { mim := nm.identity.(*identitymanagermocks.Manager) mim.On("VerifyIdentityChain", nm.ctx, mock.AnythingOfType("*core.Identity")).Return(nil, false, nil) + mdm := nm.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", nm.ctx, "ns1").Return(nil) + mbm := nm.broadcast.(*broadcastmocks.Manager) mbm.On("BroadcastIdentityClaim", nm.ctx, "ns1", @@ -230,6 +277,7 @@ func TestRegisterIdentityRootBroadcastFail(t *testing.T) { mim.AssertExpectations(t) mbm.AssertExpectations(t) + mdm.AssertExpectations(t) } func TestRegisterIdentityMissingKey(t *testing.T) { @@ -240,6 +288,9 @@ func TestRegisterIdentityMissingKey(t *testing.T) { mim := nm.identity.(*identitymanagermocks.Manager) mim.On("VerifyIdentityChain", nm.ctx, mock.AnythingOfType("*core.Identity")).Return(nil, false, nil) + mdm := nm.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", nm.ctx, "ns1").Return(nil) + _, err := nm.RegisterIdentity(nm.ctx, "ns1", &core.IdentityCreateDTO{ Name: "custom1", Parent: fftypes.NewUUID().String(), @@ -247,6 +298,7 @@ func TestRegisterIdentityMissingKey(t *testing.T) { assert.Regexp(t, "FF10352", err) mim.AssertExpectations(t) + mdm.AssertExpectations(t) } func TestRegisterIdentityVerifyFail(t *testing.T) { @@ -257,6 +309,9 @@ func TestRegisterIdentityVerifyFail(t *testing.T) { mim := nm.identity.(*identitymanagermocks.Manager) mim.On("VerifyIdentityChain", nm.ctx, mock.AnythingOfType("*core.Identity")).Return(nil, false, fmt.Errorf("pop")) + mdm := nm.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", nm.ctx, "ns1").Return(nil) + _, err := nm.RegisterIdentity(nm.ctx, "ns1", &core.IdentityCreateDTO{ Name: "custom1", Parent: fftypes.NewUUID().String(), @@ -264,6 +319,7 @@ func TestRegisterIdentityVerifyFail(t *testing.T) { assert.Regexp(t, "pop", err) mim.AssertExpectations(t) + mdm.AssertExpectations(t) } func TestRegisterIdentityBadParent(t *testing.T) { @@ -272,7 +328,7 @@ func TestRegisterIdentityBadParent(t *testing.T) { defer cancel() mim := nm.identity.(*identitymanagermocks.Manager) - mim.On("CachedIdentityLookupMustExist", nm.ctx, "did:firefly:org/1").Return(nil, false, fmt.Errorf("pop")) + mim.On("CachedIdentityLookupMustExist", nm.ctx, "ns1", "did:firefly:org/1").Return(nil, false, fmt.Errorf("pop")) _, err := nm.RegisterIdentity(nm.ctx, "ns1", &core.IdentityCreateDTO{ Name: "custom1", diff --git a/internal/networkmap/register_node.go b/internal/networkmap/register_node.go index c0e5dfc7a..90a35d261 100644 --- a/internal/networkmap/register_node.go +++ b/internal/networkmap/register_node.go @@ -25,9 +25,9 @@ import ( "github.com/hyperledger/firefly/pkg/core" ) -func (nm *networkMap) RegisterNode(ctx context.Context, waitConfirm bool) (identity *core.Identity, err error) { +func (nm *networkMap) RegisterNode(ctx context.Context, ns string, waitConfirm bool) (identity *core.Identity, err error) { - nodeOwningOrg, err := nm.identity.GetNodeOwnerOrg(ctx) + nodeOwningOrg, err := nm.identity.GetMultipartyRootOrg(ctx, ns) if err != nil { return nil, err } @@ -52,5 +52,5 @@ func (nm *networkMap) RegisterNode(ctx context.Context, waitConfirm bool) (ident } nodeRequest.Profile = dxInfo - return nm.RegisterIdentity(ctx, core.SystemNamespace, nodeRequest, waitConfirm) + return nm.RegisterIdentity(ctx, ns, nodeRequest, waitConfirm) } diff --git a/internal/networkmap/register_node_test.go b/internal/networkmap/register_node_test.go index 45d2a8ec8..5932c761a 100644 --- a/internal/networkmap/register_node_test.go +++ b/internal/networkmap/register_node_test.go @@ -25,6 +25,7 @@ import ( "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/mocks/broadcastmocks" "github.com/hyperledger/firefly/mocks/dataexchangemocks" + "github.com/hyperledger/firefly/mocks/datamocks" "github.com/hyperledger/firefly/mocks/identitymanagermocks" "github.com/hyperledger/firefly/pkg/core" "github.com/stretchr/testify/assert" @@ -43,11 +44,14 @@ func TestRegisterNodeOk(t *testing.T) { parentOrg := testOrg("org1") mim := nm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", nm.ctx).Return(parentOrg, nil) + mim.On("GetMultipartyRootOrg", nm.ctx, "ns1").Return(parentOrg, nil) mim.On("VerifyIdentityChain", nm.ctx, mock.AnythingOfType("*core.Identity")).Return(parentOrg, false, nil) signerRef := &core.SignerRef{Key: "0x23456"} mim.On("ResolveIdentitySigner", nm.ctx, parentOrg).Return(signerRef, nil) + mdm := nm.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", nm.ctx, "ns1").Return(nil) + mdx := nm.exchange.(*dataexchangemocks.Plugin) mdx.On("GetEndpointInfo", nm.ctx).Return(fftypes.JSONObject{ "id": "peer1", @@ -57,15 +61,19 @@ func TestRegisterNodeOk(t *testing.T) { mockMsg := &core.Message{Header: core.MessageHeader{ID: fftypes.NewUUID()}} mbm := nm.broadcast.(*broadcastmocks.Manager) mbm.On("BroadcastIdentityClaim", nm.ctx, - core.SystemNamespace, + "ns1", mock.AnythingOfType("*core.IdentityClaim"), signerRef, core.SystemTagIdentityClaim, false).Return(mockMsg, nil) - node, err := nm.RegisterNode(nm.ctx, false) + node, err := nm.RegisterNode(nm.ctx, "ns1", false) assert.NoError(t, err) assert.Equal(t, *mockMsg.Header.ID, *node.Messages.Claim) + mim.AssertExpectations(t) + mdx.AssertExpectations(t) + mbm.AssertExpectations(t) + mdm.AssertExpectations(t) } func TestRegisterNodePeerInfoFail(t *testing.T) { @@ -80,7 +88,7 @@ func TestRegisterNodePeerInfoFail(t *testing.T) { parentOrg := testOrg("org1") mim := nm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", nm.ctx).Return(parentOrg, nil) + mim.On("GetMultipartyRootOrg", nm.ctx, "ns1").Return(parentOrg, nil) mim.On("VerifyIdentityChain", nm.ctx, mock.AnythingOfType("*core.Identity")).Return(parentOrg, false, nil) signerRef := &core.SignerRef{Key: "0x23456"} mim.On("ResolveIdentitySigner", nm.ctx, parentOrg).Return(signerRef, nil) @@ -88,7 +96,7 @@ func TestRegisterNodePeerInfoFail(t *testing.T) { mdx := nm.exchange.(*dataexchangemocks.Plugin) mdx.On("GetEndpointInfo", nm.ctx).Return(fftypes.JSONObject{}, fmt.Errorf("pop")) - _, err := nm.RegisterNode(nm.ctx, false) + _, err := nm.RegisterNode(nm.ctx, "ns1", false) assert.Regexp(t, "pop", err) } @@ -99,9 +107,9 @@ func TestRegisterNodeGetOwnerFail(t *testing.T) { defer cancel() mim := nm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", nm.ctx).Return(nil, fmt.Errorf("pop")) + mim.On("GetMultipartyRootOrg", nm.ctx, "ns1").Return(nil, fmt.Errorf("pop")) - _, err := nm.RegisterNode(nm.ctx, false) + _, err := nm.RegisterNode(nm.ctx, "ns1", false) assert.Regexp(t, "pop", err) } diff --git a/internal/networkmap/register_org.go b/internal/networkmap/register_org.go index 82144d076..11136071e 100644 --- a/internal/networkmap/register_org.go +++ b/internal/networkmap/register_org.go @@ -19,7 +19,6 @@ package networkmap import ( "context" - "github.com/hyperledger/firefly-common/pkg/config" "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/internal/coremsgs" @@ -27,27 +26,27 @@ import ( ) // RegisterNodeOrganization is a convenience helper to register the org configured on the node, without any extra info -func (nm *networkMap) RegisterNodeOrganization(ctx context.Context, waitConfirm bool) (*core.Identity, error) { +func (nm *networkMap) RegisterNodeOrganization(ctx context.Context, ns string, waitConfirm bool) (*core.Identity, error) { - key, err := nm.identity.GetNodeOwnerBlockchainKey(ctx) + key, err := nm.identity.GetMultipartyRootVerifier(ctx, ns) if err != nil { return nil, err } orgRequest := &core.IdentityCreateDTO{ - Name: config.GetString(coreconfig.OrgName), + Name: nm.namespace.GetMultipartyConfig(ns, coreconfig.OrgName), IdentityProfile: core.IdentityProfile{ - Description: config.GetString(coreconfig.OrgDescription), + Description: nm.namespace.GetMultipartyConfig(ns, coreconfig.OrgDescription), }, Key: key.Value, } if orgRequest.Name == "" { return nil, i18n.NewError(ctx, coremsgs.MsgNodeAndOrgIDMustBeSet) } - return nm.RegisterOrganization(ctx, orgRequest, waitConfirm) + return nm.RegisterOrganization(ctx, ns, orgRequest, waitConfirm) } -func (nm *networkMap) RegisterOrganization(ctx context.Context, orgRequest *core.IdentityCreateDTO, waitConfirm bool) (*core.Identity, error) { +func (nm *networkMap) RegisterOrganization(ctx context.Context, ns string, orgRequest *core.IdentityCreateDTO, waitConfirm bool) (*core.Identity, error) { orgRequest.Type = core.IdentityTypeOrg - return nm.RegisterIdentity(ctx, core.SystemNamespace, orgRequest, waitConfirm) + return nm.RegisterIdentity(ctx, ns, orgRequest, waitConfirm) } diff --git a/internal/networkmap/register_org_test.go b/internal/networkmap/register_org_test.go index 2a0c92655..4ce2e6dfe 100644 --- a/internal/networkmap/register_org_test.go +++ b/internal/networkmap/register_org_test.go @@ -25,7 +25,9 @@ import ( "github.com/hyperledger/firefly-common/pkg/fftypes" "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/mocks/broadcastmocks" + "github.com/hyperledger/firefly/mocks/datamocks" "github.com/hyperledger/firefly/mocks/identitymanagermocks" + "github.com/hyperledger/firefly/mocks/namespacemocks" "github.com/hyperledger/firefly/pkg/core" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -36,7 +38,7 @@ func testOrg(name string) *core.Identity { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), Type: core.IdentityTypeOrg, - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: name, }, IdentityProfile: core.IdentityProfile{ @@ -58,29 +60,39 @@ func TestRegisterNodeOrgOk(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() - config.Set(coreconfig.OrgName, "org1") config.Set(coreconfig.NodeDescription, "Node 1") mim := nm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerBlockchainKey", nm.ctx).Return(&core.VerifierRef{ + mim.On("GetMultipartyRootVerifier", nm.ctx, "ns1").Return(&core.VerifierRef{ Value: "0x12345", }, nil) mim.On("VerifyIdentityChain", nm.ctx, mock.AnythingOfType("*core.Identity")).Return(nil, false, nil) + mdm := nm.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", nm.ctx, "ns1").Return(nil) + mockMsg := &core.Message{Header: core.MessageHeader{ID: fftypes.NewUUID()}} mbm := nm.broadcast.(*broadcastmocks.Manager) mbm.On("BroadcastIdentityClaim", nm.ctx, - core.SystemNamespace, + "ns1", mock.AnythingOfType("*core.IdentityClaim"), mock.MatchedBy(func(sr *core.SignerRef) bool { return sr.Key == "0x12345" }), core.SystemTagIdentityClaim, false).Return(mockMsg, nil) - org, err := nm.RegisterNodeOrganization(nm.ctx, false) + mns := nm.namespace.(*namespacemocks.Manager) + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgName).Return("org1") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgDescription).Return("") + + org, err := nm.RegisterNodeOrganization(nm.ctx, "ns1", false) assert.NoError(t, err) assert.Equal(t, *mockMsg.Header.ID, *org.Messages.Claim) + mim.AssertExpectations(t) + mbm.AssertExpectations(t) + mdm.AssertExpectations(t) + mns.AssertExpectations(t) } func TestRegisterNodeOrgNoName(t *testing.T) { @@ -88,18 +100,22 @@ func TestRegisterNodeOrgNoName(t *testing.T) { nm, cancel := newTestNetworkmap(t) defer cancel() - config.Set(coreconfig.OrgName, "") config.Set(coreconfig.NodeDescription, "") mim := nm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerBlockchainKey", nm.ctx).Return(&core.VerifierRef{ + mim.On("GetMultipartyRootVerifier", nm.ctx, "ns1").Return(&core.VerifierRef{ Value: "0x12345", }, nil) - mim.On("VerifyIdentityChain", nm.ctx, mock.AnythingOfType("*core.Identity")).Return(nil, false, nil) - _, err := nm.RegisterNodeOrganization(nm.ctx, false) + mns := nm.namespace.(*namespacemocks.Manager) + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgName).Return("") + mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgDescription).Return("") + + _, err := nm.RegisterNodeOrganization(nm.ctx, "ns1", false) assert.Regexp(t, "FF10216", err) + mim.AssertExpectations(t) + mns.AssertExpectations(t) } func TestRegisterNodeGetOwnerBlockchainKeyFail(t *testing.T) { @@ -111,9 +127,9 @@ func TestRegisterNodeGetOwnerBlockchainKeyFail(t *testing.T) { config.Set(coreconfig.NodeDescription, "") mim := nm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerBlockchainKey", nm.ctx).Return(nil, fmt.Errorf("pop")) + mim.On("GetMultipartyRootVerifier", nm.ctx, "ns1").Return(nil, fmt.Errorf("pop")) - _, err := nm.RegisterNodeOrganization(nm.ctx, false) + _, err := nm.RegisterNodeOrganization(nm.ctx, "ns1", false) assert.Regexp(t, "pop", err) } diff --git a/internal/networkmap/update_identity_test.go b/internal/networkmap/update_identity_test.go index 7bd209cc2..8954f5da2 100644 --- a/internal/networkmap/update_identity_test.go +++ b/internal/networkmap/update_identity_test.go @@ -44,7 +44,7 @@ func TestUpdateIdentityProfileOk(t *testing.T) { mbm := nm.broadcast.(*broadcastmocks.Manager) mbm.On("BroadcastDefinition", nm.ctx, - core.SystemNamespace, + "ns1", mock.AnythingOfType("*core.IdentityUpdate"), mock.MatchedBy(func(sr *core.SignerRef) bool { return sr.Key == "0x12345" @@ -78,7 +78,7 @@ func TestUpdateIdentityProfileBroadcastFail(t *testing.T) { mbm := nm.broadcast.(*broadcastmocks.Manager) mbm.On("BroadcastDefinition", nm.ctx, - core.SystemNamespace, + "ns1", mock.AnythingOfType("*core.IdentityUpdate"), mock.MatchedBy(func(sr *core.SignerRef) bool { return sr.Key == "0x12345" diff --git a/internal/orchestrator/orchestrator.go b/internal/orchestrator/orchestrator.go index e13f1a69b..f265e8570 100644 --- a/internal/orchestrator/orchestrator.go +++ b/internal/orchestrator/orchestrator.go @@ -91,7 +91,7 @@ type Orchestrator interface { PrivateMessaging() privatemessaging.Manager // Status - GetStatus(ctx context.Context) (*core.NodeStatus, error) + GetStatus(ctx context.Context, ns string) (*core.NodeStatus, error) // Subscription management GetSubscriptions(ctx context.Context, ns string, filter database.AndFilter) ([]*core.Subscription, *database.FilterResult, error) @@ -142,7 +142,7 @@ type Orchestrator interface { RequestReply(ctx context.Context, ns string, msg *core.MessageInOut) (reply *core.MessageInOut, err error) // Network Operations - SubmitNetworkAction(ctx context.Context, action *core.NetworkAction) error + SubmitNetworkAction(ctx context.Context, ns string, action *core.NetworkAction) error } type orchestrator struct { @@ -228,7 +228,7 @@ func (or *orchestrator) Start() (err error) { } var ns *core.Namespace if err == nil { - ns, err = or.database.GetNamespace(or.ctx, core.SystemNamespace) + ns, err = or.database.GetNamespace(or.ctx, core.LegacySystemNamespace) } if err == nil { for _, el := range or.blockchains { @@ -420,7 +420,6 @@ func (or *orchestrator) initDataExchange(ctx context.Context) (err error) { fb := database.IdentityQueryFactory.NewFilter(ctx) nodes, _, err := or.database.GetIdentities(ctx, fb.And( fb.Eq("type", core.IdentityTypeNode), - fb.Eq("namespace", core.SystemNamespace), )) if err != nil { return err @@ -795,7 +794,7 @@ func (or *orchestrator) initComponents(ctx context.Context) (err error) { } if or.identity == nil { - or.identity, err = identity.NewIdentityManager(ctx, or.database, or.identityPlugins, or.blockchain, or.data) + or.identity, err = identity.NewIdentityManager(ctx, or.database, or.identityPlugins, or.blockchain, or.data, or.namespace) if err != nil { return err } @@ -870,7 +869,7 @@ func (or *orchestrator) initComponents(ctx context.Context) (err error) { } if or.networkmap == nil { - or.networkmap, err = networkmap.NewNetworkMap(ctx, or.database, or.broadcast, or.dataexchange, or.identity, or.syncasync) + or.networkmap, err = networkmap.NewNetworkMap(ctx, or.database, or.data, or.broadcast, or.dataexchange, or.identity, or.syncasync, or.namespace) if err != nil { return err } @@ -888,13 +887,18 @@ func (or *orchestrator) initNamespaces(ctx context.Context) (err error) { return or.namespace.Init(ctx, or.database) } -func (or *orchestrator) SubmitNetworkAction(ctx context.Context, action *core.NetworkAction) error { - verifier, err := or.identity.GetNodeOwnerBlockchainKey(ctx) +func (or *orchestrator) SubmitNetworkAction(ctx context.Context, ns string, action *core.NetworkAction) error { + key, err := or.identity.NormalizeSigningKey(ctx, ns, "", identity.KeyNormalizationBlockchainPlugin) if err != nil { return err } - if action.Type != core.NetworkActionTerminate { + if action.Type == core.NetworkActionTerminate { + if ns != core.LegacySystemNamespace { + // For now, "terminate" only works on ff_system + return i18n.NewError(ctx, coremsgs.MsgTerminateNotSupported, ns) + } + } else { return i18n.NewError(ctx, coremsgs.MsgUnrecognizedNetworkAction, action.Type) } - return or.blockchain.SubmitNetworkAction(ctx, fftypes.NewUUID(), verifier.Value, action.Type) + return or.blockchain.SubmitNetworkAction(ctx, fftypes.NewUUID(), key, action.Type) } diff --git a/internal/orchestrator/orchestrator_test.go b/internal/orchestrator/orchestrator_test.go index c2fbb1321..cf3c98ae3 100644 --- a/internal/orchestrator/orchestrator_test.go +++ b/internal/orchestrator/orchestrator_test.go @@ -28,6 +28,7 @@ import ( "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/internal/database/difactory" "github.com/hyperledger/firefly/internal/dataexchange/dxfactory" + identitymanager "github.com/hyperledger/firefly/internal/identity" "github.com/hyperledger/firefly/internal/identity/iifactory" "github.com/hyperledger/firefly/internal/sharedstorage/ssfactory" "github.com/hyperledger/firefly/internal/tokens/tifactory" @@ -1151,24 +1152,31 @@ func TestInitDataExchangeWithNodes(t *testing.T) { func TestNetworkAction(t *testing.T) { or := newTestOrchestrator() or.blockchain = or.mbi - verifier := &core.VerifierRef{Value: "0x123"} - or.mim.On("GetNodeOwnerBlockchainKey", context.Background()).Return(verifier, nil) + or.mim.On("NormalizeSigningKey", context.Background(), "ff_system", "", identitymanager.KeyNormalizationBlockchainPlugin).Return("0x123", nil) or.mbi.On("SubmitNetworkAction", context.Background(), mock.Anything, "0x123", core.NetworkActionTerminate).Return(nil) - err := or.SubmitNetworkAction(context.Background(), &core.NetworkAction{Type: core.NetworkActionTerminate}) + err := or.SubmitNetworkAction(context.Background(), "ff_system", &core.NetworkAction{Type: core.NetworkActionTerminate}) assert.NoError(t, err) } func TestNetworkActionBadKey(t *testing.T) { or := newTestOrchestrator() - or.mim.On("GetNodeOwnerBlockchainKey", context.Background()).Return(nil, fmt.Errorf("pop")) - err := or.SubmitNetworkAction(context.Background(), &core.NetworkAction{Type: core.NetworkActionTerminate}) + or.mim.On("NormalizeSigningKey", context.Background(), "ff_system", "", identitymanager.KeyNormalizationBlockchainPlugin).Return("", fmt.Errorf("pop")) + err := or.SubmitNetworkAction(context.Background(), "ff_system", &core.NetworkAction{Type: core.NetworkActionTerminate}) assert.EqualError(t, err, "pop") } func TestNetworkActionBadType(t *testing.T) { or := newTestOrchestrator() - verifier := &core.VerifierRef{Value: "0x123"} - or.mim.On("GetNodeOwnerBlockchainKey", context.Background()).Return(verifier, nil) - err := or.SubmitNetworkAction(context.Background(), &core.NetworkAction{Type: "bad"}) + or.mim.On("NormalizeSigningKey", context.Background(), "ff_system", "", identitymanager.KeyNormalizationBlockchainPlugin).Return("0x123", nil) + err := or.SubmitNetworkAction(context.Background(), "ff_system", &core.NetworkAction{Type: "bad"}) assert.Regexp(t, "FF10397", err) } + +func TestNetworkActionTerminateBadNamespace(t *testing.T) { + or := newTestOrchestrator() + or.blockchain = or.mbi + or.mim.On("NormalizeSigningKey", context.Background(), "ns1", "", identitymanager.KeyNormalizationBlockchainPlugin).Return("0x123", nil) + or.mbi.On("SubmitNetworkAction", context.Background(), mock.Anything, "0x123", core.NetworkActionTerminate).Return(nil) + err := or.SubmitNetworkAction(context.Background(), "ns1", &core.NetworkAction{Type: core.NetworkActionTerminate}) + assert.Regexp(t, "FF10399", err) +} diff --git a/internal/orchestrator/status.go b/internal/orchestrator/status.go index f105286cc..bd9f3a0ca 100644 --- a/internal/orchestrator/status.go +++ b/internal/orchestrator/status.go @@ -89,11 +89,11 @@ func (or *orchestrator) getPlugins() core.NodeStatusPlugins { } } -func (or *orchestrator) GetNodeUUID(ctx context.Context) (node *fftypes.UUID) { +func (or *orchestrator) GetNodeUUID(ctx context.Context, ns string) (node *fftypes.UUID) { if or.node != nil { return or.node } - status, err := or.GetStatus(ctx) + status, err := or.GetStatus(ctx, ns) if err != nil { log.L(or.ctx).Warnf("Failed to query local node UUID: %s", err) return nil @@ -106,21 +106,19 @@ func (or *orchestrator) GetNodeUUID(ctx context.Context) (node *fftypes.UUID) { return or.node } -func (or *orchestrator) GetStatus(ctx context.Context) (status *core.NodeStatus, err error) { +func (or *orchestrator) GetStatus(ctx context.Context, ns string) (status *core.NodeStatus, err error) { - org, err := or.identity.GetNodeOwnerOrg(ctx) + org, err := or.identity.GetMultipartyRootOrg(ctx, ns) if err != nil { log.L(ctx).Warnf("Failed to query local org for status: %s", err) } status = &core.NodeStatus{ + Namespace: ns, Node: core.NodeStatusNode{ Name: config.GetString(coreconfig.NodeName), }, Org: core.NodeStatusOrg{ - Name: config.GetString(coreconfig.OrgName), - }, - Defaults: core.NodeStatusDefaults{ - Namespace: config.GetString(coreconfig.NamespacesDefault), + Name: or.namespace.GetMultipartyConfig(ns, coreconfig.OrgName), }, Plugins: or.getPlugins(), } @@ -129,7 +127,11 @@ func (or *orchestrator) GetStatus(ctx context.Context) (status *core.NodeStatus, status.Org.Registered = true status.Org.ID = org.ID status.Org.DID = org.DID - verifiers, _, err := or.networkmap.GetIdentityVerifiers(ctx, core.SystemNamespace, org.ID.String(), database.VerifierQueryFactory.NewFilter(ctx).And()) + + // It's possible namespace will fallback to SystemNamespace (if configured to do so) + ns = org.Namespace + + verifiers, _, err := or.networkmap.GetIdentityVerifiers(ctx, ns, org.ID.String(), database.VerifierQueryFactory.NewFilter(ctx).And()) if err != nil { return nil, err } @@ -138,7 +140,7 @@ func (or *orchestrator) GetStatus(ctx context.Context) (status *core.NodeStatus, status.Org.Verifiers[i] = &v.VerifierRef } - node, _, err := or.identity.CachedIdentityLookupNilOK(ctx, fmt.Sprintf("%s%s", core.FireFlyNodeDIDPrefix, status.Node.Name)) + node, _, err := or.identity.CachedIdentityLookupNilOK(ctx, ns, fmt.Sprintf("%s%s", core.FireFlyNodeDIDPrefix, status.Node.Name)) if err != nil { return nil, err } diff --git a/internal/orchestrator/status_test.go b/internal/orchestrator/status_test.go index 69706df2a..bdceb1979 100644 --- a/internal/orchestrator/status_test.go +++ b/internal/orchestrator/status_test.go @@ -25,6 +25,7 @@ import ( "github.com/hyperledger/firefly/internal/coreconfig" "github.com/hyperledger/firefly/mocks/eventmocks" "github.com/hyperledger/firefly/mocks/identitymanagermocks" + "github.com/hyperledger/firefly/mocks/namespacemocks" "github.com/hyperledger/firefly/mocks/networkmapmocks" "github.com/hyperledger/firefly/pkg/core" "github.com/stretchr/testify/assert" @@ -88,42 +89,45 @@ func TestGetStatusRegistered(t *testing.T) { coreconfig.Reset() config.Set(coreconfig.NamespacesDefault, "default") - config.Set(coreconfig.OrgName, "org1") config.Set(coreconfig.NodeName, "node1") orgID := fftypes.NewUUID() nodeID := fftypes.NewUUID() mim := or.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", or.ctx).Return(&core.Identity{ + mim.On("GetMultipartyRootOrg", or.ctx, "ns").Return(&core.Identity{ IdentityBase: core.IdentityBase{ - ID: orgID, - Name: "org1", - DID: "did:firefly:org/org1", + ID: orgID, + Name: "org1", + Namespace: "ns", + DID: "did:firefly:org/org1", }, }, nil) - mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(&core.Identity{ + mim.On("CachedIdentityLookupNilOK", or.ctx, "ns", "did:firefly:node/node1").Return(&core.Identity{ IdentityBase: core.IdentityBase{ ID: nodeID, Name: "node1", Parent: orgID, }, }, false, nil) - nmn := or.networkmap.(*networkmapmocks.Manager) - nmn.On("GetIdentityVerifiers", or.ctx, core.SystemNamespace, orgID.String(), mock.Anything).Return([]*core.Verifier{ + mnm := or.networkmap.(*networkmapmocks.Manager) + mnm.On("GetIdentityVerifiers", or.ctx, "ns", orgID.String(), mock.Anything).Return([]*core.Verifier{ {Hash: fftypes.NewRandB32(), VerifierRef: core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: "0x12345", }}, }, nil, nil) + mns := or.namespace.(*namespacemocks.Manager) + mns.On("GetMultipartyConfig", "ns", coreconfig.OrgName).Return("org1") + mem := or.events.(*eventmocks.EventManager) mem.On("GetPlugins").Return(mockEventPlugins) - status, err := or.GetStatus(or.ctx) + status, err := or.GetStatus(or.ctx, "ns") assert.NoError(t, err) - assert.Equal(t, "default", status.Defaults.Namespace) + assert.Equal(t, "ns", status.Namespace) assert.Equal(t, "org1", status.Org.Name) assert.True(t, status.Org.Registered) @@ -144,8 +148,8 @@ func TestGetStatusRegistered(t *testing.T) { assert.ElementsMatch(t, pluginsResult.SharedStorage, status.Plugins.SharedStorage) assert.ElementsMatch(t, pluginsResult.Tokens, status.Plugins.Tokens) - assert.True(t, or.GetNodeUUID(or.ctx).Equals(nodeID)) - assert.True(t, or.GetNodeUUID(or.ctx).Equals(nodeID)) // cached + assert.True(t, or.GetNodeUUID(or.ctx, "ns").Equals(nodeID)) + assert.True(t, or.GetNodeUUID(or.ctx, "ns").Equals(nodeID)) // cached } @@ -154,34 +158,37 @@ func TestGetStatusVerifierLookupFail(t *testing.T) { coreconfig.Reset() config.Set(coreconfig.NamespacesDefault, "default") - config.Set(coreconfig.OrgName, "org1") config.Set(coreconfig.NodeName, "node1") orgID := fftypes.NewUUID() nodeID := fftypes.NewUUID() mim := or.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", or.ctx).Return(&core.Identity{ + mim.On("GetMultipartyRootOrg", or.ctx, "ns").Return(&core.Identity{ IdentityBase: core.IdentityBase{ - ID: orgID, - Name: "org1", - DID: "did:firefly:org/org1", + ID: orgID, + Name: "org1", + Namespace: "ns", + DID: "did:firefly:org/org1", }, }, nil) - mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(&core.Identity{ + mim.On("CachedIdentityLookupNilOK", or.ctx, "ns", "did:firefly:node/node1").Return(&core.Identity{ IdentityBase: core.IdentityBase{ ID: nodeID, Name: "node1", Parent: fftypes.NewUUID(), }, }, false, nil) - nmn := or.networkmap.(*networkmapmocks.Manager) - nmn.On("GetIdentityVerifiers", or.ctx, core.SystemNamespace, orgID.String(), mock.Anything).Return(nil, nil, fmt.Errorf("pop")) + mnm := or.networkmap.(*networkmapmocks.Manager) + mnm.On("GetIdentityVerifiers", or.ctx, "ns", orgID.String(), mock.Anything).Return(nil, nil, fmt.Errorf("pop")) + + mns := or.namespace.(*namespacemocks.Manager) + mns.On("GetMultipartyConfig", "ns", coreconfig.OrgName).Return("org1") mem := or.events.(*eventmocks.EventManager) mem.On("GetPlugins").Return(mockEventPlugins) - _, err := or.GetStatus(or.ctx) + _, err := or.GetStatus(or.ctx, "ns") assert.Regexp(t, "pop", err) } @@ -191,42 +198,45 @@ func TestGetStatusWrongNodeOwner(t *testing.T) { coreconfig.Reset() config.Set(coreconfig.NamespacesDefault, "default") - config.Set(coreconfig.OrgName, "org1") config.Set(coreconfig.NodeName, "node1") orgID := fftypes.NewUUID() nodeID := fftypes.NewUUID() mim := or.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", or.ctx).Return(&core.Identity{ + mim.On("GetMultipartyRootOrg", or.ctx, "ns").Return(&core.Identity{ IdentityBase: core.IdentityBase{ - ID: orgID, - Name: "org1", - DID: "did:firefly:org/org1", + ID: orgID, + Name: "org1", + Namespace: "ns", + DID: "did:firefly:org/org1", }, }, nil) - mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(&core.Identity{ + mim.On("CachedIdentityLookupNilOK", or.ctx, "ns", "did:firefly:node/node1").Return(&core.Identity{ IdentityBase: core.IdentityBase{ ID: nodeID, Name: "node1", Parent: fftypes.NewUUID(), }, }, false, nil) - nmn := or.networkmap.(*networkmapmocks.Manager) - nmn.On("GetIdentityVerifiers", or.ctx, core.SystemNamespace, orgID.String(), mock.Anything).Return([]*core.Verifier{ + mnm := or.networkmap.(*networkmapmocks.Manager) + mnm.On("GetIdentityVerifiers", or.ctx, "ns", orgID.String(), mock.Anything).Return([]*core.Verifier{ {Hash: fftypes.NewRandB32(), VerifierRef: core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: "0x12345", }}, }, nil, nil) + mns := or.namespace.(*namespacemocks.Manager) + mns.On("GetMultipartyConfig", "ns", coreconfig.OrgName).Return("org1") + mem := or.events.(*eventmocks.EventManager) mem.On("GetPlugins").Return(mockEventPlugins) - status, err := or.GetStatus(or.ctx) + status, err := or.GetStatus(or.ctx, "ns") assert.NoError(t, err) - assert.Equal(t, "default", status.Defaults.Namespace) + assert.Equal(t, "ns", status.Namespace) assert.Equal(t, "org1", status.Org.Name) assert.True(t, status.Org.Registered) @@ -244,19 +254,21 @@ func TestGetStatusUnregistered(t *testing.T) { coreconfig.Reset() config.Set(coreconfig.NamespacesDefault, "default") - config.Set(coreconfig.OrgName, "org1") config.Set(coreconfig.NodeName, "node1") mim := or.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", or.ctx).Return(nil, fmt.Errorf("pop")) + mim.On("GetMultipartyRootOrg", or.ctx, "ns").Return(nil, fmt.Errorf("pop")) + + mns := or.namespace.(*namespacemocks.Manager) + mns.On("GetMultipartyConfig", "ns", coreconfig.OrgName).Return("org1") mem := or.events.(*eventmocks.EventManager) mem.On("GetPlugins").Return(mockEventPlugins) - status, err := or.GetStatus(or.ctx) + status, err := or.GetStatus(or.ctx, "ns") assert.NoError(t, err) - assert.Equal(t, "default", status.Defaults.Namespace) + assert.Equal(t, "ns", status.Namespace) assert.Equal(t, "org1", status.Org.Name) assert.False(t, status.Org.Registered) @@ -264,7 +276,7 @@ func TestGetStatusUnregistered(t *testing.T) { assert.Equal(t, "node1", status.Node.Name) assert.False(t, status.Node.Registered) - assert.Nil(t, or.GetNodeUUID(or.ctx)) + assert.Nil(t, or.GetNodeUUID(or.ctx, "ns")) } @@ -273,35 +285,38 @@ func TestGetStatusOrgOnlyRegistered(t *testing.T) { coreconfig.Reset() config.Set(coreconfig.NamespacesDefault, "default") - config.Set(coreconfig.OrgName, "org1") config.Set(coreconfig.NodeName, "node1") orgID := fftypes.NewUUID() mim := or.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", or.ctx).Return(&core.Identity{ + mim.On("GetMultipartyRootOrg", or.ctx, "ns").Return(&core.Identity{ IdentityBase: core.IdentityBase{ - ID: orgID, - Name: "org1", - DID: "did:firefly:org/org1", + ID: orgID, + Name: "org1", + Namespace: "ns", + DID: "did:firefly:org/org1", }, }, nil) - mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(nil, false, nil) - nmn := or.networkmap.(*networkmapmocks.Manager) - nmn.On("GetIdentityVerifiers", or.ctx, core.SystemNamespace, orgID.String(), mock.Anything).Return([]*core.Verifier{ + mim.On("CachedIdentityLookupNilOK", or.ctx, "ns", "did:firefly:node/node1").Return(nil, false, nil) + mnm := or.networkmap.(*networkmapmocks.Manager) + mnm.On("GetIdentityVerifiers", or.ctx, "ns", orgID.String(), mock.Anything).Return([]*core.Verifier{ {Hash: fftypes.NewRandB32(), VerifierRef: core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: "0x12345", }}, }, nil, nil) + mns := or.namespace.(*namespacemocks.Manager) + mns.On("GetMultipartyConfig", "ns", coreconfig.OrgName).Return("org1") + mem := or.events.(*eventmocks.EventManager) mem.On("GetPlugins").Return(mockEventPlugins) - status, err := or.GetStatus(or.ctx) + status, err := or.GetStatus(or.ctx, "ns") assert.NoError(t, err) - assert.Equal(t, "default", status.Defaults.Namespace) + assert.Equal(t, "ns", status.Namespace) assert.Equal(t, "org1", status.Org.Name) assert.True(t, status.Org.Registered) @@ -320,7 +335,7 @@ func TestGetStatusOrgOnlyRegistered(t *testing.T) { assert.ElementsMatch(t, pluginsResult.SharedStorage, status.Plugins.SharedStorage) assert.ElementsMatch(t, pluginsResult.Tokens, status.Plugins.Tokens) - assert.Nil(t, or.GetNodeUUID(or.ctx)) + assert.Nil(t, or.GetNodeUUID(or.ctx, "ns")) } func TestGetStatusNodeError(t *testing.T) { @@ -328,34 +343,37 @@ func TestGetStatusNodeError(t *testing.T) { coreconfig.Reset() config.Set(coreconfig.NamespacesDefault, "default") - config.Set(coreconfig.OrgName, "org1") config.Set(coreconfig.NodeName, "node1") orgID := fftypes.NewUUID() mim := or.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", or.ctx).Return(&core.Identity{ + mim.On("GetMultipartyRootOrg", or.ctx, "ns").Return(&core.Identity{ IdentityBase: core.IdentityBase{ - ID: orgID, - Name: "org1", - DID: "did:firefly:org/org1", + ID: orgID, + Name: "org1", + Namespace: "ns", + DID: "did:firefly:org/org1", }, }, nil) - mim.On("CachedIdentityLookupNilOK", or.ctx, "did:firefly:node/node1").Return(nil, false, fmt.Errorf("pop")) - nmn := or.networkmap.(*networkmapmocks.Manager) - nmn.On("GetIdentityVerifiers", or.ctx, core.SystemNamespace, orgID.String(), mock.Anything).Return([]*core.Verifier{ + mim.On("CachedIdentityLookupNilOK", or.ctx, "ns", "did:firefly:node/node1").Return(nil, false, fmt.Errorf("pop")) + mnm := or.networkmap.(*networkmapmocks.Manager) + mnm.On("GetIdentityVerifiers", or.ctx, "ns", orgID.String(), mock.Anything).Return([]*core.Verifier{ {Hash: fftypes.NewRandB32(), VerifierRef: core.VerifierRef{ Type: core.VerifierTypeEthAddress, Value: "0x12345", }}, }, nil, nil) + mns := or.namespace.(*namespacemocks.Manager) + mns.On("GetMultipartyConfig", "ns", coreconfig.OrgName).Return("org1") + mem := or.events.(*eventmocks.EventManager) mem.On("GetPlugins").Return(mockEventPlugins) - _, err := or.GetStatus(or.ctx) + _, err := or.GetStatus(or.ctx, "ns") assert.EqualError(t, err, "pop") - assert.Nil(t, or.GetNodeUUID(or.ctx)) + assert.Nil(t, or.GetNodeUUID(or.ctx, "ns")) } diff --git a/internal/privatemessaging/groupmanager_test.go b/internal/privatemessaging/groupmanager_test.go index 9a1768a00..b38871749 100644 --- a/internal/privatemessaging/groupmanager_test.go +++ b/internal/privatemessaging/groupmanager_test.go @@ -92,7 +92,7 @@ func TestResolveInitGroupMissingData(t *testing.T) { _, err := pm.ResolveInitGroup(pm.ctx, &core.Message{ Header: core.MessageHeader{ ID: fftypes.NewUUID(), - Namespace: core.SystemNamespace, + Namespace: "ns1", Tag: core.SystemTagDefineGroup, Group: fftypes.NewRandB32(), SignerRef: core.SignerRef{ @@ -117,7 +117,7 @@ func TestResolveInitGroupBadData(t *testing.T) { _, err := pm.ResolveInitGroup(pm.ctx, &core.Message{ Header: core.MessageHeader{ ID: fftypes.NewUUID(), - Namespace: core.SystemNamespace, + Namespace: "ns1", Tag: core.SystemTagDefineGroup, Group: fftypes.NewRandB32(), SignerRef: core.SignerRef{ @@ -142,7 +142,7 @@ func TestResolveInitGroupBadValidation(t *testing.T) { _, err := pm.ResolveInitGroup(pm.ctx, &core.Message{ Header: core.MessageHeader{ ID: fftypes.NewUUID(), - Namespace: core.SystemNamespace, + Namespace: "ns1", Tag: core.SystemTagDefineGroup, Group: fftypes.NewRandB32(), SignerRef: core.SignerRef{ @@ -180,7 +180,7 @@ func TestResolveInitGroupBadGroupID(t *testing.T) { _, err := pm.ResolveInitGroup(pm.ctx, &core.Message{ Header: core.MessageHeader{ ID: fftypes.NewUUID(), - Namespace: core.SystemNamespace, + Namespace: "ns1", Tag: core.SystemTagDefineGroup, Group: fftypes.NewRandB32(), SignerRef: core.SignerRef{ @@ -220,7 +220,7 @@ func TestResolveInitGroupUpsertFail(t *testing.T) { _, err := pm.ResolveInitGroup(pm.ctx, &core.Message{ Header: core.MessageHeader{ ID: fftypes.NewUUID(), - Namespace: core.SystemNamespace, + Namespace: "ns1", Tag: core.SystemTagDefineGroup, Group: group.Hash, SignerRef: core.SignerRef{ @@ -261,7 +261,7 @@ func TestResolveInitGroupNewOk(t *testing.T) { group, err := pm.ResolveInitGroup(pm.ctx, &core.Message{ Header: core.MessageHeader{ ID: fftypes.NewUUID(), - Namespace: core.SystemNamespace, + Namespace: "ns1", Tag: core.SystemTagDefineGroup, Group: group.Hash, SignerRef: core.SignerRef{ diff --git a/internal/privatemessaging/message_test.go b/internal/privatemessaging/message_test.go index f8b00970d..2998c0774 100644 --- a/internal/privatemessaging/message_test.go +++ b/internal/privatemessaging/message_test.go @@ -41,7 +41,7 @@ func newTestOrg(name string) *core.Identity { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), Type: core.IdentityTypeOrg, - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: name, Parent: nil, }, @@ -55,7 +55,7 @@ func newTestNode(name string, owner *core.Identity) *core.Identity { IdentityBase: core.IdentityBase{ ID: fftypes.NewUUID(), Type: core.IdentityTypeNode, - Namespace: core.SystemNamespace, + Namespace: "ns1", Name: name, Parent: owner.ID, }, @@ -81,8 +81,8 @@ func TestSendConfirmMessageE2EOk(t *testing.T) { intermediateOrg.Parent = rootOrg.ID localNode := newTestNode("node1", intermediateOrg) mim.On("ResolveInputSigningIdentity", pm.ctx, "ns1", mock.Anything).Return(nil) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(intermediateOrg, nil) - mim.On("CachedIdentityLookupMustExist", pm.ctx, "org1").Return(intermediateOrg, false, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(intermediateOrg, nil) + mim.On("CachedIdentityLookupMustExist", pm.ctx, "ns1", "org1").Return(intermediateOrg, false, nil) mim.On("CachedIdentityLookupByID", pm.ctx, rootOrg.ID).Return(rootOrg, nil) mdm := pm.data.(*datamocks.Manager) @@ -232,13 +232,13 @@ func TestResolveAndSendBadInlineData(t *testing.T) { localOrg := newTestOrg("localorg") localNode := newTestNode("node1", localOrg) mim.On("ResolveInputSigningIdentity", pm.ctx, "ns1", mock.Anything).Return(nil) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localOrg, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localOrg, nil) mim.On("ResolveInputSigningIdentity", pm.ctx, "ns1", mock.Anything).Run(func(args mock.Arguments) { identity := args[2].(*core.SignerRef) identity.Author = "localorg" identity.Key = "localkey" }).Return(nil) - mim.On("CachedIdentityLookupMustExist", pm.ctx, "localorg").Return(localOrg, false, nil) + mim.On("CachedIdentityLookupMustExist", pm.ctx, "ns1", "localorg").Return(localOrg, false, nil) mdi := pm.database.(*databasemocks.Plugin) mdi.On("GetIdentities", pm.ctx, mock.Anything).Return([]*core.Identity{localNode}, nil, nil).Once() @@ -351,13 +351,13 @@ func TestMessagePrepare(t *testing.T) { localOrg := newTestOrg("localorg") localNode := newTestNode("node1", localOrg) mim.On("ResolveInputSigningIdentity", pm.ctx, "ns1", mock.Anything).Return(nil) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localOrg, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localOrg, nil) mim.On("ResolveInputSigningIdentity", pm.ctx, "ns1", mock.Anything).Run(func(args mock.Arguments) { identity := args[2].(*core.SignerRef) identity.Author = "localorg" identity.Key = "localkey" }).Return(nil) - mim.On("CachedIdentityLookupMustExist", pm.ctx, "localorg").Return(localOrg, false, nil) + mim.On("CachedIdentityLookupMustExist", pm.ctx, "ns1", "localorg").Return(localOrg, false, nil) mdi := pm.database.(*databasemocks.Plugin) mdi.On("GetIdentities", pm.ctx, mock.Anything).Return([]*core.Identity{localNode}, nil, nil).Once() @@ -696,7 +696,7 @@ func TestDispatchedUnpinnedMessageOK(t *testing.T) { assert.Equal(t, "localorg", identity.Author) return true })).Return(nil) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localOrg, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localOrg, nil) mdx := pm.exchange.(*dataexchangemocks.Plugin) mdx.On("SendMessage", pm.ctx, mock.Anything, "node2-peer", mock.Anything).Return(nil) @@ -724,8 +724,9 @@ func TestDispatchedUnpinnedMessageOK(t *testing.T) { err := pm.dispatchUnpinnedBatch(pm.ctx, &batch.DispatchState{ Persisted: core.BatchPersisted{ BatchHeader: core.BatchHeader{ - ID: fftypes.NewUUID(), - Group: groupID, + ID: fftypes.NewUUID(), + Group: groupID, + Namespace: "ns1", }, }, Messages: []*core.Message{ @@ -762,7 +763,7 @@ func TestSendDataTransferBlobsFail(t *testing.T) { assert.Equal(t, "localorg", identity.Author) return true })).Return(nil) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localOrg, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localOrg, nil) mdi := pm.database.(*databasemocks.Plugin) mdi.On("GetBlobMatchingHash", pm.ctx, mock.Anything).Return(nil, fmt.Errorf("pop")) @@ -770,8 +771,9 @@ func TestSendDataTransferBlobsFail(t *testing.T) { err := pm.sendData(pm.ctx, &core.TransportWrapper{ Batch: &core.Batch{ BatchHeader: core.BatchHeader{ - ID: fftypes.NewUUID(), - Group: groupID, + ID: fftypes.NewUUID(), + Group: groupID, + Namespace: "ns1", }, Payload: core.BatchPayload{ Messages: []*core.Message{ @@ -810,7 +812,7 @@ func TestSendDataTransferFail(t *testing.T) { nodes := []*core.Identity{node2} mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localOrg, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localOrg, nil) mom := pm.operations.(*operationmocks.Manager) mom.On("AddOrReuseOperation", pm.ctx, mock.Anything).Return(nil) @@ -822,8 +824,9 @@ func TestSendDataTransferFail(t *testing.T) { err := pm.sendData(pm.ctx, &core.TransportWrapper{ Batch: &core.Batch{ BatchHeader: core.BatchHeader{ - ID: fftypes.NewUUID(), - Group: groupID, + ID: fftypes.NewUUID(), + Group: groupID, + Namespace: "ns1", }, Payload: core.BatchPayload{ Messages: []*core.Message{ @@ -862,7 +865,7 @@ func TestSendDataTransferInsertOperationFail(t *testing.T) { assert.Equal(t, "localorg", identity.Author) return true })).Return(nil) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localOrg, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localOrg, nil) mom := pm.operations.(*operationmocks.Manager) mom.On("AddOrReuseOperation", pm.ctx, mock.Anything).Return(fmt.Errorf("pop")) @@ -870,8 +873,9 @@ func TestSendDataTransferInsertOperationFail(t *testing.T) { err := pm.sendData(pm.ctx, &core.TransportWrapper{ Batch: &core.Batch{ BatchHeader: core.BatchHeader{ - ID: fftypes.NewUUID(), - Group: groupID, + ID: fftypes.NewUUID(), + Group: groupID, + Namespace: "ns1", }, Payload: core.BatchPayload{ Messages: []*core.Message{ diff --git a/internal/privatemessaging/privatemessaging.go b/internal/privatemessaging/privatemessaging.go index 3d2fc2dfb..af2cfa0fb 100644 --- a/internal/privatemessaging/privatemessaging.go +++ b/internal/privatemessaging/privatemessaging.go @@ -269,7 +269,7 @@ func (pm *privateMessaging) sendData(ctx context.Context, tw *core.TransportWrap batch := tw.Batch // Lookup the local org - localOrg, err := pm.identity.GetNodeOwnerOrg(ctx) + localOrg, err := pm.identity.GetMultipartyRootOrg(ctx, batch.Namespace) if err != nil { return err } diff --git a/internal/privatemessaging/privatemessaging_test.go b/internal/privatemessaging/privatemessaging_test.go index 93f0393b1..9c0f167fe 100644 --- a/internal/privatemessaging/privatemessaging_test.go +++ b/internal/privatemessaging/privatemessaging_test.go @@ -135,7 +135,7 @@ func TestDispatchBatchWithBlobs(t *testing.T) { mim := pm.identity.(*identitymanagermocks.Manager) mom := pm.operations.(*operationmocks.Manager) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localOrg, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localOrg, nil) mdi.On("GetGroupByHash", pm.ctx, groupID).Return(&core.Group{ Hash: fftypes.NewRandB32(), GroupIdentity: core.GroupIdentity{ @@ -274,7 +274,7 @@ func TestSendAndSubmitBatchUnregisteredNode(t *testing.T) { }, nil) mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(nil, fmt.Errorf("pop")) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(nil, fmt.Errorf("pop")) err := pm.dispatchPinnedBatch(pm.ctx, &batch.DispatchState{ Persisted: core.BatchPersisted{ @@ -283,6 +283,7 @@ func TestSendAndSubmitBatchUnregisteredNode(t *testing.T) { SignerRef: core.SignerRef{ Author: "badauthor", }, + Namespace: "ns1", }, }, }) @@ -323,7 +324,7 @@ func TestSendSubmitInsertOperationFail(t *testing.T) { node2 := newTestNode("node2", newTestOrg("remoteorg")) mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localOrg, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localOrg, nil) mdi := pm.database.(*databasemocks.Plugin) mdi.On("GetIdentityByID", pm.ctx, node1.ID).Return(node1, nil).Once() @@ -348,6 +349,7 @@ func TestSendSubmitInsertOperationFail(t *testing.T) { SignerRef: core.SignerRef{ Author: "org1", }, + Namespace: "ns1", }, }, }) @@ -368,7 +370,7 @@ func TestSendSubmitBlobTransferFail(t *testing.T) { blob1 := fftypes.NewRandB32() mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localOrg, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localOrg, nil) mdi := pm.database.(*databasemocks.Plugin) mdi.On("GetIdentityByID", pm.ctx, node1.ID).Return(node1, nil).Once() @@ -403,6 +405,7 @@ func TestSendSubmitBlobTransferFail(t *testing.T) { SignerRef: core.SignerRef{ Author: "org1", }, + Namespace: "ns1", }, }, Data: core.DataArray{ @@ -427,7 +430,7 @@ func TestWriteTransactionSubmitBatchPinFail(t *testing.T) { blob1 := fftypes.NewRandB32() mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localOrg, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localOrg, nil) mdi := pm.database.(*databasemocks.Plugin) mdi.On("GetIdentityByID", pm.ctx, node1.ID).Return(node1, nil).Once() @@ -475,6 +478,7 @@ func TestWriteTransactionSubmitBatchPinFail(t *testing.T) { SignerRef: core.SignerRef{ Author: "org1", }, + Namespace: "ns1", }, }, Data: core.DataArray{ diff --git a/internal/privatemessaging/recipients.go b/internal/privatemessaging/recipients.go index 3230a23e2..be3954f90 100644 --- a/internal/privatemessaging/recipients.go +++ b/internal/privatemessaging/recipients.go @@ -79,7 +79,7 @@ func (pm *privateMessaging) getFirstNodeForOrg(ctx context.Context, identity *co func (pm *privateMessaging) resolveNode(ctx context.Context, identity *core.Identity, nodeInput string) (node *core.Identity, err error) { retryable := true if nodeInput != "" { - node, retryable, err = pm.identity.CachedIdentityLookupMustExist(ctx, nodeInput) + node, retryable, err = pm.identity.CachedIdentityLookupMustExist(ctx, identity.Namespace, nodeInput) } else { // Find any node owned by this organization inputIdentityDebugInfo := fmt.Sprintf("%s (%s)", identity.DID, identity.ID) @@ -107,7 +107,7 @@ func (pm *privateMessaging) resolveNode(ctx context.Context, identity *core.Iden func (pm *privateMessaging) getRecipients(ctx context.Context, in *core.MessageInOut) (gi *core.GroupIdentity, err error) { - localOrg, err := pm.identity.GetNodeOwnerOrg(ctx) + localOrg, err := pm.identity.GetMultipartyRootOrg(ctx, in.Header.Namespace) if err != nil { return nil, err } @@ -120,7 +120,7 @@ func (pm *privateMessaging) getRecipients(ctx context.Context, in *core.MessageI } for i, rInput := range in.Group.Members { // Resolve the identity - identity, _, err := pm.identity.CachedIdentityLookupMustExist(ctx, rInput.Identity) + identity, _, err := pm.identity.CachedIdentityLookupMustExist(ctx, in.Header.Namespace, rInput.Identity) if err != nil { return nil, err } diff --git a/internal/privatemessaging/recipients_test.go b/internal/privatemessaging/recipients_test.go index f8cde3ea3..96f4a910b 100644 --- a/internal/privatemessaging/recipients_test.go +++ b/internal/privatemessaging/recipients_test.go @@ -48,8 +48,8 @@ func TestResolveMemberListNewGroupE2E(t *testing.T) { mdi.On("UpsertGroup", pm.ctx, mock.Anything, database.UpsertOptimizationNew).Return(nil) mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("CachedIdentityLookupMustExist", pm.ctx, "remoteorg").Return(remoteOrg, false, nil) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localOrg, nil) + mim.On("CachedIdentityLookupMustExist", pm.ctx, "ns1", "remoteorg").Return(remoteOrg, false, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localOrg, nil) ud := mdi.On("UpsertData", pm.ctx, mock.Anything, database.UpsertOptimizationNew).Return(nil) ud.RunFn = func(a mock.Arguments) { data := a[1].(*core.Data) @@ -115,8 +115,8 @@ func TestResolveMemberListExistingGroup(t *testing.T) { mdi.On("GetIdentities", pm.ctx, mock.Anything).Return([]*core.Identity{localNode}, nil, nil) mdi.On("GetGroupByHash", pm.ctx, mock.Anything, mock.Anything).Return(&core.Group{Hash: fftypes.NewRandB32()}, nil, nil).Once() mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("CachedIdentityLookupMustExist", pm.ctx, "org1").Return(localOrg, false, nil) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localNode, nil) + mim.On("CachedIdentityLookupMustExist", pm.ctx, "ns1", "org1").Return(localOrg, false, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localNode, nil) err := pm.resolveRecipientList(pm.ctx, &core.MessageInOut{ Message: core.Message{ @@ -124,6 +124,7 @@ func TestResolveMemberListExistingGroup(t *testing.T) { SignerRef: core.SignerRef{ Author: "org1", }, + Namespace: "ns1", }, }, Group: &core.InputGroup{ @@ -147,8 +148,8 @@ func TestResolveMemberListLookupFail(t *testing.T) { localNode := newTestNode("node1", localOrg) mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("CachedIdentityLookupMustExist", pm.ctx, "org1").Return(nil, true, fmt.Errorf("pop")) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localNode, nil) + mim.On("CachedIdentityLookupMustExist", pm.ctx, "ns1", "org1").Return(nil, true, fmt.Errorf("pop")) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localNode, nil) err := pm.resolveRecipientList(pm.ctx, &core.MessageInOut{ Message: core.Message{ @@ -156,6 +157,7 @@ func TestResolveMemberListLookupFail(t *testing.T) { SignerRef: core.SignerRef{ Author: "org1", }, + Namespace: "ns1", }, }, Group: &core.InputGroup{ @@ -181,8 +183,8 @@ func TestResolveMemberListGetGroupsFail(t *testing.T) { mdi.On("GetIdentities", pm.ctx, mock.Anything).Return([]*core.Identity{localNode}, nil, nil) mdi.On("GetGroupByHash", pm.ctx, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("CachedIdentityLookupMustExist", pm.ctx, "org1").Return(localOrg, false, nil) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localNode, nil) + mim.On("CachedIdentityLookupMustExist", pm.ctx, "ns1", "org1").Return(localOrg, false, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localNode, nil) err := pm.resolveRecipientList(pm.ctx, &core.MessageInOut{ Message: core.Message{ @@ -190,6 +192,7 @@ func TestResolveMemberListGetGroupsFail(t *testing.T) { SignerRef: core.SignerRef{ Author: "org1", }, + Namespace: "ns1", }, }, Group: &core.InputGroup{ @@ -210,7 +213,7 @@ func TestResolveMemberListLocalOrgUnregistered(t *testing.T) { defer cancel() mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(nil, fmt.Errorf("pop")) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(nil, fmt.Errorf("pop")) err := pm.resolveRecipientList(pm.ctx, &core.MessageInOut{ Message: core.Message{ @@ -218,6 +221,7 @@ func TestResolveMemberListLocalOrgUnregistered(t *testing.T) { SignerRef: core.SignerRef{ Author: "org1", }, + Namespace: "ns1", }, }, Group: &core.InputGroup{ @@ -247,8 +251,8 @@ func TestResolveMemberListMissingLocalMemberLookupFailed(t *testing.T) { mdi.On("GetIdentities", pm.ctx, mock.Anything).Return([]*core.Identity{localNode}, nil, fmt.Errorf("pop")).Once() mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("CachedIdentityLookupMustExist", pm.ctx, "org1").Return(localOrg, false, nil) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localNode, nil) + mim.On("CachedIdentityLookupMustExist", pm.ctx, "ns1", "org1").Return(localOrg, false, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localNode, nil) err := pm.resolveRecipientList(pm.ctx, &core.MessageInOut{ Message: core.Message{ @@ -256,6 +260,7 @@ func TestResolveMemberListMissingLocalMemberLookupFailed(t *testing.T) { SignerRef: core.SignerRef{ Author: "org1", }, + Namespace: "ns1", }, }, Group: &core.InputGroup{ @@ -282,8 +287,8 @@ func TestResolveMemberListNodeNotFound(t *testing.T) { mdi.On("GetIdentities", pm.ctx, mock.Anything).Return([]*core.Identity{}, nil, nil).Once() mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("CachedIdentityLookupMustExist", pm.ctx, "org1").Return(localOrg, false, nil) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(localNode, nil) + mim.On("CachedIdentityLookupMustExist", pm.ctx, "ns1", "org1").Return(localOrg, false, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(localNode, nil) err := pm.resolveRecipientList(pm.ctx, &core.MessageInOut{ Message: core.Message{ @@ -291,6 +296,7 @@ func TestResolveMemberListNodeNotFound(t *testing.T) { SignerRef: core.SignerRef{ Author: "org1", }, + Namespace: "ns1", }, }, Group: &core.InputGroup{ @@ -320,8 +326,8 @@ func TestResolveMemberNodeOwnedParentOrg(t *testing.T) { mdi.On("GetIdentities", pm.ctx, mock.Anything).Return([]*core.Identity{localNode}, nil, nil) mdi.On("GetGroupByHash", pm.ctx, mock.Anything, mock.Anything).Return(&core.Group{Hash: fftypes.NewRandB32()}, nil, nil).Once() mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("GetNodeOwnerOrg", pm.ctx).Return(parentOrg, nil) - mim.On("CachedIdentityLookupMustExist", pm.ctx, "org1").Return(childOrg, false, nil) + mim.On("GetMultipartyRootOrg", pm.ctx, "ns1").Return(parentOrg, nil) + mim.On("CachedIdentityLookupMustExist", pm.ctx, "ns1", "org1").Return(childOrg, false, nil) mim.On("CachedIdentityLookupByID", pm.ctx, parentOrg.ID).Return(parentOrg, nil) err := pm.resolveRecipientList(pm.ctx, &core.MessageInOut{ @@ -330,6 +336,7 @@ func TestResolveMemberNodeOwnedParentOrg(t *testing.T) { SignerRef: core.SignerRef{ Author: "org1", }, + Namespace: "ns1", }, }, Group: &core.InputGroup{ @@ -349,7 +356,7 @@ func TestGetNodeFail(t *testing.T) { defer cancel() mim := pm.identity.(*identitymanagermocks.Manager) - mim.On("CachedIdentityLookupMustExist", pm.ctx, "id-node1").Return(nil, true, fmt.Errorf("pop")) + mim.On("CachedIdentityLookupMustExist", pm.ctx, "ns1", "id-node1").Return(nil, true, fmt.Errorf("pop")) _, err := pm.resolveNode(pm.ctx, newTestOrg("org1"), "id-node1") assert.Regexp(t, "pop", err) diff --git a/internal/sysmessaging/localnodeinfo.go b/internal/sysmessaging/localnodeinfo.go index 60ad1aa90..586e31829 100644 --- a/internal/sysmessaging/localnodeinfo.go +++ b/internal/sysmessaging/localnodeinfo.go @@ -24,6 +24,6 @@ import ( // LocalNodeInfo provides an interface to query the local node info type LocalNodeInfo interface { - // GetNodeUUID returns the local node UUID, or nil if the node is not yet registered. It is cached for fast access - GetNodeUUID(ctx context.Context) *fftypes.UUID + // GetNodeUUID returns the local node UUID in a namespace, or nil if the node is not yet registered. It is cached for fast access + GetNodeUUID(ctx context.Context, ns string) *fftypes.UUID } diff --git a/mocks/blockchainmocks/plugin.go b/mocks/blockchainmocks/plugin.go index 3950ae24f..557e9702c 100644 --- a/mocks/blockchainmocks/plugin.go +++ b/mocks/blockchainmocks/plugin.go @@ -187,6 +187,27 @@ func (_m *Plugin) Name() string { return r0 } +// NetworkVersion provides a mock function with given fields: ctx +func (_m *Plugin) NetworkVersion(ctx context.Context) (int, error) { + ret := _m.Called(ctx) + + var r0 int + if rf, ok := ret.Get(0).(func(context.Context) int); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(int) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // NormalizeContractLocation provides a mock function with given fields: ctx, location func (_m *Plugin) NormalizeContractLocation(ctx context.Context, location *fftypes.JSONAny) (*fftypes.JSONAny, error) { ret := _m.Called(ctx, location) diff --git a/mocks/broadcastmocks/manager.go b/mocks/broadcastmocks/manager.go index fcec958a9..4a1f658ed 100644 --- a/mocks/broadcastmocks/manager.go +++ b/mocks/broadcastmocks/manager.go @@ -133,29 +133,6 @@ func (_m *Manager) BroadcastMessage(ctx context.Context, ns string, in *core.Mes return r0, r1 } -// BroadcastNamespace provides a mock function with given fields: ctx, ns, waitConfirm -func (_m *Manager) BroadcastNamespace(ctx context.Context, ns *core.Namespace, waitConfirm bool) (*core.Message, error) { - ret := _m.Called(ctx, ns, waitConfirm) - - var r0 *core.Message - if rf, ok := ret.Get(0).(func(context.Context, *core.Namespace, bool) *core.Message); ok { - r0 = rf(ctx, ns, waitConfirm) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*core.Message) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *core.Namespace, bool) error); ok { - r1 = rf(ctx, ns, waitConfirm) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // BroadcastTokenPool provides a mock function with given fields: ctx, ns, pool, waitConfirm func (_m *Manager) BroadcastTokenPool(ctx context.Context, ns string, pool *core.TokenPoolAnnouncement, waitConfirm bool) (*core.Message, error) { ret := _m.Called(ctx, ns, pool, waitConfirm) diff --git a/mocks/identitymanagermocks/manager.go b/mocks/identitymanagermocks/manager.go index 51c1de547..dfc85a778 100644 --- a/mocks/identitymanagermocks/manager.go +++ b/mocks/identitymanagermocks/manager.go @@ -39,13 +39,13 @@ func (_m *Manager) CachedIdentityLookupByID(ctx context.Context, id *fftypes.UUI return r0, r1 } -// CachedIdentityLookupMustExist provides a mock function with given fields: ctx, did -func (_m *Manager) CachedIdentityLookupMustExist(ctx context.Context, did string) (*core.Identity, bool, error) { - ret := _m.Called(ctx, did) +// CachedIdentityLookupMustExist provides a mock function with given fields: ctx, namespace, did +func (_m *Manager) CachedIdentityLookupMustExist(ctx context.Context, namespace string, did string) (*core.Identity, bool, error) { + ret := _m.Called(ctx, namespace, did) var r0 *core.Identity - if rf, ok := ret.Get(0).(func(context.Context, string) *core.Identity); ok { - r0 = rf(ctx, did) + if rf, ok := ret.Get(0).(func(context.Context, string, string) *core.Identity); ok { + r0 = rf(ctx, namespace, did) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*core.Identity) @@ -53,15 +53,15 @@ func (_m *Manager) CachedIdentityLookupMustExist(ctx context.Context, did string } var r1 bool - if rf, ok := ret.Get(1).(func(context.Context, string) bool); ok { - r1 = rf(ctx, did) + if rf, ok := ret.Get(1).(func(context.Context, string, string) bool); ok { + r1 = rf(ctx, namespace, did) } else { r1 = ret.Get(1).(bool) } var r2 error - if rf, ok := ret.Get(2).(func(context.Context, string) error); ok { - r2 = rf(ctx, did) + if rf, ok := ret.Get(2).(func(context.Context, string, string) error); ok { + r2 = rf(ctx, namespace, did) } else { r2 = ret.Error(2) } @@ -69,13 +69,13 @@ func (_m *Manager) CachedIdentityLookupMustExist(ctx context.Context, did string return r0, r1, r2 } -// CachedIdentityLookupNilOK provides a mock function with given fields: ctx, did -func (_m *Manager) CachedIdentityLookupNilOK(ctx context.Context, did string) (*core.Identity, bool, error) { - ret := _m.Called(ctx, did) +// CachedIdentityLookupNilOK provides a mock function with given fields: ctx, namespace, did +func (_m *Manager) CachedIdentityLookupNilOK(ctx context.Context, namespace string, did string) (*core.Identity, bool, error) { + ret := _m.Called(ctx, namespace, did) var r0 *core.Identity - if rf, ok := ret.Get(0).(func(context.Context, string) *core.Identity); ok { - r0 = rf(ctx, did) + if rf, ok := ret.Get(0).(func(context.Context, string, string) *core.Identity); ok { + r0 = rf(ctx, namespace, did) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*core.Identity) @@ -83,15 +83,15 @@ func (_m *Manager) CachedIdentityLookupNilOK(ctx context.Context, did string) (* } var r1 bool - if rf, ok := ret.Get(1).(func(context.Context, string) bool); ok { - r1 = rf(ctx, did) + if rf, ok := ret.Get(1).(func(context.Context, string, string) bool); ok { + r1 = rf(ctx, namespace, did) } else { r1 = ret.Get(1).(bool) } var r2 error - if rf, ok := ret.Get(2).(func(context.Context, string) error); ok { - r2 = rf(ctx, did) + if rf, ok := ret.Get(2).(func(context.Context, string, string) error); ok { + r2 = rf(ctx, namespace, did) } else { r2 = ret.Error(2) } @@ -99,13 +99,13 @@ func (_m *Manager) CachedIdentityLookupNilOK(ctx context.Context, did string) (* return r0, r1, r2 } -// CachedVerifierLookup provides a mock function with given fields: ctx, vType, ns, value -func (_m *Manager) CachedVerifierLookup(ctx context.Context, vType core.FFEnum, ns string, value string) (*core.Verifier, error) { - ret := _m.Called(ctx, vType, ns, value) +// CachedVerifierLookup provides a mock function with given fields: ctx, vType, namespace, value +func (_m *Manager) CachedVerifierLookup(ctx context.Context, vType core.FFEnum, namespace string, value string) (*core.Verifier, error) { + ret := _m.Called(ctx, vType, namespace, value) var r0 *core.Verifier if rf, ok := ret.Get(0).(func(context.Context, core.FFEnum, string, string) *core.Verifier); ok { - r0 = rf(ctx, vType, ns, value) + r0 = rf(ctx, vType, namespace, value) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*core.Verifier) @@ -114,7 +114,7 @@ func (_m *Manager) CachedVerifierLookup(ctx context.Context, vType core.FFEnum, var r1 error if rf, ok := ret.Get(1).(func(context.Context, core.FFEnum, string, string) error); ok { - r1 = rf(ctx, vType, ns, value) + r1 = rf(ctx, vType, namespace, value) } else { r1 = ret.Error(1) } @@ -145,22 +145,22 @@ func (_m *Manager) FindIdentityForVerifier(ctx context.Context, iTypes []core.FF return r0, r1 } -// GetNodeOwnerBlockchainKey provides a mock function with given fields: ctx -func (_m *Manager) GetNodeOwnerBlockchainKey(ctx context.Context) (*core.VerifierRef, error) { - ret := _m.Called(ctx) +// GetMultipartyRootOrg provides a mock function with given fields: ctx, namespace +func (_m *Manager) GetMultipartyRootOrg(ctx context.Context, namespace string) (*core.Identity, error) { + ret := _m.Called(ctx, namespace) - var r0 *core.VerifierRef - if rf, ok := ret.Get(0).(func(context.Context) *core.VerifierRef); ok { - r0 = rf(ctx) + var r0 *core.Identity + if rf, ok := ret.Get(0).(func(context.Context, string) *core.Identity); ok { + r0 = rf(ctx, namespace) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*core.VerifierRef) + r0 = ret.Get(0).(*core.Identity) } } var r1 error - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, namespace) } else { r1 = ret.Error(1) } @@ -168,22 +168,22 @@ func (_m *Manager) GetNodeOwnerBlockchainKey(ctx context.Context) (*core.Verifie return r0, r1 } -// GetNodeOwnerOrg provides a mock function with given fields: ctx -func (_m *Manager) GetNodeOwnerOrg(ctx context.Context) (*core.Identity, error) { - ret := _m.Called(ctx) +// GetMultipartyRootVerifier provides a mock function with given fields: ctx, namespace +func (_m *Manager) GetMultipartyRootVerifier(ctx context.Context, namespace string) (*core.VerifierRef, error) { + ret := _m.Called(ctx, namespace) - var r0 *core.Identity - if rf, ok := ret.Get(0).(func(context.Context) *core.Identity); ok { - r0 = rf(ctx) + var r0 *core.VerifierRef + if rf, ok := ret.Get(0).(func(context.Context, string) *core.VerifierRef); ok { + r0 = rf(ctx, namespace) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*core.Identity) + r0 = ret.Get(0).(*core.VerifierRef) } } var r1 error - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, namespace) } else { r1 = ret.Error(1) } @@ -191,20 +191,20 @@ func (_m *Manager) GetNodeOwnerOrg(ctx context.Context) (*core.Identity, error) return r0, r1 } -// NormalizeSigningKey provides a mock function with given fields: ctx, namespace, keyNormalizationMode -func (_m *Manager) NormalizeSigningKey(ctx context.Context, namespace string, keyNormalizationMode int) (string, error) { - ret := _m.Called(ctx, namespace, keyNormalizationMode) +// NormalizeSigningKey provides a mock function with given fields: ctx, namespace, inputKey, keyNormalizationMode +func (_m *Manager) NormalizeSigningKey(ctx context.Context, namespace string, inputKey string, keyNormalizationMode int) (string, error) { + ret := _m.Called(ctx, namespace, inputKey, keyNormalizationMode) var r0 string - if rf, ok := ret.Get(0).(func(context.Context, string, int) string); ok { - r0 = rf(ctx, namespace, keyNormalizationMode) + if rf, ok := ret.Get(0).(func(context.Context, string, string, int) string); ok { + r0 = rf(ctx, namespace, inputKey, keyNormalizationMode) } else { r0 = ret.Get(0).(string) } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string, int) error); ok { - r1 = rf(ctx, namespace, keyNormalizationMode) + if rf, ok := ret.Get(1).(func(context.Context, string, string, int) error); ok { + r1 = rf(ctx, namespace, inputKey, keyNormalizationMode) } else { r1 = ret.Error(1) } @@ -235,27 +235,13 @@ func (_m *Manager) ResolveIdentitySigner(ctx context.Context, _a1 *core.Identity return r0, r1 } -// ResolveInputSigningIdentity provides a mock function with given fields: ctx, namespace, msgSignerRef -func (_m *Manager) ResolveInputSigningIdentity(ctx context.Context, namespace string, msgSignerRef *core.SignerRef) error { - ret := _m.Called(ctx, namespace, msgSignerRef) +// ResolveInputSigningIdentity provides a mock function with given fields: ctx, namespace, signerRef +func (_m *Manager) ResolveInputSigningIdentity(ctx context.Context, namespace string, signerRef *core.SignerRef) error { + ret := _m.Called(ctx, namespace, signerRef) var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, *core.SignerRef) error); ok { - r0 = rf(ctx, namespace, msgSignerRef) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ResolveNodeOwnerSigningIdentity provides a mock function with given fields: ctx, msgSignerRef -func (_m *Manager) ResolveNodeOwnerSigningIdentity(ctx context.Context, msgSignerRef *core.SignerRef) error { - ret := _m.Called(ctx, msgSignerRef) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *core.SignerRef) error); ok { - r0 = rf(ctx, msgSignerRef) + r0 = rf(ctx, namespace, signerRef) } else { r0 = ret.Error(0) } diff --git a/mocks/namespacemocks/manager.go b/mocks/namespacemocks/manager.go index cec55ed53..297e12530 100644 --- a/mocks/namespacemocks/manager.go +++ b/mocks/namespacemocks/manager.go @@ -5,7 +5,10 @@ package namespacemocks import ( context "context" + config "github.com/hyperledger/firefly-common/pkg/config" + database "github.com/hyperledger/firefly/pkg/database" + mock "github.com/stretchr/testify/mock" ) @@ -14,6 +17,34 @@ type Manager struct { mock.Mock } +// GetDefaultKey provides a mock function with given fields: ns +func (_m *Manager) GetDefaultKey(ns string) string { + ret := _m.Called(ns) + + var r0 string + if rf, ok := ret.Get(0).(func(string) string); ok { + r0 = rf(ns) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// GetMultipartyConfig provides a mock function with given fields: ns, key +func (_m *Manager) GetMultipartyConfig(ns string, key config.RootKey) string { + ret := _m.Called(ns, key) + + var r0 string + if rf, ok := ret.Get(0).(func(string, config.RootKey) string); ok { + r0 = rf(ns, key) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + // Init provides a mock function with given fields: ctx, di func (_m *Manager) Init(ctx context.Context, di database.Plugin) error { ret := _m.Called(ctx, di) diff --git a/mocks/networkmapmocks/manager.go b/mocks/networkmapmocks/manager.go index 50498cf63..802446dec 100644 --- a/mocks/networkmapmocks/manager.go +++ b/mocks/networkmapmocks/manager.go @@ -18,13 +18,13 @@ type Manager struct { mock.Mock } -// GetDIDDocForIndentityByDID provides a mock function with given fields: ctx, did -func (_m *Manager) GetDIDDocForIndentityByDID(ctx context.Context, did string) (*networkmap.DIDDocument, error) { - ret := _m.Called(ctx, did) +// GetDIDDocForIndentityByDID provides a mock function with given fields: ctx, ns, did +func (_m *Manager) GetDIDDocForIndentityByDID(ctx context.Context, ns string, did string) (*networkmap.DIDDocument, error) { + ret := _m.Called(ctx, ns, did) var r0 *networkmap.DIDDocument - if rf, ok := ret.Get(0).(func(context.Context, string) *networkmap.DIDDocument); ok { - r0 = rf(ctx, did) + if rf, ok := ret.Get(0).(func(context.Context, string, string) *networkmap.DIDDocument); ok { + r0 = rf(ctx, ns, did) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*networkmap.DIDDocument) @@ -32,8 +32,8 @@ func (_m *Manager) GetDIDDocForIndentityByDID(ctx context.Context, did string) ( } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, did) + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, ns, did) } else { r1 = ret.Error(1) } @@ -96,38 +96,6 @@ func (_m *Manager) GetIdentities(ctx context.Context, ns string, filter database return r0, r1, r2 } -// GetIdentitiesGlobal provides a mock function with given fields: ctx, filter -func (_m *Manager) GetIdentitiesGlobal(ctx context.Context, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) { - ret := _m.Called(ctx, filter) - - var r0 []*core.Identity - if rf, ok := ret.Get(0).(func(context.Context, database.AndFilter) []*core.Identity); ok { - r0 = rf(ctx, filter) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*core.Identity) - } - } - - var r1 *database.FilterResult - if rf, ok := ret.Get(1).(func(context.Context, database.AndFilter) *database.FilterResult); ok { - r1 = rf(ctx, filter) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*database.FilterResult) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(context.Context, database.AndFilter) error); ok { - r2 = rf(ctx, filter) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - // GetIdentitiesWithVerifiers provides a mock function with given fields: ctx, ns, filter func (_m *Manager) GetIdentitiesWithVerifiers(ctx context.Context, ns string, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) { ret := _m.Called(ctx, ns, filter) @@ -160,45 +128,13 @@ func (_m *Manager) GetIdentitiesWithVerifiers(ctx context.Context, ns string, fi return r0, r1, r2 } -// GetIdentitiesWithVerifiersGlobal provides a mock function with given fields: ctx, filter -func (_m *Manager) GetIdentitiesWithVerifiersGlobal(ctx context.Context, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) { - ret := _m.Called(ctx, filter) - - var r0 []*core.IdentityWithVerifiers - if rf, ok := ret.Get(0).(func(context.Context, database.AndFilter) []*core.IdentityWithVerifiers); ok { - r0 = rf(ctx, filter) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*core.IdentityWithVerifiers) - } - } - - var r1 *database.FilterResult - if rf, ok := ret.Get(1).(func(context.Context, database.AndFilter) *database.FilterResult); ok { - r1 = rf(ctx, filter) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).(*database.FilterResult) - } - } - - var r2 error - if rf, ok := ret.Get(2).(func(context.Context, database.AndFilter) error); ok { - r2 = rf(ctx, filter) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// GetIdentityByDID provides a mock function with given fields: ctx, did -func (_m *Manager) GetIdentityByDID(ctx context.Context, did string) (*core.Identity, error) { - ret := _m.Called(ctx, did) +// GetIdentityByDID provides a mock function with given fields: ctx, ns, did +func (_m *Manager) GetIdentityByDID(ctx context.Context, ns string, did string) (*core.Identity, error) { + ret := _m.Called(ctx, ns, did) var r0 *core.Identity - if rf, ok := ret.Get(0).(func(context.Context, string) *core.Identity); ok { - r0 = rf(ctx, did) + if rf, ok := ret.Get(0).(func(context.Context, string, string) *core.Identity); ok { + r0 = rf(ctx, ns, did) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*core.Identity) @@ -206,8 +142,8 @@ func (_m *Manager) GetIdentityByDID(ctx context.Context, did string) (*core.Iden } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, did) + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, ns, did) } else { r1 = ret.Error(1) } @@ -215,13 +151,13 @@ func (_m *Manager) GetIdentityByDID(ctx context.Context, did string) (*core.Iden return r0, r1 } -// GetIdentityByDIDWithVerifiers provides a mock function with given fields: ctx, did -func (_m *Manager) GetIdentityByDIDWithVerifiers(ctx context.Context, did string) (*core.IdentityWithVerifiers, error) { - ret := _m.Called(ctx, did) +// GetIdentityByDIDWithVerifiers provides a mock function with given fields: ctx, ns, did +func (_m *Manager) GetIdentityByDIDWithVerifiers(ctx context.Context, ns string, did string) (*core.IdentityWithVerifiers, error) { + ret := _m.Called(ctx, ns, did) var r0 *core.IdentityWithVerifiers - if rf, ok := ret.Get(0).(func(context.Context, string) *core.IdentityWithVerifiers); ok { - r0 = rf(ctx, did) + if rf, ok := ret.Get(0).(func(context.Context, string, string) *core.IdentityWithVerifiers); ok { + r0 = rf(ctx, ns, did) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*core.IdentityWithVerifiers) @@ -229,8 +165,8 @@ func (_m *Manager) GetIdentityByDIDWithVerifiers(ctx context.Context, did string } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, did) + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, ns, did) } else { r1 = ret.Error(1) } @@ -316,13 +252,13 @@ func (_m *Manager) GetIdentityVerifiers(ctx context.Context, ns string, id strin return r0, r1, r2 } -// GetNodeByNameOrID provides a mock function with given fields: ctx, nameOrID -func (_m *Manager) GetNodeByNameOrID(ctx context.Context, nameOrID string) (*core.Identity, error) { - ret := _m.Called(ctx, nameOrID) +// GetNodeByNameOrID provides a mock function with given fields: ctx, ns, nameOrID +func (_m *Manager) GetNodeByNameOrID(ctx context.Context, ns string, nameOrID string) (*core.Identity, error) { + ret := _m.Called(ctx, ns, nameOrID) var r0 *core.Identity - if rf, ok := ret.Get(0).(func(context.Context, string) *core.Identity); ok { - r0 = rf(ctx, nameOrID) + if rf, ok := ret.Get(0).(func(context.Context, string, string) *core.Identity); ok { + r0 = rf(ctx, ns, nameOrID) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*core.Identity) @@ -330,8 +266,8 @@ func (_m *Manager) GetNodeByNameOrID(ctx context.Context, nameOrID string) (*cor } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, nameOrID) + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, ns, nameOrID) } else { r1 = ret.Error(1) } @@ -339,13 +275,13 @@ func (_m *Manager) GetNodeByNameOrID(ctx context.Context, nameOrID string) (*cor return r0, r1 } -// GetNodes provides a mock function with given fields: ctx, filter -func (_m *Manager) GetNodes(ctx context.Context, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) { - ret := _m.Called(ctx, filter) +// GetNodes provides a mock function with given fields: ctx, ns, filter +func (_m *Manager) GetNodes(ctx context.Context, ns string, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) { + ret := _m.Called(ctx, ns, filter) var r0 []*core.Identity - if rf, ok := ret.Get(0).(func(context.Context, database.AndFilter) []*core.Identity); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, string, database.AndFilter) []*core.Identity); ok { + r0 = rf(ctx, ns, filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*core.Identity) @@ -353,8 +289,8 @@ func (_m *Manager) GetNodes(ctx context.Context, filter database.AndFilter) ([]* } var r1 *database.FilterResult - if rf, ok := ret.Get(1).(func(context.Context, database.AndFilter) *database.FilterResult); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, string, database.AndFilter) *database.FilterResult); ok { + r1 = rf(ctx, ns, filter) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*database.FilterResult) @@ -362,8 +298,8 @@ func (_m *Manager) GetNodes(ctx context.Context, filter database.AndFilter) ([]* } var r2 error - if rf, ok := ret.Get(2).(func(context.Context, database.AndFilter) error); ok { - r2 = rf(ctx, filter) + if rf, ok := ret.Get(2).(func(context.Context, string, database.AndFilter) error); ok { + r2 = rf(ctx, ns, filter) } else { r2 = ret.Error(2) } @@ -371,13 +307,13 @@ func (_m *Manager) GetNodes(ctx context.Context, filter database.AndFilter) ([]* return r0, r1, r2 } -// GetOrganizationByNameOrID provides a mock function with given fields: ctx, nameOrID -func (_m *Manager) GetOrganizationByNameOrID(ctx context.Context, nameOrID string) (*core.Identity, error) { - ret := _m.Called(ctx, nameOrID) +// GetOrganizationByNameOrID provides a mock function with given fields: ctx, ns, nameOrID +func (_m *Manager) GetOrganizationByNameOrID(ctx context.Context, ns string, nameOrID string) (*core.Identity, error) { + ret := _m.Called(ctx, ns, nameOrID) var r0 *core.Identity - if rf, ok := ret.Get(0).(func(context.Context, string) *core.Identity); ok { - r0 = rf(ctx, nameOrID) + if rf, ok := ret.Get(0).(func(context.Context, string, string) *core.Identity); ok { + r0 = rf(ctx, ns, nameOrID) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*core.Identity) @@ -385,8 +321,8 @@ func (_m *Manager) GetOrganizationByNameOrID(ctx context.Context, nameOrID strin } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, nameOrID) + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, ns, nameOrID) } else { r1 = ret.Error(1) } @@ -394,13 +330,13 @@ func (_m *Manager) GetOrganizationByNameOrID(ctx context.Context, nameOrID strin return r0, r1 } -// GetOrganizations provides a mock function with given fields: ctx, filter -func (_m *Manager) GetOrganizations(ctx context.Context, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) { - ret := _m.Called(ctx, filter) +// GetOrganizations provides a mock function with given fields: ctx, ns, filter +func (_m *Manager) GetOrganizations(ctx context.Context, ns string, filter database.AndFilter) ([]*core.Identity, *database.FilterResult, error) { + ret := _m.Called(ctx, ns, filter) var r0 []*core.Identity - if rf, ok := ret.Get(0).(func(context.Context, database.AndFilter) []*core.Identity); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, string, database.AndFilter) []*core.Identity); ok { + r0 = rf(ctx, ns, filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*core.Identity) @@ -408,8 +344,8 @@ func (_m *Manager) GetOrganizations(ctx context.Context, filter database.AndFilt } var r1 *database.FilterResult - if rf, ok := ret.Get(1).(func(context.Context, database.AndFilter) *database.FilterResult); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, string, database.AndFilter) *database.FilterResult); ok { + r1 = rf(ctx, ns, filter) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*database.FilterResult) @@ -417,8 +353,8 @@ func (_m *Manager) GetOrganizations(ctx context.Context, filter database.AndFilt } var r2 error - if rf, ok := ret.Get(2).(func(context.Context, database.AndFilter) error); ok { - r2 = rf(ctx, filter) + if rf, ok := ret.Get(2).(func(context.Context, string, database.AndFilter) error); ok { + r2 = rf(ctx, ns, filter) } else { r2 = ret.Error(2) } @@ -426,13 +362,13 @@ func (_m *Manager) GetOrganizations(ctx context.Context, filter database.AndFilt return r0, r1, r2 } -// GetOrganizationsWithVerifiers provides a mock function with given fields: ctx, filter -func (_m *Manager) GetOrganizationsWithVerifiers(ctx context.Context, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) { - ret := _m.Called(ctx, filter) +// GetOrganizationsWithVerifiers provides a mock function with given fields: ctx, ns, filter +func (_m *Manager) GetOrganizationsWithVerifiers(ctx context.Context, ns string, filter database.AndFilter) ([]*core.IdentityWithVerifiers, *database.FilterResult, error) { + ret := _m.Called(ctx, ns, filter) var r0 []*core.IdentityWithVerifiers - if rf, ok := ret.Get(0).(func(context.Context, database.AndFilter) []*core.IdentityWithVerifiers); ok { - r0 = rf(ctx, filter) + if rf, ok := ret.Get(0).(func(context.Context, string, database.AndFilter) []*core.IdentityWithVerifiers); ok { + r0 = rf(ctx, ns, filter) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*core.IdentityWithVerifiers) @@ -440,8 +376,8 @@ func (_m *Manager) GetOrganizationsWithVerifiers(ctx context.Context, filter dat } var r1 *database.FilterResult - if rf, ok := ret.Get(1).(func(context.Context, database.AndFilter) *database.FilterResult); ok { - r1 = rf(ctx, filter) + if rf, ok := ret.Get(1).(func(context.Context, string, database.AndFilter) *database.FilterResult); ok { + r1 = rf(ctx, ns, filter) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*database.FilterResult) @@ -449,8 +385,8 @@ func (_m *Manager) GetOrganizationsWithVerifiers(ctx context.Context, filter dat } var r2 error - if rf, ok := ret.Get(2).(func(context.Context, database.AndFilter) error); ok { - r2 = rf(ctx, filter) + if rf, ok := ret.Get(2).(func(context.Context, string, database.AndFilter) error); ok { + r2 = rf(ctx, ns, filter) } else { r2 = ret.Error(2) } @@ -536,13 +472,13 @@ func (_m *Manager) RegisterIdentity(ctx context.Context, ns string, dto *core.Id return r0, r1 } -// RegisterNode provides a mock function with given fields: ctx, waitConfirm -func (_m *Manager) RegisterNode(ctx context.Context, waitConfirm bool) (*core.Identity, error) { - ret := _m.Called(ctx, waitConfirm) +// RegisterNode provides a mock function with given fields: ctx, ns, waitConfirm +func (_m *Manager) RegisterNode(ctx context.Context, ns string, waitConfirm bool) (*core.Identity, error) { + ret := _m.Called(ctx, ns, waitConfirm) var r0 *core.Identity - if rf, ok := ret.Get(0).(func(context.Context, bool) *core.Identity); ok { - r0 = rf(ctx, waitConfirm) + if rf, ok := ret.Get(0).(func(context.Context, string, bool) *core.Identity); ok { + r0 = rf(ctx, ns, waitConfirm) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*core.Identity) @@ -550,8 +486,8 @@ func (_m *Manager) RegisterNode(ctx context.Context, waitConfirm bool) (*core.Id } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, bool) error); ok { - r1 = rf(ctx, waitConfirm) + if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { + r1 = rf(ctx, ns, waitConfirm) } else { r1 = ret.Error(1) } @@ -559,13 +495,13 @@ func (_m *Manager) RegisterNode(ctx context.Context, waitConfirm bool) (*core.Id return r0, r1 } -// RegisterNodeOrganization provides a mock function with given fields: ctx, waitConfirm -func (_m *Manager) RegisterNodeOrganization(ctx context.Context, waitConfirm bool) (*core.Identity, error) { - ret := _m.Called(ctx, waitConfirm) +// RegisterNodeOrganization provides a mock function with given fields: ctx, ns, waitConfirm +func (_m *Manager) RegisterNodeOrganization(ctx context.Context, ns string, waitConfirm bool) (*core.Identity, error) { + ret := _m.Called(ctx, ns, waitConfirm) var r0 *core.Identity - if rf, ok := ret.Get(0).(func(context.Context, bool) *core.Identity); ok { - r0 = rf(ctx, waitConfirm) + if rf, ok := ret.Get(0).(func(context.Context, string, bool) *core.Identity); ok { + r0 = rf(ctx, ns, waitConfirm) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*core.Identity) @@ -573,8 +509,8 @@ func (_m *Manager) RegisterNodeOrganization(ctx context.Context, waitConfirm boo } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, bool) error); ok { - r1 = rf(ctx, waitConfirm) + if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { + r1 = rf(ctx, ns, waitConfirm) } else { r1 = ret.Error(1) } @@ -582,13 +518,13 @@ func (_m *Manager) RegisterNodeOrganization(ctx context.Context, waitConfirm boo return r0, r1 } -// RegisterOrganization provides a mock function with given fields: ctx, org, waitConfirm -func (_m *Manager) RegisterOrganization(ctx context.Context, org *core.IdentityCreateDTO, waitConfirm bool) (*core.Identity, error) { - ret := _m.Called(ctx, org, waitConfirm) +// RegisterOrganization provides a mock function with given fields: ctx, ns, org, waitConfirm +func (_m *Manager) RegisterOrganization(ctx context.Context, ns string, org *core.IdentityCreateDTO, waitConfirm bool) (*core.Identity, error) { + ret := _m.Called(ctx, ns, org, waitConfirm) var r0 *core.Identity - if rf, ok := ret.Get(0).(func(context.Context, *core.IdentityCreateDTO, bool) *core.Identity); ok { - r0 = rf(ctx, org, waitConfirm) + if rf, ok := ret.Get(0).(func(context.Context, string, *core.IdentityCreateDTO, bool) *core.Identity); ok { + r0 = rf(ctx, ns, org, waitConfirm) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*core.Identity) @@ -596,8 +532,8 @@ func (_m *Manager) RegisterOrganization(ctx context.Context, org *core.IdentityC } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *core.IdentityCreateDTO, bool) error); ok { - r1 = rf(ctx, org, waitConfirm) + if rf, ok := ret.Get(1).(func(context.Context, string, *core.IdentityCreateDTO, bool) error); ok { + r1 = rf(ctx, ns, org, waitConfirm) } else { r1 = ret.Error(1) } diff --git a/mocks/orchestratormocks/orchestrator.go b/mocks/orchestratormocks/orchestrator.go index 48be922cc..3585e04cd 100644 --- a/mocks/orchestratormocks/orchestrator.go +++ b/mocks/orchestratormocks/orchestrator.go @@ -1012,13 +1012,13 @@ func (_m *Orchestrator) GetPins(ctx context.Context, filter database.AndFilter) return r0, r1, r2 } -// GetStatus provides a mock function with given fields: ctx -func (_m *Orchestrator) GetStatus(ctx context.Context) (*core.NodeStatus, error) { - ret := _m.Called(ctx) +// GetStatus provides a mock function with given fields: ctx, ns +func (_m *Orchestrator) GetStatus(ctx context.Context, ns string) (*core.NodeStatus, error) { + ret := _m.Called(ctx, ns) var r0 *core.NodeStatus - if rf, ok := ret.Get(0).(func(context.Context) *core.NodeStatus); ok { - r0 = rf(ctx) + if rf, ok := ret.Get(0).(func(context.Context, string) *core.NodeStatus); ok { + r0 = rf(ctx, ns) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*core.NodeStatus) @@ -1026,8 +1026,8 @@ func (_m *Orchestrator) GetStatus(ctx context.Context) (*core.NodeStatus, error) } var r1 error - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, ns) } else { r1 = ret.Error(1) } @@ -1347,13 +1347,13 @@ func (_m *Orchestrator) Start() error { return r0 } -// SubmitNetworkAction provides a mock function with given fields: ctx, action -func (_m *Orchestrator) SubmitNetworkAction(ctx context.Context, action *core.NetworkAction) error { - ret := _m.Called(ctx, action) +// SubmitNetworkAction provides a mock function with given fields: ctx, ns, action +func (_m *Orchestrator) SubmitNetworkAction(ctx context.Context, ns string, action *core.NetworkAction) error { + ret := _m.Called(ctx, ns, action) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *core.NetworkAction) error); ok { - r0 = rf(ctx, action) + if rf, ok := ret.Get(0).(func(context.Context, string, *core.NetworkAction) error); ok { + r0 = rf(ctx, ns, action) } else { r0 = ret.Error(0) } diff --git a/mocks/sysmessagingmocks/local_node_info.go b/mocks/sysmessagingmocks/local_node_info.go index b9c9d758e..0d6b0175e 100644 --- a/mocks/sysmessagingmocks/local_node_info.go +++ b/mocks/sysmessagingmocks/local_node_info.go @@ -14,13 +14,13 @@ type LocalNodeInfo struct { mock.Mock } -// GetNodeUUID provides a mock function with given fields: ctx -func (_m *LocalNodeInfo) GetNodeUUID(ctx context.Context) *fftypes.UUID { - ret := _m.Called(ctx) +// GetNodeUUID provides a mock function with given fields: ctx, ns +func (_m *LocalNodeInfo) GetNodeUUID(ctx context.Context, ns string) *fftypes.UUID { + ret := _m.Called(ctx, ns) var r0 *fftypes.UUID - if rf, ok := ret.Get(0).(func(context.Context) *fftypes.UUID); ok { - r0 = rf(ctx) + if rf, ok := ret.Get(0).(func(context.Context, string) *fftypes.UUID); ok { + r0 = rf(ctx, ns) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*fftypes.UUID) diff --git a/pkg/blockchain/plugin.go b/pkg/blockchain/plugin.go index f180e987a..fc65a9603 100644 --- a/pkg/blockchain/plugin.go +++ b/pkg/blockchain/plugin.go @@ -89,6 +89,9 @@ type Plugin interface { // GenerateEventSignature generates a strigified signature for the event, incorporating any fields significant to identifying the event as unique GenerateEventSignature(ctx context.Context, event *core.FFIEventDefinition) string + + // NetworkVersion returns the version of the network rules being used by this plugin + NetworkVersion(ctx context.Context) (int, error) } const FireFlyActionPrefix = "firefly:" diff --git a/pkg/core/constants.go b/pkg/core/constants.go index 9cfeeb0ec..65aa8dd16 100644 --- a/pkg/core/constants.go +++ b/pkg/core/constants.go @@ -18,8 +18,8 @@ package core const ( - // SystemNamespace is the system reserved namespace name - SystemNamespace = "ff_system" + // LegacySystemNamespace is the system reserved namespace name (deprecated) + LegacySystemNamespace = "ff_system" ) const ( diff --git a/pkg/core/identity.go b/pkg/core/identity.go index c1db12f23..9e4714c47 100644 --- a/pkg/core/identity.go +++ b/pkg/core/identity.go @@ -43,7 +43,7 @@ const ( FireFlyDIDPrefix = "did:firefly:" FireFlyOrgDIDPrefix = "did:firefly:org/" FireFlyNodeDIDPrefix = "did:firefly:node/" - FireFlyCustomDIDPrefix = "did:firefly:ns/" + FireFlyCustomDIDPrefix = "did:firefly:" ) type IdentityMessages struct { @@ -172,9 +172,14 @@ func (i *IdentityBase) Validate(ctx context.Context) (err error) { if err = ValidateFFNameFieldNoUUID(ctx, i.Name, "name"); err != nil { return err } + + var legacyDID string + if i.Type == IdentityTypeCustom { + legacyDID = fmt.Sprintf("%sns/%s/%s", FireFlyCustomDIDPrefix, i.Namespace, i.Name) + } if requiredDID, err := i.GenerateDID(ctx); err != nil { return err - } else if i.DID != requiredDID { + } else if i.DID == "" || (i.DID != requiredDID && i.DID != legacyDID) { return i18n.NewError(ctx, i18n.MsgInvalidDIDForType, i.DID, i.Type, i.Namespace, i.Name) } return nil @@ -183,25 +188,19 @@ func (i *IdentityBase) Validate(ctx context.Context) (err error) { func (i *IdentityBase) GenerateDID(ctx context.Context) (string, error) { switch i.Type { case IdentityTypeCustom: - if i.Namespace == SystemNamespace { - return "", i18n.NewError(ctx, i18n.MsgCustomIdentitySystemNS, SystemNamespace) + if i.Namespace == LegacySystemNamespace { + return "", i18n.NewError(ctx, i18n.MsgCustomIdentitySystemNS, LegacySystemNamespace) } if i.Parent == nil { return "", i18n.NewError(ctx, i18n.MsgNilParentIdentity, i.Type) } - return fmt.Sprintf("%s%s/%s", FireFlyCustomDIDPrefix, i.Namespace, i.Name), nil + return fmt.Sprintf("%s%s", FireFlyCustomDIDPrefix, i.Name), nil case IdentityTypeNode: - if i.Namespace != SystemNamespace { - return "", i18n.NewError(ctx, i18n.MsgSystemIdentityCustomNS, SystemNamespace) - } if i.Parent == nil { return "", i18n.NewError(ctx, i18n.MsgNilParentIdentity, i.Type) } return fmt.Sprintf("%s%s", FireFlyNodeDIDPrefix, i.Name), nil case IdentityTypeOrg: - if i.Namespace != SystemNamespace { - return "", i18n.NewError(ctx, i18n.MsgSystemIdentityCustomNS, SystemNamespace) - } return fmt.Sprintf("%s%s", FireFlyOrgDIDPrefix, i.Name), nil default: return "", i18n.NewError(ctx, i18n.MsgUnknownIdentityType, i.Type) diff --git a/pkg/core/identity_test.go b/pkg/core/identity_test.go index 7b6fb235d..94839c4b7 100644 --- a/pkg/core/identity_test.go +++ b/pkg/core/identity_test.go @@ -31,7 +31,7 @@ func testOrg() *Identity { ID: fftypes.NewUUID(), DID: "did:firefly:org/org1", Type: IdentityTypeOrg, - Namespace: SystemNamespace, + Namespace: "ns1", Name: "org1", }, IdentityProfile: IdentityProfile{ @@ -50,7 +50,7 @@ func testNode() *Identity { DID: "did:firefly:node/node1", Parent: fftypes.NewUUID(), Type: IdentityTypeNode, - Namespace: SystemNamespace, + Namespace: "ns1", Name: "node1", }, IdentityProfile: IdentityProfile{ @@ -66,7 +66,7 @@ func testCustom(ns, name string) *Identity { return &Identity{ IdentityBase: IdentityBase{ ID: fftypes.NewUUID(), - DID: fmt.Sprintf("did:firefly:ns/%s/%s", ns, name), + DID: fmt.Sprintf("did:firefly:%s", name), Parent: fftypes.NewUUID(), Type: IdentityTypeCustom, Namespace: ns, @@ -113,10 +113,6 @@ func TestIdentityValidationOrgs(t *testing.T) { o.DID = "did:firefly:node/node1" assert.Regexp(t, "FF00120", o.Validate(ctx)) - o = testOrg() - o.Namespace = "nonsystem" - assert.Regexp(t, "FF00122", o.Validate(ctx)) - } func TestIdentityValidationNodes(t *testing.T) { @@ -133,10 +129,6 @@ func TestIdentityValidationNodes(t *testing.T) { n.DID = "did:firefly:org/org1" assert.Regexp(t, "FF00120", n.Validate(ctx)) - n = testNode() - n.Namespace = "nonsystem" - assert.Regexp(t, "FF00122", n.Validate(ctx)) - } func TestIdentityValidationCustom(t *testing.T) { @@ -144,6 +136,8 @@ func TestIdentityValidationCustom(t *testing.T) { ctx := context.Background() c := testCustom("ns1", "custom1") assert.NoError(t, c.Validate(ctx)) + c.DID = fmt.Sprintf("did:firefly:ns/%s/%s", c.Namespace, c.Name) + assert.NoError(t, c.Validate(ctx)) c = testCustom("ns1", "custom1") c.Parent = nil @@ -154,7 +148,7 @@ func TestIdentityValidationCustom(t *testing.T) { assert.Regexp(t, "FF00120", c.Validate(ctx)) c = testCustom("ns1", "custom1") - c.Namespace = SystemNamespace + c.Namespace = LegacySystemNamespace assert.Regexp(t, "FF00121", c.Validate(ctx)) } diff --git a/pkg/core/node.go b/pkg/core/node.go index a0838397b..d009a1614 100644 --- a/pkg/core/node.go +++ b/pkg/core/node.go @@ -42,7 +42,7 @@ type DeprecatedDXInfo struct { } // Migrate creates and maintains a migrated IdentityClaim object, which -// is used when processing an old-style nodeanization broadcast received when +// is used when processing an old-style node broadcast received when // joining an existing network func (node *DeprecatedNode) Migrated() *IdentityClaim { if node.identityClaim != nil { @@ -53,7 +53,7 @@ func (node *DeprecatedNode) Migrated() *IdentityClaim { IdentityBase: IdentityBase{ ID: node.ID, Type: IdentityTypeNode, - Namespace: SystemNamespace, + Namespace: LegacySystemNamespace, Name: node.Name, Parent: nil, // Must be set post migrate }, diff --git a/pkg/core/node_status.go b/pkg/core/node_status.go index 7bcd2b1fe..09f58c3ae 100644 --- a/pkg/core/node_status.go +++ b/pkg/core/node_status.go @@ -20,10 +20,10 @@ import "github.com/hyperledger/firefly-common/pkg/fftypes" // NodeStatus is a set of information that represents the health, and identity of a node type NodeStatus struct { - Node NodeStatusNode `ffstruct:"NodeStatus" json:"node"` - Org NodeStatusOrg `ffstruct:"NodeStatus" json:"org"` - Defaults NodeStatusDefaults `ffstruct:"NodeStatus" json:"defaults"` - Plugins NodeStatusPlugins `ffstruct:"NodeStatus" json:"plugins"` + Namespace string `ffstruct:"NodeStatus" json:"namespace"` + Node NodeStatusNode `ffstruct:"NodeStatus" json:"node"` + Org NodeStatusOrg `ffstruct:"NodeStatus" json:"org"` + Plugins NodeStatusPlugins `ffstruct:"NodeStatus" json:"plugins"` } // NodeStatusNode is the information about the local node, returned in the node status @@ -42,11 +42,6 @@ type NodeStatusOrg struct { Verifiers []*VerifierRef `ffstruct:"NodeStatusOrg" json:"verifiers,omitempty"` } -// NodeStatusDefaults is information about core configuration th -type NodeStatusDefaults struct { - Namespace string `ffstruct:"NodeStatusDefaults" json:"namespace"` -} - // NodeStatusPlugins is a map of plugins configured on the node type NodeStatusPlugins struct { Blockchain []*NodeStatusPlugin `ffstruct:"NodeStatusPlugins" json:"blockchain"` diff --git a/pkg/core/node_test.go b/pkg/core/node_test.go index aab2e998f..7210112ce 100644 --- a/pkg/core/node_test.go +++ b/pkg/core/node_test.go @@ -43,7 +43,7 @@ func TestNodeigration(t *testing.T) { ID: node.ID, Type: IdentityTypeNode, DID: "did:firefly:node/node1", - Namespace: SystemNamespace, + Namespace: LegacySystemNamespace, Name: "node1", Parent: parentID, }, diff --git a/pkg/core/organization.go b/pkg/core/organization.go index b30bed7e2..e215fea76 100644 --- a/pkg/core/organization.go +++ b/pkg/core/organization.go @@ -49,7 +49,7 @@ func (org *DeprecatedOrganization) Migrated() *IdentityClaim { IdentityBase: IdentityBase{ ID: org.ID, Type: IdentityTypeOrg, - Namespace: SystemNamespace, + Namespace: LegacySystemNamespace, Name: org.Name, Parent: nil, // No support for child identity migration (see FIR-9 for details) }, diff --git a/pkg/core/organization_test.go b/pkg/core/organization_test.go index 04a2331f6..b8285f69d 100644 --- a/pkg/core/organization_test.go +++ b/pkg/core/organization_test.go @@ -39,7 +39,7 @@ func TestOrgMigration(t *testing.T) { ID: org.ID, Type: IdentityTypeOrg, DID: "did:firefly:org/org1", - Namespace: SystemNamespace, + Namespace: LegacySystemNamespace, Name: "org1", }, IdentityProfile: IdentityProfile{ diff --git a/smart_contracts/ethereum/solidity_firefly/contracts/Firefly.sol b/smart_contracts/ethereum/solidity_firefly/contracts/Firefly.sol index ed54b863e..b993a20e7 100644 --- a/smart_contracts/ethereum/solidity_firefly/contracts/Firefly.sol +++ b/smart_contracts/ethereum/solidity_firefly/contracts/Firefly.sol @@ -18,4 +18,7 @@ contract Firefly { emit BatchPin(msg.sender, block.timestamp, namespace, uuids, batchHash, payloadRef, contexts); } + function networkVersion() public pure returns (uint8) { + return 2; + } } diff --git a/smart_contracts/fabric/firefly-go/chaincode/contract.go b/smart_contracts/fabric/firefly-go/chaincode/contract.go index 71e691b33..529ffaf76 100644 --- a/smart_contracts/fabric/firefly-go/chaincode/contract.go +++ b/smart_contracts/fabric/firefly-go/chaincode/contract.go @@ -58,3 +58,7 @@ func (s *SmartContract) PinBatch(ctx contractapi.TransactionContextInterface, na ctx.GetStub().SetEvent("BatchPin", bytes) return nil } + +func (s *SmartContract) NetworkVersion() int { + return 2 +} diff --git a/test/e2e/restclient_test.go b/test/e2e/restclient_test.go index 1354438e3..db80aa05f 100644 --- a/test/e2e/restclient_test.go +++ b/test/e2e/restclient_test.go @@ -64,8 +64,8 @@ var ( urlContractListeners = "/namespaces/default/contracts/listeners" urlContractAPI = "/namespaces/default/apis" urlBlockchainEvents = "/namespaces/default/blockchainevents" - urlGetOrganizations = "/network/organizations" - urlGetOrgKeys = "/namespaces/ff_system/identities/%s/verifiers" + urlGetOrganizations = "/namespaces/default/network/organizations" + urlGetOrgKeys = "/namespaces/default/identities/%s/verifiers" ) func NewResty(t *testing.T) *resty.Client {