mirror of
https://github.com/corda/corda.git
synced 2024-12-28 00:38:55 +00:00
846 lines
65 KiB
HTML
846 lines
65 KiB
HTML
<!-- If you edit this, then please make the same changes to layout_for_doc_website.html, as that is used for the web
|
|
doc site generation which we put analytics tracking on to identify any potential problem pages -->
|
|
|
|
|
|
|
|
|
|
<!DOCTYPE html>
|
|
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
|
|
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
|
|
<head>
|
|
<meta charset="utf-8">
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<title>Writing flows — R3 Corda latest documentation</title>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
|
|
|
|
|
|
|
|
|
|
|
|
<link rel="top" title="R3 Corda latest documentation" href="index.html"/>
|
|
<link rel="next" title="Writing flow tests" href="flow-testing.html"/>
|
|
<link rel="prev" title="Building transactions" href="tutorial-building-transactions.html"/>
|
|
|
|
|
|
<script src="_static/js/modernizr.min.js"></script>
|
|
|
|
</head>
|
|
|
|
<body class="wy-body-for-nav" role="document">
|
|
|
|
<div class="wy-grid-for-nav">
|
|
|
|
|
|
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
|
<div class="wy-side-scroll">
|
|
<div class="wy-side-nav-search">
|
|
|
|
|
|
|
|
|
|
<a href="index.html" class="icon icon-home"> R3 Corda
|
|
|
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
<div class="version">
|
|
latest
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div role="search">
|
|
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
|
<input type="text" name="q" placeholder="Search docs" />
|
|
<input type="hidden" name="check_keywords" value="yes" />
|
|
<input type="hidden" name="area" value="default" />
|
|
</form>
|
|
</div>
|
|
|
|
|
|
<br>
|
|
<a href="api/index.html">API reference</a>
|
|
<br>
|
|
<a href="https://discourse.corda.net">Discourse Forums</a>
|
|
<br>
|
|
<a href="http://slack.corda.net">Slack</a>
|
|
<br>
|
|
|
|
</div>
|
|
|
|
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
|
|
|
|
|
|
|
<p class="caption"><span class="caption-text">Getting started</span></p>
|
|
<ul>
|
|
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What’s included?</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="getting-set-up-fault-finding.html">Troubleshooting</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="running-the-demos.html">Running the demos</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="CLI-vs-IDE.html">CLI vs IDE</a></li>
|
|
</ul>
|
|
<p class="caption"><span class="caption-text">Key concepts</span></p>
|
|
<ul>
|
|
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="transaction-data-types.html">Data types</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="merkle-trees.html">Transaction tear-offs</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="consensus.html">Consensus model</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="clauses.html">Clauses</a></li>
|
|
</ul>
|
|
<p class="caption"><span class="caption-text">CorDapps</span></p>
|
|
<ul>
|
|
<li class="toctree-l1"><a class="reference internal" href="creating-a-cordapp.html">CorDapp basics</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="tutorial-cordapp.html">The CorDapp template</a></li>
|
|
</ul>
|
|
<p class="caption"><span class="caption-text">The Corda node</span></p>
|
|
<ul>
|
|
<li class="toctree-l1"><a class="reference internal" href="clientrpc.html">Client RPC</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="messaging.html">Networking and messaging</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="persistence.html">Persistence</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="node-administration.html">Node administration</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="corda-configuration-file.html">Node configuration</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="corda-plugins.html">The Corda plugin framework</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="node-services.html">Brief introduction to the node services</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="node-explorer.html">Node Explorer</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="permissioning.html">Network permissioning</a></li>
|
|
</ul>
|
|
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
|
<ul class="current">
|
|
<li class="toctree-l1"><a class="reference internal" href="tutorial-contract.html">Writing a contract</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="tutorial-contract-clauses.html">Writing a contract using clauses</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="tutorial-test-dsl.html">Writing a contract test</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="tutorial-integration-testing.html">Integration testing</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="tutorial-clientrpc-api.html">Client RPC API tutorial</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="tutorial-building-transactions.html">Building transactions</a></li>
|
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">Writing flows</a><ul>
|
|
<li class="toctree-l2"><a class="reference internal" href="#introduction">Introduction</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="#theory">Theory</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="#a-two-party-trading-flow">A two party trading flow</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="#suspendable-functions">Suspendable functions</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="#starting-your-flow">Starting your flow</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="#implementing-the-seller">Implementing the seller</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="#sub-flows">Sub-flows</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="#implementing-the-buyer">Implementing the buyer</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="#progress-tracking">Progress tracking</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="#versioning">Versioning</a></li>
|
|
<li class="toctree-l2"><a class="reference internal" href="#future-features">Future features</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="toctree-l1"><a class="reference internal" href="flow-testing.html">Writing flow tests</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="running-a-notary.html">Running a notary service</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="using-a-notary.html">Using a notary service</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="oracles.html">Writing oracle services</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="tutorial-attachments.html">Using attachments</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="event-scheduling.html">Event scheduling</a></li>
|
|
</ul>
|
|
<p class="caption"><span class="caption-text">Other</span></p>
|
|
<ul>
|
|
<li class="toctree-l1"><a class="reference internal" href="network-simulator.html">Network Simulator</a></li>
|
|
</ul>
|
|
<p class="caption"><span class="caption-text">Component library</span></p>
|
|
<ul>
|
|
<li class="toctree-l1"><a class="reference internal" href="contract-catalogue.html">Contract catalogue</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="contract-irs.html">Interest rate swaps</a></li>
|
|
</ul>
|
|
<p class="caption"><span class="caption-text">Appendix</span></p>
|
|
<ul>
|
|
<li class="toctree-l1"><a class="reference internal" href="loadtesting.html">Load testing</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="setting-up-a-corda-network.html">What is a corda network?</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="secure-coding-guidelines.html">Secure coding guidelines</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="release-process.html">Release process</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="release-notes.html">Release notes</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="codestyle.html">Code style guide</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="building-the-docs.html">Building the documentation</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="publishing-corda.html">Publishing Corda</a></li>
|
|
<li class="toctree-l1"><a class="reference internal" href="azure-vm.html">Working with the Corda Demo on Azure Marketplace</a></li>
|
|
</ul>
|
|
<p class="caption"><span class="caption-text">Glossary</span></p>
|
|
<ul>
|
|
<li class="toctree-l1"><a class="reference internal" href="glossary.html">Glossary</a></li>
|
|
</ul>
|
|
|
|
|
|
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
|
|
|
|
|
<nav class="wy-nav-top" role="navigation" aria-label="top navigation">
|
|
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
|
<a href="index.html">R3 Corda</a>
|
|
</nav>
|
|
|
|
|
|
|
|
<div class="wy-nav-content">
|
|
<div class="rst-content">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div role="navigation" aria-label="breadcrumbs navigation">
|
|
<ul class="wy-breadcrumbs">
|
|
<li><a href="index.html">Docs</a> »</li>
|
|
|
|
<li>Writing flows</li>
|
|
<li class="wy-breadcrumbs-aside">
|
|
|
|
|
|
<a href="_sources/flow-state-machines.txt" rel="nofollow"> View page source</a>
|
|
|
|
|
|
</li>
|
|
</ul>
|
|
<hr/>
|
|
</div>
|
|
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
|
<div itemprop="articleBody">
|
|
|
|
<script type="text/javascript" src="_static/jquery.js"></script>
|
|
<script type="text/javascript" src="_static/codesets.js"></script><div class="section" id="writing-flows">
|
|
<h1>Writing flows<a class="headerlink" href="#writing-flows" title="Permalink to this headline">¶</a></h1>
|
|
<p>This article explains our approach to modelling financial flows in code. It explains how the
|
|
platform’s state machine framework is used, and takes you through the code for a simple 2-party asset trading flow
|
|
which is included in the source.</p>
|
|
<div class="section" id="introduction">
|
|
<h2>Introduction<a class="headerlink" href="#introduction" title="Permalink to this headline">¶</a></h2>
|
|
<p>Shared distributed ledgers are interesting because they allow many different, mutually distrusting parties to
|
|
share a single source of truth about the ownership of assets. Digitally signed transactions are used to update that
|
|
shared ledger, and transactions may alter many states simultaneously and atomically.</p>
|
|
<p>Blockchain systems such as Bitcoin support the idea of building up a finished, signed transaction by passing around
|
|
partially signed invalid transactions outside of the main network, and by doing this you can implement
|
|
<em>delivery versus payment</em> such that there is no chance of settlement failure, because the movement of cash and the
|
|
traded asset are performed atomically by the same transaction. To perform such a trade involves a multi-step flow
|
|
in which messages are passed back and forth privately between parties, checked, signed and so on.</p>
|
|
<p>Despite how useful these flows are, platforms such as Bitcoin and Ethereum do not assist the developer with the rather
|
|
tricky task of actually building them. That is unfortunate. There are many awkward problems in their implementation
|
|
that a good platform would take care of for you, problems like:</p>
|
|
<ul class="simple">
|
|
<li>Avoiding “callback hell” in which code that should ideally be sequential is turned into an unreadable mess due to the
|
|
desire to avoid using up a thread for every flow instantiation.</li>
|
|
<li>Surviving node shutdowns/restarts that may occur in the middle of the flow without complicating things. This
|
|
implies that the state of the flow must be persisted to disk.</li>
|
|
<li>Error handling.</li>
|
|
<li>Message routing.</li>
|
|
<li>Serialisation.</li>
|
|
<li>Catching type errors, in which the developer gets temporarily confused and expects to receive/send one type of message
|
|
when actually they need to receive/send another.</li>
|
|
<li>Unit testing of the finished flow.</li>
|
|
</ul>
|
|
<p>Actor frameworks can solve some of the above but they are often tightly bound to a particular messaging layer, and
|
|
we would like to keep a clean separation. Additionally, they are typically not type safe, and don’t make persistence or
|
|
writing sequential code much easier.</p>
|
|
<p>To put these problems in perspective, the <em>payment channel protocol</em> in the bitcoinj library, which allows bitcoins to
|
|
be temporarily moved off-chain and traded at high speed between two parties in private, consists of about 7000 lines of
|
|
Java and took over a month of full time work to develop. Most of that code is concerned with the details of persistence,
|
|
message passing, lifecycle management, error handling and callback management. Because the business logic is quite
|
|
spread out the code can be difficult to read and debug.</p>
|
|
<p>As small contract-specific trading flows are a common occurrence in finance, we provide a framework for the
|
|
construction of them that automatically handles many of the concerns outlined above.</p>
|
|
</div>
|
|
<div class="section" id="theory">
|
|
<h2>Theory<a class="headerlink" href="#theory" title="Permalink to this headline">¶</a></h2>
|
|
<p>A <em>continuation</em> is a suspended stack frame stored in a regular object that can be passed around, serialised,
|
|
unserialised and resumed from where it was suspended. This concept is sometimes referred to as “fibers”. This may
|
|
sound abstract but don’t worry, the examples below will make it clearer. The JVM does not natively support
|
|
continuations, so we implement them using a library called Quasar which works through behind-the-scenes
|
|
bytecode rewriting. You don’t have to know how this works to benefit from it, however.</p>
|
|
<p>We use continuations for the following reasons:</p>
|
|
<ul class="simple">
|
|
<li>It allows us to write code that is free of callbacks, that looks like ordinary sequential code.</li>
|
|
<li>A suspended continuation takes far less memory than a suspended thread. It can be as low as a few hundred bytes.
|
|
In contrast a suspended Java thread stack can easily be 1mb in size.</li>
|
|
<li>It frees the developer from thinking (much) about persistence and serialisation.</li>
|
|
</ul>
|
|
<p>A <em>state machine</em> is a piece of code that moves through various <em>states</em>. These are not the same as states in the data
|
|
model (that represent facts about the world on the ledger), but rather indicate different stages in the progression
|
|
of a multi-stage flow. Typically writing a state machine would require the use of a big switch statement and some
|
|
explicit variables to keep track of where you’re up to. The use of continuations avoids this hassle.</p>
|
|
</div>
|
|
<div class="section" id="a-two-party-trading-flow">
|
|
<h2>A two party trading flow<a class="headerlink" href="#a-two-party-trading-flow" title="Permalink to this headline">¶</a></h2>
|
|
<p>We would like to implement the “hello world” of shared transaction building flows: a seller wishes to sell some
|
|
<em>asset</em> (e.g. some commercial paper) in return for <em>cash</em>. The buyer wishes to purchase the asset using his cash. They
|
|
want the trade to be atomic so neither side is exposed to the risk of settlement failure. We assume that the buyer
|
|
and seller have found each other and arranged the details on some exchange, or over the counter. The details of how
|
|
the trade is arranged isn’t covered in this article.</p>
|
|
<p>Our flow has two parties (B and S for buyer and seller) and will proceed as follows:</p>
|
|
<ol class="arabic simple">
|
|
<li>S sends a <code class="docutils literal"><span class="pre">StateAndRef</span></code> pointing to the state they want to sell to B, along with info about the price they require
|
|
B to pay.</li>
|
|
<li>B sends to S a <code class="docutils literal"><span class="pre">SignedTransaction</span></code> that includes the state as input, B’s cash as input, the state with the new
|
|
owner key as output, and any change cash as output. It contains a single signature from B but isn’t valid because
|
|
it lacks a signature from S authorising movement of the asset.</li>
|
|
<li>S signs it and hands the now finalised <code class="docutils literal"><span class="pre">SignedTransaction</span></code> back to B.</li>
|
|
</ol>
|
|
<p>You can find the implementation of this flow in the file <code class="docutils literal"><span class="pre">finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt</span></code>.</p>
|
|
<p>Assuming no malicious termination, they both end the flow being in possession of a valid, signed transaction that
|
|
represents an atomic asset swap.</p>
|
|
<p>Note that it’s the <em>seller</em> who initiates contact with the buyer, not vice-versa as you might imagine.</p>
|
|
<p>We start by defining a wrapper that namespaces the flow code, two functions to start either the buy or sell side
|
|
of the flow, and two classes that will contain the flow definition. We also pick what data will be used by
|
|
each side.</p>
|
|
<div class="admonition note">
|
|
<p class="first admonition-title">Note</p>
|
|
<p class="last">The code samples in this tutorial are only available in Kotlin, but you can use any JVM language to
|
|
write them and the approach is the same.</p>
|
|
</div>
|
|
<div class="codeset container">
|
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">object</span> <span class="nc">TwoPartyTradeFlow</span> <span class="p">{</span>
|
|
|
|
<span class="k">class</span> <span class="nc">UnacceptablePriceException</span><span class="p">(</span><span class="k">val</span> <span class="py">givenPrice</span><span class="p">:</span> <span class="n">Amount</span><span class="p"><</span><span class="n">Currency</span><span class="p">>)</span> <span class="p">:</span> <span class="n">Exception</span><span class="p">(</span><span class="s">"Unacceptable price: $givenPrice"</span><span class="p">)</span>
|
|
<span class="k">class</span> <span class="nc">AssetMismatchException</span><span class="p">(</span><span class="k">val</span> <span class="py">expectedTypeName</span><span class="p">:</span> <span class="n">String</span><span class="p">,</span> <span class="k">val</span> <span class="py">typeName</span><span class="p">:</span> <span class="n">String</span><span class="p">)</span> <span class="p">:</span> <span class="n">Exception</span><span class="p">()</span> <span class="p">{</span>
|
|
<span class="k">override</span> <span class="k">fun</span> <span class="nf">toString</span><span class="p">()</span> <span class="p">=</span> <span class="s">"The submitted asset didn't match the expected type: $expectedTypeName vs $typeName"</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="c1">// This object is serialised to the network and is the first flow message the seller sends to the buyer.</span>
|
|
<span class="k">data</span> <span class="k">class</span> <span class="nc">SellerTradeInfo</span><span class="p">(</span>
|
|
<span class="k">val</span> <span class="py">assetForSale</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">OwnableState</span><span class="p">>,</span>
|
|
<span class="k">val</span> <span class="py">price</span><span class="p">:</span> <span class="n">Amount</span><span class="p"><</span><span class="n">Currency</span><span class="p">>,</span>
|
|
<span class="k">val</span> <span class="py">sellerOwnerKey</span><span class="p">:</span> <span class="n">CompositeKey</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">data</span> <span class="k">class</span> <span class="nc">SignaturesFromSeller</span><span class="p">(</span><span class="k">val</span> <span class="py">sellerSig</span><span class="p">:</span> <span class="n">DigitalSignature</span><span class="p">.</span><span class="n">WithKey</span><span class="p">,</span>
|
|
<span class="k">val</span> <span class="py">notarySig</span><span class="p">:</span> <span class="n">DigitalSignature</span><span class="p">.</span><span class="n">LegallyIdentifiable</span><span class="p">)</span>
|
|
|
|
<span class="k">open</span> <span class="k">class</span> <span class="nc">Seller</span><span class="p">(</span><span class="k">val</span> <span class="py">otherParty</span><span class="p">:</span> <span class="n">Party</span><span class="p">,</span>
|
|
<span class="k">val</span> <span class="py">notaryNode</span><span class="p">:</span> <span class="n">NodeInfo</span><span class="p">,</span>
|
|
<span class="k">val</span> <span class="py">assetToSell</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">OwnableState</span><span class="p">>,</span>
|
|
<span class="k">val</span> <span class="py">price</span><span class="p">:</span> <span class="n">Amount</span><span class="p"><</span><span class="n">Currency</span><span class="p">>,</span>
|
|
<span class="k">val</span> <span class="py">myKeyPair</span><span class="p">:</span> <span class="n">KeyPair</span><span class="p">,</span>
|
|
<span class="k">override</span> <span class="k">val</span> <span class="py">progressTracker</span><span class="p">:</span> <span class="n">ProgressTracker</span> <span class="p">=</span> <span class="n">Seller</span><span class="p">.</span><span class="n">tracker</span><span class="p">())</span> <span class="p">:</span> <span class="n">FlowLogic</span><span class="p"><</span><span class="n">SignedTransaction</span><span class="p">>()</span> <span class="p">{</span>
|
|
<span class="n">@Suspendable</span>
|
|
<span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">():</span> <span class="n">SignedTransaction</span> <span class="p">{</span>
|
|
<span class="n">TODO</span><span class="p">()</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="k">open</span> <span class="k">class</span> <span class="nc">Buyer</span><span class="p">(</span><span class="k">val</span> <span class="py">otherParty</span><span class="p">:</span> <span class="n">Party</span><span class="p">,</span>
|
|
<span class="k">val</span> <span class="py">notary</span><span class="p">:</span> <span class="n">Party</span><span class="p">,</span>
|
|
<span class="k">val</span> <span class="py">acceptablePrice</span><span class="p">:</span> <span class="n">Amount</span><span class="p"><</span><span class="n">Currency</span><span class="p">>,</span>
|
|
<span class="k">val</span> <span class="py">typeToBuy</span><span class="p">:</span> <span class="n">Class</span><span class="p"><</span><span class="k">out</span> <span class="n">OwnableState</span><span class="p">>)</span> <span class="p">:</span> <span class="n">FlowLogic</span><span class="p"><</span><span class="n">SignedTransaction</span><span class="p">>()</span> <span class="p">{</span>
|
|
<span class="n">@Suspendable</span>
|
|
<span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">():</span> <span class="n">SignedTransaction</span> <span class="p">{</span>
|
|
<span class="n">TODO</span><span class="p">()</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
</div>
|
|
<p>This code defines several classes nested inside the main <code class="docutils literal"><span class="pre">TwoPartyTradeFlow</span></code> singleton. Some of the classes are
|
|
simply flow messages or exceptions. The other two represent the buyer and seller side of the flow.</p>
|
|
<p>Going through the data needed to become a seller, we have:</p>
|
|
<ul class="simple">
|
|
<li><code class="docutils literal"><span class="pre">otherParty:</span> <span class="pre">Party</span></code> - the party with which you are trading.</li>
|
|
<li><code class="docutils literal"><span class="pre">notaryNode:</span> <span class="pre">NodeInfo</span></code> - the entry in the network map for the chosen notary. See “<a class="reference internal" href="consensus.html"><span class="doc">Consensus model</span></a>” for more
|
|
information on notaries.</li>
|
|
<li><code class="docutils literal"><span class="pre">assetToSell:</span> <span class="pre">StateAndRef<OwnableState></span></code> - a pointer to the ledger entry that represents the thing being sold.</li>
|
|
<li><code class="docutils literal"><span class="pre">price:</span> <span class="pre">Amount<Currency></span></code> - the agreed on price that the asset is being sold for (without an issuer constraint).</li>
|
|
<li><code class="docutils literal"><span class="pre">myKeyPair:</span> <span class="pre">KeyPair</span></code> - the key pair that controls the asset being sold. It will be used to sign the transaction.</li>
|
|
</ul>
|
|
<p>And for the buyer:</p>
|
|
<ul class="simple">
|
|
<li><code class="docutils literal"><span class="pre">acceptablePrice:</span> <span class="pre">Amount<Currency></span></code> - the price that was agreed upon out of band. If the seller specifies
|
|
a price less than or equal to this, then the trade will go ahead.</li>
|
|
<li><code class="docutils literal"><span class="pre">typeToBuy:</span> <span class="pre">Class<out</span> <span class="pre">OwnableState></span></code> - the type of state that is being purchased. This is used to check that the
|
|
sell side of the flow isn’t trying to sell us the wrong thing, whether by accident or on purpose.</li>
|
|
</ul>
|
|
<p>Alright, so using this flow shouldn’t be too hard: in the simplest case we can just create a Buyer or Seller
|
|
with the details of the trade, depending on who we are. We then have to start the flow in some way. Just
|
|
calling the <code class="docutils literal"><span class="pre">call</span></code> function ourselves won’t work: instead we need to ask the framework to start the flow for
|
|
us. More on that in a moment.</p>
|
|
</div>
|
|
<div class="section" id="suspendable-functions">
|
|
<h2>Suspendable functions<a class="headerlink" href="#suspendable-functions" title="Permalink to this headline">¶</a></h2>
|
|
<p>The <code class="docutils literal"><span class="pre">call</span></code> function of the buyer/seller classes is marked with the <code class="docutils literal"><span class="pre">@Suspendable</span></code> annotation. What does this mean?</p>
|
|
<p>As mentioned above, our flow framework will at points suspend the code and serialise it to disk. For this to work,
|
|
any methods on the call stack must have been pre-marked as <code class="docutils literal"><span class="pre">@Suspendable</span></code> so the bytecode rewriter knows to modify
|
|
the underlying code to support this new feature. A flow is suspended when calling either <code class="docutils literal"><span class="pre">receive</span></code>, <code class="docutils literal"><span class="pre">send</span></code> or
|
|
<code class="docutils literal"><span class="pre">sendAndReceive</span></code> which we will learn more about below. For now, just be aware that when one of these methods is
|
|
invoked, all methods on the stack must have been marked. If you forget, then in the unit test environment you will
|
|
get a useful error message telling you which methods you didn’t mark. The fix is simple enough: just add the annotation
|
|
and try again.</p>
|
|
<div class="admonition note">
|
|
<p class="first admonition-title">Note</p>
|
|
<p class="last">Java 9 is likely to remove this pre-marking requirement completely.</p>
|
|
</div>
|
|
<div class="admonition note">
|
|
<p class="first admonition-title">Note</p>
|
|
<p class="last">Accessing the vault from inside an @Suspendable function (e.g. via <code class="docutils literal"><span class="pre">serviceHub.vaultService</span></code>) can cause a serialisation error when the fiber suspends. Instead, vault access should be performed from a helper non-suspendable function, which you then call from the @Suspendable function. We are working to fix this.</p>
|
|
</div>
|
|
</div>
|
|
<div class="section" id="starting-your-flow">
|
|
<h2>Starting your flow<a class="headerlink" href="#starting-your-flow" title="Permalink to this headline">¶</a></h2>
|
|
<p>The <code class="docutils literal"><span class="pre">StateMachineManager</span></code> is the class responsible for taking care of all running flows in a node. It knows
|
|
how to register handlers with the messaging system (see “<a class="reference internal" href="messaging.html"><span class="doc">Networking and messaging</span></a>”) and iterate the right state machine
|
|
when messages arrive. It provides the send/receive/sendAndReceive calls that let the code request network
|
|
interaction and it will save/restore serialised versions of the fiber at the right times.</p>
|
|
<p>Flows can be invoked in several ways. For instance, they can be triggered by scheduled events,
|
|
see “<a class="reference internal" href="event-scheduling.html"><span class="doc">Event scheduling</span></a>” to learn more about this. Or they can be triggered directly via the Java-level node RPC
|
|
APIs from your app code.</p>
|
|
<p>You request a flow to be invoked by using the <code class="docutils literal"><span class="pre">CordaRPCOps.startFlowDynamic</span></code> method. This takes a
|
|
Java reflection <code class="docutils literal"><span class="pre">Class</span></code> object that describes the flow class to use (in this case, either <code class="docutils literal"><span class="pre">Buyer</span></code> or <code class="docutils literal"><span class="pre">Seller</span></code>).
|
|
It also takes a set of arguments to pass to the constructor. Because it’s possible for flow invocations to
|
|
be requested by untrusted code (e.g. a state that you have been sent), the types that can be passed into the
|
|
flow are checked against a whitelist, which can be extended by apps themselves at load time. There are also a series
|
|
of inlined extension functions of the form <code class="docutils literal"><span class="pre">CordaRPCOps.startFlow</span></code> which help with invoking flows in a type
|
|
safe manner.</p>
|
|
<p>The process of starting a flow returns a <code class="docutils literal"><span class="pre">FlowHandle</span></code> that you can use to either observe
|
|
the result, observe its progress and which also contains a permanent identifier for the invoked flow in the form
|
|
of the <code class="docutils literal"><span class="pre">StateMachineRunId</span></code>.</p>
|
|
<p>In a two party flow only one side is to be manually started using <code class="docutils literal"><span class="pre">CordaRPCOps.startFlow</span></code>. The other side
|
|
has to be registered by its node to respond to the initiating flow via <code class="docutils literal"><span class="pre">PluginServiceHub.registerFlowInitiator</span></code>.
|
|
In our example it doesn’t matter which flow is the initiator and which is the initiated. For example, if we are to
|
|
take the seller as the initiator then we would register the buyer as such:</p>
|
|
<div class="codeset container">
|
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">val</span> <span class="py">services</span><span class="p">:</span> <span class="n">PluginServiceHub</span> <span class="p">=</span> <span class="n">TODO</span><span class="p">()</span>
|
|
<span class="n">services</span><span class="p">.</span><span class="n">registerFlowInitiator</span><span class="p">(</span><span class="n">Seller</span><span class="o">::</span><span class="k">class</span><span class="p">)</span> <span class="p">{</span> <span class="n">otherParty</span> <span class="p">-></span>
|
|
<span class="k">val</span> <span class="py">notary</span> <span class="p">=</span> <span class="n">services</span><span class="p">.</span><span class="n">networkMapCache</span><span class="p">.</span><span class="n">notaryNodes</span><span class="p">[</span><span class="m">0</span><span class="p">]</span>
|
|
<span class="k">val</span> <span class="py">acceptablePrice</span> <span class="p">=</span> <span class="n">TODO</span><span class="p">()</span>
|
|
<span class="k">val</span> <span class="py">typeToBuy</span> <span class="p">=</span> <span class="n">TODO</span><span class="p">()</span>
|
|
<span class="n">Buyer</span><span class="p">(</span><span class="n">otherParty</span><span class="p">,</span> <span class="n">notary</span><span class="p">,</span> <span class="n">acceptablePrice</span><span class="p">,</span> <span class="n">typeToBuy</span><span class="p">)</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
</div>
|
|
<p>This is telling the buyer node to fire up an instance of <code class="docutils literal"><span class="pre">Buyer</span></code> (the code in the lambda) when the initiating flow
|
|
is a seller (<code class="docutils literal"><span class="pre">Seller::class</span></code>).</p>
|
|
</div>
|
|
<div class="section" id="implementing-the-seller">
|
|
<h2>Implementing the seller<a class="headerlink" href="#implementing-the-seller" title="Permalink to this headline">¶</a></h2>
|
|
<p>Let’s implement the <code class="docutils literal"><span class="pre">Seller.call</span></code> method. This will be run when the flow is invoked.</p>
|
|
<div class="codeset container">
|
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="n">@Suspendable</span>
|
|
<span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">():</span> <span class="n">SignedTransaction</span> <span class="p">{</span>
|
|
<span class="k">val</span> <span class="py">partialTX</span><span class="p">:</span> <span class="n">SignedTransaction</span> <span class="p">=</span> <span class="n">receiveAndCheckProposedTransaction</span><span class="p">()</span>
|
|
<span class="k">val</span> <span class="py">ourSignature</span><span class="p">:</span> <span class="n">DigitalSignature</span><span class="p">.</span><span class="n">WithKey</span> <span class="p">=</span> <span class="n">computeOurSignature</span><span class="p">(</span><span class="n">partialTX</span><span class="p">)</span>
|
|
<span class="k">val</span> <span class="py">allPartySignedTx</span> <span class="p">=</span> <span class="n">partialTX</span> <span class="p">+</span> <span class="n">ourSignature</span>
|
|
<span class="k">val</span> <span class="py">notarySignature</span> <span class="p">=</span> <span class="n">getNotarySignature</span><span class="p">(</span><span class="n">allPartySignedTx</span><span class="p">)</span>
|
|
<span class="k">val</span> <span class="py">result</span><span class="p">:</span> <span class="n">SignedTransaction</span> <span class="p">=</span> <span class="n">sendSignatures</span><span class="p">(</span><span class="n">allPartySignedTx</span><span class="p">,</span> <span class="n">ourSignature</span><span class="p">,</span> <span class="n">notarySignature</span><span class="p">)</span>
|
|
<span class="k">return</span> <span class="n">result</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
</div>
|
|
<p>Here we see the outline of the procedure. We receive a proposed trade transaction from the buyer and check that it’s
|
|
valid. The buyer has already attached their signature before sending it. Then we calculate and attach our own signature so that the transaction is
|
|
now signed by both the buyer and the seller. We then send this request to a notary to assert with another signature that the
|
|
timestamp in the transaction (if any) is valid and there are no double spends, and send back both
|
|
our signature and the notaries signature. Note we should not send to the notary until all other required signatures have been appended
|
|
as the notary may validate the signatures as well as verifying for itself the transactional integrity.
|
|
Finally, we hand back to the code that invoked the flow the finished transaction.</p>
|
|
<p>Let’s fill out the <code class="docutils literal"><span class="pre">receiveAndCheckProposedTransaction()</span></code> method.</p>
|
|
<div class="codeset container">
|
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="n">@Suspendable</span>
|
|
<span class="k">private</span> <span class="k">fun</span> <span class="nf">receiveAndCheckProposedTransaction</span><span class="p">():</span> <span class="n">SignedTransaction</span> <span class="p">{</span>
|
|
<span class="c1">// Make the first message we'll send to kick off the flow.</span>
|
|
<span class="k">val</span> <span class="py">myPublicKey</span> <span class="p">=</span> <span class="n">myKeyPair</span><span class="p">.</span><span class="k">public</span><span class="p">.</span><span class="n">composite</span>
|
|
<span class="k">val</span> <span class="py">hello</span> <span class="p">=</span> <span class="n">SellerTradeInfo</span><span class="p">(</span><span class="n">assetToSell</span><span class="p">,</span> <span class="n">price</span><span class="p">,</span> <span class="n">myPublicKey</span><span class="p">)</span>
|
|
|
|
<span class="k">val</span> <span class="py">maybeSTX</span> <span class="p">=</span> <span class="n">sendAndReceive</span><span class="p"><</span><span class="n">SignedTransaction</span><span class="p">>(</span><span class="n">otherSide</span><span class="p">,</span> <span class="n">hello</span><span class="p">)</span>
|
|
|
|
<span class="n">maybeSTX</span><span class="p">.</span><span class="n">unwrap</span> <span class="p">{</span>
|
|
<span class="c1">// Check that the tx proposed by the buyer is valid.</span>
|
|
<span class="k">val</span> <span class="py">wtx</span><span class="p">:</span> <span class="n">WireTransaction</span> <span class="p">=</span> <span class="n">it</span><span class="p">.</span><span class="n">verifySignatures</span><span class="p">(</span><span class="n">myPublicKey</span><span class="p">,</span> <span class="n">notaryNode</span><span class="p">.</span><span class="n">notaryIdentity</span><span class="p">.</span><span class="n">owningKey</span><span class="p">)</span>
|
|
<span class="n">logger</span><span class="p">.</span><span class="n">trace</span> <span class="p">{</span> <span class="s">"Received partially signed transaction: ${it.id}"</span> <span class="p">}</span>
|
|
|
|
<span class="c1">// Download and check all the things that this transaction depends on and verify it is contract-valid,</span>
|
|
<span class="c1">// even though it is missing signatures.</span>
|
|
<span class="n">subFlow</span><span class="p">(</span><span class="n">ResolveTransactionsFlow</span><span class="p">(</span><span class="n">wtx</span><span class="p">,</span> <span class="n">otherParty</span><span class="p">))</span>
|
|
|
|
<span class="k">if</span> <span class="p">(</span><span class="n">wtx</span><span class="p">.</span><span class="n">outputs</span><span class="p">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="k">data</span> <span class="p">}.</span><span class="n">sumCashBy</span><span class="p">(</span><span class="n">myPublicKey</span><span class="p">).</span><span class="n">withoutIssuer</span><span class="p">()</span> <span class="p">!=</span> <span class="n">price</span><span class="p">)</span>
|
|
<span class="k">throw</span> <span class="n">IllegalArgumentException</span><span class="p">(</span><span class="s">"Transaction is not sending us the right amount of cash"</span><span class="p">)</span>
|
|
|
|
<span class="k">return</span> <span class="n">it</span>
|
|
<span class="p">}</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
</div>
|
|
<p>Let’s break this down. We fill out the initial flow message with the trade info, and then call <code class="docutils literal"><span class="pre">sendAndReceive</span></code>.
|
|
This function takes a few arguments:</p>
|
|
<ul class="simple">
|
|
<li>The party on the other side.</li>
|
|
<li>The thing to send. It’ll be serialised and sent automatically.</li>
|
|
<li>Finally a type argument, which is the kind of object we’re expecting to receive from the other side. If we get
|
|
back something else an exception is thrown.</li>
|
|
</ul>
|
|
<p>Once <code class="docutils literal"><span class="pre">sendAndReceive</span></code> is called, the call method will be suspended into a continuation and saved to persistent
|
|
storage. If the node crashes or is restarted, the flow will effectively continue as if nothing had happened. Your
|
|
code may remain blocked inside such a call for seconds, minutes, hours or even days in the case of a flow that
|
|
needs human interaction!</p>
|
|
<div class="admonition note">
|
|
<p class="first admonition-title">Note</p>
|
|
<p>There are a couple of rules you need to bear in mind when writing a class that will be used as a continuation.
|
|
The first is that anything on the stack when the function is suspended will be stored into the heap and kept alive by
|
|
the garbage collector. So try to avoid keeping enormous data structures alive unless you really have to. You can
|
|
always use private methods to keep the stack uncluttered with temporary variables, or to avoid objects that
|
|
Kryo is not able to serialise correctly.</p>
|
|
<p>The second is that as well as being kept on the heap, objects reachable from the stack will be serialised. The state
|
|
of the function call may be resurrected much later! Kryo doesn’t require objects be marked as serialisable, but even so,
|
|
doing things like creating threads from inside these calls would be a bad idea. They should only contain business
|
|
logic and only do I/O via the methods exposed by the flow framework.</p>
|
|
<p class="last">It’s OK to keep references around to many large internal node services though: these will be serialised using a
|
|
special token that’s recognised by the platform, and wired up to the right instance when the continuation is
|
|
loaded off disk again.</p>
|
|
</div>
|
|
<p>The buyer is supposed to send us a transaction with all the right inputs/outputs/commands in response to the opening
|
|
message, with their cash put into the transaction and their signature on it authorising the movement of the cash.</p>
|
|
<p>You get back a simple wrapper class, <code class="docutils literal"><span class="pre">UntrustworthyData<SignedTransaction></span></code>, which is just a marker class that reminds
|
|
us that the data came from a potentially malicious external source and may have been tampered with or be unexpected in
|
|
other ways. It doesn’t add any functionality, but acts as a reminder to “scrub” the data before use.</p>
|
|
<p>Our “scrubbing” has three parts:</p>
|
|
<ol class="arabic simple">
|
|
<li>Check that the expected signatures are present and correct. At this point we expect our own signature to be missing,
|
|
because of course we didn’t sign it yet, and also the signature of the notary because that must always come last.</li>
|
|
<li>We resolve the transaction, which we will cover below.</li>
|
|
<li>We verify that the transaction is paying us the demanded price.</li>
|
|
</ol>
|
|
</div>
|
|
<div class="section" id="sub-flows">
|
|
<h2>Sub-flows<a class="headerlink" href="#sub-flows" title="Permalink to this headline">¶</a></h2>
|
|
<p>Flows can be composed via nesting. Invoking a sub-flow looks similar to an ordinary function call:</p>
|
|
<div class="codeset container">
|
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="n">@Suspendable</span>
|
|
<span class="k">private</span> <span class="k">fun</span> <span class="nf">getNotarySignature</span><span class="p">(</span><span class="n">stx</span><span class="p">:</span> <span class="n">SignedTransaction</span><span class="p">):</span> <span class="n">DigitalSignature</span><span class="p">.</span><span class="n">LegallyIdentifiable</span> <span class="p">{</span>
|
|
<span class="n">progressTracker</span><span class="p">.</span><span class="n">currentStep</span> <span class="p">=</span> <span class="n">NOTARY</span>
|
|
<span class="k">return</span> <span class="n">subFlow</span><span class="p">(</span><span class="n">NotaryFlow</span><span class="p">.</span><span class="n">Client</span><span class="p">(</span><span class="n">stx</span><span class="p">))</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
</div>
|
|
<p>In this code snippet we are using the <code class="docutils literal"><span class="pre">NotaryFlow.Client</span></code> to request notarisation of the transaction.
|
|
We simply create the flow object via its constructor, and then pass it to the <code class="docutils literal"><span class="pre">subFlow</span></code> method which
|
|
returns the result of the flow’s execution directly. Behind the scenes all this is doing is wiring up progress
|
|
tracking (discussed more below) and then running the objects <code class="docutils literal"><span class="pre">call</span></code> method. Because this little helper method can
|
|
be on the stack when network IO takes place, we mark it as <code class="docutils literal"><span class="pre">@Suspendable</span></code>.</p>
|
|
<p>Going back to the previous code snippet, we use a sub-flow called <code class="docutils literal"><span class="pre">ResolveTransactionsFlow</span></code>. This is
|
|
responsible for downloading and checking all the dependencies of a transaction, which in Corda are always retrievable
|
|
from the party that sent you a transaction that uses them. This flow returns a list of <code class="docutils literal"><span class="pre">LedgerTransaction</span></code>
|
|
objects, but we don’t need them here so we just ignore the return value.</p>
|
|
<div class="admonition note">
|
|
<p class="first admonition-title">Note</p>
|
|
<p class="last">Transaction dependency resolution assumes that the peer you got the transaction from has all of the
|
|
dependencies itself. It must do, otherwise it could not have convinced itself that the dependencies were themselves
|
|
valid. It’s important to realise that requesting only the transactions we require is a privacy leak, because if
|
|
we don’t download a transaction from the peer, they know we must have already seen it before. Fixing this privacy
|
|
leak will come later.</p>
|
|
</div>
|
|
<p>After the dependencies, we check the proposed trading transaction for validity by running the contracts for that as
|
|
well (but having handled the fact that some signatures are missing ourselves).</p>
|
|
<p>Here’s the rest of the code:</p>
|
|
<div class="codeset container">
|
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">open</span> <span class="k">fun</span> <span class="nf">calculateOurSignature</span><span class="p">(</span><span class="n">partialTX</span><span class="p">:</span> <span class="n">SignedTransaction</span><span class="p">)</span> <span class="p">=</span> <span class="n">myKeyPair</span><span class="p">.</span><span class="n">signWithECDSA</span><span class="p">(</span><span class="n">partialTX</span><span class="p">.</span><span class="n">id</span><span class="p">)</span>
|
|
|
|
<span class="n">@Suspendable</span>
|
|
<span class="k">private</span> <span class="k">fun</span> <span class="nf">sendSignatures</span><span class="p">(</span><span class="n">allPartySignedTX</span><span class="p">:</span> <span class="n">SignedTransaction</span><span class="p">,</span> <span class="n">ourSignature</span><span class="p">:</span> <span class="n">DigitalSignature</span><span class="p">.</span><span class="n">WithKey</span><span class="p">,</span>
|
|
<span class="n">notarySignature</span><span class="p">:</span> <span class="n">DigitalSignature</span><span class="p">.</span><span class="n">WithKey</span><span class="p">):</span> <span class="n">SignedTransaction</span> <span class="p">{</span>
|
|
<span class="k">val</span> <span class="py">fullySigned</span> <span class="p">=</span> <span class="n">allPartySignedTX</span> <span class="p">+</span> <span class="n">notarySignature</span>
|
|
<span class="n">logger</span><span class="p">.</span><span class="n">trace</span> <span class="p">{</span> <span class="s">"Built finished transaction, sending back to secondary!"</span> <span class="p">}</span>
|
|
<span class="n">send</span><span class="p">(</span><span class="n">otherSide</span><span class="p">,</span> <span class="n">SignaturesFromSeller</span><span class="p">(</span><span class="n">ourSignature</span><span class="p">,</span> <span class="n">notarySignature</span><span class="p">))</span>
|
|
<span class="k">return</span> <span class="n">fullySigned</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
</div>
|
|
<p>It’s all pretty straightforward from now on. Here <code class="docutils literal"><span class="pre">id</span></code> is the secure hash representing the serialised
|
|
transaction, and we just use our private key to calculate a signature over it. As a reminder, in Corda signatures do
|
|
not cover other signatures: just the core of the transaction data.</p>
|
|
<p>In <code class="docutils literal"><span class="pre">sendSignatures</span></code>, we take the two signatures we obtained and add them to the partial transaction we were sent.
|
|
There is an overload for the + operator so signatures can be added to a SignedTransaction easily. Finally, we wrap the
|
|
two signatures in a simple wrapper message class and send it back. The send won’t block waiting for an acknowledgement,
|
|
but the underlying message queue software will retry delivery if the other side has gone away temporarily.</p>
|
|
<p>You can also see that every flow instance has a logger (using the SLF4J API) which you can use to log progress
|
|
messages.</p>
|
|
<div class="admonition warning">
|
|
<p class="first admonition-title">Warning</p>
|
|
<p class="last">This sample code is <strong>not secure</strong>. Other than not checking for all possible invalid constructions, if the
|
|
seller stops before sending the finalised transaction to the buyer, the seller is left with a valid transaction
|
|
but the buyer isn’t, so they can’t spend the asset they just purchased! This sort of thing will be fixed in a
|
|
future version of the code.</p>
|
|
</div>
|
|
</div>
|
|
<div class="section" id="implementing-the-buyer">
|
|
<h2>Implementing the buyer<a class="headerlink" href="#implementing-the-buyer" title="Permalink to this headline">¶</a></h2>
|
|
<p>OK, let’s do the same for the buyer side:</p>
|
|
<div class="highlight-kotlin"><div class="highlight"><pre><span></span> @Suspendable
|
|
override fun call(): SignedTransaction {
|
|
val tradeRequest = receiveAndValidateTradeRequest()
|
|
|
|
progressTracker.currentStep = SIGNING
|
|
val (ptx, cashSigningPubKeys) = assembleSharedTX(tradeRequest)
|
|
val stx = signWithOurKeys(cashSigningPubKeys, ptx)
|
|
|
|
val signatures = swapSignaturesWithSeller(stx)
|
|
|
|
logger.trace { "Got signatures from seller, verifying ... " }
|
|
|
|
val fullySigned = stx + signatures.sellerSig + signatures.notarySig
|
|
fullySigned.verifySignatures()
|
|
|
|
logger.trace { "Signatures received are valid. Trade complete! :-)" }
|
|
return fullySigned
|
|
}
|
|
|
|
@Suspendable
|
|
private fun receiveAndValidateTradeRequest(): SellerTradeInfo {
|
|
progressTracker.currentStep = RECEIVING
|
|
// Wait for a trade request to come in from the other side
|
|
val maybeTradeRequest = receive<SellerTradeInfo>(otherParty)
|
|
|
|
progressTracker.currentStep = VERIFYING
|
|
maybeTradeRequest.unwrap {
|
|
// What is the seller trying to sell us?
|
|
val asset = it.assetForSale.state.data
|
|
val assetTypeName = asset.javaClass.name
|
|
logger.trace { "Got trade request for a $assetTypeName: ${it.assetForSale}" }
|
|
|
|
if (it.price > acceptablePrice)
|
|
throw UnacceptablePriceException(it.price)
|
|
if (!typeToBuy.isInstance(asset))
|
|
throw AssetMismatchException(typeToBuy.name, assetTypeName)
|
|
|
|
// Check the transaction that contains the state which is being resolved.
|
|
// We only have a hash here, so if we don't know it already, we have to ask for it.
|
|
subFlow(ResolveTransactionsFlow(setOf(it.assetForSale.ref.txhash), otherParty))
|
|
|
|
return it
|
|
}
|
|
}
|
|
|
|
@Suspendable
|
|
private fun swapSignaturesWithSeller(stx: SignedTransaction): SignaturesFromSeller {
|
|
progressTracker.currentStep = SWAPPING_SIGNATURES
|
|
logger.trace { "Sending partially signed transaction to seller" }
|
|
|
|
// TODO: Protect against the seller terminating here and leaving us in the lurch without the final tx.
|
|
|
|
return sendAndReceive<SignaturesFromSeller>(otherParty, stx).unwrap { it }
|
|
}
|
|
|
|
private fun signWithOurKeys(cashSigningPubKeys: List<CompositeKey>, ptx: TransactionBuilder): SignedTransaction {
|
|
// Now sign the transaction with whatever keys we need to move the cash.
|
|
for (publicKey in cashSigningPubKeys.keys) {
|
|
val privateKey = serviceHub.keyManagementService.toPrivate(publicKey)
|
|
ptx.signWith(KeyPair(publicKey, privateKey))
|
|
}
|
|
|
|
return ptx.toSignedTransaction(checkSufficientSignatures = false)
|
|
}
|
|
|
|
private fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair<TransactionBuilder, List<CompositeKey>> {
|
|
val ptx = TransactionType.General.Builder(notary)
|
|
|
|
// Add input and output states for the movement of cash, by using the Cash contract to generate the states
|
|
val (tx, cashSigningPubKeys) = serviceHub.vaultService.generateSpend(ptx, tradeRequest.price, tradeRequest.sellerOwnerKey)
|
|
|
|
// Add inputs/outputs/a command for the movement of the asset.
|
|
tx.addInputState(tradeRequest.assetForSale)
|
|
|
|
// Just pick some new public key for now. This won't be linked with our identity in any way, which is what
|
|
// we want for privacy reasons: the key is here ONLY to manage and control ownership, it is not intended to
|
|
// reveal who the owner actually is. The key management service is expected to derive a unique key from some
|
|
// initial seed in order to provide privacy protection.
|
|
val freshKey = serviceHub.keyManagementService.freshKey()
|
|
val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(freshKey.public.composite)
|
|
tx.addOutputState(state, tradeRequest.assetForSale.state.notary)
|
|
tx.addCommand(command, tradeRequest.assetForSale.state.data.owner)
|
|
|
|
// And add a request for timestamping: it may be that none of the contracts need this! But it can't hurt
|
|
// to have one.
|
|
val currentTime = serviceHub.clock.instant()
|
|
tx.setTime(currentTime, 30.seconds)
|
|
return Pair(tx, cashSigningPubKeys)
|
|
}
|
|
</pre></div>
|
|
</div>
|
|
<p>This code is longer but no more complicated. Here are some things to pay attention to:</p>
|
|
<ol class="arabic simple">
|
|
<li>We do some sanity checking on the received message to ensure we’re being offered what we expected to be offered.</li>
|
|
<li>We create a cash spend in the normal way, by using <code class="docutils literal"><span class="pre">VaultService.generateSpend</span></code>. See the vault documentation if this
|
|
part isn’t clear.</li>
|
|
<li>We access the <em>service hub</em> when we need it to access things that are transient and may change or be recreated
|
|
whilst a flow is suspended, things like the wallet or the network map.</li>
|
|
<li>Finally, we send the unfinished, invalid transaction to the seller so they can sign it. They are expected to send
|
|
back to us a <code class="docutils literal"><span class="pre">SignaturesFromSeller</span></code>, which once we verify it, should be the final outcome of the trade.</li>
|
|
</ol>
|
|
<p>As you can see, the flow logic is straightforward and does not contain any callbacks or network glue code, despite
|
|
the fact that it takes minimal resources and can survive node restarts.</p>
|
|
<div class="admonition warning">
|
|
<p class="first admonition-title">Warning</p>
|
|
<p class="last">In the current version of the platform, exceptions thrown during flow execution are not propagated
|
|
back to the sender. A thorough error handling and exceptions framework will be in a future version of the platform.</p>
|
|
</div>
|
|
</div>
|
|
<div class="section" id="progress-tracking">
|
|
<h2>Progress tracking<a class="headerlink" href="#progress-tracking" title="Permalink to this headline">¶</a></h2>
|
|
<p>Not shown in the code snippets above is the usage of the <code class="docutils literal"><span class="pre">ProgressTracker</span></code> API. Progress tracking exports information
|
|
from a flow about where it’s got up to in such a way that observers can render it in a useful manner to humans who
|
|
may need to be informed. It may be rendered via an API, in a GUI, onto a terminal window, etc.</p>
|
|
<p>A <code class="docutils literal"><span class="pre">ProgressTracker</span></code> is constructed with a series of <code class="docutils literal"><span class="pre">Step</span></code> objects, where each step is an object representing a
|
|
stage in a piece of work. It is therefore typical to use singletons that subclass <code class="docutils literal"><span class="pre">Step</span></code>, which may be defined easily
|
|
in one line when using Kotlin. Typical steps might be “Waiting for response from peer”, “Waiting for signature to be
|
|
approved”, “Downloading and verifying data” etc.</p>
|
|
<p>Each step exposes a label. By default labels are fixed, but by subclassing <code class="docutils literal"><span class="pre">RelabelableStep</span></code>
|
|
you can make a step that can update its label on the fly. That’s useful for steps that want to expose non-structured
|
|
progress information like the current file being downloaded. By defining your own step types, you can export progress
|
|
in a way that’s both human readable and machine readable.</p>
|
|
<p>Progress trackers are hierarchical. Each step can be the parent for another tracker. By altering the
|
|
<code class="docutils literal"><span class="pre">ProgressTracker.childrenFor[step]</span> <span class="pre">=</span> <span class="pre">tracker</span></code> map, a tree of steps can be created. It’s allowed to alter the hierarchy
|
|
at runtime, on the fly, and the progress renderers will adapt to that properly. This can be helpful when you don’t
|
|
fully know ahead of time what steps will be required. If you _do_ know what is required, configuring as much of the
|
|
hierarchy ahead of time is a good idea, as that will help the users see what is coming up.</p>
|
|
<p>Every tracker has not only the steps given to it at construction time, but also the singleton
|
|
<code class="docutils literal"><span class="pre">ProgressTracker.UNSTARTED</span></code> step and the <code class="docutils literal"><span class="pre">ProgressTracker.DONE</span></code> step. Once a tracker has become <code class="docutils literal"><span class="pre">DONE</span></code> its
|
|
position may not be modified again (because e.g. the UI may have been removed/cleaned up), but until that point, the
|
|
position can be set to any arbitrary set both forwards and backwards. Steps may be skipped, repeated, etc. Note that
|
|
rolling the current step backwards will delete any progress trackers that are children of the steps being reversed, on
|
|
the assumption that those subtasks will have to be repeated.</p>
|
|
<p>Trackers provide an <a class="reference external" href="http://reactivex.io/">Rx observable</a> which streams changes to the hierarchy. The top level
|
|
observable exposes all the events generated by its children as well. The changes are represented by objects indicating
|
|
whether the change is one of position (i.e. progress), structure (i.e. new subtasks being added/removed) or some other
|
|
aspect of rendering (i.e. a step has changed in some way and is requesting a re-render).</p>
|
|
<p>The flow framework is somewhat integrated with this API. Each <code class="docutils literal"><span class="pre">FlowLogic</span></code> may optionally provide a tracker by
|
|
overriding the <code class="docutils literal"><span class="pre">flowTracker</span></code> property (<code class="docutils literal"><span class="pre">getFlowTracker</span></code> method in Java). If the
|
|
<code class="docutils literal"><span class="pre">FlowLogic.subFlow</span></code> method is used, then the tracker of the sub-flow will be made a child of the current
|
|
step in the parent flow automatically, if the parent is using tracking in the first place. The framework will also
|
|
automatically set the current step to <code class="docutils literal"><span class="pre">DONE</span></code> for you, when the flow is finished.</p>
|
|
<p>Because a flow may sometimes wish to configure the children in its progress hierarchy _before_ the sub-flow
|
|
is constructed, for sub-flows that always follow the same outline regardless of their parameters it’s conventional
|
|
to define a companion object/static method (for Kotlin/Java respectively) that constructs a tracker, and then allow
|
|
the sub-flow to have the tracker it will use be passed in as a parameter. This allows all trackers to be built
|
|
and linked ahead of time.</p>
|
|
<p>In future, the progress tracking framework will become a vital part of how exceptions, errors, and other faults are
|
|
surfaced to human operators for investigation and resolution.</p>
|
|
</div>
|
|
<div class="section" id="versioning">
|
|
<h2>Versioning<a class="headerlink" href="#versioning" title="Permalink to this headline">¶</a></h2>
|
|
<p>Fibers involve persisting object-serialised stack frames to disk. Although we may do some R&D into in-place upgrades
|
|
in future, for now the upgrade process for flows is simple: you duplicate the code and rename it so it has a
|
|
new set of class names. Old versions of the flow can then drain out of the system whilst new versions are
|
|
initiated. When enough time has passed that no old versions are still waiting for anything to happen, the previous
|
|
copy of the code can be deleted.</p>
|
|
<p>Whilst kind of ugly, this is a very simple approach that should suffice for now.</p>
|
|
<div class="admonition warning">
|
|
<p class="first admonition-title">Warning</p>
|
|
<p class="last">Flows are not meant to live for months or years, and by implication they are not meant to implement entire deal
|
|
lifecycles. For instance, implementing the entire life cycle of an interest rate swap as a single flow - whilst
|
|
technically possible - would not be a good idea. The platform provides a job scheduler tool that can invoke
|
|
flows for this reason (see “<a class="reference internal" href="event-scheduling.html"><span class="doc">Event scheduling</span></a>”)</p>
|
|
</div>
|
|
</div>
|
|
<div class="section" id="future-features">
|
|
<h2>Future features<a class="headerlink" href="#future-features" title="Permalink to this headline">¶</a></h2>
|
|
<p>The flow framework is a key part of the platform and will be extended in major ways in future. Here are some of
|
|
the features we have planned:</p>
|
|
<ul class="simple">
|
|
<li>Identity based addressing</li>
|
|
<li>Exception propagation and management, with a “flow hospital” tool to manually provide solutions to unavoidable
|
|
problems (e.g. the other side doesn’t know the trade)</li>
|
|
<li>Being able to interact with internal apps and tools via RPC</li>
|
|
<li>Being able to interact with people, either via some sort of external ticketing system, or email, or a custom UI.
|
|
For example to implement human transaction authorisations.</li>
|
|
<li>A standard library of flows that can be easily sub-classed by local developers in order to integrate internal
|
|
reporting logic, or anything else that might be required as part of a communications lifecycle.</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<footer>
|
|
|
|
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
|
|
|
<a href="flow-testing.html" class="btn btn-neutral float-right" title="Writing flow tests" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
|
|
|
|
|
|
<a href="tutorial-building-transactions.html" class="btn btn-neutral" title="Building transactions" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
|
|
|
</div>
|
|
|
|
|
|
<hr/>
|
|
|
|
<div role="contentinfo">
|
|
<p>
|
|
© Copyright 2016, R3 Limited.
|
|
|
|
</p>
|
|
</div>
|
|
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
|
|
|
</footer>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</section>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
var DOCUMENTATION_OPTIONS = {
|
|
URL_ROOT:'./',
|
|
VERSION:'latest',
|
|
COLLAPSE_INDEX:false,
|
|
FILE_SUFFIX:'.html',
|
|
HAS_SOURCE: true
|
|
};
|
|
</script>
|
|
<script type="text/javascript" src="_static/jquery.js"></script>
|
|
<script type="text/javascript" src="_static/underscore.js"></script>
|
|
<script type="text/javascript" src="_static/doctools.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript" src="_static/js/theme.js"></script>
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
jQuery(function () {
|
|
SphinxRtdTheme.StickyNav.enable();
|
|
});
|
|
</script>
|
|
|
|
|
|
</body>
|
|
</html> |