Skip to content

Membership

Nadine Quin edited this page May 17, 2022 · 28 revisions

Components

Overview

Currently the membership code is grouped into six module pairs. Module pairs are comprised of an interface module and an implementation module. These modules are:

  • Group policy — provides look ups of group policy files for holding identities after they have been parsed from the CPI.
  • Membership client — client component for calling membership functionality across workers (such as starting registration from the RPC worker)
  • Membership service — server-side component for the member processor responding to calls from the membership client.
  • Membership group reader — client component for maintaining a local cache of group data (e.g. member lists) and performing lookups.
  • HTTP RPC — membership related HTTP API endpoints (e.g. for starting registration in a group).
  • Registration — components for performing group registration. This can contain multiple components for different registration types.

The registration and membership service modules are only used by the membership processor. All other modules are available for use outside of the membership processor. For example, the HTTP RPC and membership client modules are deployed as part of the RPC processor and the membership group reader and group policy modules are deployed as part of the p2p processor.

Specific components in these modules are described below.

GroupPolicyProvider

Description

The group policy provider supplies a holding identity's GroupPolicy object. It is delivered as a GroupPolicy.json file in a .cpi. CPI installation should include parsing this json as a string and publishing it to the message bus for this component to pick up.

Implementation

The GroupPolicyProvider module has only one implementation, which depends on the virtual node read component, and the .cpi information read component.

  1. The virtual node read component retrieves virtual node information for the holding identity.
  2. The .cpi information component retrieves the node's .cpi metadata, which includes the group policy files as a string.
  3. This GroupPolicyProvider component implementation parses the string into a GroupPolicy object.

GroupPolicy objects are cached, so multiple reads return the same object. The cache clears when the component stops or if it goes down due to down dependencies.

Lifecycle handling

The single default implementation of the GroupPolicyProvider component responds to incoming events with these behaviors.

