Corda-设计安全契约
最近,我一直在研究Corda中contract命令的安全性和脆弱性。为了允许不同输入、输出和命令的事务组合,一些契约命令约束是应该严格还是应该放松引起了争论 问题是,虽然我可以看到允许事务组合的好处,但我觉得放松的合同命令约束实际上会带来安全漏洞,我认为,最好在合同级别防范这些漏洞,在这种情况下,命令的签名参与者通过合同验证作为一个整体达成共识,而不是依赖流级别检查,这可能会被开发人员忽略或被恶意节点规避 示例-破产声明 此示例允许网络上的节点宣布破产。在这种情况下,假设破产声明状态只是声明破产的节点的标识和原因Corda-设计安全契约,corda,Corda,最近,我一直在研究Corda中contract命令的安全性和脆弱性。为了允许不同输入、输出和命令的事务组合,一些契约命令约束是应该严格还是应该放松引起了争论 问题是,虽然我可以看到允许事务组合的好处,但我觉得放松的合同命令约束实际上会带来安全漏洞,我认为,最好在合同级别防范这些漏洞,在这种情况下,命令的签名参与者通过合同验证作为一个整体达成共识,而不是依赖流级别检查,这可能会被开发人员忽略或被恶意节点规避 示例-破产声明 此示例允许网络上的节点宣布破产。在这种情况下,假设破产声明状态只是声明破产
@BelongsToContract(BankruptcyDeclarationContract::class)
data class BankruptcyDeclarationState(
override val owner: AbstractParty,
val reason: String
) : OwnableState { ... }
严格验证
严格的核查要求,在发行时
- 必须使用零输入状态
- 必须创建一个输出状态
- 只有所有者必须签名
- 严格的验证可以确保全局检查输入和输出,而不是检查特定的输入和输出类型,但是这有一个缺点,即输入和输出的事务组合是不可能的
- 宽松的验证确保只检查所需状态类型的输入和输出,这将允许不同输入和输出类型的事务组合
- 这里的关键是只有声明破产的节点必须签名,这意味着只能从该节点发出破产声明状态不允许任何其他人代表网络上的其他节点宣布破产。
义务状态
提供了一个合同命令,该命令发出时要求:
- 必须使用类型为
的零输入状态ObligationState
- 必须创建一个类型为
的输出状态ObligationState
- 债务人和债权人必须签字
val transaction = with(TransactionBuilder(notary)) {
addOutputState(ObligationState(alice, bob), ObligationContract.ID)
addCommand(ObligationContract.Issue(), aliceKey, bobKey)
addOutputState(BankruptcyDeclarationState(alice, "..."), BankruptcyDeclarationContract.ID)
addCommand(BankruptcyDeclarationContract.Issue(), aliceKey)
}
请记住,只有破产声明状态
的所有者必须签名,而义务状态
的债务人和债权人必须签名,因此此启动流程将从所需的交易对手处收集签名。此处的漏洞是,bob启动此事务,但包含类型为BankrupcDeclarationState
的输出,该输出归alice所有。他不应该被允许这样做,因为只有所有者才应该被允许发布破产声明状态,但在这种情况下,alice会无意中签名,因为需要为义务状态
签名
这里有一个论点是,流的编写方式可以是alice在签名之前检查事务,以确保不包括某些状态,但我觉得这还不够。这要求开发人员和节点管理员对流进行尽职调查,以确保其安全性
相比之下,严格的合同命令约束将以我认为更安全的方式防止这些漏洞——因此,只需要在合同级别进行尽职调查,而不是由每个编写使用合同的流的开发人员进行尽职调查
在这方面,我所寻找的是一些明确的指南,说明合同命令约束是否应该严格、放松,或者是否有其他我遗漏的考虑因素。谢谢。正如您正确指出的,所有交易方共享相同的合同代码。这是他们之间唯一的协议。 但是,各方通过开发自己的安全流,对自己的行为(签名)负责。书面流程的基本原理是在签署之前根据合同代码验证交易。谁会在没有阅读/检查合同的情况下以数字或其他方式签署任何东西? 我错过什么了吗
fun verifyIssue(tx: LedgerTransaction, signers: Set<PublicKey>) = requireThat {
val inputs = tx.inputsOfType<BankruptcyDeclarationState>()
val outputs = tx.outputsOfType<BankruptcyDeclarationState>()
"Zero input states of type BankruptcyDeclarationState must be consumed." using
(inputs.isEmpty())
"One output state of type BankruptcyDeclarationState must be created." using
(outputs.size == 1)
"Only the owner must sign." using (outputs.single().owner.owningKey == signers.single())
}
val transaction = with(TransactionBuilder(notary)) {
addOutputState(ObligationState(alice, bob), ObligationContract.ID)
addCommand(ObligationContract.Issue(), aliceKey, bobKey)
addOutputState(BankruptcyDeclarationState(alice, "..."), BankruptcyDeclarationContract.ID)
addCommand(BankruptcyDeclarationContract.Issue(), aliceKey)
}