You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在同一CorDapp中测试两种响应者流程及修复测试报错

解决同一CorDapp中多响应流程的测试报错问题

嘿,这个IllegalArgumentException我太熟了——核心原因就是你在同一个CorDapp里给两个响应流程都加了@InitiatedBy(Initiator::class)注解,Corda节点的流程注册器直接懵了:到底用哪个流程处理发起方的请求?再加上测试环节没明确指定节点和响应流程的对应关系,可不就报错了嘛。

下面分两步给你搞定,先把流程逻辑调对,再把测试配置做好:

一、先把流程的注解和发起逻辑捋顺

首先得去掉多个响应流程共用同一个@InitiatedBy的冲突,改成让发起方明确告诉每个交易对手该启动哪个响应流程:

  1. 发起方Initiator流程:
    保留@InitiatingFlow和@StartableByRPC注解,关键是在和Alice、Bob建立会话时,用initiateFlow的重载方法指定对方要启动的流程类,别让节点瞎猜:

    @InitiatingFlow
    @StartableByRPC
    class Initiator(private val aliceParty: Party, private val bobParty: Party) : FlowLogic<SignedTransaction>() {
        override fun call(): SignedTransaction {
            // 明确告诉Alice节点:启动AliceFlow来响应我
            val aliceSession = initiateFlow(aliceParty, flowClass = AliceFlow::class.java)
            // 同理告诉Bob节点启动BobFlow
            val bobSession = initiateFlow(bobParty, flowClass = BobFlow::class.java)
            
            // 后面就是常规的交易构建、签名逻辑了
            val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
            // 这里填充你的交易内容,比如状态、命令啥的
            
            val partSignedTx = serviceHub.signInitialTransaction(txBuilder)
            
            // 收集Alice和Bob的签名
            val fullySignedTx = subFlow(CollectSignaturesFlow(partSignedTx, listOf(aliceSession, bobSession)))
            
            return subFlow(FinalityFlow(fullySignedTx, listOf(aliceSession, bobSession)))
        }
    }
    
  2. AliceFlow和BobFlow响应流程:
    直接把@InitiatedBy(Initiator::class)注解删掉,改成普通的FlowLogic子类,接收发起方的会话就行。注意加个无参构造,不然测试框架会报错:

    class AliceFlow(private val otherSession: FlowSession) : FlowLogic<SignedTransaction>() {
        // 测试用的无参构造,必须加
        @Suppress("unused")
        constructor() : this(FlowSessionImpl())
        
        override fun call(): SignedTransaction {
            // 这里写Alice的响应逻辑,比如校验交易、签名
            val signTxFlow = object : SignTransactionFlow(otherSession) {
                override fun checkTransaction(stx: SignedTransaction) {
                    // 自定义校验逻辑,比如检查交易内容是否合法
                }
            }
            val txId = subFlow(signTxFlow)
            return subFlow(ReceiveFinalityFlow(otherSession, expectedTxId = txId.id))
        }
    }
    

    BobFlow的结构和AliceFlow完全一样,只需要替换类名和校验逻辑就行。

二、配置测试用的MockNetwork

用Corda的MockNetwork测试时,得给每个节点注册所有需要的流程,确保节点知道该处理哪些流程:

class MultiResponderFlowTest {
    private lateinit var mockNetwork: MockNetwork
    private lateinit var initiatorNode: StartedNode<MockNode>
    private lateinit var aliceNode: StartedNode<MockNode>
    private lateinit var bobNode: StartedNode<MockNode>

    @Before
    fun setup() {
        // 初始化MockNetwork,加载你的CorDapp
        mockNetwork = MockNetwork(MockNetworkParameters(cordappsForAllNodes = listOf(
            TestCordapp.findCordapp("com.template")
        )))
        // 创建三个测试节点
        initiatorNode = mockNetwork.createNode()
        aliceNode = mockNetwork.createNode()
        bobNode = mockNetwork.createNode()
        
        // 给每个节点注册两个响应流程,避免节点找不到流程
        listOf(initiatorNode, aliceNode, bobNode).forEach { node ->
            node.registerInitiatedFlow(AliceFlow::class.java)
            node.registerInitiatedFlow(BobFlow::class.java)
        }
        
        // 启动网络
        mockNetwork.runNetwork()
    }

    @After
    fun tearDown() {
        // 测试结束后停止所有节点
        mockNetwork.stopNodes()
    }

    @Test
    fun `test multi responder flow execution`() {
        val aliceParty = aliceNode.info.legalIdentities.first()
        val bobParty = bobNode.info.legalIdentities.first()
        
        // 启动发起方流程
        val flow = Initiator(aliceParty, bobParty)
        val future = initiatorNode.startFlow(flow)
        
        // 运行网络直到所有流程执行完
        mockNetwork.runNetwork()
        
        // 验证交易结果,比如检查交易输出是否存在
        val signedTx = future.get()
        assert(signedTx.tx.outputs.isNotEmpty())
        // 这里可以加更多自定义验证逻辑,比如检查签名数量
    }
}

几个关键注意点

  • 如果你的Corda版本比较老,initiateFlow的flowClass参数可能不存在,那可以改用SendAndReceiveFlow传递自定义消息,让响应流程根据消息类型判断是否处理。
  • 要是你非要保留@InitiatedBy注解(比如需要支持RPC启动响应流程),那可以在响应流程里加身份校验:只有当发起方是预期的节点时才处理,否则抛异常,比如:
    @InitiatedBy(Initiator::class)
    class AliceFlow(private val otherSession: FlowSession) : FlowLogic<SignedTransaction>() {
        override fun call(): SignedTransaction {
            // 只处理来自Initiator节点的请求
            val initiatorParty = initiatorNode.info.legalIdentities.first()
            if (otherSession.counterparty != initiatorParty) {
                throw IllegalArgumentException("这个流程只响应来自${initiatorParty.name}的Initiator请求")
            }
            // 后续逻辑...
        }
    }
    

内容的提问来源于stack exchange,提问作者Joel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:27:25