I am new to Corda and have a use case where I need to do the following:
I have 3 Nodes:
BankA, BankB, BankC.
AccountA is an account on BankA, AccountB is an account on BankB and AccountC is an account on BankC
AccountA sends an invoice to AccountB
AccountB automatically sends the full amount of payment to AccountA
On receipt of the money from AccountB, AccountA automatically sends 20% of this payment to AccountC
The remaining 80% goes to AccountA.
In this simple scenario I am not concerned if AccountB has enough money to make a payment, it is a proof of concept. The only 'manual' step is the sending of an invoice by AccoutA, the remaining steps are automatically triggered.
Is there a way to do this in Corda using Accounts and if so how ? Example code/pseudocode would be great.
I will try to be as detailed as possible:
Create a state Invoice that has amount attribute and 3 attributes of type AbstractParty (i.e. accountA, accountB, and accountC).
Your state will have 2 commands: Issue and Exit (or Pay; I'll use Pay, you can use any name of course).
Create a flow that creates an Invoice state (i.e. Issue command).
Your proof of concept is about sending the amounts from accountA to accountB to accountC, but what proves that this amount distribution actually happened? It can be proved if all 3 accounts signed your transaction, more on that below.
Your Invoice state will have a contract (to validate the different commands), for the Pay command you must check that all 3 accounts are required signers.
Now create the pay invoice flow which will be 2 parts (initiator and responder).
The initiator will create a transaction, the input is the Invoice state, the required signers are the 3 accounts, there is no output (meaning the invoice is paid and now it's off-ledger).
Then the flow will use the sendAndReceive function to send the invoice state to NodeB.
Now inside the responder flow which is installed on NodeB, NodeB can do many checks, for example NodeB will check that accountB (which it extracts from the received Invoice) is hosted on it, if not; it will throw an error.
You said you don't care about payment for now, so you can send back to NodeA an object of type Double with amount that equals to the amount in the received invoice (remember that we used sendAndReceive in the initator so NodeA is waiting for a send from the responder; otherwise the flow will be stuck). If you want to see an example of using the tokens SDK to pay for a state; have a look here: https://github.com/corda/token-sdk/blob/master/docs/DvPTutorial.md, in your case instead of a token that represents a House, you'll have a normal state (i.e. Invoice extends LinearState or ContractState).
Now back to the initiator, the initiator just received the amount from NodeB; it will first check that the received amount equals to the invoice amount
Now it will send 20% to NodeC: you can use send to send the invoice and an object of type Double to represent the amount (the 20%).
As you can see, there is lot of juggling between the initiator and responder for each receive from the initiator, a send from the responder must exist (btw, CollectSignaturesFlow which you call inside the initiator has a receive, which you fulfill by calling SignTransactionFlow -which has a send- in the responder).
Now inside the responder that is installed inside NodeC, it will receive the Double; you can do some validation (e.g. accountC is hosted on NodeC, for example validate that the amount of object Double equals to 20% of the received invoice amount).
Now back to the initiator, it will call the CollectSignaturesFlow to collect signature of accountB from NodeB, and signature of accountC form NodeC.
Inside responder of NodeB call SignTransactionFlow.
Inside responder of NodeC call SignTransactionFlow.
Inside the initiator call the finality flow.
Inside the responders call the receive finality flow.
Notice how I mentioned that there are 2 responders (one installed on NodeB and one on NodeC), that's because NodeB has a different response logic from NodeC. Having 2 responders is a bit of an advanced topic; so instead just create one responder flow and inside of it check the party of the flow session, if it's PartyB; then do PartyB logic, otherwise do PartyC logic.
Below is the pseudo code of the above steps:
// Notice the sequence of the numbers; that's how the juggeling is happening
// Initiator (NodeA)
1. Choose notary
2. Create empty transaction
3. Set input (Invoice), required signers (the 3 accounts), command (Pay)
4. Validate tx (state contract is called)
5. Send invoice to PartyB
9. Receive double
10. Validate that Double.amount = 100% invoice.amount
11. Send invoice and Double(20 %) to NodeC
14. Sign transaction with NodeA key and accountA key (node A must sign with its key
because it initiated the flow)
15. Call CollectSignaturesFlow
16. Call Finality flow
// Responder
If (session.party = PartyB)
6. Receive invoice
7. Validate that accountB is hosted on NodeB
8. Send new Double(100% invoice amount)
16. Call SignTransactionFlow
17. Call receive finality flow
// PartyC
else
12. Receive invoice and Double
13. Validate accountC is hosted here and Double.amount == 20%
16. Call SignTransactionFlow
17. Call receive finality flow
Related
Recently I came across a problem when it seems that when the flow ends on initiator's node, and immediately after that I query the output state of the transaction in vaults of all transaction participant nodes, it turns out that the state is present only on initiator's node, and only after a while it appears in vaults of other participants nodes.
Reading the documentations here "Send the transaction to the counterparty for recording", and it does not say that it will wait until counterparty will successfully record the transaction and its states to their vault, which sort of confirms that the issue that I am facing is actually the way Corda is implemented and not a bug or so.
On the other hand, it seems not that logical to end a flow without being sure that on the counterparty node everything was finished successfully and all the states are written in their vaults.
And also I looked into the code, and reached to this method in ServiceHubInternal, which seems to be responsible for recording states into vault.
fun recordTransactions(statesToRecord: StatesToRecord,
txs: Collection<SignedTransaction>,
validatedTransactions: WritableTransactionStorage,
stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage,
vaultService: VaultServiceInternal,
database: CordaPersistence) {
database.transaction {
require(txs.isNotEmpty()) { "No transactions passed in for recording" }
val orderedTxs = topologicalSort(txs)
val (recordedTransactions, previouslySeenTxs) = if (statesToRecord != StatesToRecord.ALL_VISIBLE) {
orderedTxs.filter(validatedTransactions::addTransaction) to emptyList()
} else {
orderedTxs.partition(validatedTransactions::addTransaction)
}
val stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id
if (stateMachineRunId != null) {
recordedTransactions.forEach {
stateMachineRecordedTransactionMapping.addMapping(stateMachineRunId, it.id)
}
} else {
log.warn("Transactions recorded from outside of a state machine")
}
vaultService.notifyAll(statesToRecord, recordedTransactions.map { it.coreTransaction }, previouslySeenTxs.map { it.coreTransaction })
}
}
And it does not seem to me that this method is doing something async, so I am really confused.
And so the actual question is:
Does Initiator flow in Corda actually waits until all the relevant states are recorded in vaults of all participant nodes before it finishes, or it finishes right after it sends the states to participant nodes for recording without waiting for a confirmation from their side that states were recorded?
Edited
So in case that Corda by default does not wait for counterparty flows to store states in their vaults, but my implementation needs this behavior anyways, would it be good solution to implement the following:
At the very end of Initiator flow, before returning from method call receiveAll in order to suspend and wait, then in the very end of the receiver flow, before returning from method do a vault query with trackBy to wait until the state of an interest is recorded in the vault, and whenever it is, call sendAll to notify the initiator's receiveAll method. And finish the initiator's flow only when it has received confirmation from all receivers..
Would it be a normal approach to solve this problem? can it have any drawbacks or side effects that you can think of?
Corda is able to handle your scenario, it is actually explained here under the Error handling behaviour section; below is an excerpt, but I recommend reading the full section:
To recover from this scenario, the receiver’s finality handler will automatically be sent to the Flow Hospital where it’s suspended and retried from its last checkpoint upon node restart
The Initiator flow is not responsible for the storage of the states in the Responder's vault. So, there is no storage confirmation from the Responder, since it has already checked the transaction and provided signatures. So, from the Initiator's point of view it's all good once the Transaction has been notarised and stored on its side, it is up to the Responder to manage errors in its storage phase, as mentioned in the previous comment.
I'm working with corda accounts.
In my scenario the account is created on node M and shared with node D.
Node D runs a state creation flow, where the account is a participant.
By modeling the solution, the transaction must be registered on node D, but not on node M.
The problem is that when using the account belonging to node M, session of node M is required. And when I don't execute a ReceiveFinalityFlow on Responder Flow, an UnexpectedFlowEndException exception is generated.
And I need to be able to make a vaultquery through accountId.
The question is, can I have an account on a node and trade with that account without the state staying on the account node?
FinalityFlow will throw an error if you don't provide a FlowSession for each participant (see here), and in your case the account is a participant; so you need to provide a FlowSession for node M.
Since you pass a FlowSession for node M, then there should be a responder flow where node M calls ReceiveFinalityFlow; otherwise your initiator flow will hang because FinalityFlow will execute a send() to send the transaction to M, while M doesn't have a receive() call (which ReceiveFinalityFlow executes).
You can achieve the requirement that you're asking for by calling ReceiveFinalityFlow and set the input parameter statesToRecord to NONE; by default, that parameter is set to ONLY_RELEVANT (see flow definition here). The various types of StatesToRecord are explained here.
Your responder flow must have an if statement, if getOurIdentity() is node M, then call ReceiveFinalityFlow with statesToRecord == NONE (because you don't want M to record the state), if it's node D then call ReceiveFinalityFlow with statesToRecord == RELEVANT (because you want D to record the state).
Please note that just because you wrote a responder a certain way, doesn't guarantee that node M will execute your version; writing a responder flow is usually the responsibility of the other node; their developers can write their own version of the responder in which they call ReceiveFinalityFlow with statesToRecord == RELEVANT (meaning node M will register the resulting state). Read the first This is not true in this article.
Once you implement the above, please write a flow test that checks that node M:
Didn't register the resulting transaction in its transaction storage
Didn't register the resulting state in its vault
The reason I'm asking you to do this is because I noticed in Corda's code the following:
ReceiveFinalityFlow calls ReceiveTransactionFlow here
ReceiveTransactionFlow calls ResolveTransactionFlow here
ResolveTransactionFlow overrides statesToRecord from NONE to RELEVANT here and this statement has me worried; I just want to make sure that when you set statesToRecord to NONE in ReceiveFinalityFlow for node M, it doesn't record the transaction or the state
Let me know how things go.
Also to query by account, read in my article the below 2 sections:
Search for Finally, how do you query the vault by account?
Also read This is very important!
I was going through Corda training material and there is one activity "Impose a constraint on the required signatures in IOUContract.verify". How to implement this constraint requiring both the participant (lender and borrower) to sign the
transaction?
If anybody is aware of then please let me know.
My code:
if (!((command.getSigners().contains(state.getLender().getOwningKey())) && (command.getSigners().contains(state.getBorrower().getOwningKey()))))
throw new IllegalArgumentException("Both lender and borrower together only may sign IOU issue transaction.");
Your code looks legit.
If you cannot pass the test, please make sure the getter of the lender and the borrower are correct in the IOUState class.
If you are uncertain, you can look into this contract class example.
Also, you can check here for an alternative syntax to implement the contract class.
The code you paste in is from the contract of a cordapp. You will initiate the signature collection in the flow of the cordapp.
Let's assume the case that there are only lender and borrower in this transaction (no third party moderator), and the lender is the flow initiator. The lender will draft the state and sign it himself, and sent a half signed transaction to the borrower via a session and the borrower will check it and sign it via the responder.
At that moment you will have a fully signed transaction.
As for code, I would suggest you to take look at this simple negotiation cordapp for reference. https://github.com/corda/samples/blob/release-V4/negotiation-cordapp/workflows-kotlin/src/main/kotlin/negotiation/flows/AcceptanceFlow.kt
val partStx = serviceHub.signInitialTransaction(txBuilder)
// Gathering the counterparty's signature.
val counterparty = if (ourIdentity == input.proposer) input.proposee else input.proposer
val counterpartySession = initiateFlow(counterparty)
val fullyStx = subFlow(CollectSignaturesFlow(partStx, listOf(counterpartySession)))
The state contract will enforce who are the required signers. In this example, the contract is expecting all participants (i.e. lender and borrower) to be required signers of the Create command.
When the flow is crafting a transaction to create a new IOU, it must specify which command to use (Create IOU in our case); and who will sign this command (so the flow will work on collecting those signatures).
The flow will verify the transaction before signing it, which in return will run the contract; if the requested signatures that were specified on the Create command inside the flow don't match what the contract is expecting to see as required signers; then the contract will fail the transaction. Otherwise the contract will pass the transaction, and the flow will proceed to collect the signature of the lender, then collect the signature of the borrower.
So in short, the contract will say who are the required signers for a certain state command, and the flow that's creating a transaction for that state/command combination must obey the contract rules by specifying the same set of signers and collect their signatures.
Thanks, everyone for the detailed explanation. I have implemented the below code and it's working fine now.
List<PublicKey> signers = tx.getCommands().get(0).getSigners();
HashSet<PublicKey> signersSet = new HashSet<>();
for (PublicKey key: signers) {signersSet.add(key);}
List<AbstractParty> participants = tx.getOutputStates().get(0).getParticipants();
HashSet<PublicKey> participantKeys = new HashSet<>();
for (AbstractParty party: participants) {participantKeys.add(party.getOwningKey());}
require.using("Both lender and borrower together only may sign IOU issue transaction.", signersSet.containsAll(participantKeys) && signersSet.size() == 2);
Recently I've been looking into the security and vulnerability aspects of contract commands in Corda. A debate arose as to whether some contract command constraints should be strict or whether they should be relaxed in order to allow transaction composition of different inputs, outputs and commands.
The problem is that whilst I can see the benefit of allowing transaction composition, I feel like relaxed contract command constraints actually pose security vulnerabilities, and in my opinion, it would be better to secure against these vulnerabilities at the contract level, in such that the signing participants of the command reach consensus through contract verification as a whole, rather than relying on flow level checks, which could be overlooked by a developer or circumvented by a malicious node.
Example - Bankruptcy Declaration
This example allows nodes on the network to declare bankruptcy. Assume in this case that a bankruptcy declaration state is simply the identity of the node declaring bankruptcy, and a reason.
#BelongsToContract(BankruptcyDeclarationContract::class)
data class BankruptcyDeclarationState(
override val owner: AbstractParty,
val reason: String
) : OwnableState { ... }
Strict Verification
Strict verification requires that, on issuance...
Zero input states must be consumed.
One output state must be created.
Only the owner must sign.
fun verifyIssue(tx: LedgerTransaction, signers: Set<PublicKey>) = requireThat {
"Zero input states must be consumed." using (tx.inputs.isEmpty())
"One output state must be created." using (tx.outputs.size == 1)
val state = tx.outputsOfType<BankruptcyDeclarationState>().single()
"Only the owner must sign." using (state.owner.owningKey == signers.single())
}
Relaxed Verification
Relaxed verification requires that, on issuance...
Zero input states of type BankruptcyDeclarationState must be consumed.
One output state of type BankruptcyDeclarationState must be created.
Only the owner must sign.
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())
}
Observations
Strict verification ensures that the inputs and outputs are checked globally, rather than checking for specific input and output types, however this has a disadvantage that transaction composition of inputs and outputs is impossible.
Relaxed verification ensures that only inputs and outputs of the required state type are checked, which would allow transaction composition of different input and output types.
The key here is that only the node declaring bankruptcy must sign, which means that issuance of a BankruptcyDeclarationState can only occur from that node. Nobody else should be allowed to declare bankruptcy on behalf of another node on the network.
Identifying The Vulnerability
Suppose we chose to model our contract command constraints to be relaxed, so that we can compose transactions. Also, suppose that we have a contract command for some ObligationState which when issued, requires that:
Zero input states of type ObligationState must be consumed.
One output state of type ObligationState must be created.
The obligor and obligee must sign.
Now that we have two state types and two contract commands, we can compose a transaction that uses both, and identify the vulnerability. Assume here that bob is initiating this transaction.
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)
}
Remember that only the owner of a BankruptcyDeclarationState must sign, but the obligor and obligee of an ObligationState must sign, therefore this initiating flow will collect signatures from the required counter-parties. The vulnerability here is that bob initiates this transaction, but includes an output of type BankruptcyDeclarationState which is owned by alice. He shouldn't be allowed to do that because only the owner should be allowed to issue BankruptcyDeclarationState but in this case alice will unwittingly sign because of the requirement to sign for the ObligationState.
There is an argument to be made here that the flows could be written in such a way that alice would check the transaction before signing to ensure that certain states were not included, but I do not feel like this is enough. This requires developers and node administrators to carry out due diligence of flows ensuring their security.
In contrast, strict contract command constraints would prevent these vulnerabilities in what I believe to be a much more secure way - due diligence is therefore only required at a contract level, rather than from every developer writing flows that consume the contracts.
What I am looking for in this respect is some definitive guide to whether contract command constraints should be strict, relaxed, or whether there are other considerations to be made that I've missed. Thanks.
As you pointed out correctly, the identical contract code is shared among all transacting parties. That is the only agreement among them.
But each party is responsible for his/her action (signing) by developing his/her own secure flows. The fundamental in writing flows is to verify the transaction against the contract code before signing. Who would sign anything digitally or otherwise without reading/checking the contract?
Did I miss anything?
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 overriding signTransaction
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!