All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.corda.flows.IssuerFlow.kt Maven / Gradle / Ivy

There is a newer version: 0.12.1
Show newest version
package net.corda.flows

import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.*
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.node.PluginServiceHub
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
import java.util.*

/**
 *  This flow enables a client to request issuance of some [FungibleAsset] from a
 *  server acting as an issuer (see [Issued]) of FungibleAssets.
 *
 *  It is not intended for production usage, but rather for experimentation and testing purposes where it may be
 *  useful for creation of fake assets.
 */
object IssuerFlow {
    data class IssuanceRequestState(val amount: Amount, val issueToParty: Party, val issuerPartyRef: OpaqueBytes)

    /**
     * IssuanceRequester should be used by a client to ask a remote node to issue some [FungibleAsset] with the given details.
     * Returns the transaction created by the Issuer to move the cash to the Requester.
     */
    class IssuanceRequester(val amount: Amount, val issueToParty: Party, val issueToPartyRef: OpaqueBytes,
                            val issuerBankParty: Party): FlowLogic() {
        @Suspendable
        @Throws(CashException::class)
        override fun call(): SignedTransaction {
            val issueRequest = IssuanceRequestState(amount, issueToParty, issueToPartyRef)
            return sendAndReceive(issuerBankParty, issueRequest).unwrap { it }
        }
    }

    /**
     * Issuer refers to a Node acting as a Bank Issuer of [FungibleAsset], and processes requests from a [IssuanceRequester] client.
     * Returns the generated transaction representing the transfer of the [Issued] [FungibleAsset] to the issue requester.
     */
    class Issuer(val otherParty: Party): FlowLogic() {
        companion object {
            object AWAITING_REQUEST : ProgressTracker.Step("Awaiting issuance request")
            object ISSUING : ProgressTracker.Step("Self issuing asset")
            object TRANSFERRING : ProgressTracker.Step("Transferring asset to issuance requester")
            object SENDING_CONFIRM : ProgressTracker.Step("Confirming asset issuance to requester")
            fun tracker() = ProgressTracker(AWAITING_REQUEST, ISSUING, TRANSFERRING, SENDING_CONFIRM)
            private val VALID_CURRENCIES = listOf(USD, GBP, EUR, CHF)
        }

        override val progressTracker: ProgressTracker = tracker()

        @Suspendable
        @Throws(CashException::class)
        override fun call(): SignedTransaction {
            progressTracker.currentStep = AWAITING_REQUEST
            val issueRequest = receive(otherParty).unwrap {
                // validate request inputs (for example, lets restrict the types of currency that can be issued)
                if (it.amount.token !in VALID_CURRENCIES) throw FlowException("Currency must be one of $VALID_CURRENCIES")
                it
            }
            // TODO: parse request to determine Asset to issue
            val txn = issueCashTo(issueRequest.amount, issueRequest.issueToParty, issueRequest.issuerPartyRef)
            progressTracker.currentStep = SENDING_CONFIRM
            send(otherParty, txn)
            return txn
        }

        // TODO: resolve race conditions caused by the 2 separate Cashflow commands (Issue and Pay) not reusing the same
        //       state references (thus causing Notarisation double spend exceptions).
        @Suspendable
        private fun issueCashTo(amount: Amount,
                                issueTo: Party,
                                issuerPartyRef: OpaqueBytes): SignedTransaction {
            // TODO: pass notary in as request parameter
            val notaryParty = serviceHub.networkMapCache.notaryNodes[0].notaryIdentity
            // invoke Cash subflow to issue Asset
            progressTracker.currentStep = ISSUING
            val bankOfCordaParty = serviceHub.myInfo.legalIdentity
            val issueCashFlow = CashFlow(CashCommand.IssueCash(amount, issuerPartyRef, bankOfCordaParty, notaryParty))
            val issueTx = subFlow(issueCashFlow)
            // NOTE: issueCashFlow performs a Broadcast (which stores a local copy of the txn to the ledger)
            // short-circuit when issuing to self
            if (issueTo == serviceHub.myInfo.legalIdentity)
                return issueTx
            // now invoke Cash subflow to Move issued assetType to issue requester
            progressTracker.currentStep = TRANSFERRING
            val moveCashFlow = CashFlow(CashCommand.PayCash(amount.issuedBy(bankOfCordaParty.ref(issuerPartyRef)), issueTo))
            val moveTx = subFlow(moveCashFlow)
            // NOTE: CashFlow PayCash calls FinalityFlow which performs a Broadcast (which stores a local copy of the txn to the ledger)
            return moveTx
        }

        class Service(services: PluginServiceHub) {
            init {
                services.registerFlowInitiator(IssuanceRequester::class, ::Issuer)
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy