在Corda交易中添加多输出状态时合约验证失败问题
Corda多输出状态合约验证失败问题排查与解决
最近我在Corda里做交易时,尝试用toTypedArray在单个交易中添加多个同合约但不同签名者的IOU输出状态,结果运行后合约验证直接失败,抛出**"List has more than one element"**的异常,折腾了好一会儿才找到问题根源,分享给大家:
我的代码片段
交易构建代码
val states: ArrayList<IOU>() for(i in data.indices) { val state = IOU(data[i], initiator, acceptor) signers = state.participants.map{it.owningKey} txbuilder.addCommand(contract1, signers) states.add(state) } txbuilder.withItems(*states.map { StateAndContract(it, Contract.ID) }).toTypedArray txbuilder.verify(serviceHub)
合约验证代码
out = tx.outputsOfType<IOU>().single() "The sender and the recipient cannot be the same entity." using (out.party1!= out.party2)
问题根源
一眼就能定位到问题:合约里用了single()方法获取输出状态——这个方法的特性是要求集合必须恰好有一个元素,多了少了都会抛出异常。而我现在的交易里包含多个IOU输出状态,直接触发了这个限制,导致验证失败。
解决方案
1. 修复合约验证逻辑
把针对单个状态的验证改成遍历所有输出状态,逐个检查规则:
// 替换原来的single()逻辑,用forEach遍历所有IOU输出 tx.outputsOfType<IOU>().forEach { out -> "The sender and the recipient cannot be the same entity." using (out.party1!= out.party2) // 其他针对单个IOU的验证规则也放在这里 }
2. 优化交易命令添加逻辑
原来的循环里每次都添加一个命令,会导致交易里存在多个重复的合约命令,既没必要也可能引发其他问题。应该收集所有需要的签名者,统一添加一次命令(如果所有输出对应的是同一种命令类型):
val states: ArrayList<IOU>() val allSigners = mutableSetOf<PublicKey>() // 用Set避免重复公钥 for(i in data.indices) { val state = IOU(data[i], initiator, acceptor) val stateSigners = state.participants.map{it.owningKey} allSigners.addAll(stateSigners) states.add(state) } // 统一添加一次命令,包含所有需要签名的公钥 txbuilder.addCommand(contract1, allSigners.toList()) // 注意toTypedArray()要加括号,原来的代码漏了! txbuilder.withItems(*states.map { StateAndContract(it, Contract.ID) }.toTypedArray()) txbuilder.verify(serviceHub)
额外注意点
- 别漏了
toTypedArray()的括号,不然只是引用方法而非执行它,会导致withItems接收的参数不符合要求; - 确保每个IOU状态的
participants都正确设置,签名者集合要覆盖所有需要签署交易的节点公钥,避免后续签名环节出错。
内容的提问来源于stack exchange,提问作者viswa
相关产品推荐
相关产品推荐

