Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
DevDay: A Tale of Corda Slack Adventures, R3
1. A tale of Corda Slack
adventures
Common CorDapp
mistakes and how to fix
them
Dan Newton – Platform Engineer, r3
2. What will we cover today?
• The most common mistake made by Corda developers
• Making sure that your CorDapps strictly enforce rules on transactions
• Preventing your flow from waiting for a call that never comes
4. Does this stacktrace look
familiar?
Transaction context missing. This might happen if a suspendable method is not annotated with
@Suspendable annotation
🔍
5. Does this stacktrace look
familiar?
Transaction context missing. This might happen if a suspendable method is not annotated with
@Suspendable annotation
🔍
🤷🤷♀️
6. Does this stacktrace look
familiar?
Transaction context missing. This might happen if a suspendable method is not annotated with
@Suspendable annotation
🔍
7. • All functions that suspend must be annotated with @Suspendable
Common mistake 1
Forgetting to add the @Suspendable
annotation
8. • All functions that suspend must be annotated with @Suspendable
Common mistake 1
Forgetting to add the @Suspendable
annotation
Where does this
annotation come from?
9. • All functions that suspend must be annotated with @Suspendable
Common mistake 1
Forgetting to add the @Suspendable
annotation
Where does this
annotation come from?
When does a flow suspend?
10. Where does this annotation come
from?
• @Suspendable comes from Quasar
• Quasar is a library used by Corda to provide async functionality
11. Where does this annotation come
from?
• @Suspendable comes from Quasar
• Quasar is a library used by Corda to provide async functionality
• @Suspendable tells Quasar to do some magic stuff to functions
• Corda tries its best to hide Quasar from you
🤷🤷♂️
12. When does a flow suspend?
• A flow suspends while it waits for a notification to continue
• Performing any of the actions below suspends a flow:
• send
• receive
• sendAndReceive
• receiveAll
• sleep
• waitForLedgerCommit
• getCounterpartyFlowInfo
• executeAsync
13. When does a flow suspend?
• A flow suspends while it waits for a notification to continue
• Performing any of the actions below suspends a flow:
• send
• receive
• sendAndReceive
• receiveAll
• sleep
• waitForLedgerCommit
• getCounterpartyFlowInfo
• executeAsync
These are the most
common actions that
you would use
14. • All functions that suspend must be annotated with @Suspendable
• Functions that call suspendable functions must also be annotated
Common mistake 1
Forgetting to add the @Suspendable
annotation
15. • All functions that suspend must be annotated with @Suspendable
• Functions that call suspendable functions must also be annotated
Common mistake 1
Forgetting to add the @Suspendable
annotation
fun run()
@Suspendable
fun action()
Calls function
16. • All functions that suspend must be annotated with @Suspendable
• Functions that call suspendable functions must also be annotated
Common mistake 1
Forgetting to add the @Suspendable
annotation
@Suspendable
fun run()
@Suspendable
fun action()
Calls function
19. Common mistake 1
Forgetting to add the @Suspendable
annotation
• All functions that suspend must be annotated with @Suspendable
• Functions that call suspendable functions must also be annotated
• Most flows that Corda provides suspend at some point
• Functions that call these must be annotated with @Suspendable
22. Suspending flows
• Noteworthy flows:
• CollectSignaturesFlow
• SignTransactionFlow
• FinalityFlow
• ReceiveFinalityFlow
• SendTransactionFlow
• ReceiveTransactionFlow
• Functions calling these flows must be annotated with
@Suspendable
Delegate to these
flows
23. Suspending flows
• Noteworthy flows:
• CollectSignaturesFlow
• SignTransactionFlow
• FinalityFlow
• ReceiveFinalityFlow
• SendTransactionFlow
• ReceiveTransactionFlow
• Functions calling these flows must be annotated with
@Suspendable
• Your own flows need to be treated the same way
Delegate to these
flows
27. Making sure that your CorDapps strictly enforce rules on transactions
(Contract validation)
28. What could go wrong?
IOUState
amount = 10000000
Cash
amount = £10000000
Alice Bob
IOUState
amount = paid off
Cash
amount = £1
Alice lends Bob some Cash
Bob sneakily pays off his loan
💰
😕⁉️
32. Common mistake 2
Leaving your contract verification
functions empty
• Important!
• Ensures that your states are not spent incorrectly
33. Common mistake 2
Leaving your contract verification
functions empty
• Important!
• Ensures that your states are not spent incorrectly
• Contracts are tied to your states
34. Common mistake 2
Leaving your contract verification
functions empty
• Important!
• Ensures that your states are not spent incorrectly
• Contracts are tied to your states
So what validation should I add?
35. • Amounts / key fields
• Mixture / composition of states
• Relations between states
• Signers
Important validation to include
36. • Amounts / key fields
• Mixture / composition of states
• Relations between states
• Signers
Important validation to include
37. • Amounts / key fields
• Mixture / composition of states
• Relations between states
• Signers
Important validation to include
38. • Amounts / key fields
• Mixture / composition of states
• Relations between states
• Signers
Important validation to include
41. Making sure that your
CorDapps strictly enforce
rules on transactions
(Responder flow validation)
42. What could go wrong?
IOUState
amount = 10000000
Cash
amount = £10000000
Alice Bob
IOUState
amount = paid off
Cash
amount = £10000000
Alice lends Bob some Cash
(Alice expects interest to be paid)
Bob doesn’t pay interest on his loan
💰
🤷
49. Preventing invalid spending
of broadcasted states
Actually
Useful!
I proof read my blog posts
but not my code I guess…
🤷
50. • Business level validation
Common mistake 3
Leaving `checkTransaction`
blank in your responder flows
51. • Business level validation
• Contract validation is different
Common mistake 3
Leaving `checkTransaction`
blank in your responder flows
52. • Business level validation
• Contract validation is different
• Prevents a node from accepting all transactions
Common mistake 3
Leaving `checkTransaction`
blank in your responder flows
53.
54.
55.
56.
57. Overriding a responder flow to
customise validation
• Allows a flow in a shared CorDapp to contain logic specific to your
needs
• Every node can run the same CorDapp but include their own custom
validation
61. • A send must have a corresponding receive
Common mistake 4
Unbalanced sends and receives
62. • A send must have a corresponding receive
• Leads to stuck flows or session errors
• Worst case – flow is stuck waiting forever
Flow with id e396b6f5-16ea-43a6-8ffe-d64655b7e845 has been waiting for 118 seconds to receive messages from parties [O=Alice, L=London, C=GB].
Flow with id e396b6f5-16ea-43a6-8ffe-d64655b7e845 has been waiting for 178 seconds to receive messages from parties [O=Alice, L=London, C=GB].
Flow with id e396b6f5-16ea-43a6-8ffe-d64655b7e845 has been waiting for 238 seconds to receive messages from parties [O=Alice, L=London, C=GB].
Flow with id e396b6f5-16ea-43a6-8ffe-d64655b7e845 has been waiting for 298 seconds to receive messages from parties [O=Alice, L=London, C=GB].
Common mistake 4
Unbalanced sends and receives
63. • A send must have a corresponding receive
• Leads to stuck flows or session errors
• Worst case – flow is stuck waiting forever
• Best case – flow fails due to session error
net.corda.core.flows.UnexpectedFlowEndException: Tried to access ended session SessionId(toLong=-1785299589856785694)
at net.corda.node.services.statemachine.FlowStateMachineImpl.fillInLocalStackTrace(FlowStateMachineImpl.kt:174) ~[corda-node-4.1.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.processEventsUntilFlowIsResumed(FlowStateMachineImpl.kt:162) ~[corda-node-4.1.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.suspend(FlowStateMachineImpl.kt:446) ~[corda-node-4.1.jar:?]
at net.corda.node.services.statemachine.FlowSessionImpl.sendAndReceive(FlowSessionImpl.kt:54) ~[corda-node-4.1.jar:?]
at net.corda.node.services.statemachine.FlowSessionImpl.sendAndReceive(FlowSessionImpl.kt:61) ~[corda-node-4.1.jar:?]
at dev.lankydan.tutorial.flows.UnbalancedSendsAndReceivesFlow.call(UnbalancedSendsAndReceives.kt:72) ~[classes/:?]
at dev.lankydan.tutorial.flows.UnbalancedSendsAndReceivesFlow.call(UnbalancedSendsAndReceives.kt:10) ~[classes/:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:269) ~[corda-node-4.1.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:45) ~[corda-node-4.1.jar:?]
Common mistake 4
Unbalanced sends and receives
64. Tries to receive a
message straight
away
Forever waiting for the
other to make the first
move
receives straight
away (which makes
sense)
Both are waiting for
a message that
never comes
😰
65. Tries to receive a
message straight
away
Forever waiting for the
other to make the first
move
receives straight
away (which makes
sense)
Both are waiting for
a message that
never comes
😰
Flow with id e396b6f5-16ea-43a6-8ffe-d64655b7e845 has been waiting for 118 seconds to receive messages from parties [O=Alice, L=London, C=GB].
Flow with id e396b6f5-16ea-43a6-8ffe-d64655b7e845 has been waiting for 178 seconds to receive messages from parties [O=Alice, L=London, C=GB].
Flow with id e396b6f5-16ea-43a6-8ffe-d64655b7e845 has been waiting for 238 seconds to receive messages from parties [O=Alice, L=London, C=GB].
Flow with id e396b6f5-16ea-43a6-8ffe-d64655b7e845 has been waiting for 298 seconds to receive messages from parties [O=Alice, L=London, C=GB].
66. Replace with a send
Forever waiting for the
other to make the first
move
receive now
matches to the send
68. 🤷🤷♂️
sendAndReceive
matches to a
receive and send
pair
Fails due to ended
session error
net.corda.core.flows.UnexpectedFlowEndException: Tried to access ended session SessionId(toLong=-1785299589856785694)
at net.corda.node.services.statemachine.FlowStateMachineImpl.fillInLocalStackTrace(FlowStateMachineImpl.kt:174) ~[corda-node-4.1.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.processEventsUntilFlowIsResumed(FlowStateMachineImpl.kt:162) ~[corda-node-4.1.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.suspend(FlowStateMachineImpl.kt:446) ~[corda-node-4.1.jar:?]
at net.corda.node.services.statemachine.FlowSessionImpl.sendAndReceive(FlowSessionImpl.kt:54) ~[corda-node-4.1.jar:?]
at net.corda.node.services.statemachine.FlowSessionImpl.sendAndReceive(FlowSessionImpl.kt:61) ~[corda-node-4.1.jar:?]
at dev.lankydan.tutorial.flows.UnbalancedSendsAndReceivesFlow.call(UnbalancedSendsAndReceives.kt:72) ~[classes/:?]
at dev.lankydan.tutorial.flows.UnbalancedSendsAndReceivesFlow.call(UnbalancedSendsAndReceives.kt:10) ~[classes/:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:269) ~[corda-node-4.1.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:45) ~[corda-node-4.1.jar:?]
70. • A send must have a corresponding receive
• Leads to stuck flows or session errors
• Worst case – flow is stuck waiting forever
• Best case – flow fails due to session error
• Commonly happens when a subset of parties must sign a
transaction while a larger number receive it
• Non-signing parties wait forever
• Signing parties are fine
Common mistake 4
Unbalanced sends and receives
71. • Unfortunately – There is not enough time to cover this now
• Fortunately – I wrote a blog post about this
• https://lankydan.dev/saving-transactions-where-only-a-subset-of-parties-
are-signers
Unbalanced signers
73. What we covered
• When to add the @Suspendable annotation
• What validation to include in contracts and responder flows
• Keep your sends and receives in pairs
74. What
comes
next
• Jump onto the Corda
slack channel (if you are
not already there)
• Keep a look out for
questions
• Answer questions using
what you learnt here
today
@LankyDanDev
Notes de l'éditeur
Welcome to my talk.
I spend a lot of time checking the Corda slack channel and answer as many questions as I can (or beg my colleagues to help instead).
During this time I have seen a number of questions pop up and some general mistakes that seem to happen more frequently than others.
In this talk, I will be covering a small selection of these common mistakes.
Which mistakes will we be looking at today?.
We will start with the most common issue that I have seen over the last year.
Moving onto properly enforcing rules on your transactions.
And finally, how to prevent your flows from becoming stuck while waiting for a call that never comes.
At the end of each section, I will answer 1 or 2 quick questions. These can be asked through the CordaCon app. Due to time constraints I can only answer questions that are promptly written.
Onto the first section, exploring the most common mistake made by Corda developers.
Does this stacktrace look familiar to anyone?
Personally, I have seen it way too many times. Especially when I was starting out with Corda.
Actually, when I started, the error message was much much worse and didn’t help at all.
NEXT SLIDE (show v3 error message)
In Corda 3, it looked like this. Would you really have any clue what that is trying to say without going to stackoverflow?
I mean really, what the hell does ”Was expecting to find transaction set on current strand” mean?
NEXT SLIDE (show suspendable stack trace again)
Anyway, luckily, the error message is quite clear now. An annotation is missing somewhere.
But, the question is, where to put this annotation.
Does this stacktrace look familiar to anyone?
Personally, I have seen it way too many times. Especially when I was starting out with Corda.
Actually, when I started, the error message was much much worse and didn’t help at all.
NEXT SLIDE (show v3 error message)
In Corda 3, it looked like this. Would you really have any clue what that is trying to say without going to stackoverflow?
I mean really, what the hell does ”Was expecting to find transaction set on current strand” mean?
NEXT SLIDE (show suspendable stack trace again)
Anyway, luckily, the error message is quite clear now. An annotation is missing somewhere.
But, the question is, where to put this annotation.
Does this stacktrace look familiar to anyone?
Personally, I have seen it way too many times. Especially when I was starting out with Corda.
Actually, when I started, the error message was much much worse and didn’t help at all.
NEXT SLIDE (show v3 error message)
In Corda 3, it looked like this. Would you really have any clue what that is trying to say without going to stackoverflow?
I mean really, what the hell does ”Was expecting to find transaction set on current strand” mean?
NEXT SLIDE (show suspendable stack trace again)
Anyway, luckily, the error message is quite clear now. An annotation is missing somewhere.
But, the question is, where to put this annotation.
As the error suggested, the first common mistake is forgetting to add the `@Suspendable` annotation.
The primary step to fixing this issue, is annotating all functions that can suspend with `@Suspendable`.
That is a bit of a loaded sentence though.
To fully understand what it means, it needs to be broken down.
NEXT SLIDE
-> Where does this annotation actually come from?
NEXT SLIDE
-> When does a function or flow suspend?
As the error suggested, the first common mistake is forgetting to add the `@Suspendable` annotation.
The primary step to fixing this issue, is annotating all functions that can suspend with `@Suspendable`.
That is a bit of a loaded sentence though.
To fully understand what it means, it needs to be broken down.
NEXT SLIDE
-> Where does this annotation actually come from?
NEXT SLIDE
-> When does a function or flow suspend?
As the error suggested, the first common mistake is forgetting to add the `@Suspendable` annotation.
The primary step to fixing this issue, is annotating all functions that can suspend with `@Suspendable`.
That is a bit of a loaded sentence though.
To fully understand what it means, it needs to be broken down.
NEXT SLIDE
-> Where does this annotation actually come from?
NEXT SLIDE
-> When does a function or flow suspend?
The annotation is provided by Quasar.
Quasar is an external library that we use inside Corda to provide async functionality while allowing you write seemingly synchronous code.
NEXT SLIDE
The annotation tells Quasar to do some magic stuff behind the scenes to your functions.
As much as I would like to give you more information on this subject. From your perspective as a cordapp developer, 99% of the time, you shouldn’t need to know any more than this.
I also don’t have enough time in this talk to go into it.
But, that’s fine. Corda handles this all for you.
Just accept that it does some magic to your code. Let me me and my team worry about this. Trust me, you a better off not knowing.
The only part you need to do, is add the annotation in the right places.
The annotation is provided by Quasar.
Quasar is an external library that we use inside Corda to provide async functionality while allowing you write seemingly synchronous code.
NEXT SLIDE
The annotation tells Quasar to do some magic stuff behind the scenes to your functions.
As much as I would like to give you more information on this subject. From your perspective as a cordapp developer, 99% of the time, you shouldn’t need to know any more than this.
I also don’t have enough time in this talk to go into it.
But, that’s fine. Corda handles this all for you.
Just accept that it does some magic to your code. Let me and my team worry about this. Trust me, you a better off not knowing.
The only part you need to do, is add the annotation in the right places.
When does a flow suspend?
A flow suspends while it waits for a notification telling it to continue.
These are all the actions that can suspend a flow.
NEXT SLIDE
You might have seen or used a number of these actions. I expect that most of you have used `send`, `receive`, `sendAndReceive` or used a flow that does.
When does a flow suspend?
A flow suspends while it waits for a notification telling it to continue.
There are a series of actions that can cause a flow to suspend.
NEXT SLIDE
These are all the actions that can suspend a flow.
You might have seen or used a number of these actions. I expect that most of you have used `send`, `receive`, `sendAndReceive` or used a flow that does.
Now that you know when a flow suspends and where the annotation comes from, we can continue exploring how to fix the error we saw earlier.
The next important point is that, all functions that call suspendable functions must also be annotated with `@Suspendable`.
An example makes this clearer.
NEXT SLIDE
`run` calls the `action` function. `action` is annotated with `@Suspendable`.
NEXT SLIDE
Therefore `run` will also need the annotation.
That’s it.
Now that you know when a flow suspends and where the annotation comes from, we can continue exploring to fix the error we saw earlier.
The next important point is that, all functions that call suspendable functions must also be annotated with `@Suspendable`.
An example makes this clearer.
NEXT SLIDE
`run` calls the `action` function. `action` is annotated with `@Suspendable`.
NEXT SLIDE
Therefore `run` will also need the annotation.
That’s it.
Now that you know when a flow suspends and where the annotation comes from, we can continue exploring to fix the error we saw earlier.
The next important point is that, all functions that call suspendable functions must also be annotated with `@Suspendable`.
An example makes this clearer.
NEXT SLIDE
`run` calls the `action` function. `action` is annotated with `@Suspendable`.
NEXT SLIDE
Therefore `run` will also need the annotation.
That’s it.
You should now be able to understand what is wrong with this code and how to fix it.
The `call` function delegates down to `receive`, which in turn executes the `receive` action.
Remember, the `receive` action causes the flow to suspend.
NEXT SLIDE
Because of this suspension, it must be annotated with `@Suspendable`.
Also notice, that `call` has the annotation as well. Since it is calling `receive`, which is suspendable.
Taking all the information from the previous slides, you should be able to understand what is wrong with this code and how to fix it.
The `call` function delegates down to `receive`, which in turn executes the `receive` action.
Remember, the `receive` action causes the flow to suspend.
NEXT SLIDE
Because of this suspension, it must be annotated with `@Suspendable`.
Also notice, that `call` has the annotation as well. Since it is calling `receive`, which is suspendable.
Now it's great that you know when you need the annotation. But, most of the time, you are not directly calling actions that can suspend a flow.
Instead, you are calling a subflow that does this for you.
Just like the previous point. Any function that calls a flow, that also suspends, needs to have the annotation.
You have probably used most of these flows in your code, especially the first 4.
NEXT SLIDE
I expect fewer of you have used the last 2, send and receive transaction flow. However, they are both thoroughly used by the other platform flows.
More importantly, all of these flows suspend.
NEXT SLIDE
Therefore, any functions that call these flows must be annotated with `@Suspendable`.
NEXT SLIDE
The same will also apply to any flows that you write. If you try to call your own flow which can suspend, you will also need to remember the annotation.
You have probably used most of these flows in your code, especially the first 4.
NEXT SLIDE
I expect fewer of you have used the last 2, send and receive transaction flow. However, they are both thoroughly used by the other platform flows.
More importantly, all of these flows suspend.
NEXT SLIDE
Therefore, any functions that call these flows must be annotated with `@Suspendable`.
NEXT SLIDE
The same will also apply to any flows that you write. If you try to call your own flow which can suspend, you will also need to remember the annotation.
You have probably used most of these flows in your code, especially the first 4.
NEXT SLIDE
I expect fewer of you have used the last 2, send and receive transaction flow. However, they are both thoroughly used by the other platform flows.
More importantly, all of these flows suspend.
NEXT SLIDE
Therefore, any functions that call these flows must be annotated with `@Suspendable`.
NEXT SLIDE
The same will also apply to any flows that you write. If you try to call your own flow which can suspend, you will also need to remember the annotation.
You have probably used most of these flows in your code, especially the first 4.
NEXT SLIDE
I expect fewer of you have used the last 2, send and receive transaction flow. However, they are both thoroughly used by the other platform flows.
More importantly, all of these flows suspend.
NEXT SLIDE
Therefore, any functions that call these flows must be annotated with `@Suspendable`.
NEXT SLIDE
The same will also apply to any flows that you write. If you try to call your own flow which can suspend, you will also need to remember the annotation.
Like before, you now know enough to figure out what is wrong with this code.
There are 2 functions, that each call another flow, `CollectSignatures` and `Finality` flow. Remember that both of these flows suspend.
NEXT SLIDE
Both of these functions must be annotated, as they call flows that suspend.
Also note that `call` has been annotated, as the 2 other functions are suspendable.
Like before, you now know enough to figure out what is wrong with this code.
There are 2 functions, that each call another flow, `CollectSignatures` and `Finality` flow. Remember that both of these flows suspend.
NEXT SLIDE
Both of these functions must be annotated, as they call flows that suspend.
Also note that `call` has been annotated, as the 2 other functions are suspendable.
Questions
In this section, we will look at strictly enforcing rules on transactions. More specifically, contract validation.
To explain why you need contract validation, first it is worth seeing what could go wrong if it's missing.
Here we have Alice who sends some money to Bob along with an IOU to indicate what he owes.
But Bob is sneaky. He states that he has paid off the loan. But in fact, he only paid £1.
Why did this work?
Because the IOU’s contract looked like this.
Literally anything could be done. At least Bob was kind enough to pay £1. He could have paid nothing if he wanted to.
What Bob did could have been prevented by adding the following to the contract.
It checks that the money passed in is equal to or greater than amount owed.
This is much much much better than the contract being empty, but there is still room for improvement.
Here we have the 2nd common mistake, leaving your contract verification functions empty.
Preventing situations like the one we just looked at is why contract verification is so important.
NEXT SLIDE
It is needed to prevent your states from being spent incorrectly.
NEXT SLIDE
The importance of this validation is highlighted by its usefulness.
A contract is tied to a state, and any time a transaction is validated, its rules are run against the contents of the transaction.
This means that no matter where the state is used. There are a set of rules that always hold and cannot break.
NEXT SLIDE
Ok. Great. You now know why contract validation is important. I should probably tell you what to check for.
Here we have the 2nd common mistake, leaving your contract verification functions empty.
Preventing situations like the one we just looked at is why contract verification is so important.
NEXT SLIDE
It is needed to prevent your states from being spent incorrectly.
NEXT SLIDE
The importance of this validation is highlighted by its usefulness.
A contract is tied to a state, and any time a transaction is validated, its rules are run against the contents of the transaction.
This means that no matter where the state is used. There are a set of rules that always hold and cannot break.
NEXT SLIDE
Ok. Great. You now know why contract validation is important. I should probably tell you what to check for.
Here we have the 2nd common mistake, leaving your contract verification functions empty.
Preventing situations like the one we just looked at is why contract verification is so important.
NEXT SLIDE
It is needed to prevent your states from being spent incorrectly.
NEXT SLIDE
The importance of this validation is highlighted by its usefulness.
A contract is tied to a state, and any time a transaction is validated, its rules are run against the contents of the transaction.
This means that no matter where the state is used. There are a set of rules that always hold and cannot break.
NEXT SLIDE
Ok. Great. You now know why contract validation is important. I should probably tell you what to check for.
Here we have the 2nd common mistake, leaving your contract verification functions empty.
Preventing situations like the one we just looked at is why contract verification is so important.
NEXT SLIDE
It is needed to prevent your states from being spent incorrectly.
NEXT SLIDE
The importance of this validation is highlighted by its usefulness.
A contract is tied to a state, and any time a transaction is validated, its rules are run against the contents of the transaction.
This means that no matter where the state is used. There are a set of rules that always hold and cannot break.
NEXT SLIDE
Ok. Great. You now know why contract validation is important. I should probably tell you what to check for.
First up, amounts and other key fields.
Probably the most common validation to include.
You might want to check that the amount of money passed into a transaction is not negative, which is similar to this example.
Or maybe there is an Enum field, where only a certain set of values are allowed in the current context.
Really, all I am trying to say, is that you should consider validating any important fields that exist on your states.
For some usecases you might need to validate the composition of states found in a transaction.
This validation ensures that states that should be found together, are together.
Or the opposite, where a state should never be mixed with another state.
Verifying relationships between states is a progression from looking at the composition of states.
It is more precise and directed.
The easiest way for me to explain this one is via an example. The example here, has 2 message states, one for the original message and another for the reply.
The relationship is verified by checking that the reply is sent from the original recipient and sent to the original sender.
Finally you should verify the signers of a transaction. This is most likely to be missed, but is also one of the most important.
Adding this check ensures that all the parties that need to sign the transaction, will actually sign it.
Leaving this out can allow states to be spent without an owner being required to give it the all clear
No, your eyes haven’t just gone crazy.
I have purposely blurred this code to stop you from trying to read it.
This is some contract code from the Tokens SDK. All I want from this slide, is for you to appreciate how much validation goes into a well written contract.
Every point that we went over is included.
For inspiration on how to write good contracts, I definitely recommend taking a look at the Tokens SDK.
Questions.
Continuing with the trend of transaction validation, in this section will look at why you should include rules in your responder flows.
Using the same example as before, and assuming that the contract has been fixed.
We will look at a slightly different scenario.
Alice lends Bob some money, but also expects interest to be paid on the loan, which Bob doesn’t do.
He pays off the exact loan amount, which is all the contract required.
Why did this work?
Because there was no validation at the flow level to ensure that interest was paid.
Alice could have prevented this by verifying that the interest was paid when Bob sent her the transaction to sign.
To see how this should be done, let me show you some examples from my blog posts.
Oh, mmm, nevermind.
Ok, maybe even I am doing it wrong.
A bit better. I actually checked something but it was a pretty useless check.
Finally. This was my 18th blog post on Corda. After 18 posts, I finally started to do it correctly…
This brings us to the 3rd common mistake, leaving `checkTransaction` in your responder flows blank.
`checkTransaction` is for business level validation and is different from contract validation.
NEXT SLIDE
Contract validation checks that states are being used correctly at a general level.
Business validation include the checks that are important to your business.
What values do you want to accept
Does this transaction contain states important to you
Or any other important requirements that you need
I just want to stress that this validation purely important to your business. Not the collective, which is what contracts are enforcing.
NEXT SLIDE
If you don’t include this validation, then any transaction set to you that passes contract verification, will be accepted.
Even if it contained data not important to you. There is nothing preventing it. The code just does what it says. Which is nothing.
This brings us to the 3rd common mistake, leaving `checkTransaction` in your responder flows blank.
`checkTransaction` is for business level validation and is different from contract validation.
NEXT SLIDE
Contract validation checks that states are being used correctly at a general level.
Business validation include the checks that are important to your business.
What values do you want to accept
Does this transaction contain states important to you
Or any other important requirements that you need
I just want to stress that this validation purely important to your business. Not the collective, which is what contracts are enforcing.
NEXT SLIDE
If you don’t include this validation, then any transaction set to you that passes contract verification, will be accepted.
Even if it contained data not important to you. There is nothing preventing it. The code just does what it says. Which is nothing.
This brings us to the 3rd common mistake, leaving `checkTransaction` in your responder flows blank.
`checkTransaction` is for business level validation and is different from contract validation.
NEXT SLIDE
Contract validation checks that states are being used correctly at a general level.
Business validation include the checks that are important to your business.
What values do you want to accept
Does this transaction contain states important to you
Or any other important requirements that you need
I just want to stress that this validation purely important to your business. Not the collective, which is what contracts are enforcing.
NEXT SLIDE
If you don’t include this validation, then any transaction set to you that passes contract verification, will be accepted.
Even if it contained data not important to you. There is nothing preventing it. The code just does what it says. Which is nothing.
Here is a example I put together.
NEXT SLIDE
Contract validation executes before this `checkTransaction` function.
NEXT SLIDE
This means that you can assume what is in the transaction based on the contract validation.
NEXT SLIDE
Finally, any rules that are important to you are added.
None of these rules would make sense inside of contract code, but, for some, this might be deseriable validation. Probably not though.
Here is a example I put together.
NEXT SLIDE
Contract validation executes before this `checkTransaction` function.
NEXT SLIDE
This means that you can assume what is in the transaction based on the contract validation.
NEXT SLIDE
Finally, any rules that are important to you are added.
None of these rules would make sense inside of contract code, but, for some, this might be deseriable validation. Probably not though.
Here is a example I put together.
NEXT SLIDE
Contract validation executes before this `checkTransaction` function.
NEXT SLIDE
This means that you can assume what is in the transaction based on the contract validation.
NEXT SLIDE
Finally, any rules that are important to you are added.
None of these rules would make sense inside of contract code, but, for some, this might be deseriable validation. Probably not though.
Here is a example I put together.
NEXT SLIDE
Contract validation executes before this `checkTransaction` function.
NEXT SLIDE
This means that you can assume what is in the transaction based on the contract validation.
NEXT SLIDE
Finally, any rules that are important to you are added.
None of these rules would make sense inside of contract code, but, for some, this might be deseriable validation. Probably not though.
The validation we just looked at can be taken a step further.
Moving CorDapps further away from purely shared code. To applications that ensure that your organisation’s specific needs are always met.
Flow overriding was added in Corda 4 and allows you to do what it says. Override flows.
This feature makes perfect sense when it comes to including company specific validation.
To implement this you must do the following.
Have an overridable base responder implementation, this is `SendMessageResponder` in this example.
Provide a function to be implemented that should contain transaction verification.
Finally, call this function from inside of `SignTransactionFlow.checkTransaction`.
That is the base responder finished.
A new flow should be created, that extends the base responder and implements the transaction validation function.
Calls to the original base flow will now be routed to your implementation that contains your extra rules.
Following this pattern allows flow code to be shared, while allowing it to meet all parties’ specific needs.
Questions.
In this final section, I will show you how to prevent your flows from waiting for a call that never comes.
The last common mistake I will cover today, is having unbalanced sends and receives.
Every `send` must have a corresponding `receive`. Period.
NEXT SLIDE
Not following this rule can lead to two different outcomes:
Worst case – A flow is stuck waiting for a responding flow to do something. A lot of you have probably seen these “waiting to receive message” log lines before.
NEXT SLIDE
Best case – A flow fails instantly due to a ended session error. I call this best case, since it fails and gives you feedback straight away.
The last common mistake I will cover today, is having unbalanced sends and receives.
Every `send` must have a corresponding `receive`. Period.
NEXT SLIDE
Not following this rule can lead to two different outcomes:
Worst case – A flow is stuck waiting for a responding flow to do something. A lot of you have probably seen these “waiting to receive message” log lines before.
NEXT SLIDE
Best case – A flow fails instantly due to a ended session error. I call this best case, since it fails and gives you feedback straight away.
The last common mistake I will cover today, is having unbalanced sends and receives.
Every `send` must have a corresponding `receive`. Period.
NEXT SLIDE
Not following this rule can lead to two different outcomes:
Worst case – A flow is stuck waiting for a responding flow to do something. A lot of you have probably seen these “waiting to receive message” log lines before.
NEXT SLIDE
Best case – A flow fails instantly due to a ended session error. I call this best case, since it fails and gives you feedback straight away.
Here is an example of the worst case, a flow that waits forever.
Both sides of the flow call `receive`. After doing that nothing happens.
NEXT SLIDE
And you start to see the waiting logs again.
NEXT SLIDE
To fix this. The `receive` on the initiating flow needs to be switched to a `send`. It doesn’t make any sense that it instantly receives…
There is now a `send` and `receive` pair and it starts to work again.
Here is an example of the worst case, a flow that waits forever.
Both sides of the flow call `receive`. After doing that nothing happens.
NEXT SLIDE
And you start to see the waiting logs again.
NEXT SLIDE
To fix this. The `receive` on the initiating flow needs to be switched to a `send`. It doesn’t make any sense that it instantly receives…
There is now a `send` and `receive` pair and it starts to work again.
Here is an example of the worst case, a flow that waits forever.
Both sides of the flow call `receive`. After doing that nothing happens.
NEXT SLIDE
And you start to see the waiting logs again.
NEXT SLIDE
To fix this. The `receive` on the initiating flow needs to be switched to a `send`. It doesn’t make any sense that it instantly receives…
There is now a `send` and `receive` pair and it starts to work again.
This example causes an ended session error.
The initiator calls `sendAndReceive` which matches up to the `receive` and `send` on the responder.
After the final call to `send`, the responder flow finishes.
The initiator does another `sendAndReceive` that tries to contact the ended flow.
NEXT SLIDE
Leading to the ended session error.
NEXT SLIDE
Removing the second call to `sendAndReceive` fixes this error, as only the matching calls are left. You could also add another set of `receive` and `send` on the responding side as an alternative solution.
This example causes an ended session error.
The initiator calls `sendAndReceive` which matches up to the `receive` and `send` on the responder.
After the final call to `send`, the responder flow finishes.
The initiator does another `sendAndReceive` that tries to contact the ended flow.
NEXT SLIDE
Leading to the ended session error.
NEXT SLIDE
Removing the second call to `sendAndReceive` fixes this error, as only the matching calls are left. You could also add another set of `receive` and `send` on the responding side as an alternative solution.
This example causes an ended session error.
The initiator calls `sendAndReceive` which matches up to the `receive` and `send` on the responder.
After the final call to `send`, the responder flow finishes.
The initiator does another `sendAndReceive` that tries to contact the ended flow.
NEXT SLIDE
Leading to the ended session error.
NEXT SLIDE
Removing the second call to `sendAndReceive` fixes this error, as only the matching calls are left. You could also add another set of `receive` and `send` on the responding side as an alternative solution.
Those were both pretty basic scenarios.
Realistically, this is could happen when signing and recording a transaction with only a subset of parties signing the transaction.
Non-signing parties wait forever due to a mismatched `receive`.
Signing parties are fine as all calls sync up correctly.
Unfortunately, I don’t have enough time to cover this.
Fortunately, I wrote a blog post about this topic already which gives it the proper explanation it deserves.
Questions.
You now know when you need to use the `@Suspendable` annotation. Meaning you will never see the exception that you saw at the beginning of this talk ever again.
You know why it is important to include transaction validation. Including both contract validation and verification inside of responder flows.
Finally, you have seen the behavior that occurs when your `send`s and `receive`s are not found in pairs and what needs to be done to correct your flow.
After this talk you should join the Corda slack channel if you are not already there.
You should then look out for questions.
And then using what you learnt here today, hopefully be able to help out other Corda developers.
You can also nag people to post their questions on Stackoverflow, but let’s not get into that.