Start event:

  1. Creates registration handle for dependencies (if it hasn't already been created).

Registration changed to status UP event:

  1. Creates cache map (if it hasn't already been created or it has been closed).
  2. Creates callback with virtual node service so that the cache is updated when virtual node info changes.
  3. Sets lifecycle status to UP.

Registration changed to status DOWN event:

  1. Sets lifecycle status to DOWN.
  2. Closes the handle on virtual node component callback.
  3. Closes (nullifies) the cache map.

Stop event:

  1. Closes the handle on virtual node component callback.
  2. Closes the handle on registration for dependency components.
  3. Closes (nullifies) the cache map.

Usage

The GroupPolicyProvider is used to expose the GroupPolicy objects internally to any interested services. This component can be included with any worker requiring group policy lookups (e.g. the member or p2p worker). For static networks, GroupPolicy.json defines the static member list. This component is used in the static registration implementation of the MemberRegistrationService to parse static group configurations. Also during registration, the group policy lookup is used to decide which registration implementation to use for a member.

MembershipGroupReaderProvider

Description

This component provides a group reader for a holding identity. A network member can call it to access the group data it has permission to see, such as group parameters, and to access functionality such as member lookups.

Implementation

There is only one implementation of this component, which creates group reader instances on request and caches them for faster lookups later. It also creates subscriptions to receive group data, which it caches and uses later to create the group readers as needed. These caches are cleared when this component stops or goes down and they are recreated when the component starts or comes back up.

Lifecycle handling

The single default implementation of the MembershipGroupReaderProvider component responds to incoming events with these behaviors.

Start event:

  1. Initiates caches for group data and group readers (if they haven't already been created).
  2. Creates registration handle for dependency components (if one hasn't already been created).

Registration changed to status UP event:

  1. Registers configuration change callback handler.

Registration changed to status DOWN event:

  1. Sets lifecycle status to DOWN.
  2. Closes the handle on registered configuration callback.

Configuration received event

  1. Sets lifecycle status to DOWN.
  2. Stops subscriptions.
  3. Closes caches.
  4. Recreates caches.
  5. Recreates subscriptions with updated configuration.
  6. Sets lifecycle status to UP.

Currently, only the MESSAGING configuration is handled.

Stop event:

  1. Stops subscriptions.
  2. Closes caches.
  3. Closes registered callback handler for configuration and dependency services status changes.

Usage

Any internal component can use the MembershipGroupReaderProvider if it requires member lookups or a member's view of group data, such as group parameters or the .cpi whitelist. For example, P2P components can use it to look up member information.

RegistrationProvider

Description

The RegistrationProvider is called to start registration or check on the status of a registration request. This service retrieves the group policy file from the GroupPolicyProvider, check the name of the registration protocol to use, loads that service and calls the appropriate registration function call on that service.

Note: We plan to remove this service in favour of a proxy service in the future.

Implementation

There is only one implementation of this component interface; RegistrationProviderImpl. This service depends on the group policy provider and retrieves a list of all member registration services.

  1. The group policy provider is used to find the configured registration protocol.
  2. The configured registration protocol is used to select the correct registration service from the list of injected services. A new registration protocol can be made available by just adding a new component implementing MemberRegistrationService. Then the class name of that component can be set in the group policy and is available for use once deployed.

Lifecycle handling

Start event:

  1. Starts all member registration components that were injected into this service.
  2. Creates registration handle for dependency components (if one hasn't already been created).

Registration changed to status UP event:

  1. Sets lifecycle status to UP.

Registration changed to status DOWN event:

  1. Sets lifecycle status to DOWN.

Stop event:

  1. Closes registered callback handler for dependency services status changes.

Usage

This component is intended only for use by internal membership processor services. Specifically, clients wishing to start registration can use the membership client service to send and RPC request picked up by the membership service on the member processor which will call the registration provider.

MemberRegistrationService

Description

Implementations of member registration service are responsible for registering a specific member in a group and checking on the registration status for that member.

Implementations

StaticMemberRegistrationService

The static-registration-service implementation of the registration service performs registration for a member in the context of a static group. This means that no registration request is sent to an MGM. Instead, all member data is loaded from the group policy file. Members can be set in this file which will be parsed and published to kafka for the registering member (including its own member info).

Lifecycle handling

Start event:

  1. Creates registration handle for dependency components (if one hasn't already been created).

Registration changed to status UP event:

  1. Registers configuration change callback handler.

Registration changed to status DOWN event:

  1. Sets lifecycle status to DOWN.
  2. Closes the handle on registered configuration callback.

Configuration received event

  1. Start (or restart) kafka publisher.
  2. Set lifecycle status to UP.

Currently, only the MESSAGING configuration is handled.

Stop event:

  1. Stops the publisher.
  2. Closes registered callback handler for configuration and dependency services status changes.

MGMRegistrationService

This is not yet implemented but in the future we will have a service responsible for registering with a real MGM. Further documentation will follow when that exists.

Usage

Implementations of MemberRegistrationService should never be used directly. The MemberRegistrationProvider is responsible for selecting the correct registration service and calling it. The starting point for registration should always be the membership client component.

MemberRegistrationRpcOps

Description

This component defines the HTTP API endpoints for member registration. Currently our component specifically calls out a virtual node ID as a parameter in the API. This is temporary until there is common functionality across all of our API to retrieve the virtual node ID from the API URL. Once this is available, we will adjust our endpoints to remove these parameters.

Implementation

There is a single implementation of this class which receives API calls, maps the versioned API requests to client component DTOs, and calls functionality from the membership ops client component. The package containing this implementation and request/response objects is versioned so it is possible that in the future more implementations will exist for different versions.

Lifecycle handling

Start event:

  1. Creates registration handle for dependency components (if one hasn't already been created).

Registration changed to status UP event:

  1. Sets lifecycle status to UP.

Registration changed to status DOWN event:

  1. Sets lifecycle status to DOWN.

Stop event:

  1. Closes registered callback handler for dependency services status changes.

Usage

This component is to be included as a runtime dependency of the RPC processor. It is intended that the RPC endpoint handling code automatically picks up this component when it is a runtime dependency and manage its lifecycle so this component will never directly be referenced by any other component.

MemberOpsClient

Description

This client component allows member services to be grouped in one component and potentially be called remotely from another processor. The member components can also run in the same process and the client component could still call member services. This provides flexibility in splitting where services are run. MemberOpsClient uses DTOs in its API so as to break any dependency between the HTTP API and the client so that can evolve independently. As a result, the RPC processor handles starting this component in the case of the HTTP endpoints rather than the HTTP API endpoint component.

Implementation

There is a single implementation of this client.

Lifecycle handling

Start event:

  1. Creates registration handle for dependency components (if one hasn't already been created).

Registration changed to status UP event:

  1. Registers configuration change callback handler.

Registration changed to status DOWN event:

  1. Sets lifecycle status to DOWN.
  2. Closes the handle on registered configuration callback.

Configuration received event

  1. Start (or restart) RPC sender.
  2. Set lifecycle status to UP.

Currently, only the MESSAGING configuration is handled.

Stop event:

  1. Stops the RPC sender.
  2. Closes registered callback handler for configuration and dependency services status changes.

Usage

As an example, the registration services are currently deployed as part of a member processor, while the membership endpoints are deployed as part of the RPC worker. In this example, this client component is also deployed on the RPC processor and calls are forwarded over the message bus to the member worker for processing.

MemberOpsService

Description

The MemberOpsService is the other side component to the MemberOpsClient. Every RPC request published to the message bus by the MemberOpsClient needs a handler on the MemberOpsService side. The MemberOpsService subscribes to incoming requests, calls the appropriate member service, and returns the result to the client over the message bus.

Implementation

A single implementation of this service exists.

Lifecycle handling

Start event:

  1. Creates registration handle for dependency components (if one hasn't already been created).

Registration changed to status UP event:

  1. Registers configuration change callback handler.

Registration changed to status DOWN event:

  1. Sets lifecycle status to DOWN.
  2. Closes the handle on registered configuration callback.
  3. Closes RPC subscription.

Configuration received event

  1. Start (or restart) RPC subscription.
  2. Set lifecycle status to UP.

Currently, only the MESSAGING configuration is handled.

Stop event:

  1. Stops the RPC subscription.
  2. Closes registered callback handler for configuration and dependency services status changes.

Usage

This is exclusivelyan internal membership component for listening for RPC requests and calling the appropriate service. This should never be required by another component. It can only be injected into a processor to be started.


Services

MembershipGroupReader

Description

The MembershipGroupReader service is closely connected to the MembershipGroupReaderProvider component. This class should only be initialized by the MembershipGroupReaderProvider and should always be accessed via that component i.e. references to instances of MembershipGroupReader should not be held long term. Instead use the provider component to get the group reader each time you are trying to read group data.

MembershipGroupReader is created in the context of a holding ID. It is used to retrieve a specific members view on the group. It will primarily be used for member lookups, but can also provide access to the group parameters and CPI whitelist.

Instances of MembershipGroupReader do not implement Lifecycle. Instead, the MembershipGroupReaderProvider described previously has lifecycle and the reader services provide member views on the member group cache. The MembershipGroupReaderProvider can modify/clear cached data in response to lifecycle events and updated data, which is why references to the MembershipGroupReader instances should not be held.


Group Policy

The group policy file is a definition of group protocols and protocol configurations. The group policy file is always packaged within the CPI file (the CPI is a combination of a CPB and a GroupPolicy.json file).

There are two scenarios in which a group policy file will be generated; when we need the initial group definition to bootstrap a group, and when we need to export the configuration of a running group to allow new members to join.

In the first scenario, bootstrapping a group, corda-cli is used to create a GroupPolicy file locally. This can be used as part of a CPI for an MGM, or to set up a static group. At the time of writing, the latter is more fleshed out while the former still requires some design work.

In the second scenario, exporting group policy for joining members, a running MGM is required. A HTTP API endpoint is exposed on the MGM virtual node, which returns a group policy when called that can then be packaged into the CPI distributed to members. The corda-cli will expose functionality which calls that endpoint so that the group policy for an active group can be retrieved via the corda-cli also.

Internally, we will expose this file to components as a parsed object from the component GroupPolicyProvider. We may decide to provide a schema for this in the future when the structure is less fluid during development.

Sample

The following is a sample of what the group policy file will look like. Most of this file is currently just placeholder and is not actioned. The entire file is still evolving as we develop.

{
  "fileFormatVersion": 1,
  "groupId": "ABC123",
  "registrationProtocol": "net.corda.membership.impl.registration.staticnetwork.StaticMemberRegistrationService",
  "synchronisationProtocolFactory": "net.corda.v5.mgm.MGMSynchronisationProtocolFactory",
  "protocolParameters": {
    "identityTrustStore": [
      "-----BEGIN CERTIFICATE-----\nMIICCDCJDBZFSiI=\n-----END CERTIFICATE-----\n",
      "-----BEGIN CERTIFICATE-----\nMIIFPDCzIlifT20M\n-----END CERTIFICATE-----"
    ],
    "tlsTrustStore": [
      "-----BEGIN CERTIFICATE-----\nMIIDxTCCE6N36B9K\n-----END CERTIFICATE-----\n",
      "-----BEGIN CERTIFICATE-----\nMIIDdTCCKSZp4A==\n-----END CERTIFICATE-----",
      "-----BEGIN CERTIFICATE-----\nMIIFPDCCIlifT20M\n-----END CERTIFICATE-----"
    ],
    "mgmInfo": {
      "name": "C=GB, L=London, O=Corda Network, OU=MGM, CN=Corda Network MGM",
      "sessionKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHK+B3YGgcIALw==\n-----END PUBLIC KEY-----\n",
      "certificate": [
        "-----BEGIN CERTIFICATE-----\nMIICxjCCRG11cu1\n-----END CERTIFICATE-----\n",
        "-----BEGIN CERTIFICATE-----\nMIIB/TCCDJOIjhJ\n-----END CERTIFICATE-----\n",
        "-----BEGIN CERTIFICATE-----\nMIICCDCCDZFSiI=\n-----END CERTIFICATE-----\n",
        "-----BEGIN CERTIFICATE-----\nMIIFPDCClifT20M\n-----END CERTIFICATE-----"
      ],
      "ecdhKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDH8Tc=\n-----END PUBLIC KEY-----\n",
      "keys": [
        "-----BEGIN PUBLIC KEY-----\nMFkwEwYHgcIALw==\n-----END PUBLIC KEY-----\n"
      ],
      "endpoints": [
        {
          "url": "https://mgm.corda5.r3.com:10000",
          "protocolVersion": 1
        },
        {
          "url": "https://mgm-dr.corda5.r3.com:10000",
          "protocolVersion": 1
        }
      ],
      "platformVersion": 1,
      "softwareVersion": "5.0.0",
      "serial": 1
    },
    "identityPKI": "Standard",
    "identityKeyPolicy": "Combined",
    "tlsPKI": "C5",
    "p2pProtocolMode": "AUTHENTICATED_ENCRYPTION",
    "cipherSuite": {
      "corda.provider": "default",
      "corda.signature.provider": "default",
      "corda.signature.default": "ECDSA_SECP256K1_SHA256",
      "corda.signature.FRESH_KEYS": "ECDSA_SECP256K1_SHA256",
      "corda.digest.default": "SHA256",
      "corda.cryptoservice.provider": "default"
    },
    "roles" : {
      "default" : {
        "validator" : "net.corda.v5.mgm.DefaultMemberInfoValidator",
        "requiredMemberInfo" : [
        ],
        "optionalMemberInfo" : [
        ]
      },
      "notary" : {
        "validator" : "net.corda.v5.mgm.NotaryMemberInfoValidator",
        "requiredMemberInfo" : [
          "notaryServiceParty"
        ],
        "optionalMemberInfo" : [
        ]
      }
    },
    "staticNetwork": {
      "mgm": {
        "keyAlias": "mgm-alias"
      },
      "members": [
        {
          "name": "C=GB, L=London, O=Alice",
          "keyAlias": "alice-alias",
          "rotatedKeyAlias-1": "alice-historic-alias-1",
          "memberStatus": "ACTIVE",
          "endpointUrl-1": "https://alice.corda5.r3.com:10000",
          "endpointProtocol-1": 1
        },
        {
          "name": "C=GB, L=London, O=Bob",
          "keyAlias": "bob-alias",
          "rotatedKeyAlias-1": "bob-historic-alias-1",
          "rotatedKeyAlias-2": "bob-historic-alias-2",
          "memberStatus": "ACTIVE",
          "endpointUrl-1": "https://bob.corda5.r3.com:10000",
          "endpointProtocol-1": 1
        },
        {
          "name": "C=GB, L=London, O=Charlie",
          "keyAlias": "charlie-alias",
          "memberStatus": "SUSPENDED",
          "endpointUrl-1": "https://charlie.corda5.r3.com:10000",
          "endpointProtocol-1": 1,
          "endpointUrl-2": "https://charlie-dr.corda5.r3.com:10001",
          "endpointProtocol-2": 1
        }
      ]
    }
  }
}

Current usage

The following is a simplified version of the above to show what is actually being used at the current stage of development.

{
  "groupId": "ABC123",
  "registrationProtocol": "net.corda.membership.impl.registration.staticnetwork.StaticMemberRegistrationService",
  "protocolParameters": {
    "staticNetwork": {
      "mgm": {
        "keyAlias": "mgm-alias"
      },
      "members": [
        {
          "name": "C=GB, L=London, O=Alice",
          "keyAlias": "alice-alias",
          "rotatedKeyAlias-1": "alice-historic-alias-1",
          "memberStatus": "ACTIVE",
          "endpointUrl-1": "https://alice.corda5.r3.com:10000",
          "endpointProtocol-1": 1
        },
        {
          "name": "C=GB, L=London, O=Bob",
          "keyAlias": "bob-alias",
          "rotatedKeyAlias-1": "bob-historic-alias-1",
          "rotatedKeyAlias-2": "bob-historic-alias-2",
          "memberStatus": "ACTIVE",
          "endpointUrl-1": "https://bob.corda5.r3.com:10000",
          "endpointProtocol-1": 1
        },
        {
          "name": "C=GB, L=London, O=Charlie",
          "keyAlias": "charlie-alias",
          "memberStatus": "SUSPENDED",
          "endpointUrl-1": "https://charlie.corda5.r3.com:10000",
          "endpointProtocol-1": 1,
          "endpointUrl-2": "https://charlie-dr.corda5.r3.com:10001",
          "endpointProtocol-2": 1
        }
      ]
    }
  }
}