From release-M13 of Corda, in the CordApp-Tutorial example, there are some constraints checks made within the flow itself (ExampleFlow.Acceptor). My question is, what constraints may I check on the flow, and what constraints in the Contract? Or it's just an organization issue?
This is a great question. I believe you are referring to:
@InitiatedBy(Initiator::class)
class Acceptor(val otherParty: Party) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
val signTransactionFlow = object : SignTransactionFlow(otherParty) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
val output = stx.tx.outputs.single().data
"This must be an IOU transaction." using (output is IOUState)
val iou = output as IOUState
"The IOU's value can't be too high." using (iou.iou.value < 100)
}
}
return subFlow(signTransactionFlow)
}
}
The CollectSignaturesFlow
and its counterpart, the SignTransactionFlow
automates the collection of signatures for any type of transaction. This automation is super useful because developers don't have to manually write flows for signature collection anymore! However, developers have to be aware that given any valid transaction - as per the referenced contract code in the transaction - the counter-party will always sign! This is because transactions are validated in isolation, not relative to some expected external values.
Let me provide two examples:
- If I have access to one of your unspent cash states from a previous transaction, then perhaps I could create a cash spend transaction (from you to me) and ask you to sign it via the
CollectSignaturesFlow
. If the transaction is valid then, without any additional checking, you'll sign it which will result in you sending me the cash. Clearly you don't want this! - The contract code can only go part of the way to verifying a transaction. If you want to check that the transaction represents a deal you want to enter into e.g. price < some amount then you'll have to do some additional checking. The contract code cannot opine on what constitutes a commercially viable deal for you. This checking has to be done as part of the
SignTransactionFlow
by overridingsignTransaction
In a production CorDapp, one may wish to defer to human judgement on whether to sign a transaction and enter into a deal. Or alternatively, this process could be automated by reaching out to some external reference data system via HTTP API or MQ to determine if the deal is one that should be entered into.
In the code example above, we added two simple constraints:
- One which prevents a borrower from creating an overly large (greater than 100) IOU state
- One which ensures the transaction does indeed contain an IOU state and not some other state we are not expecting
Note that these two constraints cannot be placed inside the contract code. Contract code is more appropriate for defining the constraints that govern how an asset or agreement should evolve over time. For example, with regards to an IOU:
- Issuances must be signed by lender and borrower
- Issuances must be for a value greater than zero
- Redemptions must involve a cash payment of the correct currency
- The IOU must be redeemed before the expiry date
- More examples available here: https://github.com/roger3cev/iou-cordapp-v2/blob/master/src/main/kotlin/net/corda/iou/contract/IOUContract.kt
Remember, Corda is designed for potentially mutually distrusting parties to enter into consensus about shared facts. As such, nodes cannot implicitly trust what they receive over the wire from their counter-parties, therefore we always have to check what we receive is what we expect to receive.
Hope that makes sense!
© 2022 - 2024 — McMap. All rights reserved.