Contents of this article assume the reader's familiarity with the concepts of Business Networks. Please see this page for more information.
Business Network Membership Service aims to solve the following problems:
- On-boarding of new members to a Business Network.
- Suspension of members from a Business Network.
- Distribution of a membership list to the Business Network members.
- Association of a custom metadata with a node's identity.
- Participation in multiple Business Networks by a single node.
BNMS provides the following API extension points:
- Custom Membership Metadata. Nodes can associate a custom metadata with their membership states. Membership Metadata might contain such fields as role, address, displayed name, email and other used-defined fields. Membership Metadata can also be represented with different classes for different business networks. Associated metadata is distributed to Business Network members as a part of the general membership distribution mechanism.
- Automatic acceptance of memberships requests. BNOs can implement a custom verification code that would be run against every incoming membership request to determine whether it's eligible for auto-activation. Auto-activated members can start transacting on the Business Network straight-away without a need for a separate membership activation step. Please see Membership Auto Approvals section.
- Custom membership metadata verification. Custom verification can be added by overriding
RequestMembershipFlowResponder
andAmendMembershipMetadataFlowResponder
flows (please see further sections). Please see Custom Membership Metadata Verification section.
Please see the design doc for more information about the technical design considerations.
Please see FullBNMSFlowDemo for a detailed how-to-use example.
Add the following lines to the repositories
and dependencies
blocks of your build.gradle
file:
repositories {
maven {
url 'https://ci-artifactory.corda.r3cev.com/artifactory/corda-solutions-releases'
}
}
dependencies {
cordapp "com.r3.businessnetworks:membership-service:2.0"
cordapp "com.r3.businessnetworks:membership-service-contracts-and-states:2.0"
}
BNMS contains implementations of flows, states and contracts to model memberships on a Business Network. It consists of 2 CorDapps:
membership-service-contracts-and-states
- contracts and statesmembership-service
- flows for both BNO and member CorDapps
Both CorDapps have to be installed on the nodes of BNOs and members.
Memberships are represented with a MembershipState. Users can associate a custom metadata with their MembershipState
via membershipMetadata
field. MembershipState
is generic and doesn't enforce any restrictions over the type of the metadata.
MembershipState
can exist in the following statuses:
PENDING
- the very first status for all newly issued memberships. To be able to transact on the Business NetworkPENDING
memberships need to be activated first.ACTIVE
- active membership holders can transact on the Business Network.SUSPENDED
- Business Network members can be temporarily suspended by their BNO, for example as a result of a governance action. Suspended members can't transact on the Business Network.
MembershipState
evolution is curated by MembershipContract. MembershipContract
verifies the evolution of MembershipState
s only without verifying Membership Metadata, as it's a generic parameter.
Membership Metadata evolution can be verified by overriding responder flows at the BNO side. Please see Custom Membership Metadata Verification section for more information.
BNMS flows are split into two packages: bno
and member
(with the flows for BNOs and members respectively).
Flows that can be invoked by members:
RequestMembershipFlow
- to request a membership.AmendMembershipMetadataFlow
- to update a membership metadataGetMembershipsFlow
- to pull down a memberships list from a BNO. Members retrieve the full list on the first invocation only. All subsequent updates are delivered via push notifications from the BNO. Memberships cache can be force-refreshed by settingforceRefresh
ofGetMembershipsFlow
to true. Members that are missing from the Network Map are filtered out from the result list.
Flows that can be invoked by BNO:
ActivateMembershipFlow
- to activate aPENDING
membership.SuspendMembershipFlow
- to suspend anACTIVE
membership.
Activation and suspension transactions don't require the member's signature. BNO is eligible to suspend memberships unilaterally, for example as a result of a governance action.
BNMS provides a support for multiple business networks. Business Networks are uniquely identified by BNO's Party
object. All flows that assume any member -> BNO interactions require BNO's identity as a mandatory parameter.
CorDapp configuration is red from cordapps/config/membership-service.conf
file with a fallback to membership-service.conf
on the CorDapp's classpath.
// whitelist of accpted BNOs. Attempt to communicate to not whitelisted BNO would result into an exception
bnoWhitelist = ["O=BNO,L=New York,C=US", "O=BNO,L=London,C=GB"]
// Name of the Notary
notaryName = "O=Notary,L=Longon,C=GB"
As Business Networks is an application level concept, memberships have to be verified manually inside the flows of your CorDapp. Corda does not perform any membership checks at the platform level.
An example of logic for a counterparty's membership verification can be found in BusinessNetworkAwareInitiatedFlow:
/**
* Extend from this class if you are a business network member and you don't want to be checking yourself whether
* the initiating party is also a member. Your code (inside onCounterpartyMembershipVerified) will be called only after
* that check is performed. If the initiating party is not a member an exception is thrown.
*/
abstract class BusinessNetworkAwareInitiatedFlow<out T>(protected val flowSession : FlowSession) : FlowLogic<T>() {
@Suspendable
override fun call(): T {
verifyMembership(flowSession.counterparty)
return onOtherPartyMembershipVerified()
}
/**
* Will be called once counterpart's membership is successfully verified
*/
@Suspendable
abstract fun onOtherPartyMembershipVerified() : T
/**
* Identity of the BNO to verify counterpart's membership against
*/
abstract fun bnoIdentity() : Party
@Suspendable
private fun verifyMembership(initiator : Party) {
// Memberships list contains valid active memberships only. So we need to just make sure that the membership exists.
subFlow(GetMembershipsFlow(bnoIdentity()))[initiator] ?: throw NotAMemberException(initiator)
}
}
The easiest way of making your flow "business network aware" is to extend from BusinessNetworkAwareInitiatedFlow
.
Please note that during development you can take an advantage of extending from
BusinessNetworkOperatorFlowLogic
andBusinessNetworkOperatorInitiatedFlow
if you are developing custom flows for BNO.
To implement custom verification of Membership Metadata:
- Override
RequestMembershipFlowResponder
andAmendMembershipMetadataFlowResponder
flows. - Add your custom verification logic to the
verifyTransaction
method.
For example:
@InitiatedBy(RequestMembershipFlow::class)
class RequestMembershipFlowResponderWithMetadataVerification(session : FlowSession) : RequestMembershipFlowResponder(session) {
@Suspendable
override fun verifyTransaction(builder : TransactionBuilder) {
super.verifyTransaction(builder)
val membership = builder.outputStates().filter { it.data is MembershipState<*> }.single().data as MembershipState<MyMembershipMetadata>
if (membership.membershipMetadata.role != "BORROWER") {
throw FlowException("Invalid role ${membership.membershipMetadata.role}")
}
}
}
To implement automatic membership approvals:
- Override
RequestMembershipFlowResponder
flow. - Add your custom verification logic to the
activateRightAway
method.
For example:
@InitiatedBy(RequestMembershipFlow::class)
class RequestMembershipFlowResponderWithAutoApproval(session : FlowSession) : RequestMembershipFlowResponder(session) {
@Suspendable
override fun activateRightAway(membershipState : MembershipState<Any>, configuration : BNOConfigurationService) : Boolean {
// Add your custom request verification logic here.
// You can call an external system, verify the request against a pre-configured whitelist and etc.
// For example to approve *any* incoming membership request - just return *true* from this method.
return true
}
}