在我的Corda中,我试图使用CordaRPCOps的startFlowDynamic调用流,但是在进行调用以初始化流时却遇到此错误:



我已经看到了这个thread of the same exception,但是不能解决我的问题。我认为我传递给流程的参数是正确的,但不确定为什么我仍然遇到此异常。

这是我的ObligationApi.kt,它接受我的表单输入,然后触发issue-obligation,然后触发CreateObligation流:

package com.r3.corda.finance.obligation

import com.r3.corda.finance.obligation.flows.CreateObligation
import com.r3.corda.finance.obligation.states.Obligation
import net.corda.core.contracts.Amount
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.getOrThrow
import java.util.*
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.QueryParam
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response

@Path("obligation")
class ObligationApi(val rpcOps: CordaRPCOps) {

    private val myIdentity = rpcOps.nodeInfo().legalIdentities.first()
    private val notaryIdentity = rpcOps.notaryIdentities().first()

    @GET
    @Path("me")
    @Produces(MediaType.APPLICATION_JSON)
    fun me() = mapOf("me" to myIdentity)

    @GET
    @Path("peers")
    @Produces(MediaType.APPLICATION_JSON)
    fun peers() = mapOf("peers" to rpcOps.networkMapSnapshot()
            .filter { nodeInfo ->
                nodeInfo.legalIdentities.first() != myIdentity
                        && nodeInfo.legalIdentities.first() != notaryIdentity
                        && nodeInfo.legalIdentities.first().name.organisation != "Oracle"
            }
            .map { it.legalIdentities.first().name.organisation })

    @GET
    @Path("all-nodes")
    @Produces(MediaType.APPLICATION_JSON)
    fun allNodes() = mapOf("allNodes" to rpcOps.networkMapSnapshot()
            .filter { nodeInfo ->
                nodeInfo.legalIdentities.first() != myIdentity
            }
            .map { it.legalIdentities.first().name.organisation })

    @GET
    @Path("issue-obligation")
    fun issueObligation(@QueryParam(value = "role") role: String,
                        @QueryParam(value = "party") party: String,
                        @QueryParam(value = "currency") currency: String,
                        @QueryParam(value = "amount") amount: Int,
                        @QueryParam(value = "duedate") duedate: Int
    ): Response {
        println("!! TESTING - ObligationApi.kt 1.0")
        println(myIdentity)

        // 1. Get party objects for the counterparty.
        val obligorIdentity = rpcOps.partiesFromName(party, exactMatch = false).singleOrNull()
                ?: throw IllegalStateException("Couldn't lookup node identity for $party.")
        // 2. Create an amount object.
        val issueAmount = Amount(amount.toLong() * 100, Currency.getInstance(currency))

        // 3. Get role class
        val roleClass: CreateObligation.InitiatorRole
        if (role == "OBLIGEE") {
            roleClass = CreateObligation.InitiatorRole.OBLIGEE
        } else {
            roleClass = CreateObligation.InitiatorRole.OBLIGOR
        }

        // 4. Start the IssueObligation flow. We block and wait for the flow to return.
        val (status, message) = try {
            println("!! TESTING - ObligationApi.kt 1.1")

            val flowHandle = rpcOps.startFlowDynamic(
                    CreateObligation.Initiator::class.java,
                    issueAmount,
                    roleClass,
                    obligorIdentity,
                    duedate,
                    true
            )
            println("!! TESTING - ObligationApi.kt 1.2")

            val result = flowHandle.returnValue.getOrThrow()
            flowHandle.close()
            Response.Status.CREATED to "Transaction id ${result.id} committed to ledger.\n${result.outputs}"
        } catch (e: Exception) {
            Response.Status.BAD_REQUEST to e.message
        }

        // 4. Return the result.
        return Response.status(status).entity(message).build()
    }

    @GET
    @Path("obligations")
    @Produces(MediaType.APPLICATION_JSON)
    fun obligations(): List<Obligation<*>> {
        println("!! TESTING - ObligationApi.kt 2.0")

        val statesAndRefs = rpcOps.vaultQuery(Obligation::class.java).states

        return statesAndRefs
                .map { stateAndRef -> stateAndRef.state.data }
                .map { state ->
                    // We map the anonymous lender and borrower to well-known identities if possible.
                    val possiblyWellKnownLender = rpcOps.wellKnownPartyFromAnonymous(state.obligee) ?: state.obligee
                    val possiblyWellKnownBorrower = rpcOps.wellKnownPartyFromAnonymous(state.obligor) ?: state.obligor
                    println("!! TESTING - ObligationApi.kt 2.1")

                    Obligation(state.faceAmount,
                            possiblyWellKnownBorrower,
                            possiblyWellKnownLender,
                            state.dueBy,
                            state.createdAt,
                            state.settlementMethod,
                            state.payments,
                            state.linearId)
                }
    }
}

我的CreateObligation.kt流:
package com.r3.corda.finance.obligation.flows

import co.paralleluniverse.fibers.Suspendable
import com.r3.corda.finance.obligation.commands.ObligationCommands
import com.r3.corda.finance.obligation.contracts.ObligationContract
import com.r3.corda.finance.obligation.states.Obligation
import com.r3.corda.finance.obligation.types.Money
import net.corda.confidential.SwapIdentitiesFlow
import net.corda.core.contracts.Amount
import net.corda.core.flows.*
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.seconds
import java.security.PublicKey
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneOffset

