A Brief Introduction to the Accounts Library in Corda Blockchain

Read about the latest Accounts Library feature on Corda blockchain platform. It enables several users on a single node to execute transactions

Accounts Library in Corda Blockchain?

The accounts library enables a Corda node’s vault partition into several subsets. The vault refers to a set of state objects.  Each subset is a representation of an account. In different words, the account’s library enables the operator of a Corda node to segment the vault into several “logical” sub-vaults, which benefits in the following ways.

  • Node operators can host multiple entities as accounts on a single node, therefore, lowering the costs.
  • Node operators can do the partition of the vault on a per entity requirement.

Adding Accounts Dependencies on a CorDapp

First, add a variable for the accounts release group and the version you wish to use, and add the Corda version that should’ve installed locally.

buildscript {
    ext {
        corda_release_version = '4.3-RC01'
        corda_gradle_plugins_version = '4.0.42'
        accounts_release_version = '1.0-RC03'
        quasar_version = '0.7.10'
        kotlin_version = '1.2.71'
        testng_version = '6.14.3'
    }

    repositories {
        mavenCentral()
        jcenter()
        maven { url 'https://ci-artifactory.corda.r3cev.com/artifactory/corda' }
    }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "net.corda.plugins:cordapp:$corda_gradle_plugins_version"
        classpath "net.corda.plugins:cordformation:$corda_gradle_plugins_version"
    }
}

Second, you must add the accounts artifactory repository,  the list of repositories for your project :

allprojects {
    repositories {
        jcenter()
        mavenCentral()
        maven { url 'https://ci-artifactory.corda.r3cev.com/artifactory/corda' }
        //Corda testing node-driver requires gradle-tooling-api
        maven { url 'https://repo.gradle.org/gradle/libs-releases-local/' }
       
        //This is needed until the accounts sdk is official
        maven { url "http://ci-artifactory.corda.r3cev.com/artifactory/corda-lib-dev" }
        maven { url 'http://ci-artifactory.corda.r3cev.com/artifactory/corda-lib' }
    }

You can add the Corda and accounts dependencies to the dependencies block in each module of your CorDapp.

dependencies {
    cordaCompile "net.corda:corda-node-api:$corda_release_version"

    cordapp "com.r3.corda.lib.accounts:accounts-contracts:$accounts_release_version"
    cordapp "com.r3.corda.lib.accounts:accounts-workflows:$accounts_release_version"
}

These should also be added to the deploy nodes task with the following syntax:

nodeDefaults {
        projectCordapp {
            deploy = false
        }
        cordapp("com.r3.corda.lib.accounts:accounts-contracts:$accounts_release_version")
        cordapp("com.r3.corda.lib.accounts:accounts-workflows:$accounts_release_version")
        rpcUsers = [[user: "user1", "password": "test", "permissions": ["ALL"]]]
    }

How to share IOUvalue from one node to another is described in Corda’s official website IOU example.

In this blog, we will explain how to share value from one account to another on the same node.

An Example of IOU using Account Library

The IOUState 

The shared facts on the blockchain are represented as states. Our task will define a new state ” TransactionState” to represent an IOU.

Corda state is an instance of a class that implements the ContractState interface.

In Corda, the ContractState interface has a single field, participants. It represents a list of the entities for which this state is relevant.

@BelongsToContract(TransactionContracts::class)
class TransactionState(
    val IOUvalue: Int,
    val lender: PublicKey,
    val borrower: PublicKey

) : ContractState {
    override val participants: List<AbstractParty> get() = listOf(lender, borrower).map { AnonymousParty(it)}
}

 

The IOUContract

In Corda, every contract must implement the Contract interface.

In Corda, the contract has a single methodverify, which takes a Transaction as input.

  • Verify function, Throws an IllegalArgumentException if it rejects the transaction proposal.
  • Verify function, Returns silently if it accepts the transaction proposal.
class TransactionContracts : Contract {
    companion object
    {
        @JvmStatic
        val Trans_CONTRACT_ID = TransactionContracts::class.java.name
    }
    interface Commands : CommandData{
        class Create : TypeOnlyCommandData(),Commands
    }

    override fun verify(tx: LedgerTransaction) {
        
    }
}

 

The IOU Flow

In Corda, recording a transaction or sending a transaction to a counterparty is very common. Corda developer, instead of forcing to reimplement their logic to handle these tasks, use Corda provided several library’s flows to handle these tasks. Using these flows, we invoke in the context of a larger flow to handle a repeatable task sub-flows.

Corda Flow is instances of classes, made of FlowLogic subclass.

@InitiatingFlow means that this flow is part of a flow pair and that it triggers the other side to run the counterpart flow (which in our case is the TransactionResponderFlow defined below).

In Corda @StartableByRPC , RPC interface [startFlowDynamic] Used to initiate any flow [FlowLogic]  and [startTrackedFlowDynamic]) must have this annotation. If it’s missing, the flow will not be allowed to start and an exception will be thrown.

@InitiatingFlow
@StartableByRPC
class TransactionFlow (val IouValue : Int,
                       val lenderId : UUID,
                       val borrowerId: UUID

) : FlowLogic<SignedTransaction>() {

    override fun call() :SignedTransaction{

        val notary = serviceHub.networkMapCache.notaryIdentities.first();

        val lenderAccountInfo = accountService.accountInfo(lenderId) ?: throw IllegalStateException("Can't find account to move from $lenderId")

        val borrowerAccountInfo = accountService.accountInfo(borrowerId) ?: throw IllegalStateException("Can't find account to move from $borrowerId")

        val lenderKey = subFlow(RequestKeyForAccount(lenderAccountInfo.state.data)).owningKey

        val browerKey = subFlow(RequestKeyForAccount(borrowerAccountInfo.state.data)).owningKey

        val transactionState = TransactionState(IouValue,lenderKey,browerKey)

        val commond = Command(TransactionContracts.Commands.Create(),transactionState.participants.map {it.owningKey})

        val txbuilder = TransactionBuilder(notary).
            addOutputState(transactionState,TransactionContracts.Trans_CONTRACT_ID).addCommand(commond)

        txbuilder.verify(serviceHub)

        var keysToSignWith = mutableListOf(ourIdentity.owningKey, lenderKey)

        //Only add the borrower account if it is hosted on this node (potentially it might be on a different node)

        if (borrowerAccountInfo.state.data.host == serviceHub.myInfo.legalIdentitiesAndCerts.first().party) {
            keysToSignWith.add(browerKey)
        }

        val locallySignedTx = serviceHub.signInitialTransaction(txbuilder,keysToSignWith)

        //We have to do 2 different flows depending on whether the other account is on our node or a different node

        if (borrowerAccountInfo.state.data.host == serviceHub.myInfo.legalIdentitiesAndCerts.first().party) {
            //Notarise and record the transaction in just our vault.

            return subFlow(FinalityFlow(locallySignedTx, emptyList()))
        } else{

        val borrowerSession = initiateFlow(borrowerAccountInfo.state.data.host)
        val borrowerSignature = subFlow(CollectSignatureFlow(locallySignedTx, borrowerSession, browerKey))
        val fullySignedTx = locallySignedTx.withAdditionalSignatures(borrowerSignature)

        return subFlow(FinalityFlow(fullySignedTx, listOf(borrowerSession)))
    }
    }
}

The IOU Responder Flow

TransactionResponderFlow, in this flow, the borrower has to use ReceiveFinalityFlow to receive and record transactions. It responds to the flow of the lender.

@InitiatedBy(TransactionFlow::class)
class TransactionResponderFlow(val counterPartySession : FlowSession) : FlowLogic<SignedTransaction>() {
    override fun call(): SignedTransaction {
        val signTransactionFlow = object : SignTransactionFlow(counterPartySession) {
            override fun checkTransaction(stx: SignedTransaction) = requireThat {
                val output = stx.tx.outputs.single().data
                "This must be an lender and borrower  transaction." using (output is TransactionState<*>)
            }
        }
        val txId = subFlow(signTransactionFlow).id

        return subFlow(ReceiveFinalityFlow(counterPartySession, expectedTxId = txId))
       
    }
}

Conclusion

In this post, we implemented the process of an IOU using the account library while inspecting the components that are required to do so. In the Cordapp, States are facts that are shared among parties on the network, contracts are used to validate transactions and flows contain the logic to propose new transactions.

At Cliqcube, along with offering Corda blockchain development services, we keep planning to write more on the development aspects of Corda and its complex features.

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Leave a Comment

Your email address will not be published. Required fields are marked *

More From Cliqcube

We would love to hear from you!

Cliqcube | Blockchain Development Company

Scroll to Top