mirror of
https://github.com/corda/corda.git
synced 2024-12-22 14:22:28 +00:00
e8b6f5f2f2
FinalityHandler is insecure in that it is open to receive any transaction from any party. Any CorDapp targeting platform version 4 or above is required use the new c'tors which take in FlowSession objects to the counterpart flow. This flow must subcall ReceiveFinalityFlow to receive and record the finalised transaction. Old CorDapps (with target platform version < 4) will continue to work as previously. However if there are no old CorDapps loaded then the node will disable FinalityHandler.
146 lines
6.7 KiB
ReStructuredText
146 lines
6.7 KiB
ReStructuredText
.. highlight:: kotlin
|
|
.. raw:: html
|
|
|
|
<script type="text/javascript" src="_static/jquery.js"></script>
|
|
<script type="text/javascript" src="_static/codesets.js"></script>
|
|
|
|
Updating the flow
|
|
=================
|
|
|
|
We now need to update our flow to achieve three things:
|
|
|
|
* Verifying that the transaction proposal we build fulfills the ``IOUContract`` constraints
|
|
* Updating the lender's side of the flow to request the borrower's signature
|
|
* Creating a response flow for the borrower that responds to the signature request from the lender
|
|
|
|
We'll do this by modifying the flow we wrote in the previous tutorial.
|
|
|
|
Verifying the transaction
|
|
-------------------------
|
|
In ``IOUFlow.java``/``Flows.kt``, change the imports block to the following:
|
|
|
|
.. container:: codeset
|
|
|
|
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/kotlin/tutorial/twoparty/IOUFlow.kt
|
|
:language: kotlin
|
|
:start-after: DOCSTART 01
|
|
:end-before: DOCEND 01
|
|
|
|
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlow.java
|
|
:language: java
|
|
:start-after: DOCSTART 01
|
|
:end-before: DOCEND 01
|
|
|
|
And update ``IOUFlow.call`` to the following:
|
|
|
|
.. container:: codeset
|
|
|
|
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/kotlin/tutorial/twoparty/IOUFlow.kt
|
|
:language: kotlin
|
|
:start-after: DOCSTART 02
|
|
:end-before: DOCEND 02
|
|
:dedent: 8
|
|
|
|
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlow.java
|
|
:language: java
|
|
:start-after: DOCSTART 02
|
|
:end-before: DOCEND 02
|
|
:dedent: 8
|
|
|
|
In the original CorDapp, we automated the process of notarising a transaction and recording it in every party's vault
|
|
by invoking a built-in flow called ``FinalityFlow`` as a subflow. We're going to use another pre-defined flow,
|
|
``CollectSignaturesFlow``, to gather the borrower's signature.
|
|
|
|
First, we need to update the command. We are now using ``IOUContract.Create``, rather than
|
|
``TemplateContract.Commands.Action``. We also want to make the borrower a required signer, as per the contract
|
|
constraints. This is as simple as adding the borrower's public key to the transaction's command.
|
|
|
|
We also need to add the output state to the transaction using a reference to the ``IOUContract``, instead of to the old
|
|
``TemplateContract``.
|
|
|
|
Now that our state is governed by a real contract, we'll want to check that our transaction proposal satisfies these
|
|
requirements before kicking off the signing process. We do this by calling ``TransactionBuilder.verify`` on our
|
|
transaction proposal before finalising it by adding our signature.
|
|
|
|
Requesting the borrower's signature
|
|
-----------------------------------
|
|
|
|
Previously we wrote a responder flow for the borrower in order to receive the finalised transaction from the lender.
|
|
We use this same flow to first request their signature over the transaction.
|
|
|
|
We gather the borrower's signature using ``CollectSignaturesFlow``, which takes:
|
|
|
|
* A transaction signed by the flow initiator
|
|
* A list of flow-sessions between the flow initiator and the required signers
|
|
|
|
And returns a transaction signed by all the required signers.
|
|
|
|
We can then pass this fully-signed transaction into ``FinalityFlow``.
|
|
|
|
Updating the borrower's flow
|
|
----------------------------
|
|
On the lender's side, we used ``CollectSignaturesFlow`` to automate the collection of signatures. To allow the borrower
|
|
to respond, we need to update its responder flow to first receive the partially signed transaction for signing. Update
|
|
``IOUFlowResponder.call`` to be the following:
|
|
|
|
.. container:: codeset
|
|
|
|
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/kotlin/tutorial/twoparty/IOUFlowResponder.kt
|
|
:language: kotlin
|
|
:start-after: DOCSTART 01
|
|
:end-before: DOCEND 01
|
|
:dedent: 8
|
|
|
|
.. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlowResponder.java
|
|
:language: java
|
|
:start-after: DOCSTART 01
|
|
:end-before: DOCEND 01
|
|
:dedent: 8
|
|
|
|
We could write our own flow to handle this process. However, there is also a pre-defined flow called
|
|
``SignTransactionFlow`` that can handle the process automatically. The only catch is that ``SignTransactionFlow`` is an
|
|
abstract class - we must subclass it and override ``SignTransactionFlow.checkTransaction``.
|
|
|
|
CheckTransactions
|
|
^^^^^^^^^^^^^^^^^
|
|
``SignTransactionFlow`` will automatically verify the transaction and its signatures before signing it. However, just
|
|
because a transaction is contractually valid doesn't mean we necessarily want to sign. What if we don't want to deal
|
|
with the counterparty in question, or the value is too high, or we're not happy with the transaction's structure?
|
|
|
|
Overriding ``SignTransactionFlow.checkTransaction`` allows us to define these additional checks. In our case, we are
|
|
checking that:
|
|
|
|
* The transaction involves an ``IOUState`` - this ensures that ``IOUContract`` will be run to verify the transaction
|
|
* The IOU's value is less than some amount (100 in this case)
|
|
|
|
If either of these conditions are not met, we will not sign the transaction - even if the transaction and its
|
|
signatures are contractually valid.
|
|
|
|
Once we've defined the ``SignTransactionFlow`` subclass, we invoke it using ``FlowLogic.subFlow``, and the
|
|
communication with the borrower's and the lender's flow is conducted automatically.
|
|
|
|
``SignedTransactionFlow`` returns the newly signed transaction. We pass in the transaction's ID to ``ReceiveFinalityFlow``
|
|
to ensure we are recording the correct notarised transaction from the lender.
|
|
|
|
Conclusion
|
|
----------
|
|
We have now updated our flow to verify the transaction and gather the lender's signature, in line with the constraints
|
|
defined in ``IOUContract``. We can now re-run our updated CorDapp, using the
|
|
:doc:`same instructions as before <hello-world-running>`.
|
|
|
|
Our CorDapp now imposes restrictions on the issuance of IOUs. Most importantly, IOU issuance now requires agreement
|
|
from both the lender and the borrower before an IOU can be created on the blockchain. This prevents either the lender or
|
|
the borrower from unilaterally updating the ledger in a way that only benefits themselves.
|
|
|
|
After completing this tutorial, your CorDapp should look like this:
|
|
|
|
* Java: https://github.com/corda/corda-tut2-solution-java
|
|
* Kotlin: https://github.com/corda/corda-tut2-solution-kotlin
|
|
|
|
You should now be ready to develop your own CorDapps. You can also find a list of sample CorDapps
|
|
`here <https://www.corda.net/samples/>`_. As you write CorDapps, you'll also want to learn more about the
|
|
:doc:`Corda API <corda-api>`.
|
|
|
|
If you get stuck at any point, please reach out on `Slack <https://slack.corda.net/>`_ or
|
|
`Stack Overflow <https://stackoverflow.com/questions/tagged/corda>`_.
|