object CreateObligation {

    @CordaSerializable
    enum class InitiatorRole {
        OBLIGOR,
        OBLIGEE
    }

    @InitiatingFlow
    @StartableByRPC
    class Initiator<T : Money>(
            private val amount: Amount<T>,
            private val role: InitiatorRole,
            private val counterparty: Party,
            private val dueBy: Instant? = null,
            private val anonymous: Boolean = true
    ) : FlowLogic<WireTransaction>() {

        companion object {
            object INITIALISING : ProgressTracker.Step("Performing initial steps.")
            object BUILDING : ProgressTracker.Step("Building and verifying transaction. @@@@@ Testing In  CorDapp CreateObligation.kt @@@@@")
            object SIGNING : ProgressTracker.Step("signing transaction.")
            object COLLECTING : ProgressTracker.Step("Collecting counterparty signature.") {
                override fun childProgressTracker() = CollectSignaturesFlow.tracker()
            }

            object FINALISING : ProgressTracker.Step("Finalising transaction.") {
                override fun childProgressTracker() = FinalityFlow.tracker()
            }

            fun tracker() = ProgressTracker(INITIALISING, BUILDING, SIGNING, COLLECTING, FINALISING)
        }

        override val progressTracker: ProgressTracker = tracker()

        @Suspendable
        private fun createAnonymousObligation(): Pair<Obligation<T>, PublicKey> {
            val txKeys = subFlow(SwapIdentitiesFlow(counterparty))
            // SwapIdentityFlow should return two keys.
            check(txKeys.size == 2) { "Something went wrong when generating confidential identities." }
            val anonymousMe = txKeys[ourIdentity] ?: throw FlowException("Couldn't create our conf. identity.")
            val anonymousObligor = txKeys[counterparty]
                    ?: throw FlowException("Couldn't create lender's conf. identity.")
            return createObligation(us = anonymousMe, them = anonymousObligor)
        }

        private fun createObligation(us: AbstractParty, them: AbstractParty): Pair<Obligation<T>, PublicKey> {
            check(us != them) { "You cannot create an obligation to yourself" }
            val obligation = when (role) {
                InitiatorRole.OBLIGEE -> Obligation(amount, them, us, dueBy)
                InitiatorRole.OBLIGOR -> Obligation(amount, us, them, dueBy)
            }
            return Pair(obligation, us.owningKey)
        }

        @Suspendable
        override fun call(): WireTransaction {
            println("!! TESTING - CreateObligation.kt 1.0")

            // Step 1. Initialisation.
            progressTracker.currentStep = INITIALISING
            val (obligation, signingKey) = if (anonymous) {
                createAnonymousObligation()
            } else {
                createObligation(us = ourIdentity, them = counterparty)
            }

            // Step 2. Check parameters.
            if (dueBy != null) {
                val todayUTC = LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC)
                require(dueBy > todayUTC) {
                    "Due by date must be in the future."
                }
            }

            // Step 3. Building.
            progressTracker.currentStep = BUILDING
            val notary = serviceHub.networkMapCache.notaryIdentities.firstOrNull()
                    ?: throw FlowException("No available notary.")
            val utx = TransactionBuilder(notary = notary).apply {
                addOutputState(obligation, ObligationContract.CONTRACT_REF)
                val signers = obligation.participants.map { it.owningKey }
                addCommand(ObligationCommands.Create(), signers)
                setTimeWindow(serviceHub.clock.instant(), 30.seconds)
            }

            // Step 4. Sign the transaction.
            progressTracker.currentStep = SIGNING
            val ptx = serviceHub.signInitialTransaction(utx, signingKey)

            // Step 5. Get the counterparty signature.
            progressTracker.currentStep = COLLECTING
            val lenderFlow = initiateFlow(counterparty)
            val stx = subFlow(CollectSignaturesFlow(
                    partiallySignedTx = ptx,
                    sessionsToCollectFrom = setOf(lenderFlow),
                    myOptionalKeys = listOf(signingKey),
                    progressTracker = COLLECTING.childProgressTracker())
            )

            // Step 6. Finalise and return the transaction.
            progressTracker.currentStep = FINALISING
            val ntx = subFlow(FinalityFlow(stx, FINALISING.childProgressTracker()))
            return ntx.tx
        }
    }

    @InitiatedBy(Initiator::class)
    class Responder(val otherFlow: FlowSession) : FlowLogic<WireTransaction>() {
        @Suspendable
        override fun call(): WireTransaction {
            val flow = object : SignTransactionFlow(otherFlow) {
                @Suspendable
                override fun checkTransaction(stx: SignedTransaction) {
                    // TODO: Do some basic checking here.
                    // Reach out to human operator when HCI is available.
                }
            }
            val stx = subFlow(flow)
            // Suspend this flow until the transaction is committed.
            return waitForLedgerCommit(stx.id).tx
        }
    }
}

最佳答案

如果我没记错的话,那么您的流构造函数中将没有默认参数。我相信我们有一个问题可以解决。

07-24 14:26