CommercialPaper教程,创建发行,移动和兑换货币的流程

时间:2018-03-05 14:52:36

标签: flow corda

我正在尝试创建一个发行和移动货币的流程。 我使用的合同是教程中的CommercialPaper  https://docs.corda.net/tutorial-contract.html。但我无法让它发挥作用。这是我的代码流程。启动所有节点后,我在CLI中使用以下命令(notery / networkmap,PartyA,PartyB)

  1. 启动CPIssueFlow值:6
  2. 启动CPMoveFlow值:3,otherParty:“O = PartyB,L =纽约,C = US”
  3. 我得到的错误是来自'gatherOurInputs'函数的'资金不足'。我该如何解决这个问题?

    更新: github回购是:https://github.com/WesleyCap/Corda/ 该准则已更新。 CPIssueFlow无法正常工作。现在我得到了下一个错误。

    合同验证失败:要求失败:对于发行人C = GB的参考[00],L =伦敦,O = PartyA余额:6 - 0!= 0,合同:net.corda.finance.contracts.asset .Cash @ 58e904ed,交易:71F70042CDA3D46E05ABE319DA5F14D3BDBBB1C80A24753AA9AC660DCD830109

     package com.template
    
    import co.paralleluniverse.fibers.Suspendable
    import com.template.CommercialPaperContract.Companion.CP_PROGRAM_ID
    import net.corda.core.contracts.*
    import net.corda.core.flows.*
    import net.corda.core.identity.AbstractParty
    import net.corda.core.identity.Party
    import net.corda.core.node.ServiceHub
    import net.corda.core.node.services.VaultService
    import net.corda.core.node.services.vault.QueryCriteria
    import net.corda.core.transactions.SignedTransaction
    import net.corda.core.transactions.TransactionBuilder
    import net.corda.core.utilities.ProgressTracker
    import net.corda.finance.DOLLARS
    import net.corda.finance.EUR
    import net.corda.finance.contracts.asset.CASH
    import net.corda.finance.contracts.asset.Cash
    import net.corda.finance.contracts.asset.PartyAndAmount
    import net.corda.finance.issuedBy
    import java.time.Instant
    import java.util.*
    
    
    import net.corda.core.node.services.vault.builder
    import net.corda.core.utilities.OpaqueBytes
    import net.corda.finance.flows.AbstractCashFlow
    import net.corda.finance.flows.CashIssueFlow
    import net.corda.finance.schemas.CashSchemaV1
    
    
    
    
    import net.corda.core.contracts.Amount
    import net.corda.core.flows.StartableByRPC
    
    
    
    
    // *********
    // * Flows *
    // *********
    object CPIssueFlow {
    
        @InitiatingFlow
        @StartableByRPC
        class Initiator(val value: Long) : FlowLogic<Unit>() {
    
            /** The progress tracker provides checkpoints indicating the progress of the flow to observers. */
            override val progressTracker = tracker()
    
            companion object {
                object PREPARING : ProgressTracker.Step("Gathering the required inputs.")
                object CREATECURRENCY : ProgressTracker.Step("Creating cash.")
                object SIGNING : ProgressTracker.Step("Sign the transaction.")
                object TOVAULT : ProgressTracker.Step("Returning the newly-issued cash state.")
    
                fun tracker() = ProgressTracker(PREPARING, CREATECURRENCY, SIGNING, TOVAULT)
            }
    
            /** The flow logic is encapsulated within the call() method. */
            @Suspendable
            override fun call() {
    
                progressTracker.currentStep = PREPARING
                val notary = serviceHub.networkMapCache.notaryIdentities[0]
                val builder = TransactionBuilder(notary)
                val amount = Amount(value , EUR)
                val issuer = ourIdentity.ref(1)
    
                progressTracker.currentStep = CREATECURRENCY
                val signers = Cash().generateIssue(builder, amount.issuedBy(issuer), ourIdentity, notary)
    
                progressTracker.currentStep = SIGNING
                val tx = serviceHub.signInitialTransaction(builder, signers)
    
                progressTracker.currentStep = TOVAULT
                subFlow(FinalityFlow(tx))
            }
        }
    
        @InitiatedBy(CPIssueFlow.Initiator::class)
        class Acceptor(val otherPartyFlow: FlowSession) : FlowLogic<SignedTransaction>() {
            @Suspendable
            override fun call(): SignedTransaction {
                val signTransactionFlow = object : SignTransactionFlow(otherPartyFlow) {
                    override fun checkTransaction(stx: SignedTransaction) = requireThat {
                        val output = stx.tx.outputs.single().data
                        "This must be an CommercialPaperState transaction." using (output is CommercialPaperState)
    
                    }
                }
    
                return subFlow(signTransactionFlow)
            }
        }
    }
    
    object CPMoveFlow {
        @InitiatingFlow
        @StartableByRPC
        class Initiator(val value: Long, val otherParty: Party) : FlowLogic<Unit>() {
    
            /** The progress tracker provides checkpoints indicating the progress of the flow to observers. */
            override val progressTracker = tracker()
    
            companion object {
                object PREPARING : ProgressTracker.Step("Getting the needed information")
                object PREPARESTATES : ProgressTracker.Step("Creating inputstates,outputstates and commands")
                object ADDSTATESTOTX : ProgressTracker.Step("Add inputstates,outputstates and commands to the transaction")
                object VERIFYTX : ProgressTracker.Step("Verify transaction")
                object SIGNING : ProgressTracker.Step("Signing the transaction")
                object SENDTOVAULT : ProgressTracker.Step("Put the transaction in the vault")
    
                fun tracker() = ProgressTracker(PREPARING, PREPARESTATES, ADDSTATESTOTX, VERIFYTX, SIGNING, SENDTOVAULT)
            }
    
            /** The flow logic is encapsulated within the call() method. */
            @Suspendable
            override fun call() {
    
                progressTracker.currentStep = PREPARING
                val notary = serviceHub.networkMapCache.notaryIdentities[0]
                val txBuilder = TransactionBuilder(notary = notary)
                val issuer = ourIdentity.ref(1)
                val amount = Amount(value , EUR)
    
                progressTracker.currentStep = PREPARESTATES
                // We create the transaction components.
                val (inputStates, residual) = gatherOurInputs(serviceHub,runId.uuid ,amount.issuedBy(issuer) , notary)
                val outputState = CommercialPaperState(issuer, otherParty, amount.issuedBy(issuer), Instant.now())
                val outputContractAndState = StateAndContract(outputState, CP_PROGRAM_ID)
                val cmd = Command(CommercialPaperContract.Commands.Move(), listOf(ourIdentity.owningKey, otherParty.owningKey))
    
                progressTracker.currentStep = ADDSTATESTOTX
                txBuilder.withItems(outputContractAndState, cmd)
                txBuilder.addInputState(inputStates[0])
    
                progressTracker.currentStep = VERIFYTX
                txBuilder.verify(serviceHub)
    
                progressTracker.currentStep = SIGNING
                val signedTx = serviceHub.signInitialTransaction(txBuilder)
                val otherpartySession = initiateFlow(otherParty)// Creating a session with the other party.
                val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, listOf(otherpartySession), CollectSignaturesFlow.tracker())) // Obtaining the counterparty's signature.
    
                progressTracker.currentStep = SENDTOVAULT
                // Finalising the transaction.
                subFlow(FinalityFlow(fullySignedTx))
            }
        }
    
        @InitiatedBy(CPMoveFlow.Initiator::class)
        class Acceptor(val otherPartyFlow: FlowSession) : FlowLogic<SignedTransaction>() {
            @Suspendable
            override fun call(): SignedTransaction {
                val signTransactionFlow = object : SignTransactionFlow(otherPartyFlow) {
                    override fun checkTransaction(stx: SignedTransaction) = requireThat {
                        val output = stx.tx.outputs.single().data
                        "This must be an IOU transaction." using (output is CommercialPaperState)
    
                    }
                }
    
                return subFlow(signTransactionFlow)
            }
        }
    
        // This is equivalent to the Cash.generateSpend
        // Which is brought here to make the filtering logic more visible in the example
        private fun gatherOurInputs(serviceHub: ServiceHub,
                                    lockId: UUID,
                                    amountRequired: Amount<Issued<Currency>>,
                                    notary: Party?): Pair<List<StateAndRef<Cash.State>>, Long> {
            // extract our identity for convenience
            val ourKeys = serviceHub.keyManagementService.keys
            val ourParties = ourKeys.map { serviceHub.identityService.partyFromKey(it) ?: throw IllegalStateException("Unable to resolve party from key") }
            val fungibleCriteria = QueryCriteria.FungibleAssetQueryCriteria(owner = ourParties)
    
            val notaries = notary ?: serviceHub.networkMapCache.notaryIdentities.first()
            val vaultCriteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(notary = listOf(notaries as AbstractParty))
    
            val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.equal(amountRequired.token.product.currencyCode) }
            val cashCriteria = QueryCriteria.VaultCustomQueryCriteria(logicalExpression)
    
            val fullCriteria = fungibleCriteria.and(vaultCriteria).and(cashCriteria)
    
            val eligibleStates = serviceHub.vaultService.tryLockFungibleStatesForSpending(lockId, fullCriteria, amountRequired.withoutIssuer(), Cash.State::class.java)
    
            check(eligibleStates.isNotEmpty()) { "Insufficient funds" }
            val amount = eligibleStates.fold(0L) { tot, (state) -> tot + state.data.amount.quantity }
            val change = amount - amountRequired.quantity
    
            return Pair(eligibleStates, change)
        }
    
    }
    

1 个答案:

答案 0 :(得分:0)

在CPMoveFlow期间,您正试图从保险库中收取现金。但是,此时你还没有。

为了将现金用作此交易的输入,它需要来自某个地方。在这些你正在构建原型/测试的情况下,你最好的选择是自己发现自己的现金。

查看流程here中的代码。

编辑: gatherOurInputs不足以正确地花钱。您将需要输入和输出,其中输入是您以前自己发行的现金,输出将x现金的数量与另一方现在作为所有者。

最简单的方法是使用Cash.generateSpend函数,它将为您进行交易的输入和输出,例如

  

Cash.generateSpend(serviceHub,txBuilder,amount,otherParty)

现在,您将看到与商业票据合同验证失败有关的其他错误,但我会留给您调试。 Corda内部的单元测试框架对此非常有用。