Unlocking New Opportunities with Accounts on Corda

November 18, 2019

With the release of Accounts on Corda we’ve greatly expanded the number of use cases for which Corda is a best-in-class solution.

Whether you’re building an CordApp that represents a large number of customers, have a business network that includes many smaller entities or powers a consumer-focused mobile experience; Accounts on Corda allows you to run a single node that can represent all of the identities as accounts on that node. Here’s when you need accounts:

  1. Cost of hosting is a barrier to entry

Say you are running a blockchain solution company, and you have already developed your CordApp and built a network using Corda Enterprise Network Manager. On pitching the application to the network participants, it became clear that the cost of running a node for each participant was a barrier to entry.

Or, say you’re building a solution for a road side repair service that interfaces with thousands of garages, or represents a large chain of physical stores, or want to use Corda to power the back-end transactions in your mobile app. Using Accounts on Corda provides a scalable solution without the cost of running a node for each identity.

2. Data get badly stored when multiple internal teams use the same company node for external transactions

Perhaps you realized that all of the teams in your company are operating on behalf of the same node identity when making external interactions. All the transactions are tagged with this node (company) identity. And internal teams are able to query another team’s database. Using Accounts on Corda allow your application to achieve data isolation from the application level. Each account user will then have limited visibilities of the entire node database.

In each of these cases running a node is sub-optimal for client onboarding and internal compliance. With the Accounts feature, a new library available in Corda 4.3, these issues become a thing of the past. And more exciting than that, the ability to represent multiple identities on a single node opens up a whole new world of possibilities with Corda.

Corda Accounts are suitable for a range of purposes — think customer accounts, balance accounts, employee accounts — and much more.The new Accounts library in Corda 4.3 can be used for:

  • Enable Business Network Operators to aggregate client or organization entities distributed identities and host them on a single node as separate accounts, while preserving privacy and security.
  • Allow individual node operators the ability to partition and manage the node database on an account basis to reduce operational complexity.

What are Corda Accounts?

The accounts library allows a Corda node operator to split the vault into multiple “logical” sub-vaults.

Rather than setting up multiple vaults/databases at extra cost and have data in multiple places, the Accounts feature enables you to store a variety of partitioned data inside a single secured vault in a manageable manner.

Think back to the two scenarios:

  • By using Accounts, Business Network Operators can now reduce the cost of hosting multiple entities or nodes on behalf of multiple participants.
  • And organizations can assign each team or entity a dedicated account, enabling different teams to make independent and private transactions with their own account and do so in a highly efficient way.

The Accounts library takes the form of a JAR which can be dropped into the CorDapps folder. Its use is optional. We intentionally designed it this way so you can decide which nodes will support accounts. Not all nodes will need to support accounts. This optionality means a flatter learning curve for new CorDapp developers.

Account Hierarchy

Accounts are created by host nodes (which are just regular Corda nodes). They make no claims or assumptions about identity at the network level. As such, accounts do not come with a network level identity or an identity rooted in the network trust root. However, Corda Accounts are discoverable by and able to transact with any regular nodes and other accounts after you share the specific accounts with your counterparties. As a result, Corda Accounts are used in the same way, as a Corda node to interact with counterparties.

In the above chart, we see that the account library gives you have the ability to make account-to-account transactions (cross-node or in-node), and account-to-node transactions.

Where Can Accounts Be Used?

Let’s jump into an example:

In the above graphic, we see that the buyer and seller nodes both have three accounts and we can see a shipping company that currently does not have sub-accounts as part of its node..

If we continue to the business flows, we see that there are five types of flows that can now take place between these counterparties’ accounts .

  • Invoice Flow: Acct <> Acct transaction (Cross node)
  • Internal Message Flow: Acct <> Acct transaction (In node)
  • Payment Flow: Acct <> Acct transaction (Cross node)
  • Shipping Request Flow: Acct <> Node transaction
  • Cargo Flow: Node <> Acct transaction

With the accounts library enabled, flows become more flexible than they are in today’s node-to-node transaction model (not to mention Corda also has the most flexible flow framework when compared with other blockchain platforms ?). We can now sync up and replicate the complex organization business flows that form transactions in real day-to-day business.

Let’s Create Some Accounts

val newAccount = accountService.createAccount(name = acctName)

Done! It was that easy!

Internally, the accountService will create an AccountInfo object that consists of 3 attributes:

  1. Name of the account
  2. The host/parent node of the account
  3. The UUID of the account in this network

Initiating Transactions using Accounts (Flow Initiator)

With the additional Accounts in the transaction now, we just need to find out about the keys and the identity you and your counterparty Accounts need for the transaction:

//Generate key & find target account for transaction

val myAccount = accountService.accountInfo(whoAmI).single().state.data

val myKey = subFlow(NewKeyForAccount(myAccount.identifier.id)).owningKey

val targetAccount = accountService.accountInfo(whereTo).single().state.data

val targetAcctAnonymousParty = subFlow(RequestKeyForAccount(targetAccount))

The transaction will get the initial signature from the Flow Initiator before trying to collect external signatures. For most cases, the Flow Initiator will be a signatory for the input or output of the transaction. When we initiate a flow that involves an account, we will have to get both the node’s signature and initiator’s account signature.

//Pass along Transaction
val locallySignedTx = serviceHub.signInitialTransaction(transactionBuilder, listOfNotNull(ourIdentity.owningKey,myKey))

The same applies to the collection of external signatures. You will have to specifically instruct your flow that you are looking for an account signature for the transaction:

val accountToMoveToSignature = subFlow(CollectSignatureFlow(locallySignedTx, sessionForAccountToSendTo, targetAcctAnonymousParty.owningKey))

Have a look at the Full Initiator flow at: Here

Responding to a Transaction as an Account (Flow Responder)

Since the Vault is partitioned into different sections, when you receive a transaction you will need to identify which partition of the vault will store the states.

override fun call() {

    //placeholder to record account information for later use
    val accountMovedTo = AtomicReference<AccountInfo>()

    //extract account information from transaction
    val transactionSigner = object : SignTransactionFlow(counterpartySession) {

        override fun checkTransaction(tx: SignedTransaction) {
            val keyStateMovedTo = tx.coreTransaction.outRefsOfType(InvoiceState::class.java).first().state.data.recipient

            keyStateMovedTo?.let {
    accountMovedTo.set(accountService.accountInfo(keyStateMovedTo.owningKey)?.state?.data)
            }

            if (accountMovedTo.get() == null) {
                throw IllegalStateException("Account to move to was not found on this node")
            }
        }
    }

Now that we are aware of where to store the state, we will finalize the state and associated it with the account.

val transaction = subFlow(transactionSigner)
    if (counterpartySession.counterparty != serviceHub.myInfo.legalIdentities.first()) {
        val recievedTx = subFlow(ReceiveFinalityFlow(counterpartySession, expectedTxId = transaction.id, statesToRecord = StatesToRecord.ALL_VISIBLE))
        val accountInfo = accountMovedTo.get()
        if (accountInfo != null) {
            subFlow(BroadcastToCarbonCopyReceiversFlow(accountInfo, recievedTx.coreTransaction.outRefsOfType(InvoiceState::class.java).first()))
        }
    }
}

After, that we just complete the account to account transaction.

Querying the Vault with Accounts

The accounts library divides partitions the vault, so now you need to pay special attention to how you run a vault query. Vault query is handled by VaultService at the node level. You will need to specify the permission of each vault query at the application level.

For example, a query flow that returns all of the InternalMessageStates by a specific account

class ViewInboxByAccount(
        val acctname : String
) : FlowLogic<List<String>>() {
    @Suspendable
    override fun call(): List<String> {
        val myAccount = accountService.accountInfo(acctname).single().state.data
        val criteria = QueryCriteria.VaultQueryCriteria(
                externalIds = listOf(myAccount.identifier.id)
        )
        val InternalMessages = serviceHub.vaultService.queryBy(
                contractStateType = InternalMessageState::class.java,
                criteria = criteria
        ).states.map { "n" + "Internal Message: " + it.state.data.task }
        return InternalMessages 
    }
}

Points to Note:

1. Accounts is a feature we developed to maximize the use of nodes by partitioning the vault into different sub-vaults, and to open up new ways to manage business flows and data. It will change the design of the flow logic by offering more flexibility and options that were not previously possible with Corda’s flow logic.

2. Accounts is optional. Even on the same network, a company can choose to use the accounts library or not, depending on their organizational and operational needs.

3. Versions: Currently, Accounts is only supported from Corda 4.3.

Links

Full Code is available here

Accounts Briefing by Roger Wills is available here

Thanks to Roger Wills and The Corda Team

Thanks for reading — Peter Li, Developer Evangelist (R3)


Unlocking New Opportunities with Accounts on Corda was originally published in Corda on Medium, where people are continuing the conversation by highlighting and responding to this story.

Share: