A Brief Introduction to the Accounts Library in Corda Blockchain

A Brief Introduction to the Accounts Library in Corda Blockchain

Posted by : deepak.maurya | 16-Dec-2019

  • Accounts Library Corda Blockchain

    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 Oodles, along with offering Corda blockchain development services, we keep planning to write more on the development aspects of Corda and its complex features.


Leave a Comment

Name is required

Comment is required

No Comments Yet.

About Author

deepak.maurya (Writer)

More From Oodles

Cookies are important to the proper functioning of a site. To improve your experience, we use cookies to remember log-in details and provide secure log-in, collect statistics to optimize site functionality, and deliver content tailored to your interests. Click Agree and Proceed to accept cookies and go directly to the site or click on View Cookie Settings to see detailed descriptions of the types of cookies and choose whether to accept certain cookies while on the site.