Initially, I thought I could use TransactionBuilder to build a transaction with the notary of my choice, add the inputs, outputs, and commands. I then realized this is not how it works.
Below is what you are going to end up with:
java.lang.IllegalArgumentException: Input state requires notary “O=NotaryA, L=London, C=GB” which does not match the transaction notary “O=NotaryB, L=London, C=GB”
New to Corda? A great way to start with Corda is to take a look at one of our online bootcamp webinars. The recording for one of them is available here: https://www.youtube.com/watch?v=tVE1rKbFA3g
You may also consider joining us for one of our in-person or live virtual bootcamps. Keep an eye on the link below to know what events are coming up: https://www.corda.net/blockchain-bootcamp/
This is certainly not what we expected. Let’s take a step back and try to understand why this exception occurred.
How does a Notary prevent double-spending?
Notaries are a core component of a Corda network. They help prevent double-spending and maintain the sanity of data in a Corda network. In order to understand why we were not able to spend a state with a different notary we really need to understand how a notary actually prevents double-spending.
A notary keeps track of inputs and outputs of transactions to identity double-spending attempts. Whenever a transaction is sent to a notary, it would keep a note of its transaction id and the outputs it produces.
To understand further, let’s take an example of an issuance transaction as shown below.
When the transaction reaches the notary, it would take note of the transaction id and output indexes produced. Therefore the notary now knows that there was a transaction and the transaction produced an output.
Note: This is an over-simplification of how things work, in-practice an issuance transaction is never notarized since you can’t double spend if there is no input. Also the notary just sees the indexes of outputs rather than the entire output object contents.
Now let’s take a look at what happens when a move transaction occurs. Let’s continue with our previous example. Say we want to move (sell) the vehicle issued in the previous transaction to another party. That would involve some cash as well in the transaction.
We now have a transaction with two inputs and two outputs, one for the vehicle and another for the cash. When this transaction reaches the notary, the notary would take note of the transaction id and the output indexes just like it did for the issuance transaction. However, since we have inputs in the transaction it also needs to check for double-spending. It does that by checking if the inputs used in the transaction have already been used in any previous transaction. If not, it knows that this is not an attempt of double spending and hence it signs the transaction as well as takes note of which outputs of previous transactions are getting consumed, in order to prevent double-spending attempts later.
Now consider selling the vehicle again, but this time use a different notary than the one we have been using till now. It’s pretty clear that this new notary would not have any of the previous transaction information, thus would not be able to prevent a double-spending attempt. The entire purpose of using a notary is compromised. That’s the reason why a state can only be spent with a notary that it is tagged to.
This doesn’t mean that once you issued a state on a notary, you can’t spend it on a different notary. Let me introduce you to Notary Change Transaction.
Notary Change Transaction
Anytime we want to spend a Corda State at a different notary, we first need to do a Notary Change Transaction. This would mark the state spend on the current notary and reissue the state on the new notary. And there you go, we can now spend it on the new notary. It’s that simple!
To make the life of developers easier Corda has a library flow called NotaryChangeFlow which would do all the heavy lifting. All we need to do is call the NotaryChangeFlow and pass the Corda State and the new Notary.
subFlow(new NotaryChangeFlow<IOUState>(inputStateAndRef, newNotary);
It would run a Notary Change Transaction and the state would be consumed at the current notary and reissued at the new notary. It would also make sure that the updated state is recorded in the participant’s ledgers. We can now start using the new notary for all subsequent transactions without any problems.
Thank you so much for reading.
Want to learn more about building awesome blockchain applications on Corda? Be sure to visit corda.net, check out our community page to learn how to connect with other Corda developers, and sign up for one of our newsletters for the latest updates.
— Ashutosh Meher is a Developer Evangelist at R3, an enterprise blockchain software firm working with a global ecosystem of more than 350 participants across multiple industries from both the private and public sectors to develop on Corda, its open-source blockchain platform, and Corda Enterprise, a commercial version of Corda for enterprise usage.
Follow Ashutosh on Twitter here.