Merged in asset-trade-protocol (pull request #9)

Asset trade protocol
This commit is contained in:
Mike Hearn 2015-12-15 13:17:58 +01:00
commit 1bb729d329
33 changed files with 3110 additions and 136 deletions

View File

@ -24,6 +24,9 @@ buildscript {
repositories {
mavenCentral()
maven {
url 'http://oss.sonatype.org/content/repositories/snapshots'
}
jcenter()
}
@ -33,11 +36,29 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "com.google.guava:guava:18.0"
compile "com.esotericsoftware:kryo:3.0.3"
compile "de.javakaffee:kryo-serializers:0.37"
compile "com.google.code.findbugs:jsr305:3.0.1"
// Logging
compile "org.slf4j:slf4j-jdk14:1.7.13"
// For the continuations in the state machine tests. Note: JavaFlow is old and unmaintained but still seems to work
// just fine, once the patch here is applied to update it to a Java8 compatible asm:
//
// https://github.com/playframework/play1/commit/e0e28e6780a48c000e7ed536962f1f284cef9437
//
// Obviously using this year-old upload to Maven Central by the Maven Play Plugin team is a short term hack for
// experimenting. Using this for real would mean forking JavaFlow and taking over maintenance (luckily it's small
// and Java is stable, so this is unlikely to be a big burden). We have to manually force an in-place upgrade to
// asm 5.0.3 here (javaflow wants 5.0.2) in order to avoid version conflicts. This is also something that should be
// fixed in any fork. Sadly, even Jigsaw doesn't solve this problem out of the box.
compile "com.google.code.maven-play-plugin.org.apache.commons:commons-javaflow:1590792-patched-play-1.3.0"
compile "org.ow2.asm:asm:5.0.3"
compile "org.ow2.asm:asm-analysis:5.0.3"
compile "org.ow2.asm:asm-tree:5.0.3"
compile "org.ow2.asm:asm-commons:5.0.3"
compile "org.ow2.asm:asm-util:5.0.3"
// For visualisation
compile "org.graphstream:gs-core:1.3"
compile "org.graphstream:gs-ui:1.3"

View File

@ -26,6 +26,8 @@ Read on to learn:
overview
getting-set-up
tutorial
messaging
protocol-state-machines
visualiser
roadmap

97
docs/build/html/_sources/messaging.txt vendored Normal file
View File

@ -0,0 +1,97 @@
Networking and messaging
========================
Although the platform does not currently provide a network backend, some preliminary interfaces are defined along with
an in-memory implementation provided for use by unit tests and other exploratory code. An implementation based on Apache
Kafka is also being developed, which should be sufficient for real use cases to be implemented in the short run, even
though in the long run a fully peer to peer protocol will be required.
This article quickly explains the basic networking interfaces in the code.
Messaging vs networking
-----------------------
It is important to understand that the code expects any networking module to provide the following services:
- Persistent, reliable and secure delivery of complete messages. The module is expected to retry delivery if initial
attempts fail.
- Ability to send messages both 1:1 and 1:many, where 'many' may mean the entire group of network users.
The details of how this is achieved are not exposed to the rest of the code.
Interfaces
----------
The most important interface is called ``MessagingService`` and is defined in the ``core/messaging/Messaging.kt`` file.
It declares an interface with the following operations:
- ``addMessageHandler(topic: String, executor: Executor, callback: (Message, MessageHandlerRegistration) -> Unit)``
- ``createMessage(topic: String, data: ByteArray): Message``
- ``send(message: Message, targetRecipients: MessageRecipients)``
- ``stop()``
along with a few misc others that are not important enough to discuss here.
A *topic* is simply a string that identifies the kind of message that is being sent. When a message is received, the
topic is compared exactly to the list of registered message handlers and if it matches, the callback is invoked.
Adding a handler returns a ``MessageHandlerRegistration`` object that can be used to remove the handler, and that
registration object is also passed to each invocation to simplify the case where a handler wishes to remove itself.
Some helper functions are also provided that simplify the process of sending a message by using Kryo serialisation, and
registering one-shot handlers that remove themselves once they finished running, but those don't need to be implemented
by network module authors themselves.
Destinations are represented using opaque classes (i.e. their contents are defined by the implementation). The
``MessageRecipients`` interface represents any possible set of recipients: it's used when a piece of code doesn't
care who is going to get a message, just that someone does. The ``SingleMessageRecipient`` interface inherits from
``MessageRecipients`` and represents a handle to some specific individual receiver on the network. Whether they are
identified by IP address, public key, message router ID or some other kind of address is not exposed at this level.
``MessageRecipientGroup`` is not used anywhere at the moment but represents multiple simultaneous recipients. And
finally ``AllPossibleRecipients`` is used for network wide broadcast. It's also unused right now, outside of unit tests.
In memory implementation
------------------------
To ease unit testing of business logic, a simple in-memory messaging service is provided. To access this you can inherit
your test case class from the ``TestWithInMemoryNetwork`` class. This provides a few utility methods to help test
code that involves message passing.
You can run a mock network session in one of two modes:
- Manually "pumped"
- Automatically pumped with background threads
"Pumping" is the act of telling a mock network node to pop a message off its queue and process it. Typically you want
unit tests to be fast, repeatable and you want to be able to insert your own changes into the middle of any given
message sequence. This is what the manual mode is for. In this mode, all logic runs on the same thread (the thread
running the unit tests). You can create and use a node like this:
.. container:: codeset
.. sourcecode:: kotlin
val (aliceAddr, aliceNode) = makeNode(inBackground = false)
val (bobAddr, bobNode) = makeNode(false)
aliceNode.send("test.topic", aliceAddr, "foo")
bobNode.pump(blocking = false)
.. note:: Currently only Kotlin examples are available for networking and protocol state machines. Java examples may
follow later. Naming arguments in Kotlin like above is optional but sometimes useful to make code examples clearer.
The above code won't actually do anything because no message handler is registered for "test.topic" so the message will
go into a holding area. If/when we add a handler that can accept test.topic, the message will be delivered then.
Sometimes you don't want to have to call the pump method over and over again. You can use the ``runNetwork { .. }``
construct to fix this: any code inside the block will be run, and then all nodes you created will be pumped over and
over until all of them have reported that they have no work left to do. This means any ping-pongs of messages will
be run until everything settles.
You can see more examples of how to use this in the file ``InMemoryMessagingTests.kt``.
If you specify ``inBackground = true`` to ``makeNode`` then each node will create its own background thread which will
sit around waiting for messages to be delivered. Handlers will then be invoked on that background thread. This is a
more difficult style of programming that can be used to increase the realism of the unit tests by ensuring multiple
nodes run in parallel, just as they would on a real network spread over multiple machines.

View File

@ -0,0 +1,416 @@
.. highlight:: kotlin
.. raw:: html
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/codesets.js"></script>
Protocol state machines
=======================
This article explains our experimental approach to modelling financial protocols 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 protocol
which is included in the source.
Introduction
------------
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.
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
*delivery versus payment* 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 protocol
in which messages are passed back and forth privately between parties, checked, signed and so on.
Despite how useful these protocols 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:
* 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 protocol instantiation.
* Surviving node shutdowns/restarts that may occur in the middle of the protocol without complicating things. This
implies that the state of the protocol must be persisted to disk.
* Error handling.
* Message routing.
* Serialisation.
* 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.
* Unit testing of the finished protocol.
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.
To put these problems in perspective the *payment channel protocol* 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.
As small contract-specific trading protocols are a common occurence in finance, we provide a framework for the
construction of them that automatically handles many of the concerns outlined above.
Theory
------
A *continuation* 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 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 a library called
JavaFlow which works through behind-the-scenes bytecode rewriting. You don't have to know how this works to benefit
from it, however.
We use continuations for the following reasons:
* It allows us to write code that is free of callbacks, that looks like ordinary sequential code.
* 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 stack can easily be 1mb in size.
* It frees the developer from thinking (much) about persistence and serialisation.
A *state machine* is a piece of code that moves through various *states*. 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 protocol. 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.
A two party trading protocol
----------------------------
We would like to implement the "hello world" of shared transaction building protocols: a seller wishes to sell some
*asset* (e.g. some commercial paper) in return for *cash*. 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.
Our protocol has two parties (B and S for buyer and seller) and will proceed as follows:
1. S sends a ``StateAndRef`` pointing to the state they want to sell to B, along with info about the price they require
B to pay.
2. B sends to S a ``SignedWireTransaction`` 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.
3. S signs it and hands the now finalised ``SignedWireTransaction`` back to B.
You can find the implementation of this protocol in the file ``contracts/protocols/TwoPartyTradeProtocol.kt``.
Assuming no malicious termination, they both end the protocol being in posession of a valid, signed transaction that
represents an atomic asset swap.
Note that it's the *seller* who initiates contact with the buyer, not vice-versa as you might imagine.
We start by defining an abstract base class to encapsulate the protocol. This is what code that invokes the protocol
will see:
.. container:: codeset
.. sourcecode:: kotlin
abstract class TwoPartyTradeProtocol {
class SellerInitialArgs(
val assetToSell: StateAndRef<OwnableState>,
val price: Amount,
val myKeyPair: KeyPair,
val buyerSessionID: Long
)
abstract fun runSeller(otherSide: SingleMessageRecipient, args: SellerInitialArgs): Seller
class BuyerInitialArgs(
val acceptablePrice: Amount,
val typeToBuy: Class<out OwnableState>,
val sessionID: Long
)
abstract fun runBuyer(otherSide: SingleMessageRecipient, args: BuyerInitialArgs): Buyer
abstract class Buyer : ProtocolStateMachine<BuyerInitialArgs, Pair<TimestampedWireTransaction, LedgerTransaction>>()
abstract class Seller : ProtocolStateMachine<SellerInitialArgs, Pair<TimestampedWireTransaction, LedgerTransaction>>()
companion object {
@JvmStatic fun create(smm: StateMachineManager): TwoPartyTradeProtocol {
return TwoPartyTradeProtocolImpl(smm)
}
}
}
Let's unpack what this code does:
- It defines a several classes nested inside the main ``TwoPartyTradeProtocol`` class, and a couple of methods, one to
run the buyer side of the protocol and one to run the seller side.
- Two of the classes are simply wrappers for parameters to the trade; things like what is being sold, what the price
of the asset is, how much the buyer is willing to pay and so on. The ``myKeyPair`` field is simply the public key
that the seller wishes the buyer to send the cash to. The session ID field is sent from buyer to seller when the
trade is being set up and is used to keep messages separated on the network, and stop malicious entities trying to
interfere with the message stream.
- The other two classes define empty abstract classes called ``Buyer`` and ``Seller``. These inherit from a class
called ``ProtocolStateMachine`` and provide two type parameters: the arguments class we just defined for each side
and the type of the object that the protocol finally produces (this doesn't have to be identical for each side, even
though in this case it is).
- Finally it simply defines a static method that creates an instance of an object that inherits from this base class
and returns it, with a ``StateMachineManager`` as an instance. The Impl class will be defined below.
.. note:: Session IDs keep different traffic streams separated, so for security they must be large and random enough
to be unguessable. 63 bits is good enough.
Alright, so using this protocol shouldn't be too hard: in the simplest case we can just pass in the details of the trade
to either runBuyer or runSeller, depending on who we are, and then call ``.get()`` on the resulting future to block the
calling thread until the protocol has finished. Or we could register a callback on the returned future that will be
invoked when it's done, where we could e.g. update a user interface.
The only tricky part is how to get one of these things. We need a ``StateMachineManager``. Where does that come from
and why do we need one?
The state machine manager
-------------------------
The SMM is a class responsible for taking care of all running protocols in a node. It knows how to register handlers
with a ``MessagingService`` and iterate the right state machine when the time comes. It provides the
send/receive/sendAndReceive calls that let the code request network interaction and it will store a serialised copy of
each state machine before it's suspended to wait for the network.
To get a ``StateMachineManager``, you currently have to build one by passing in a ``ServiceHub`` and a thread or thread
pool which it can use. This will change in future so don't worry about the details of this too much: just check the
unit tests to see how it's done.
Implementing the seller
-----------------------
.. container:: codeset
.. sourcecode:: kotlin
private class TwoPartyTradeProtocolImpl(private val smm: StateMachineManager) : TwoPartyTradeProtocol() {
companion object {
val TRADE_TOPIC = "com.r3cev.protocols.trade"
}
class SellerImpl : Seller() {
override fun call(args: SellerInitialArgs): Pair<TimestampedWireTransaction, LedgerTransaction> {
TODO()
}
}
class BuyerImpl : Buyer() {
override fun call(args: BuyerInitialArgs): Pair<TimestampedWireTransaction, LedgerTransaction> {
TODO()
}
}
override fun runSeller(otherSide: SingleMessageRecipient, args: SellerInitialArgs): Seller {
return smm.add(otherSide, args, "$TRADE_TOPIC.seller", SellerImpl::class.java)
}
override fun runBuyer(otherSide: SingleMessageRecipient, args: BuyerInitialArgs): Buyer {
return smm.add(otherSide, args, "$TRADE_TOPIC.buyer", BuyerImpl::class.java)
}
}
We start with a skeleton on which we will build the protocol. Putting things in a *companion object* in Kotlin is like
declaring them as static members in Java. Here, we define a "topic" that will identify trade related messages that
arrive at a node (see :doc:`messaging` for details).
The runSeller and runBuyer methods simply start the state machines, passing in a reference to the classes and the topics
each side will use.
Now let's try implementing the seller side. Firstly, we're going to need a message to send to the buyer describing what
we want to trade. Remember: this data comes from whatever system was used to find the trading partner to begin with.
It could be as simple as a chat room or as complex as a 24/7 exchange.
.. container:: codeset
.. sourcecode:: kotlin
// This object is serialised to the network and is the first protocol message the seller sends to the buyer.
class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>,
val price: Amount,
val sellerOwnerKey: PublicKey,
val buyerSessionID: Long
)
That's simple enough: our opening protocol message will be serialised before being sent over the wire, and it contains
the details that were agreed so we can double check them. It also contains a session ID so we can identify this
trade's messages, and a pointer to where the asset that is being sold can be found on the ledger.
Next we add some code to the ``SellerImpl.call`` method:
.. container:: codeset
.. sourcecode:: kotlin
val sessionID = random63BitValue()
// Make the first message we'll send to kick off the protocol.
val hello = SellerTradeInfo(args.assetToSell, args.price, args.myKeyPair.public, sessionID)
// Zero is a special session ID that is being listened to by the buyer (i.e. before a session is started).
val partialTX = sendAndReceive<SignedWireTransaction>(TRADE_TOPIC, args.buyerSessionID, sessionID, hello)
logger().trace { "Received partially signed transaction" }
That's pretty straight forward. We generate a session ID to identify what's happening on the seller side, fill out
the initial protocol message, and then call ``sendAndReceive``. This function takes a few arguments:
- A type argument, which is the object we're expecting to receive from the other side.
- The topic string that ensures the message is routed to the right bit of code in the other side's node.
- The session IDs that ensure the messages don't get mixed up with other simultaneous trades.
- And finally, the thing to send. It'll be serialised and sent automatically.
Once sendAndReceive is called, the call method will be suspended into a continuation. When it gets back we'll do a log
message. The buyer is supposed to send us a transaction with all the right inputs/outputs/commands in return, with their
cash put into the transaction and their signature on it authorising the movement of the cash.
.. note:: There are a few 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.
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.
The third rule to bear in mind is that you can't declare variables or methods in these classes and access
them from outside of the class, due to the bytecode rewriting and classloader tricks that are used to make this all
work. If you want access to something inside the BuyerImpl or SellerImpl classes, you must define a super-interface
or super-class (like ``Buyer``/``Seller``) and put what you want to access there.
OK, let's keep going:
.. container:: codeset
.. sourcecode:: kotlin
partialTX.verifySignatures()
val wtx = partialTX.txBits.deserialize<WireTransaction>()
requireThat {
"transaction sends us the right amount of cash" by (wtx.outputStates.sumCashBy(args.myKeyPair.public) == args.price)
// There are all sorts of funny games a malicious secondary might play here, we should fix them:
//
// - This tx may attempt to send some assets we aren't intending to sell to the secondary, if
// we're reusing keys! So don't reuse keys!
// - This tx may not be valid according to the contracts of the input states, so we must resolve
// and fully audit the transaction chains to convince ourselves that it is actually valid.
// - This tx may include output states that impose odd conditions on the movement of the cash,
// once we implement state pairing.
//
// but the goal of this code is not to be fully secure, but rather, just to find good ways to
// express protocol state machines on top of the messaging layer.
}
val ourSignature = args.myKeyPair.signWithECDSA(partialTX.txBits.bits)
val fullySigned: SignedWireTransaction = partialTX.copy(sigs = partialTX.sigs + ourSignature)
// We should run it through our full TransactionGroup of all transactions here.
fullySigned.verify()
val timestamped: TimestampedWireTransaction = fullySigned.toTimestampedTransaction(serviceHub.timestampingService)
logger().trace { "Built finished transaction, sending back to secondary!" }
send(TRADE_TOPIC, sessionID, timestamped)
return Pair(timestamped, timestamped.verifyToLedgerTransaction(serviceHub.timestampingService, serviceHub.identityService))
Here, we see some assertions and signature checking to satisfy ourselves that we're not about to sign something
incorrect. Once we're happy, we calculate a signature over the transaction to authorise the movement of the asset
we are selling, and then we verify things to make sure it's all OK. Finally, we request timestamping of the
transaction, and send the now finalised and validated transaction back to the buyer.
.. warning:: This code is **not secure**. 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.
Finally, the call function returns with the result of the protocol: in our case, the final transaction in two different
forms.
Implementing the buyer
----------------------
OK, let's do the same for the buyer side:
.. container:: codeset
.. sourcecode:: kotlin
class BuyerImpl : Buyer() {
override fun call(args: BuyerInitialArgs): Pair<TimestampedWireTransaction, LedgerTransaction> {
// Wait for a trade request to come in on our pre-provided session ID.
val tradeRequest = receive<SellerTradeInfo>(TRADE_TOPIC, args.sessionID)
// What is the seller trying to sell us?
val assetTypeName = tradeRequest.assetForSale.state.javaClass.name
logger().trace { "Got trade request for a $assetTypeName" }
// Check the start message for acceptability.
check(tradeRequest.sessionID > 0)
if (tradeRequest.price > args.acceptablePrice)
throw UnacceptablePriceException(tradeRequest.price)
if (!args.typeToBuy.isInstance(tradeRequest.assetForSale.state))
throw AssetMismatchException(args.typeToBuy.name, assetTypeName)
// TODO: Either look up the stateref here in our local db, or accept a long chain of states and
// validate them to audit the other side and ensure it actually owns the state we are being offered!
// For now, just assume validity!
// Generate the shared transaction that both sides will sign, using the data we have.
val ptx = PartialTransaction()
// Add input and output states for the movement of cash, by using the Cash contract to generate the states.
val wallet = serviceHub.walletService.currentWallet
val cashStates = wallet.statesOfType<Cash.State>()
val cashSigningPubKeys = Cash().craftSpend(ptx, tradeRequest.price, tradeRequest.sellerOwnerKey, cashStates)
// Add inputs/outputs/a command for the movement of the asset.
ptx.addInputState(tradeRequest.assetForSale.ref)
// Just pick some new public key for now.
val freshKey = serviceHub.keyManagementService.freshKey()
val (command, state) = tradeRequest.assetForSale.state.withNewOwner(freshKey.public)
ptx.addOutputState(state)
ptx.addArg(WireCommand(command, tradeRequest.assetForSale.state.owner))
// Now sign the transaction with whatever keys we need to move the cash.
for (k in cashSigningPubKeys) {
val priv = serviceHub.keyManagementService.toPrivate(k)
ptx.signWith(KeyPair(k, priv))
}
val stx = ptx.toSignedTransaction(checkSufficientSignatures = false)
stx.verifySignatures() // Verifies that we generated a signed transaction correctly.
// TODO: Could run verify() here to make sure the only signature missing is the sellers.
logger().trace { "Sending partially signed transaction to seller" }
// TODO: Protect against the buyer terminating here and leaving us in the lurch without the final tx.
// TODO: Protect against a malicious buyer sending us back a different transaction to the one we built.
val fullySigned = sendAndReceive<TimestampedWireTransaction>(TRADE_TOPIC,
tradeRequest.sessionID, args.sessionID, stx)
logger().trace { "Got fully signed transaction, verifying ... "}
val ltx = fullySigned.verifyToLedgerTransaction(serviceHub.timestampingService, serviceHub.identityService)
logger().trace { "Fully signed transaction was valid. Trade complete! :-)" }
return Pair(fullySigned, ltx)
}
}
This code is fairly straightforward. Here are some things to pay attention to:
1. We do some sanity checking on the received message to ensure we're being offered what we expected to be offered.
2. We create a cash spend in the normal way, by using ``Cash().craftSpend``.
3. We access the *service hub* when we need it to access things that are transient and may change or be recreated
whilst a protocol is suspended, things like the wallet or the timestamping service. Remember that a protocol may
be suspended when it waits to receive a message across node or computer restarts, so objects representing a service
or data which may frequently change should be accessed 'just in time'.
4. Finally, we send the unfinsished, invalid transaction to the seller so they can sign it. They are expected to send
back to us a ``TimestampedWireTransaction``, which once we verify it, should be the final outcome of the trade.
As you can see, the protocol 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.
.. warning:: When accessing things via the ``serviceHub`` field, avoid the temptation to stuff a reference into a local variable.
If you do this then next time your protocol waits to receive an object, the system will try and serialise all your
local variables and end up trying to serialise, e.g. the timestamping service, which doesn't make any conceptual
sense. The ``serviceHub`` field is defined by the ``ProtocolStateMachine`` superclass and is marked transient so
this problem doesn't occur. It's also restored for you after a protocol state machine is restored after a node
restart.

View File

@ -85,6 +85,8 @@
<li class="toctree-l1"><a class="reference internal" href="overview.html">Overview</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="tutorial.html">Tutorial</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="protocol-state-machines.html">Protocol state machines</a></li>
<li class="toctree-l1"><a class="reference internal" href="visualiser.html">Using the visualiser</a></li>
<li class="toctree-l1"><a class="reference internal" href="roadmap.html">Roadmap</a></li>
</ul>

View File

@ -90,6 +90,8 @@
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="tutorial.html">Tutorial</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="protocol-state-machines.html">Protocol state machines</a></li>
<li class="toctree-l1"><a class="reference internal" href="visualiser.html">Using the visualiser</a></li>
<li class="toctree-l1"><a class="reference internal" href="roadmap.html">Roadmap</a></li>
</ul>

View File

@ -85,6 +85,8 @@
<li class="toctree-l1"><a class="reference internal" href="overview.html">Overview</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="tutorial.html">Tutorial</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="protocol-state-machines.html">Protocol state machines</a></li>
<li class="toctree-l1"><a class="reference internal" href="visualiser.html">Using the visualiser</a></li>
<li class="toctree-l1"><a class="reference internal" href="roadmap.html">Roadmap</a></li>
</ul>
@ -172,6 +174,21 @@ prove or disprove the following hypothesis:</p>
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#non-asset-oriented-based-smart-contracts">Non-asset-oriented based smart contracts</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="messaging.html">Networking and messaging</a><ul>
<li class="toctree-l2"><a class="reference internal" href="messaging.html#messaging-vs-networking">Messaging vs networking</a></li>
<li class="toctree-l2"><a class="reference internal" href="messaging.html#interfaces">Interfaces</a></li>
<li class="toctree-l2"><a class="reference internal" href="messaging.html#in-memory-implementation">In memory implementation</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="protocol-state-machines.html">Protocol state machines</a><ul>
<li class="toctree-l2"><a class="reference internal" href="protocol-state-machines.html#introduction">Introduction</a></li>
<li class="toctree-l2"><a class="reference internal" href="protocol-state-machines.html#theory">Theory</a></li>
<li class="toctree-l2"><a class="reference internal" href="protocol-state-machines.html#a-two-party-trading-protocol">A two party trading protocol</a></li>
<li class="toctree-l2"><a class="reference internal" href="protocol-state-machines.html#the-state-machine-manager">The state machine manager</a></li>
<li class="toctree-l2"><a class="reference internal" href="protocol-state-machines.html#implementing-the-seller">Implementing the seller</a></li>
<li class="toctree-l2"><a class="reference internal" href="protocol-state-machines.html#implementing-the-buyer">Implementing the buyer</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="visualiser.html">Using the visualiser</a></li>
<li class="toctree-l1"><a class="reference internal" href="roadmap.html">Roadmap</a></li>
</ul>

View File

@ -86,6 +86,8 @@
<li class="toctree-l1"><a class="reference internal" href="overview.html">Overview</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="tutorial.html">Tutorial</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="protocol-state-machines.html">Protocol state machines</a></li>
<li class="toctree-l1"><a class="reference internal" href="visualiser.html">Using the visualiser</a></li>
<li class="toctree-l1"><a class="reference internal" href="roadmap.html">Roadmap</a></li>
</ul>

295
docs/build/html/messaging.html vendored Normal file
View File

@ -0,0 +1,295 @@
<!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>Networking and messaging &mdash; R3 Prototyping 0.1 documentation</title>
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
<link rel="next" title="Protocol state machines" href="protocol-state-machines.html"/>
<link rel="prev" title="Tutorial" href="tutorial.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 Prototyping
</a>
<div class="version">
0.1
</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>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What&#8217;s included?</a></li>
<li class="toctree-l1"><a class="reference internal" href="overview.html">Overview</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="tutorial.html">Tutorial</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="">Networking and messaging</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#messaging-vs-networking">Messaging vs networking</a></li>
<li class="toctree-l2"><a class="reference internal" href="#interfaces">Interfaces</a></li>
<li class="toctree-l2"><a class="reference internal" href="#in-memory-implementation">In memory implementation</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="protocol-state-machines.html">Protocol state machines</a></li>
<li class="toctree-l1"><a class="reference internal" href="visualiser.html">Using the visualiser</a></li>
<li class="toctree-l1"><a class="reference internal" href="roadmap.html">Roadmap</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 Prototyping</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> &raquo;</li>
<li>Networking and messaging</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/messaging.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">
<div class="section" id="networking-and-messaging">
<h1>Networking and messaging<a class="headerlink" href="#networking-and-messaging" title="Permalink to this headline"></a></h1>
<p>Although the platform does not currently provide a network backend, some preliminary interfaces are defined along with
an in-memory implementation provided for use by unit tests and other exploratory code. An implementation based on Apache
Kafka is also being developed, which should be sufficient for real use cases to be implemented in the short run, even
though in the long run a fully peer to peer protocol will be required.</p>
<p>This article quickly explains the basic networking interfaces in the code.</p>
<div class="section" id="messaging-vs-networking">
<h2>Messaging vs networking<a class="headerlink" href="#messaging-vs-networking" title="Permalink to this headline"></a></h2>
<p>It is important to understand that the code expects any networking module to provide the following services:</p>
<ul class="simple">
<li>Persistent, reliable and secure delivery of complete messages. The module is expected to retry delivery if initial
attempts fail.</li>
<li>Ability to send messages both 1:1 and 1:many, where &#8216;many&#8217; may mean the entire group of network users.</li>
</ul>
<p>The details of how this is achieved are not exposed to the rest of the code.</p>
</div>
<div class="section" id="interfaces">
<h2>Interfaces<a class="headerlink" href="#interfaces" title="Permalink to this headline"></a></h2>
<p>The most important interface is called <code class="docutils literal"><span class="pre">MessagingService</span></code> and is defined in the <code class="docutils literal"><span class="pre">core/messaging/Messaging.kt</span></code> file.
It declares an interface with the following operations:</p>
<ul class="simple">
<li><code class="docutils literal"><span class="pre">addMessageHandler(topic:</span> <span class="pre">String,</span> <span class="pre">executor:</span> <span class="pre">Executor,</span> <span class="pre">callback:</span> <span class="pre">(Message,</span> <span class="pre">MessageHandlerRegistration)</span> <span class="pre">-&gt;</span> <span class="pre">Unit)</span></code></li>
<li><code class="docutils literal"><span class="pre">createMessage(topic:</span> <span class="pre">String,</span> <span class="pre">data:</span> <span class="pre">ByteArray):</span> <span class="pre">Message</span></code></li>
<li><code class="docutils literal"><span class="pre">send(message:</span> <span class="pre">Message,</span> <span class="pre">targetRecipients:</span> <span class="pre">MessageRecipients)</span></code></li>
<li><code class="docutils literal"><span class="pre">stop()</span></code></li>
</ul>
<p>along with a few misc others that are not important enough to discuss here.</p>
<p>A <em>topic</em> is simply a string that identifies the kind of message that is being sent. When a message is received, the
topic is compared exactly to the list of registered message handlers and if it matches, the callback is invoked.
Adding a handler returns a <code class="docutils literal"><span class="pre">MessageHandlerRegistration</span></code> object that can be used to remove the handler, and that
registration object is also passed to each invocation to simplify the case where a handler wishes to remove itself.</p>
<p>Some helper functions are also provided that simplify the process of sending a message by using Kryo serialisation, and
registering one-shot handlers that remove themselves once they finished running, but those don&#8217;t need to be implemented
by network module authors themselves.</p>
<p>Destinations are represented using opaque classes (i.e. their contents are defined by the implementation). The
<code class="docutils literal"><span class="pre">MessageRecipients</span></code> interface represents any possible set of recipients: it&#8217;s used when a piece of code doesn&#8217;t
care who is going to get a message, just that someone does. The <code class="docutils literal"><span class="pre">SingleMessageRecipient</span></code> interface inherits from
<code class="docutils literal"><span class="pre">MessageRecipients</span></code> and represents a handle to some specific individual receiver on the network. Whether they are
identified by IP address, public key, message router ID or some other kind of address is not exposed at this level.
<code class="docutils literal"><span class="pre">MessageRecipientGroup</span></code> is not used anywhere at the moment but represents multiple simultaneous recipients. And
finally <code class="docutils literal"><span class="pre">AllPossibleRecipients</span></code> is used for network wide broadcast. It&#8217;s also unused right now, outside of unit tests.</p>
</div>
<div class="section" id="in-memory-implementation">
<h2>In memory implementation<a class="headerlink" href="#in-memory-implementation" title="Permalink to this headline"></a></h2>
<p>To ease unit testing of business logic, a simple in-memory messaging service is provided. To access this you can inherit
your test case class from the <code class="docutils literal"><span class="pre">TestWithInMemoryNetwork</span></code> class. This provides a few utility methods to help test
code that involves message passing.</p>
<p>You can run a mock network session in one of two modes:</p>
<ul class="simple">
<li>Manually &#8220;pumped&#8221;</li>
<li>Automatically pumped with background threads</li>
</ul>
<p>&#8220;Pumping&#8221; is the act of telling a mock network node to pop a message off its queue and process it. Typically you want
unit tests to be fast, repeatable and you want to be able to insert your own changes into the middle of any given
message sequence. This is what the manual mode is for. In this mode, all logic runs on the same thread (the thread
running the unit tests). You can create and use a node like this:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre>val (aliceAddr, aliceNode) = makeNode(inBackground = false)
val (bobAddr, bobNode) = makeNode(false)
aliceNode.send(&quot;test.topic&quot;, aliceAddr, &quot;foo&quot;)
bobNode.pump(blocking = false)
</pre></div>
</div>
</div>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">Currently only Kotlin examples are available for networking and protocol state machines. Java examples may
follow later. Naming arguments in Kotlin like above is optional but sometimes useful to make code examples clearer.</p>
</div>
<p>The above code won&#8217;t actually do anything because no message handler is registered for &#8220;test.topic&#8221; so the message will
go into a holding area. If/when we add a handler that can accept test.topic, the message will be delivered then.</p>
<p>Sometimes you don&#8217;t want to have to call the pump method over and over again. You can use the <code class="docutils literal"><span class="pre">runNetwork</span> <span class="pre">{</span> <span class="pre">..</span> <span class="pre">}</span></code>
construct to fix this: any code inside the block will be run, and then all nodes you created will be pumped over and
over until all of them have reported that they have no work left to do. This means any ping-pongs of messages will
be run until everything settles.</p>
<p>You can see more examples of how to use this in the file <code class="docutils literal"><span class="pre">InMemoryMessagingTests.kt</span></code>.</p>
<p>If you specify <code class="docutils literal"><span class="pre">inBackground</span> <span class="pre">=</span> <span class="pre">true</span></code> to <code class="docutils literal"><span class="pre">makeNode</span></code> then each node will create its own background thread which will
sit around waiting for messages to be delivered. Handlers will then be invoked on that background thread. This is a
more difficult style of programming that can be used to increase the realism of the unit tests by ensuring multiple
nodes run in parallel, just as they would on a real network spread over multiple machines.</p>
</div>
</div>
</div>
</div>
<footer>
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="protocol-state-machines.html" class="btn btn-neutral float-right" title="Protocol state machines" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="tutorial.html" class="btn btn-neutral" title="Tutorial" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
<hr/>
<div role="contentinfo">
<p>
&copy; Copyright 2015, R3 CEV.
</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:'0.1',
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>

View File

@ -0,0 +1,597 @@
<!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>Protocol state machines &mdash; R3 Prototyping 0.1 documentation</title>
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
<link rel="next" title="Using the visualiser" href="visualiser.html"/>
<link rel="prev" title="Networking and messaging" href="messaging.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 Prototyping
</a>
<div class="version">
0.1
</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>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="inthebox.html">What&#8217;s included?</a></li>
<li class="toctree-l1"><a class="reference internal" href="overview.html">Overview</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="tutorial.html">Tutorial</a></li>
<li class="toctree-l1"><a class="reference internal" href="messaging.html">Networking and messaging</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="">Protocol state machines</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-protocol">A two party trading protocol</a></li>
<li class="toctree-l2"><a class="reference internal" href="#the-state-machine-manager">The state machine manager</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="#implementing-the-buyer">Implementing the buyer</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="visualiser.html">Using the visualiser</a></li>
<li class="toctree-l1"><a class="reference internal" href="roadmap.html">Roadmap</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 Prototyping</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> &raquo;</li>
<li>Protocol state machines</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/protocol-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="protocol-state-machines">
<h1>Protocol state machines<a class="headerlink" href="#protocol-state-machines" title="Permalink to this headline"></a></h1>
<p>This article explains our experimental approach to modelling financial protocols in code. It explains how the
platform&#8217;s state machine framework is used, and takes you through the code for a simple 2-party asset trading protocol
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 protocol
in which messages are passed back and forth privately between parties, checked, signed and so on.</p>
<p>Despite how useful these protocols 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 &#8220;callback hell&#8221; 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 protocol instantiation.</li>
<li>Surviving node shutdowns/restarts that may occur in the middle of the protocol without complicating things. This
implies that the state of the protocol 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 protocol.</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&#8217;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 protocols are a common occurence 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 may sound abstract but don&#8217;t worry, the examples below
will make it clearer. The JVM does not natively support continuations, so we implement them using a a library called
JavaFlow which works through behind-the-scenes bytecode rewriting. You don&#8217;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 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 protocol. Typically writing a state machine would require the use of a big switch statement and some
explicit variables to keep track of where you&#8217;re up to. The use of continuations avoids this hassle.</p>
</div>
<div class="section" id="a-two-party-trading-protocol">
<h2>A two party trading protocol<a class="headerlink" href="#a-two-party-trading-protocol" title="Permalink to this headline"></a></h2>
<p>We would like to implement the &#8220;hello world&#8221; of shared transaction building protocols: 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&#8217;t covered in this article.</p>
<p>Our protocol 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">SignedWireTransaction</span></code> that includes the state as input, B&#8217;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&#8217;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">SignedWireTransaction</span></code> back to B.</li>
</ol>
<p>You can find the implementation of this protocol in the file <code class="docutils literal"><span class="pre">contracts/protocols/TwoPartyTradeProtocol.kt</span></code>.</p>
<p>Assuming no malicious termination, they both end the protocol being in posession of a valid, signed transaction that
represents an atomic asset swap.</p>
<p>Note that it&#8217;s the <em>seller</em> who initiates contact with the buyer, not vice-versa as you might imagine.</p>
<p>We start by defining an abstract base class to encapsulate the protocol. This is what code that invokes the protocol
will see:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre>abstract class TwoPartyTradeProtocol {
class SellerInitialArgs(
val assetToSell: StateAndRef&lt;OwnableState&gt;,
val price: Amount,
val myKeyPair: KeyPair,
val buyerSessionID: Long
)
abstract fun runSeller(otherSide: SingleMessageRecipient, args: SellerInitialArgs): Seller
class BuyerInitialArgs(
val acceptablePrice: Amount,
val typeToBuy: Class&lt;out OwnableState&gt;,
val sessionID: Long
)
abstract fun runBuyer(otherSide: SingleMessageRecipient, args: BuyerInitialArgs): Buyer
abstract class Buyer : ProtocolStateMachine&lt;BuyerInitialArgs, Pair&lt;TimestampedWireTransaction, LedgerTransaction&gt;&gt;()
abstract class Seller : ProtocolStateMachine&lt;SellerInitialArgs, Pair&lt;TimestampedWireTransaction, LedgerTransaction&gt;&gt;()
companion object {
@JvmStatic fun create(smm: StateMachineManager): TwoPartyTradeProtocol {
return TwoPartyTradeProtocolImpl(smm)
}
}
}
</pre></div>
</div>
</div>
<p>Let&#8217;s unpack what this code does:</p>
<ul class="simple">
<li>It defines a several classes nested inside the main <code class="docutils literal"><span class="pre">TwoPartyTradeProtocol</span></code> class, and a couple of methods, one to
run the buyer side of the protocol and one to run the seller side.</li>
<li>Two of the classes are simply wrappers for parameters to the trade; things like what is being sold, what the price
of the asset is, how much the buyer is willing to pay and so on. The <code class="docutils literal"><span class="pre">myKeyPair</span></code> field is simply the public key
that the seller wishes the buyer to send the cash to. The session ID field is sent from buyer to seller when the
trade is being set up and is used to keep messages separated on the network, and stop malicious entities trying to
interfere with the message stream.</li>
<li>The other two classes define empty abstract classes called <code class="docutils literal"><span class="pre">Buyer</span></code> and <code class="docutils literal"><span class="pre">Seller</span></code>. These inherit from a class
called <code class="docutils literal"><span class="pre">ProtocolStateMachine</span></code> and provide two type parameters: the arguments class we just defined for each side
and the type of the object that the protocol finally produces (this doesn&#8217;t have to be identical for each side, even
though in this case it is).</li>
<li>Finally it simply defines a static method that creates an instance of an object that inherits from this base class
and returns it, with a <code class="docutils literal"><span class="pre">StateMachineManager</span></code> as an instance. The Impl class will be defined below.</li>
</ul>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p class="last">Session IDs keep different traffic streams separated, so for security they must be large and random enough</p>
</div>
<p>to be unguessable. 63 bits is good enough.</p>
<p>Alright, so using this protocol shouldn&#8217;t be too hard: in the simplest case we can just pass in the details of the trade
to either runBuyer or runSeller, depending on who we are, and then call <code class="docutils literal"><span class="pre">.get()</span></code> on the resulting future to block the
calling thread until the protocol has finished. Or we could register a callback on the returned future that will be
invoked when it&#8217;s done, where we could e.g. update a user interface.</p>
<p>The only tricky part is how to get one of these things. We need a <code class="docutils literal"><span class="pre">StateMachineManager</span></code>. Where does that come from
and why do we need one?</p>
</div>
<div class="section" id="the-state-machine-manager">
<h2>The state machine manager<a class="headerlink" href="#the-state-machine-manager" title="Permalink to this headline"></a></h2>
<p>The SMM is a class responsible for taking care of all running protocols in a node. It knows how to register handlers
with a <code class="docutils literal"><span class="pre">MessagingService</span></code> and iterate the right state machine when the time comes. It provides the
send/receive/sendAndReceive calls that let the code request network interaction and it will store a serialised copy of
each state machine before it&#8217;s suspended to wait for the network.</p>
<p>To get a <code class="docutils literal"><span class="pre">StateMachineManager</span></code>, you currently have to build one by passing in a <code class="docutils literal"><span class="pre">ServiceHub</span></code> and a thread or thread
pool which it can use. This will change in future so don&#8217;t worry about the details of this too much: just check the
unit tests to see how it&#8217;s done.</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>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre>private class TwoPartyTradeProtocolImpl(private val smm: StateMachineManager) : TwoPartyTradeProtocol() {
companion object {
val TRADE_TOPIC = &quot;com.r3cev.protocols.trade&quot;
}
class SellerImpl : Seller() {
override fun call(args: SellerInitialArgs): Pair&lt;TimestampedWireTransaction, LedgerTransaction&gt; {
TODO()
}
}
class BuyerImpl : Buyer() {
override fun call(args: BuyerInitialArgs): Pair&lt;TimestampedWireTransaction, LedgerTransaction&gt; {
TODO()
}
}
override fun runSeller(otherSide: SingleMessageRecipient, args: SellerInitialArgs): Seller {
return smm.add(otherSide, args, &quot;$TRADE_TOPIC.seller&quot;, SellerImpl::class.java)
}
override fun runBuyer(otherSide: SingleMessageRecipient, args: BuyerInitialArgs): Buyer {
return smm.add(otherSide, args, &quot;$TRADE_TOPIC.buyer&quot;, BuyerImpl::class.java)
}
}
</pre></div>
</div>
</div>
<p>We start with a skeleton on which we will build the protocol. Putting things in a <em>companion object</em> in Kotlin is like
declaring them as static members in Java. Here, we define a &#8220;topic&#8221; that will identify trade related messages that
arrive at a node (see <a class="reference internal" href="messaging.html"><em>Networking and messaging</em></a> for details).</p>
<p>The runSeller and runBuyer methods simply start the state machines, passing in a reference to the classes and the topics
each side will use.</p>
<p>Now let&#8217;s try implementing the seller side. Firstly, we&#8217;re going to need a message to send to the buyer describing what
we want to trade. Remember: this data comes from whatever system was used to find the trading partner to begin with.
It could be as simple as a chat room or as complex as a 24/7 exchange.</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre><span class="c1">// This object is serialised to the network and is the first protocol message the seller sends to the buyer.</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">&lt;</span><span class="n">OwnableState</span><span class="p">&gt;,</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="k">val</span> <span class="py">sellerOwnerKey</span><span class="p">:</span> <span class="n">PublicKey</span><span class="p">,</span>
<span class="k">val</span> <span class="py">buyerSessionID</span><span class="p">:</span> <span class="n">Long</span>
<span class="p">)</span>
</pre></div>
</div>
</div>
<p>That&#8217;s simple enough: our opening protocol message will be serialised before being sent over the wire, and it contains
the details that were agreed so we can double check them. It also contains a session ID so we can identify this
trade&#8217;s messages, and a pointer to where the asset that is being sold can be found on the ledger.</p>
<p>Next we add some code to the <code class="docutils literal"><span class="pre">SellerImpl.call</span></code> method:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">val</span> <span class="py">sessionID</span> <span class="p">=</span> <span class="n">random63BitValue</span><span class="p">()</span>
<span class="c1">// Make the first message we&#39;ll send to kick off the protocol.</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">args</span><span class="p">.</span><span class="n">assetToSell</span><span class="p">,</span> <span class="n">args</span><span class="p">.</span><span class="n">price</span><span class="p">,</span> <span class="n">args</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">sessionID</span><span class="p">)</span>
<span class="c1">// Zero is a special session ID that is being listened to by the buyer (i.e. before a session is started).</span>
<span class="k">val</span> <span class="py">partialTX</span> <span class="p">=</span> <span class="n">sendAndReceive</span><span class="p">&lt;</span><span class="n">SignedWireTransaction</span><span class="p">&gt;(</span><span class="n">TRADE_TOPIC</span><span class="p">,</span> <span class="n">args</span><span class="p">.</span><span class="n">buyerSessionID</span><span class="p">,</span> <span class="n">sessionID</span><span class="p">,</span> <span class="n">hello</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">&quot;Received partially signed transaction&quot;</span> <span class="p">}</span>
</pre></div>
</div>
</div>
<p>That&#8217;s pretty straight forward. We generate a session ID to identify what&#8217;s happening on the seller side, fill out
the initial protocol message, and then call <code class="docutils literal"><span class="pre">sendAndReceive</span></code>. This function takes a few arguments:</p>
<ul class="simple">
<li>A type argument, which is the object we&#8217;re expecting to receive from the other side.</li>
<li>The topic string that ensures the message is routed to the right bit of code in the other side&#8217;s node.</li>
<li>The session IDs that ensure the messages don&#8217;t get mixed up with other simultaneous trades.</li>
<li>And finally, the thing to send. It&#8217;ll be serialised and sent automatically.</li>
</ul>
<p>Once sendAndReceive is called, the call method will be suspended into a continuation. When it gets back we&#8217;ll do a log
message. The buyer is supposed to send us a transaction with all the right inputs/outputs/commands in return, with their
cash put into the transaction and their signature on it authorising the movement of the cash.</p>
<div class="admonition note">
<p class="first admonition-title">Note</p>
<p>There are a few 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.</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&#8217;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.</p>
<p class="last">The third rule to bear in mind is that you can&#8217;t declare variables or methods in these classes and access
them from outside of the class, due to the bytecode rewriting and classloader tricks that are used to make this all
work. If you want access to something inside the BuyerImpl or SellerImpl classes, you must define a super-interface
or super-class (like <code class="docutils literal"><span class="pre">Buyer</span></code>/<code class="docutils literal"><span class="pre">Seller</span></code>) and put what you want to access there.</p>
</div>
<p>OK, let&#8217;s keep going:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">partialTX</span><span class="p">.</span><span class="n">verifySignatures</span><span class="p">()</span>
<span class="k">val</span> <span class="py">wtx</span> <span class="p">=</span> <span class="n">partialTX</span><span class="p">.</span><span class="n">txBits</span><span class="p">.</span><span class="n">deserialize</span><span class="p">&lt;</span><span class="n">WireTransaction</span><span class="p">&gt;()</span>
<span class="n">requireThat</span> <span class="p">{</span>
<span class="s">&quot;transaction sends us the right amount of cash&quot;</span> <span class="k">by</span> <span class="p">(</span><span class="n">wtx</span><span class="p">.</span><span class="n">outputStates</span><span class="p">.</span><span class="n">sumCashBy</span><span class="p">(</span><span class="n">args</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="p">==</span> <span class="n">args</span><span class="p">.</span><span class="n">price</span><span class="p">)</span>
<span class="c1">// There are all sorts of funny games a malicious secondary might play here, we should fix them:</span>
<span class="c1">//</span>
<span class="c1">// - This tx may attempt to send some assets we aren&#39;t intending to sell to the secondary, if</span>
<span class="c1">// we&#39;re reusing keys! So don&#39;t reuse keys!</span>
<span class="c1">// - This tx may not be valid according to the contracts of the input states, so we must resolve</span>
<span class="c1">// and fully audit the transaction chains to convince ourselves that it is actually valid.</span>
<span class="c1">// - This tx may include output states that impose odd conditions on the movement of the cash,</span>
<span class="c1">// once we implement state pairing.</span>
<span class="c1">//</span>
<span class="c1">// but the goal of this code is not to be fully secure, but rather, just to find good ways to</span>
<span class="c1">// express protocol state machines on top of the messaging layer.</span>
<span class="p">}</span>
<span class="k">val</span> <span class="py">ourSignature</span> <span class="p">=</span> <span class="n">args</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">txBits</span><span class="p">.</span><span class="n">bits</span><span class="p">)</span>
<span class="k">val</span> <span class="py">fullySigned</span><span class="p">:</span> <span class="n">SignedWireTransaction</span> <span class="p">=</span> <span class="n">partialTX</span><span class="p">.</span><span class="n">copy</span><span class="p">(</span><span class="n">sigs</span> <span class="p">=</span> <span class="n">partialTX</span><span class="p">.</span><span class="n">sigs</span> <span class="p">+</span> <span class="n">ourSignature</span><span class="p">)</span>
<span class="c1">// We should run it through our full TransactionGroup of all transactions here.</span>
<span class="n">fullySigned</span><span class="p">.</span><span class="n">verify</span><span class="p">()</span>
<span class="k">val</span> <span class="py">timestamped</span><span class="p">:</span> <span class="n">TimestampedWireTransaction</span> <span class="p">=</span> <span class="n">fullySigned</span><span class="p">.</span><span class="n">toTimestampedTransaction</span><span class="p">(</span><span class="n">serviceHub</span><span class="p">.</span><span class="n">timestampingService</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">&quot;Built finished transaction, sending back to secondary!&quot;</span> <span class="p">}</span>
<span class="n">send</span><span class="p">(</span><span class="n">TRADE_TOPIC</span><span class="p">,</span> <span class="n">sessionID</span><span class="p">,</span> <span class="n">timestamped</span><span class="p">)</span>
<span class="k">return</span> <span class="n">Pair</span><span class="p">(</span><span class="n">timestamped</span><span class="p">,</span> <span class="n">timestamped</span><span class="p">.</span><span class="n">verifyToLedgerTransaction</span><span class="p">(</span><span class="n">serviceHub</span><span class="p">.</span><span class="n">timestampingService</span><span class="p">,</span> <span class="n">serviceHub</span><span class="p">.</span><span class="n">identityService</span><span class="p">))</span>
</pre></div>
</div>
</div>
<p>Here, we see some assertions and signature checking to satisfy ourselves that we&#8217;re not about to sign something
incorrect. Once we&#8217;re happy, we calculate a signature over the transaction to authorise the movement of the asset
we are selling, and then we verify things to make sure it&#8217;s all OK. Finally, we request timestamping of the
transaction, and send the now finalised and validated transaction back to the buyer.</p>
<div class="admonition warning">
<p class="first admonition-title">Warning</p>
<p class="last">This 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&#8217;t, so they can&#8217;t spend the asset they just purchased! This sort of thing will be fixed in a
future version of the code.</p>
</div>
<p>Finally, the call function returns with the result of the protocol: in our case, the final transaction in two different
forms.</p>
</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&#8217;s do the same for the buyer side:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre>class BuyerImpl : Buyer() {
override fun call(args: BuyerInitialArgs): Pair&lt;TimestampedWireTransaction, LedgerTransaction&gt; {
// Wait for a trade request to come in on our pre-provided session ID.
val tradeRequest = receive&lt;SellerTradeInfo&gt;(TRADE_TOPIC, args.sessionID)
// What is the seller trying to sell us?
val assetTypeName = tradeRequest.assetForSale.state.javaClass.name
logger().trace { &quot;Got trade request for a $assetTypeName&quot; }
// Check the start message for acceptability.
check(tradeRequest.sessionID &gt; 0)
if (tradeRequest.price &gt; args.acceptablePrice)
throw UnacceptablePriceException(tradeRequest.price)
if (!args.typeToBuy.isInstance(tradeRequest.assetForSale.state))
throw AssetMismatchException(args.typeToBuy.name, assetTypeName)
// TODO: Either look up the stateref here in our local db, or accept a long chain of states and
// validate them to audit the other side and ensure it actually owns the state we are being offered!
// For now, just assume validity!
// Generate the shared transaction that both sides will sign, using the data we have.
val ptx = PartialTransaction()
// Add input and output states for the movement of cash, by using the Cash contract to generate the states.
val wallet = serviceHub.walletService.currentWallet
val cashStates = wallet.statesOfType&lt;Cash.State&gt;()
val cashSigningPubKeys = Cash().craftSpend(ptx, tradeRequest.price, tradeRequest.sellerOwnerKey, cashStates)
// Add inputs/outputs/a command for the movement of the asset.
ptx.addInputState(tradeRequest.assetForSale.ref)
// Just pick some new public key for now.
val freshKey = serviceHub.keyManagementService.freshKey()
val (command, state) = tradeRequest.assetForSale.state.withNewOwner(freshKey.public)
ptx.addOutputState(state)
ptx.addArg(WireCommand(command, tradeRequest.assetForSale.state.owner))
// Now sign the transaction with whatever keys we need to move the cash.
for (k in cashSigningPubKeys) {
val priv = serviceHub.keyManagementService.toPrivate(k)
ptx.signWith(KeyPair(k, priv))
}
val stx = ptx.toSignedTransaction(checkSufficientSignatures = false)
stx.verifySignatures() // Verifies that we generated a signed transaction correctly.
// TODO: Could run verify() here to make sure the only signature missing is the sellers.
logger().trace { &quot;Sending partially signed transaction to seller&quot; }
// TODO: Protect against the buyer terminating here and leaving us in the lurch without the final tx.
// TODO: Protect against a malicious buyer sending us back a different transaction to the one we built.
val fullySigned = sendAndReceive&lt;TimestampedWireTransaction&gt;(TRADE_TOPIC,
tradeRequest.sessionID, args.sessionID, stx)
logger().trace { &quot;Got fully signed transaction, verifying ... &quot;}
val ltx = fullySigned.verifyToLedgerTransaction(serviceHub.timestampingService, serviceHub.identityService)
logger().trace { &quot;Fully signed transaction was valid. Trade complete! :-)&quot; }
return Pair(fullySigned, ltx)
}
}
</pre></div>
</div>
</div>
<p>This code is fairly straightforward. 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&#8217;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">Cash().craftSpend</span></code>.</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 protocol is suspended, things like the wallet or the timestamping service. Remember that a protocol may
be suspended when it waits to receive a message across node or computer restarts, so objects representing a service
or data which may frequently change should be accessed &#8216;just in time&#8217;.</li>
<li>Finally, we send the unfinsished, 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">TimestampedWireTransaction</span></code>, which once we verify it, should be the final outcome of the trade.</li>
</ol>
<p>As you can see, the protocol 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">When accessing things via the <code class="docutils literal"><span class="pre">serviceHub</span></code> field, avoid the temptation to stuff a reference into a local variable.
If you do this then next time your protocol waits to receive an object, the system will try and serialise all your
local variables and end up trying to serialise, e.g. the timestamping service, which doesn&#8217;t make any conceptual
sense. The <code class="docutils literal"><span class="pre">serviceHub</span></code> field is defined by the <code class="docutils literal"><span class="pre">ProtocolStateMachine</span></code> superclass and is marked transient so
this problem doesn&#8217;t occur. It&#8217;s also restored for you after a protocol state machine is restored after a node
restart.</p>
</div>
</div>
</div>
</div>
</div>
<footer>
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="visualiser.html" class="btn btn-neutral float-right" title="Using the visualiser" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="messaging.html" class="btn btn-neutral" title="Networking and messaging" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
<hr/>
<div role="contentinfo">
<p>
&copy; Copyright 2015, R3 CEV.
</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:'0.1',
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>

View File

@ -84,6 +84,8 @@
<li class="toctree-l1"><a class="reference internal" href="overview.html">Overview</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="tutorial.html">Tutorial</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="protocol-state-machines.html">Protocol state machines</a></li>
<li class="toctree-l1"><a class="reference internal" href="visualiser.html">Using the visualiser</a></li>
<li class="toctree-l1"><a class="reference internal" href="roadmap.html">Roadmap</a></li>
</ul>

File diff suppressed because one or more lines are too long

View File

@ -26,6 +26,8 @@ Read on to learn:
overview
getting-set-up
tutorial
messaging
protocol-state-machines
visualiser
roadmap

97
docs/source/messaging.rst Normal file
View File

@ -0,0 +1,97 @@
Networking and messaging
========================
Although the platform does not currently provide a network backend, some preliminary interfaces are defined along with
an in-memory implementation provided for use by unit tests and other exploratory code. An implementation based on Apache
Kafka is also being developed, which should be sufficient for real use cases to be implemented in the short run, even
though in the long run a fully peer to peer protocol will be required.
This article quickly explains the basic networking interfaces in the code.
Messaging vs networking
-----------------------
It is important to understand that the code expects any networking module to provide the following services:
- Persistent, reliable and secure delivery of complete messages. The module is expected to retry delivery if initial
attempts fail.
- Ability to send messages both 1:1 and 1:many, where 'many' may mean the entire group of network users.
The details of how this is achieved are not exposed to the rest of the code.
Interfaces
----------
The most important interface is called ``MessagingService`` and is defined in the ``core/messaging/Messaging.kt`` file.
It declares an interface with the following operations:
- ``addMessageHandler(topic: String, executor: Executor, callback: (Message, MessageHandlerRegistration) -> Unit)``
- ``createMessage(topic: String, data: ByteArray): Message``
- ``send(message: Message, targetRecipients: MessageRecipients)``
- ``stop()``
along with a few misc others that are not important enough to discuss here.
A *topic* is simply a string that identifies the kind of message that is being sent. When a message is received, the
topic is compared exactly to the list of registered message handlers and if it matches, the callback is invoked.
Adding a handler returns a ``MessageHandlerRegistration`` object that can be used to remove the handler, and that
registration object is also passed to each invocation to simplify the case where a handler wishes to remove itself.
Some helper functions are also provided that simplify the process of sending a message by using Kryo serialisation, and
registering one-shot handlers that remove themselves once they finished running, but those don't need to be implemented
by network module authors themselves.
Destinations are represented using opaque classes (i.e. their contents are defined by the implementation). The
``MessageRecipients`` interface represents any possible set of recipients: it's used when a piece of code doesn't
care who is going to get a message, just that someone does. The ``SingleMessageRecipient`` interface inherits from
``MessageRecipients`` and represents a handle to some specific individual receiver on the network. Whether they are
identified by IP address, public key, message router ID or some other kind of address is not exposed at this level.
``MessageRecipientGroup`` is not used anywhere at the moment but represents multiple simultaneous recipients. And
finally ``AllPossibleRecipients`` is used for network wide broadcast. It's also unused right now, outside of unit tests.
In memory implementation
------------------------
To ease unit testing of business logic, a simple in-memory messaging service is provided. To access this you can inherit
your test case class from the ``TestWithInMemoryNetwork`` class. This provides a few utility methods to help test
code that involves message passing.
You can run a mock network session in one of two modes:
- Manually "pumped"
- Automatically pumped with background threads
"Pumping" is the act of telling a mock network node to pop a message off its queue and process it. Typically you want
unit tests to be fast, repeatable and you want to be able to insert your own changes into the middle of any given
message sequence. This is what the manual mode is for. In this mode, all logic runs on the same thread (the thread
running the unit tests). You can create and use a node like this:
.. container:: codeset
.. sourcecode:: kotlin
val (aliceAddr, aliceNode) = makeNode(inBackground = false)
val (bobAddr, bobNode) = makeNode(false)
aliceNode.send("test.topic", aliceAddr, "foo")
bobNode.pump(blocking = false)
.. note:: Currently only Kotlin examples are available for networking and protocol state machines. Java examples may
follow later. Naming arguments in Kotlin like above is optional but sometimes useful to make code examples clearer.
The above code won't actually do anything because no message handler is registered for "test.topic" so the message will
go into a holding area. If/when we add a handler that can accept test.topic, the message will be delivered then.
Sometimes you don't want to have to call the pump method over and over again. You can use the ``runNetwork { .. }``
construct to fix this: any code inside the block will be run, and then all nodes you created will be pumped over and
over until all of them have reported that they have no work left to do. This means any ping-pongs of messages will
be run until everything settles.
You can see more examples of how to use this in the file ``InMemoryMessagingTests.kt``.
If you specify ``inBackground = true`` to ``makeNode`` then each node will create its own background thread which will
sit around waiting for messages to be delivered. Handlers will then be invoked on that background thread. This is a
more difficult style of programming that can be used to increase the realism of the unit tests by ensuring multiple
nodes run in parallel, just as they would on a real network spread over multiple machines.

View File

@ -0,0 +1,416 @@
.. highlight:: kotlin
.. raw:: html
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/codesets.js"></script>
Protocol state machines
=======================
This article explains our experimental approach to modelling financial protocols 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 protocol
which is included in the source.
Introduction
------------
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.
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
*delivery versus payment* 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 protocol
in which messages are passed back and forth privately between parties, checked, signed and so on.
Despite how useful these protocols 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:
* 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 protocol instantiation.
* Surviving node shutdowns/restarts that may occur in the middle of the protocol without complicating things. This
implies that the state of the protocol must be persisted to disk.
* Error handling.
* Message routing.
* Serialisation.
* 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.
* Unit testing of the finished protocol.
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.
To put these problems in perspective the *payment channel protocol* 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.
As small contract-specific trading protocols are a common occurence in finance, we provide a framework for the
construction of them that automatically handles many of the concerns outlined above.
Theory
------
A *continuation* 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 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 a library called
JavaFlow which works through behind-the-scenes bytecode rewriting. You don't have to know how this works to benefit
from it, however.
We use continuations for the following reasons:
* It allows us to write code that is free of callbacks, that looks like ordinary sequential code.
* 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 stack can easily be 1mb in size.
* It frees the developer from thinking (much) about persistence and serialisation.
A *state machine* is a piece of code that moves through various *states*. 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 protocol. 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.
A two party trading protocol
----------------------------
We would like to implement the "hello world" of shared transaction building protocols: a seller wishes to sell some
*asset* (e.g. some commercial paper) in return for *cash*. 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.
Our protocol has two parties (B and S for buyer and seller) and will proceed as follows:
1. S sends a ``StateAndRef`` pointing to the state they want to sell to B, along with info about the price they require
B to pay.
2. B sends to S a ``SignedWireTransaction`` 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.
3. S signs it and hands the now finalised ``SignedWireTransaction`` back to B.
You can find the implementation of this protocol in the file ``contracts/protocols/TwoPartyTradeProtocol.kt``.
Assuming no malicious termination, they both end the protocol being in posession of a valid, signed transaction that
represents an atomic asset swap.
Note that it's the *seller* who initiates contact with the buyer, not vice-versa as you might imagine.
We start by defining an abstract base class to encapsulate the protocol. This is what code that invokes the protocol
will see:
.. container:: codeset
.. sourcecode:: kotlin
abstract class TwoPartyTradeProtocol {
class SellerInitialArgs(
val assetToSell: StateAndRef<OwnableState>,
val price: Amount,
val myKeyPair: KeyPair,
val buyerSessionID: Long
)
abstract fun runSeller(otherSide: SingleMessageRecipient, args: SellerInitialArgs): Seller
class BuyerInitialArgs(
val acceptablePrice: Amount,
val typeToBuy: Class<out OwnableState>,
val sessionID: Long
)
abstract fun runBuyer(otherSide: SingleMessageRecipient, args: BuyerInitialArgs): Buyer
abstract class Buyer : ProtocolStateMachine<BuyerInitialArgs, Pair<TimestampedWireTransaction, LedgerTransaction>>()
abstract class Seller : ProtocolStateMachine<SellerInitialArgs, Pair<TimestampedWireTransaction, LedgerTransaction>>()
companion object {
@JvmStatic fun create(smm: StateMachineManager): TwoPartyTradeProtocol {
return TwoPartyTradeProtocolImpl(smm)
}
}
}
Let's unpack what this code does:
- It defines a several classes nested inside the main ``TwoPartyTradeProtocol`` class, and a couple of methods, one to
run the buyer side of the protocol and one to run the seller side.
- Two of the classes are simply wrappers for parameters to the trade; things like what is being sold, what the price
of the asset is, how much the buyer is willing to pay and so on. The ``myKeyPair`` field is simply the public key
that the seller wishes the buyer to send the cash to. The session ID field is sent from buyer to seller when the
trade is being set up and is used to keep messages separated on the network, and stop malicious entities trying to
interfere with the message stream.
- The other two classes define empty abstract classes called ``Buyer`` and ``Seller``. These inherit from a class
called ``ProtocolStateMachine`` and provide two type parameters: the arguments class we just defined for each side
and the type of the object that the protocol finally produces (this doesn't have to be identical for each side, even
though in this case it is).
- Finally it simply defines a static method that creates an instance of an object that inherits from this base class
and returns it, with a ``StateMachineManager`` as an instance. The Impl class will be defined below.
.. note:: Session IDs keep different traffic streams separated, so for security they must be large and random enough
to be unguessable. 63 bits is good enough.
Alright, so using this protocol shouldn't be too hard: in the simplest case we can just pass in the details of the trade
to either runBuyer or runSeller, depending on who we are, and then call ``.get()`` on the resulting future to block the
calling thread until the protocol has finished. Or we could register a callback on the returned future that will be
invoked when it's done, where we could e.g. update a user interface.
The only tricky part is how to get one of these things. We need a ``StateMachineManager``. Where does that come from
and why do we need one?
The state machine manager
-------------------------
The SMM is a class responsible for taking care of all running protocols in a node. It knows how to register handlers
with a ``MessagingService`` and iterate the right state machine when the time comes. It provides the
send/receive/sendAndReceive calls that let the code request network interaction and it will store a serialised copy of
each state machine before it's suspended to wait for the network.
To get a ``StateMachineManager``, you currently have to build one by passing in a ``ServiceHub`` and a thread or thread
pool which it can use. This will change in future so don't worry about the details of this too much: just check the
unit tests to see how it's done.
Implementing the seller
-----------------------
.. container:: codeset
.. sourcecode:: kotlin
private class TwoPartyTradeProtocolImpl(private val smm: StateMachineManager) : TwoPartyTradeProtocol() {
companion object {
val TRADE_TOPIC = "com.r3cev.protocols.trade"
}
class SellerImpl : Seller() {
override fun call(args: SellerInitialArgs): Pair<TimestampedWireTransaction, LedgerTransaction> {
TODO()
}
}
class BuyerImpl : Buyer() {
override fun call(args: BuyerInitialArgs): Pair<TimestampedWireTransaction, LedgerTransaction> {
TODO()
}
}
override fun runSeller(otherSide: SingleMessageRecipient, args: SellerInitialArgs): Seller {
return smm.add(otherSide, args, "$TRADE_TOPIC.seller", SellerImpl::class.java)
}
override fun runBuyer(otherSide: SingleMessageRecipient, args: BuyerInitialArgs): Buyer {
return smm.add(otherSide, args, "$TRADE_TOPIC.buyer", BuyerImpl::class.java)
}
}
We start with a skeleton on which we will build the protocol. Putting things in a *companion object* in Kotlin is like
declaring them as static members in Java. Here, we define a "topic" that will identify trade related messages that
arrive at a node (see :doc:`messaging` for details).
The runSeller and runBuyer methods simply start the state machines, passing in a reference to the classes and the topics
each side will use.
Now let's try implementing the seller side. Firstly, we're going to need a message to send to the buyer describing what
we want to trade. Remember: this data comes from whatever system was used to find the trading partner to begin with.
It could be as simple as a chat room or as complex as a 24/7 exchange.
.. container:: codeset
.. sourcecode:: kotlin
// This object is serialised to the network and is the first protocol message the seller sends to the buyer.
class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>,
val price: Amount,
val sellerOwnerKey: PublicKey,
val buyerSessionID: Long
)
That's simple enough: our opening protocol message will be serialised before being sent over the wire, and it contains
the details that were agreed so we can double check them. It also contains a session ID so we can identify this
trade's messages, and a pointer to where the asset that is being sold can be found on the ledger.
Next we add some code to the ``SellerImpl.call`` method:
.. container:: codeset
.. sourcecode:: kotlin
val sessionID = random63BitValue()
// Make the first message we'll send to kick off the protocol.
val hello = SellerTradeInfo(args.assetToSell, args.price, args.myKeyPair.public, sessionID)
// Zero is a special session ID that is being listened to by the buyer (i.e. before a session is started).
val partialTX = sendAndReceive<SignedWireTransaction>(TRADE_TOPIC, args.buyerSessionID, sessionID, hello)
logger().trace { "Received partially signed transaction" }
That's pretty straight forward. We generate a session ID to identify what's happening on the seller side, fill out
the initial protocol message, and then call ``sendAndReceive``. This function takes a few arguments:
- A type argument, which is the object we're expecting to receive from the other side.
- The topic string that ensures the message is routed to the right bit of code in the other side's node.
- The session IDs that ensure the messages don't get mixed up with other simultaneous trades.
- And finally, the thing to send. It'll be serialised and sent automatically.
Once sendAndReceive is called, the call method will be suspended into a continuation. When it gets back we'll do a log
message. The buyer is supposed to send us a transaction with all the right inputs/outputs/commands in return, with their
cash put into the transaction and their signature on it authorising the movement of the cash.
.. note:: There are a few 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.
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.
The third rule to bear in mind is that you can't declare variables or methods in these classes and access
them from outside of the class, due to the bytecode rewriting and classloader tricks that are used to make this all
work. If you want access to something inside the BuyerImpl or SellerImpl classes, you must define a super-interface
or super-class (like ``Buyer``/``Seller``) and put what you want to access there.
OK, let's keep going:
.. container:: codeset
.. sourcecode:: kotlin
partialTX.verifySignatures()
val wtx = partialTX.txBits.deserialize<WireTransaction>()
requireThat {
"transaction sends us the right amount of cash" by (wtx.outputStates.sumCashBy(args.myKeyPair.public) == args.price)
// There are all sorts of funny games a malicious secondary might play here, we should fix them:
//
// - This tx may attempt to send some assets we aren't intending to sell to the secondary, if
// we're reusing keys! So don't reuse keys!
// - This tx may not be valid according to the contracts of the input states, so we must resolve
// and fully audit the transaction chains to convince ourselves that it is actually valid.
// - This tx may include output states that impose odd conditions on the movement of the cash,
// once we implement state pairing.
//
// but the goal of this code is not to be fully secure, but rather, just to find good ways to
// express protocol state machines on top of the messaging layer.
}
val ourSignature = args.myKeyPair.signWithECDSA(partialTX.txBits.bits)
val fullySigned: SignedWireTransaction = partialTX.copy(sigs = partialTX.sigs + ourSignature)
// We should run it through our full TransactionGroup of all transactions here.
fullySigned.verify()
val timestamped: TimestampedWireTransaction = fullySigned.toTimestampedTransaction(serviceHub.timestampingService)
logger().trace { "Built finished transaction, sending back to secondary!" }
send(TRADE_TOPIC, sessionID, timestamped)
return Pair(timestamped, timestamped.verifyToLedgerTransaction(serviceHub.timestampingService, serviceHub.identityService))
Here, we see some assertions and signature checking to satisfy ourselves that we're not about to sign something
incorrect. Once we're happy, we calculate a signature over the transaction to authorise the movement of the asset
we are selling, and then we verify things to make sure it's all OK. Finally, we request timestamping of the
transaction, and send the now finalised and validated transaction back to the buyer.
.. warning:: This code is **not secure**. 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.
Finally, the call function returns with the result of the protocol: in our case, the final transaction in two different
forms.
Implementing the buyer
----------------------
OK, let's do the same for the buyer side:
.. container:: codeset
.. sourcecode:: kotlin
class BuyerImpl : Buyer() {
override fun call(args: BuyerInitialArgs): Pair<TimestampedWireTransaction, LedgerTransaction> {
// Wait for a trade request to come in on our pre-provided session ID.
val tradeRequest = receive<SellerTradeInfo>(TRADE_TOPIC, args.sessionID)
// What is the seller trying to sell us?
val assetTypeName = tradeRequest.assetForSale.state.javaClass.name
logger().trace { "Got trade request for a $assetTypeName" }
// Check the start message for acceptability.
check(tradeRequest.sessionID > 0)
if (tradeRequest.price > args.acceptablePrice)
throw UnacceptablePriceException(tradeRequest.price)
if (!args.typeToBuy.isInstance(tradeRequest.assetForSale.state))
throw AssetMismatchException(args.typeToBuy.name, assetTypeName)
// TODO: Either look up the stateref here in our local db, or accept a long chain of states and
// validate them to audit the other side and ensure it actually owns the state we are being offered!
// For now, just assume validity!
// Generate the shared transaction that both sides will sign, using the data we have.
val ptx = PartialTransaction()
// Add input and output states for the movement of cash, by using the Cash contract to generate the states.
val wallet = serviceHub.walletService.currentWallet
val cashStates = wallet.statesOfType<Cash.State>()
val cashSigningPubKeys = Cash().craftSpend(ptx, tradeRequest.price, tradeRequest.sellerOwnerKey, cashStates)
// Add inputs/outputs/a command for the movement of the asset.
ptx.addInputState(tradeRequest.assetForSale.ref)
// Just pick some new public key for now.
val freshKey = serviceHub.keyManagementService.freshKey()
val (command, state) = tradeRequest.assetForSale.state.withNewOwner(freshKey.public)
ptx.addOutputState(state)
ptx.addArg(WireCommand(command, tradeRequest.assetForSale.state.owner))
// Now sign the transaction with whatever keys we need to move the cash.
for (k in cashSigningPubKeys) {
val priv = serviceHub.keyManagementService.toPrivate(k)
ptx.signWith(KeyPair(k, priv))
}
val stx = ptx.toSignedTransaction(checkSufficientSignatures = false)
stx.verifySignatures() // Verifies that we generated a signed transaction correctly.
// TODO: Could run verify() here to make sure the only signature missing is the sellers.
logger().trace { "Sending partially signed transaction to seller" }
// TODO: Protect against the buyer terminating here and leaving us in the lurch without the final tx.
// TODO: Protect against a malicious buyer sending us back a different transaction to the one we built.
val fullySigned = sendAndReceive<TimestampedWireTransaction>(TRADE_TOPIC,
tradeRequest.sessionID, args.sessionID, stx)
logger().trace { "Got fully signed transaction, verifying ... "}
val ltx = fullySigned.verifyToLedgerTransaction(serviceHub.timestampingService, serviceHub.identityService)
logger().trace { "Fully signed transaction was valid. Trade complete! :-)" }
return Pair(fullySigned, ltx)
}
}
This code is fairly straightforward. Here are some things to pay attention to:
1. We do some sanity checking on the received message to ensure we're being offered what we expected to be offered.
2. We create a cash spend in the normal way, by using ``Cash().craftSpend``.
3. We access the *service hub* when we need it to access things that are transient and may change or be recreated
whilst a protocol is suspended, things like the wallet or the timestamping service. Remember that a protocol may
be suspended when it waits to receive a message across node or computer restarts, so objects representing a service
or data which may frequently change should be accessed 'just in time'.
4. Finally, we send the unfinsished, invalid transaction to the seller so they can sign it. They are expected to send
back to us a ``TimestampedWireTransaction``, which once we verify it, should be the final outcome of the trade.
As you can see, the protocol 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.
.. warning:: When accessing things via the ``serviceHub`` field, avoid the temptation to stuff a reference into a local variable.
If you do this then next time your protocol waits to receive an object, the system will try and serialise all your
local variables and end up trying to serialise, e.g. the timestamping service, which doesn't make any conceptual
sense. The ``serviceHub`` field is defined by the ``ProtocolStateMachine`` superclass and is marked transient so
this problem doesn't occur. It's also restored for you after a protocol state machine is restored after a node
restart.

View File

@ -56,10 +56,12 @@ class Cash : Contract {
val amount: Amount,
/** There must be a MoveCommand signed by this key to claim the amount */
val owner: PublicKey
) : ContractState {
override val owner: PublicKey
) : OwnableState {
override val programRef = CASH_PROGRAM_ID
override fun toString() = "Cash($amount at $deposit owned by $owner)"
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
}
// Just for grouping
@ -165,7 +167,7 @@ class Cash : Contract {
*/
@Throws(InsufficientBalanceException::class)
fun craftSpend(tx: PartialTransaction, amount: Amount, to: PublicKey,
wallet: List<StateAndRef<Cash.State>>, onlyFromParties: Set<Party>? = null) {
cashStates: List<StateAndRef<Cash.State>>, onlyFromParties: Set<Party>? = null): List<PublicKey> {
// Discussion
//
// This code is analogous to the Wallet.send() set of methods in bitcoinj, and has the same general outline.
@ -188,7 +190,7 @@ class Cash : Contract {
val currency = amount.currency
val acceptableCoins = run {
val ofCurrency = wallet.filter { it.state.amount.currency == currency }
val ofCurrency = cashStates.filter { it.state.amount.currency == currency }
if (onlyFromParties != null)
ofCurrency.filter { it.state.deposit.party in onlyFromParties }
else
@ -229,7 +231,9 @@ class Cash : Contract {
for (state in gathered) tx.addInputState(state.ref)
for (state in outputs) tx.addOutputState(state)
// What if we already have a move command with the right keys? Filter it out here or in platform code?
tx.addArg(WireCommand(Commands.Move(), keysUsed.toList()))
val keysList = keysUsed.toList()
tx.addArg(WireCommand(Commands.Move(), keysList))
return keysList
}
}

View File

@ -38,13 +38,14 @@ class CommercialPaper : Contract {
data class State(
val issuance: PartyReference,
val owner: PublicKey,
override val owner: PublicKey,
val faceValue: Amount,
val maturityDate: Instant
) : ContractState {
) : OwnableState {
override val programRef = CP_PROGRAM_ID
fun withoutOwner() = copy(owner = NullPublicKey)
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
}
interface Commands : Command {

View File

@ -0,0 +1,207 @@
/*
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
* set forth therein.
*
* All other rights reserved.
*/
package contracts.protocols
import contracts.Cash
import contracts.sumCashBy
import core.*
import core.messaging.*
import core.serialization.deserialize
import core.utilities.trace
import java.security.KeyPair
import java.security.PublicKey
/**
* This asset trading protocol has two parties (B and S for buyer and seller) and the following steps:
*
* 1. S sends the [StateAndRef] pointing to what they want to sell to B, along with info about the price they require
* B to pay. For example this has probably been agreed on an exchange.
* 2. B sends to S a [SignedWireTransaction] 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.
* 3. S signs it and hands the now finalised SignedWireTransaction back to B.
*
* Assuming no malicious termination, they both end the protocol being in posession of a valid, signed transaction
* that represents an atomic asset swap.
*
* Note that it's the *seller* who initiates contact with the buyer, not vice-versa as you might imagine.
*
* To get an implementation of this class, use the static [TwoPartyTradeProtocol.create] method. Then use either
* the [runBuyer] or [runSeller] methods, depending on which side of the trade your node is taking. These methods
* return a future which will complete once the trade is over and a fully signed transaction is available: you can
* either block your thread waiting for the protocol to complete by using [ListenableFuture.get] or more usefully,
* register a callback that will be invoked when the time comes.
*
* To see an example of how to use this class, look at the unit tests.
*/
abstract class TwoPartyTradeProtocol {
class SellerInitialArgs(
val assetToSell: StateAndRef<OwnableState>,
val price: Amount,
val myKeyPair: KeyPair,
val buyerSessionID: Long
)
abstract fun runSeller(otherSide: SingleMessageRecipient, args: SellerInitialArgs): Seller
class BuyerInitialArgs(
val acceptablePrice: Amount,
val typeToBuy: Class<out OwnableState>,
val sessionID: Long
)
abstract fun runBuyer(otherSide: SingleMessageRecipient, args: BuyerInitialArgs): Buyer
abstract class Buyer : ProtocolStateMachine<BuyerInitialArgs, Pair<TimestampedWireTransaction, LedgerTransaction>>()
abstract class Seller : ProtocolStateMachine<SellerInitialArgs, Pair<TimestampedWireTransaction, LedgerTransaction>>()
companion object {
@JvmStatic fun create(smm: StateMachineManager): TwoPartyTradeProtocol {
return TwoPartyTradeProtocolImpl(smm)
}
}
}
/** The implementation of the [TwoPartyTradeProtocol] base class. */
private class TwoPartyTradeProtocolImpl(private val smm: StateMachineManager) : TwoPartyTradeProtocol() {
companion object {
val TRADE_TOPIC = "com.r3cev.protocols.trade"
}
// This object is serialised to the network and is the first protocol message the seller sends to the buyer.
class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>,
val price: Amount,
val sellerOwnerKey: PublicKey,
val sessionID: Long
)
// The seller's side of the protocol. IMPORTANT: This class is loaded in a separate classloader and auto-mangled
// by JavaFlow. Therefore, we cannot cast the object to Seller and poke it directly because the class we'd be
// trying to poke at is different to the one we saw at compile time, so we'd get ClassCastExceptions. All
// interaction with this class must be through either interfaces, the supertype, or objects passed to and from
// the continuation by the state machine framework. Please refer to the documentation website (docs/build/html) to
// learn more about the protocol state machine framework.
class SellerImpl : Seller() {
override fun call(args: SellerInitialArgs): Pair<TimestampedWireTransaction, LedgerTransaction> {
val sessionID = random63BitValue()
// Make the first message we'll send to kick off the protocol.
val hello = SellerTradeInfo(args.assetToSell, args.price, args.myKeyPair.public, sessionID)
val partialTX = sendAndReceive<SignedWireTransaction>(TRADE_TOPIC, args.buyerSessionID, sessionID, hello)
logger().trace { "Received partially signed transaction" }
partialTX.verifySignatures()
val wtx = partialTX.txBits.deserialize<WireTransaction>()
requireThat {
"transaction sends us the right amount of cash" by (wtx.outputStates.sumCashBy(args.myKeyPair.public) == args.price)
// There are all sorts of funny games a malicious secondary might play here, we should fix them:
//
// - This tx may attempt to send some assets we aren't intending to sell to the secondary, if
// we're reusing keys! So don't reuse keys!
// - This tx may not be valid according to the contracts of the input states, so we must resolve
// and fully audit the transaction chains to convince ourselves that it is actually valid.
// - This tx may include output states that impose odd conditions on the movement of the cash,
// once we implement state pairing.
//
// but the goal of this code is not to be fully secure, but rather, just to find good ways to
// express protocol state machines on top of the messaging layer.
}
val ourSignature = args.myKeyPair.signWithECDSA(partialTX.txBits.bits)
val fullySigned: SignedWireTransaction = partialTX.copy(sigs = partialTX.sigs + ourSignature)
// We should run it through our full TransactionGroup of all transactions here.
fullySigned.verify()
val timestamped: TimestampedWireTransaction = fullySigned.toTimestampedTransaction(serviceHub.timestampingService)
logger().trace { "Built finished transaction, sending back to secondary!" }
send(TRADE_TOPIC, args.buyerSessionID, timestamped)
return Pair(timestamped, timestamped.verifyToLedgerTransaction(serviceHub.timestampingService, serviceHub.identityService))
}
}
class UnacceptablePriceException(val givenPrice: Amount) : Exception()
class AssetMismatchException(val expectedTypeName: String, val typeName: String) : Exception() {
override fun toString() = "The submitted asset didn't match the expected type: $expectedTypeName vs $typeName"
}
// The buyer's side of the protocol. See note above Seller to learn about the caveats here.
class BuyerImpl : Buyer() {
override fun call(args: BuyerInitialArgs): Pair<TimestampedWireTransaction, LedgerTransaction> {
// Wait for a trade request to come in on our pre-provided session ID.
val tradeRequest = receive<SellerTradeInfo>(TRADE_TOPIC, args.sessionID)
// What is the seller trying to sell us?
val assetTypeName = tradeRequest.assetForSale.state.javaClass.name
logger().trace { "Got trade request for a $assetTypeName" }
// Check the start message for acceptability.
check(tradeRequest.sessionID > 0)
if (tradeRequest.price > args.acceptablePrice)
throw UnacceptablePriceException(tradeRequest.price)
if (!args.typeToBuy.isInstance(tradeRequest.assetForSale.state))
throw AssetMismatchException(args.typeToBuy.name, assetTypeName)
// TODO: Either look up the stateref here in our local db, or accept a long chain of states and
// validate them to audit the other side and ensure it actually owns the state we are being offered!
// For now, just assume validity!
// Generate the shared transaction that both sides will sign, using the data we have.
val ptx = PartialTransaction()
// Add input and output states for the movement of cash, by using the Cash contract to generate the states.
val wallet = serviceHub.walletService.currentWallet
val cashStates = wallet.statesOfType<Cash.State>()
val cashSigningPubKeys = Cash().craftSpend(ptx, tradeRequest.price, tradeRequest.sellerOwnerKey, cashStates)
// Add inputs/outputs/a command for the movement of the asset.
ptx.addInputState(tradeRequest.assetForSale.ref)
// Just pick some new public key for now.
val freshKey = serviceHub.keyManagementService.freshKey()
val (command, state) = tradeRequest.assetForSale.state.withNewOwner(freshKey.public)
ptx.addOutputState(state)
ptx.addArg(WireCommand(command, tradeRequest.assetForSale.state.owner))
// Now sign the transaction with whatever keys we need to move the cash.
for (k in cashSigningPubKeys) {
val priv = serviceHub.keyManagementService.toPrivate(k)
ptx.signWith(KeyPair(k, priv))
}
val stx = ptx.toSignedTransaction(checkSufficientSignatures = false)
stx.verifySignatures() // Verifies that we generated a signed transaction correctly.
// TODO: Could run verify() here to make sure the only signature missing is the sellers.
logger().trace { "Sending partially signed transaction to seller" }
// TODO: Protect against the buyer terminating here and leaving us in the lurch without the final tx.
// TODO: Protect against a malicious buyer sending us back a different transaction to the one we built.
val fullySigned = sendAndReceive<TimestampedWireTransaction>(TRADE_TOPIC,
tradeRequest.sessionID, args.sessionID, stx)
logger().trace { "Got fully signed transaction, verifying ... "}
val ltx = fullySigned.verifyToLedgerTransaction(serviceHub.timestampingService, serviceHub.identityService)
logger().trace { "Fully signed transaction was valid. Trade complete! :-)" }
return Pair(fullySigned, ltx)
}
}
override fun runSeller(otherSide: SingleMessageRecipient, args: SellerInitialArgs): Seller {
return smm.add(otherSide, args, "$TRADE_TOPIC.seller", SellerImpl::class.java)
}
override fun runBuyer(otherSide: SingleMessageRecipient, args: BuyerInitialArgs): Buyer {
return smm.add(otherSide, args, "$TRADE_TOPIC.buyer", BuyerImpl::class.java)
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
* set forth therein.
*
* All other rights reserved.
*/
package core
import core.messaging.MessagingService
import java.security.KeyPair
import java.security.PrivateKey
import java.security.PublicKey
import java.time.Instant
/**
* This file defines various 'services' which are not currently fleshed out. A service is a module that provides
* immutable snapshots of data that may be changing in response to user or network events.
*/
/**
* A wallet (name may be temporary) wraps a set of states that are useful for us to keep track of, for instance,
* because we own them. This class represents an immutable, stable state of a wallet: it is guaranteed not to
* change out from underneath you, even though the canonical currently-best-known wallet may change as we learn
* about new transactions from our peers and generate new transactiont that consume states ourselves.
*/
data class Wallet(val states: List<StateAndRef<OwnableState>>) {
@Suppress("UNCHECKED_CAST")
inline fun <reified T : OwnableState> statesOfType() = states.filter { it.state is T } as List<StateAndRef<T>>
}
/**
* A [WalletService] is responsible for securely and safely persisting the current state of a wallet to storage. The
* wallet service vends immutable snapshots of the current wallet for working with: if you build a transaction based
* on a wallet that isn't current, be aware that it may end up being invalid if the states that were used have been
* consumed by someone else first!
*/
interface WalletService {
/**
* Returns a read-only snapshot of the wallet at the time the call is made. Note that if you consume states or
* keys in this wallet, you must inform the wallet service so it can update its internal state.
*/
val currentWallet: Wallet
}
/**
* The KMS is responsible for storing and using private keys to sign things. An implementation of this may, for example,
* call out to a hardware security module that enforces various auditing and frequency-of-use requirements.
*
* The current interface is obviously not usable for those use cases: this is just where we'd put a real signing
* interface if/when one is developed.
*/
interface KeyManagementService {
val keys: Map<PublicKey, PrivateKey>
fun toPrivate(publicKey: PublicKey) = keys[publicKey] ?: throw IllegalStateException("No private key known for requested public key")
/** Generates a new random key and adds it to the exposed map. */
fun freshKey(): KeyPair
}
/**
* An identity service maintains an bidirectional map of [Party]s to their associated public keys and thus supports
* lookup of a party given its key. This is obviously very incomplete and does not reflect everything a real identity
* service would provide.
*/
interface IdentityService {
fun partyFromKey(key: PublicKey): Party?
}
/**
* Simple interface (for testing) to an abstract timestamping service, in the style of RFC 3161. Note that this is not
* 'timestamping' in the block chain sense, but rather, implies a semi-trusted third party taking a reading of the
* current time, typically from an atomic clock, and then digitally signing (current time, hash) to produce a timestamp
* triple (signature, time, hash). The purpose of these timestamps is to locate a transaction in the timeline, which is
* important in the absence of blocks. Here we model the timestamp as an opaque byte array.
*/
interface TimestamperService {
fun timestamp(hash: SecureHash): ByteArray
fun verifyTimestamp(hash: SecureHash, signedTimestamp: ByteArray): Instant
}
/**
* A sketch of an interface to a simple key/value storage system. Intended for persistence of simple blobs like
* transactions, serialised protocol state machines and so on. Again, this isn't intended to imply lack of SQL or
* anything like that, this interface is only big enough to support the prototyping work.
*/
interface StorageService {
fun <K,V> getMap(tableName: String): MutableMap<K, V>
}
/**
* A service hub simply vends references to the other services a node has. Some of those services may be missing or
* mocked out. This class is useful to pass to chunks of pluggable code that might have need of many different kinds of
* functionality and you don't want to hard-code which types in the interface.
*/
interface ServiceHub {
val walletService: WalletService
val keyManagementService: KeyManagementService
val identityService: IdentityService
val timestampingService: TimestamperService
val storageService: StorageService
val networkService: MessagingService // TODO: Rename class to be consistent.
}

View File

@ -58,9 +58,9 @@ data class WireTransaction(val inputStates: List<ContractStateRef>,
val commands: List<WireCommand>) {
fun serializeForSignature(): ByteArray = serialize()
fun toLedgerTransaction(timestamp: Instant?, partyKeyMap: Map<PublicKey, Party>, originalHash: SecureHash): LedgerTransaction {
fun toLedgerTransaction(timestamp: Instant?, identityService: IdentityService, originalHash: SecureHash): LedgerTransaction {
val authenticatedArgs = commands.map {
val institutions = it.pubkeys.mapNotNull { pk -> partyKeyMap[pk] }
val institutions = it.pubkeys.mapNotNull { pk -> identityService.partyFromKey(pk) }
AuthenticatedObject(it.pubkeys, institutions, it.command)
}
return LedgerTransaction(inputStates, outputStates, authenticatedArgs, timestamp, originalHash)
@ -133,18 +133,6 @@ class PartialTransaction(private val inputStates: MutableList<ContractStateRef>
fun commands(): List<WireCommand> = ArrayList(commands)
}
/**
* Simple interface (for testing) to an abstract timestamping service, in the style of RFC 3161. Note that this is not
* 'timestamping' in the block chain sense, but rather, implies a semi-trusted third party taking a reading of the
* current time, typically from an atomic clock, and then digitally signing (current time, hash) to produce a timestamp
* triple (signature, time, hash). The purpose of these timestamps is to locate a transaction in the timeline, which is
* important in the absence of blocks. Here we model the timestamp as an opaque byte array.
*/
interface TimestamperService {
fun timestamp(hash: SecureHash): ByteArray
fun verifyTimestamp(hash: SecureHash, signedTimestamp: ByteArray): Instant
}
data class SignedWireTransaction(val txBits: OpaqueBytes, val sigs: List<DigitalSignature.WithKey>) {
init {
check(sigs.isNotEmpty())
@ -203,11 +191,11 @@ data class TimestampedWireTransaction(
) {
val transactionID: SecureHash = serialize().sha256()
fun verifyToLedgerTransaction(timestamper: TimestamperService, partyKeyMap: Map<PublicKey, Party>): LedgerTransaction {
fun verifyToLedgerTransaction(timestamper: TimestamperService, identityService: IdentityService): LedgerTransaction {
val stx: SignedWireTransaction = signedWireTX.deserialize()
val wtx: WireTransaction = stx.verify()
val instant: Instant? = if (timestamp != null) timestamper.verifyTimestamp(signedWireTX.sha256(), timestamp.bits) else null
return wtx.toLedgerTransaction(instant, partyKeyMap, transactionID)
return wtx.toLedgerTransaction(instant, identityService, transactionID)
}
}

View File

@ -9,8 +9,14 @@
package core
import com.google.common.io.BaseEncoding
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors
import com.google.common.util.concurrent.SettableFuture
import org.slf4j.Logger
import java.security.SecureRandom
import java.time.Duration
import java.util.*
import java.util.concurrent.Executor
/** A simple class that wraps a byte array and makes the equals/hashCode/toString methods work as you actually expect */
open class OpaqueBytes(val bits: ByteArray) {
@ -38,3 +44,24 @@ val Int.days: Duration get() = Duration.ofDays(this.toLong())
val Int.hours: Duration get() = Duration.ofHours(this.toLong())
val Int.minutes: Duration get() = Duration.ofMinutes(this.toLong())
val Int.seconds: Duration get() = Duration.ofSeconds(this.toLong())
/**
* Returns a random positive long generated using a secure RNG. This function sacrifies a bit of entropy in order to
* avoid potential bugs where the value is used in a context where negative numbers are not expected.
*/
fun random63BitValue(): Long = Math.abs(SecureRandom.getInstanceStrong().nextLong())
fun <T> ListenableFuture<T>.whenComplete(executor: Executor? = null, body: () -> Unit) {
addListener(Runnable { body() }, executor ?: MoreExecutors.directExecutor())
}
/** Executes the given block and sets the future to either the result, or any exception that was thrown. */
fun <T> SettableFuture<T>.setFrom(logger: Logger? = null, block: () -> T): SettableFuture<T> {
try {
set(block())
} catch (e: Exception) {
logger?.error("Caught exception", e)
setException(e)
}
return this
}

View File

@ -12,8 +12,6 @@ import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors
import core.sha256
import core.utilities.loggerFor
import core.utilities.trace
import java.time.Instant
import java.util.*
import java.util.concurrent.Executor
@ -22,6 +20,7 @@ import javax.annotation.concurrent.GuardedBy
import javax.annotation.concurrent.ThreadSafe
import kotlin.concurrent.currentThread
import kotlin.concurrent.thread
import kotlin.test.fail
/**
* An in-memory network allows you to manufacture [Node]s for a set of participants. Each
@ -32,12 +31,13 @@ import kotlin.concurrent.thread
*/
@ThreadSafe
public class InMemoryNetwork {
companion object {
private val L = loggerFor<InMemoryNetwork>()
}
@GuardedBy("this") private var counter = 0 // -1 means stopped.
private val networkMap: MutableMap<InMemoryNodeHandle, Node> = Collections.synchronizedMap(HashMap())
private var counter = 0 // -1 means stopped.
private val networkMap = HashMap<Handle, Node>()
// All messages are kept here until the messages are pumped off the queue by a caller to the node class.
// Queues are created on-demand when a message is sent to an address: the receiving node doesn't have to have
// been created yet. If the node identified by the given handle has gone away/been shut down then messages
// stack up here waiting for it to come back. The intent of this is to simulate a reliable messaging network.
private val messageQueues = HashMap<Handle, LinkedBlockingQueue<Message>>()
/**
* Creates a node and returns the new object that identifies its location on the network to senders, and the
@ -49,91 +49,128 @@ public class InMemoryNetwork {
* executor.
*/
@Synchronized
fun createNode(manuallyPumped: Boolean): Pair<SingleMessageRecipient, MessagingSystemBuilder<Node>> {
fun createNode(manuallyPumped: Boolean): Pair<Handle, MessagingServiceBuilder<Node>> {
check(counter >= 0) { "In memory network stopped: please recreate. "}
val id = InMemoryNodeHandle(counter)
val builder = createNodeWithID(manuallyPumped, counter) as Builder
counter++
return Pair(id, Builder(manuallyPumped, id))
val id = builder.id
return Pair(id, builder)
}
val entireNetwork: AllPossibleRecipients = object : AllPossibleRecipients {}
/** Creates a node at the given address: useful if you want to recreate a node to simulate a restart */
fun createNodeWithID(manuallyPumped: Boolean, id: Int): MessagingServiceBuilder<Node> {
return Builder(manuallyPumped, Handle(id))
}
@Synchronized
private fun netSend(message: Message, recipients: MessageRecipients) {
when (recipients) {
is Handle -> getQueueForHandle(recipients).add(message)
is AllPossibleRecipients -> {
// This means all possible recipients _that the network knows about at the time_, not literally everyone
// who joins into the indefinite future.
for (handle in networkMap.keys)
getQueueForHandle(handle).add(message)
}
else -> fail("Unknown type of recipient handle")
}
}
@Synchronized
private fun netNodeHasShutdown(handle: Handle) {
networkMap.remove(handle)
}
@Synchronized
private fun getQueueForHandle(recipients: Handle) = messageQueues.getOrPut(recipients) { LinkedBlockingQueue() }
val everyoneOnline: AllPossibleRecipients = object : AllPossibleRecipients {}
@Synchronized
fun stop() {
for (node in networkMap.values) {
// toArrayList here just copies the collection, which we need because node.stop() will delete itself from
// the network map by calling netNodeHasShutdown. So we would get a CoModException if we didn't copy first.
for (node in networkMap.values.toArrayList())
node.stop()
}
counter = -1
networkMap.clear()
messageQueues.clear()
}
private inner class Builder(val manuallyPumped: Boolean, val id: InMemoryNodeHandle) : MessagingSystemBuilder<Node> {
inner class Builder(val manuallyPumped: Boolean, val id: Handle) : MessagingServiceBuilder<Node> {
override fun start(): ListenableFuture<Node> {
val node = Node(manuallyPumped)
networkMap[id] = node
return Futures.immediateFuture(node)
synchronized(this@InMemoryNetwork) {
val node = Node(manuallyPumped, id)
networkMap[id] = node
return Futures.immediateFuture(node)
}
}
}
private class InMemoryNodeHandle(val id: Int) : SingleMessageRecipient {
class Handle(val id: Int) : SingleMessageRecipient {
override fun toString() = "In memory node $id"
override fun equals(other: Any?) = other is InMemoryNodeHandle && other.id == id
override fun equals(other: Any?) = other is Handle && other.id == id
override fun hashCode() = id.hashCode()
}
/**
* An [Node] provides a [MessagingSystem] that isn't backed by any kind of network or disk storage
* An [Node] provides a [MessagingService] that isn't backed by any kind of network or disk storage
* system, but just uses regular queues on the heap instead. It is intended for unit testing and developer convenience
* when all entities on 'the network' are being simulated in-process.
*
* An instance can be obtained by creating a builder and then using the start method.
*/
inner class Node(private val manuallyPumped: Boolean): MessagingSystem {
inner class Handler(val executor: Executor?, val topic: String, val callback: (Message) -> Unit) : MessageHandlerRegistration
inner class Node(private val manuallyPumped: Boolean, private val handle: Handle): MessagingService {
inner class Handler(val executor: Executor?, val topic: String, val callback: (Message, MessageHandlerRegistration) -> Unit) : MessageHandlerRegistration
@GuardedBy("this")
protected val handlers: MutableList<Handler> = ArrayList()
@GuardedBy("this")
protected var running = true
protected val q = LinkedBlockingQueue<Message>()
@GuardedBy("this")
protected val pendingRedelivery = LinkedList<Message>()
protected val backgroundThread = if (manuallyPumped) null else thread(isDaemon = true, name = "In-memory message dispatcher ") {
while (!currentThread.isInterrupted) pumpInternal(true)
while (!currentThread.isInterrupted) {
try {
pumpInternal(true)
} catch(e: InterruptedException) {
if (synchronized(this) { running })
throw e
}
}
}
@Synchronized
override fun addMessageHandler(executor: Executor?, topic: String, callback: (Message) -> Unit): MessageHandlerRegistration {
check(running)
return Handler(executor, topic, callback).apply { handlers.add(this) }
override fun addMessageHandler(topic: String, executor: Executor?, callback: (Message, MessageHandlerRegistration) -> Unit): MessageHandlerRegistration {
checkRunning()
val handler = Handler(executor, topic, callback).apply { handlers.add(this) }
if (pendingRedelivery.isNotEmpty()) {
val items = ArrayList(pendingRedelivery)
pendingRedelivery.clear()
items.forEach { netSend(it, handle) }
}
return handler
}
@Synchronized
override fun removeMessageHandler(registration: MessageHandlerRegistration) {
check(running)
checkRunning()
check(handlers.remove(registration as Handler))
}
@Synchronized
override fun send(message: Message, target: MessageRecipients) {
check(running)
L.trace { "Sending $message to '$target'" }
when (target) {
is InMemoryNodeHandle -> {
val node = networkMap[target] ?: throw IllegalArgumentException("Unknown message recipient: $target")
node.q.put(message)
}
entireNetwork -> {
for (node in networkMap.values) {
node.q.put(message)
}
}
else -> throw IllegalArgumentException("Unhandled type of target: $target")
}
checkRunning()
netSend(message, target)
}
@Synchronized
override fun stop() {
backgroundThread?.interrupt()
running = false
backgroundThread?.interrupt()
netNodeHasShutdown(handle)
}
/** Returns the given (topic, data) pair as a newly created message object.*/
@ -151,16 +188,22 @@ public class InMemoryNetwork {
/**
* Delivers a single message from the internal queue. If there are no messages waiting to be delivered and block
* is true, waits until one has been provided on a different thread via send. If block is false, the return result
* indicates whether a message was delivered or not.
* is true, waits until one has been provided on a different thread via send. If block is false, the return
* result indicates whether a message was delivered or not.
*/
fun pump(block: Boolean): Boolean {
check(manuallyPumped)
synchronized(this) { check(running) }
checkRunning()
return pumpInternal(block)
}
@Synchronized
private fun checkRunning() {
check(running)
}
private fun pumpInternal(block: Boolean): Boolean {
val q = getQueueForHandle(handle)
val message = if (block) q.take() else q.poll()
if (message == null)
@ -170,9 +213,21 @@ public class InMemoryNetwork {
handlers.filter { if (it.topic.isBlank()) true else message.topic == it.topic }
}
if (deliverTo.isEmpty()) {
// Got no handlers for this message yet. Keep the message around and attempt redelivery after a new
// handler has been registered. The purpose of this path is to make unit tests that have multi-threading
// reliable, as a sender may attempt to send a message to a receiver that hasn't finished setting
// up a handler for yet. Most unit tests don't run threaded, but we want to test true parallelism at
// least sometimes.
synchronized(this) {
pendingRedelivery.add(message)
}
return false
}
for (handler in deliverTo) {
// Now deliver via the requested executor, or on this thread if no executor was provided at registration time.
(handler.executor ?: MoreExecutors.directExecutor()).execute { handler.callback(message) }
(handler.executor ?: MoreExecutors.directExecutor()).execute { handler.callback(message, handler) }
}
return true

View File

@ -9,13 +9,13 @@
package core.messaging
import com.google.common.util.concurrent.ListenableFuture
import java.time.Duration
import core.serialization.serialize
import java.time.Instant
import java.util.concurrent.Executor
import javax.annotation.concurrent.ThreadSafe
/**
* A [MessagingSystem] sits at the boundary between a message routing / networking layer and the core platform code.
* A [MessagingService] sits at the boundary between a message routing / networking layer and the core platform code.
*
* A messaging system must provide the ability to send 1:many messages, potentially to an abstract "group", the
* membership of which is defined elsewhere. Messages are atomic and the system guarantees that a sent message
@ -25,26 +25,25 @@ import javax.annotation.concurrent.ThreadSafe
* is *reliable* and as such messages may be stored to disk once queued.
*/
@ThreadSafe
interface MessagingSystem {
interface MessagingService {
/**
* The provided function will be invoked for each received message whose topic matches the given string, on the given
* executor. The topic can be the empty string to match all messages.
*
* If no executor is received then the callback will run on threads provided by the messaging system, and the
* If no executor is received then the callback will run on threads provided by the messaging service, and the
* callback is expected to be thread safe as a result.
*
* The returned object is an opaque handle that may be used to un-register handlers later with [addMessageHandler].
*
* If the callback throws an exception then the message is discarded and will not be retried, unless the exception
* is a subclass of [RetryMessageLaterException], in which case the message will be queued and attempted later.
* The returned object is an opaque handle that may be used to un-register handlers later with [removeMessageHandler].
* The handle is passed to the callback as well, to avoid race conditions whereby the callback wants to unregister
* itself and yet addMessageHandler hasn't returned the handle yet.
*/
fun addMessageHandler(executor: Executor? = null, topic: String = "", callback: (Message) -> Unit): MessageHandlerRegistration
fun addMessageHandler(topic: String = "", executor: Executor? = null, callback: (Message, MessageHandlerRegistration) -> Unit): MessageHandlerRegistration
/**
* Removes a handler given the object returned from [addMessageHandler]. The callback will no longer be invoked once
* this method has returned, although executions that are currently in flight will not be interrupted.
*
* @throws IllegalArgumentException if the given registration isn't valid for this messaging system.
* @throws IllegalArgumentException if the given registration isn't valid for this messaging service.
* @throws IllegalStateException if the given registration was already de-registered.
*/
fun removeMessageHandler(registration: MessageHandlerRegistration)
@ -69,25 +68,34 @@ interface MessagingSystem {
}
/**
* This class lets you start up a [MessagingSystem]. Its purpose is to stop you from getting access to the methods
* on the messaging system interface until you have successfully started up the system. One of these objects should
* be the only way to obtain a reference to a [MessagingSystem]. Startup may be a slow process: some implementations
* Registers a handler for the given topic that runs the given callback with the message and then removes itself. This
* is useful for one-shot handlers that aren't supposed to stick around permanently. Note that this callback doesn't
* take the registration object, unlike the callback to [MessagingService.addMessageHandler].
*/
fun MessagingService.runOnNextMessage(topic: String = "", executor: Executor? = null, callback: (Message) -> Unit) {
addMessageHandler(topic, executor) { msg, reg ->
callback(msg)
removeMessageHandler(reg)
}
}
fun MessagingService.send(topic: String, to: MessageRecipients, obj: Any) = send(createMessage(topic, obj.serialize()), to)
/**
* This class lets you start up a [MessagingService]. Its purpose is to stop you from getting access to the methods
* on the messaging service interface until you have successfully started up the system. One of these objects should
* be the only way to obtain a reference to a [MessagingService]. Startup may be a slow process: some implementations
* may let you cast the returned future to an object that lets you get status info.
*
* A specific implementation of the controller class will have extra features that let you customise it before starting
* it up.
*/
interface MessagingSystemBuilder<T : MessagingSystem> {
fun start(): ListenableFuture<T>
interface MessagingServiceBuilder<out T : MessagingService> {
fun start(): ListenableFuture<out T>
}
interface MessageHandlerRegistration
class RetryMessageLaterException : Exception() {
/** If set, the message will be re-queued and retried after the requested interval. */
var delayPeriod: Duration? = null
}
/**
* A message is defined, at this level, to be a (topic, timestamp, byte arrays) triple, where the topic is a string in
* Java-style reverse dns form, with "platform." being a prefix reserved by the platform for its own use. Vendor

View File

@ -0,0 +1,293 @@
/*
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
* set forth therein.
*
* All other rights reserved.
*/
package core.messaging
import com.esotericsoftware.kryo.io.Input
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.SettableFuture
import core.SecureHash
import core.ServiceHub
import core.serialization.THREAD_LOCAL_KRYO
import core.serialization.createKryo
import core.serialization.deserialize
import core.serialization.serialize
import core.utilities.trace
import core.whenComplete
import org.apache.commons.javaflow.Continuation
import org.apache.commons.javaflow.ContinuationClassLoader
import org.objenesis.instantiator.ObjectInstantiator
import org.objenesis.strategy.InstantiatorStrategy
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.*
import java.util.concurrent.Executor
import javax.annotation.concurrent.ThreadSafe
/**
* A StateMachineManager is responsible for coordination and persistence of multiple [ProtocolStateMachine] objects.
* Each such object represents an instantiation of a (two-party) protocol that has reached a particular point.
*
* An implementation of this class will persist state machines to long term storage so they can survive process restarts
* and, if run with a single-threaded executor, will ensure no two state machines run concurrently with each other
* (bad for performance, good for programmer mental health!).
*
* A "state machine" is a class with a single call method. The call method and any others it invokes are rewritten by
* a bytecode rewriting engine called JavaFlow, to ensure the code can be suspended and resumed at any point.
*
* TODO: The framework should propagate exceptions and handle error handling automatically.
* TODO: This needs extension to the >2 party case.
* TODO: Consider the issue of continuation identity more deeply: is it a safe assumption that a serialised
* continuation is always unique?
*/
@ThreadSafe
class StateMachineManager(val serviceHub: ServiceHub, val runInThread: Executor) {
// This map is backed by a database and will be used to store serialised state machines to disk, so we can resurrect
// them across node restarts.
private val checkpointsMap = serviceHub.storageService.getMap<SecureHash, ByteArray>("state machines")
// A list of all the state machines being managed by this class. We expose snapshots of it via the stateMachines
// property.
private val _stateMachines = Collections.synchronizedList(ArrayList<ProtocolStateMachine<*,*>>())
/** Returns a snapshot of the currently registered state machines. */
val stateMachines: List<ProtocolStateMachine<*,*>> get() {
synchronized(_stateMachines) {
return ArrayList(_stateMachines)
}
}
// This class will be serialised, so everything it points to transitively must also be serialisable (with Kryo).
private class Checkpoint(
val continuation: Continuation,
val otherSide: MessageRecipients,
val loggerName: String,
val awaitingTopic: String,
val awaitingObjectOfType: String // java class name
)
init {
restoreCheckpoints()
}
/** Reads the database map and resurrects any serialised state machines. */
private fun restoreCheckpoints() {
for (bytes in checkpointsMap.values) {
val kryo = createKryo()
// Set up Kryo to use the JavaFlow classloader when deserialising, so the magical continuation bytecode
// rewriting is performed correctly.
var _psm: ProtocolStateMachine<*, *>? = null
kryo.instantiatorStrategy = object : InstantiatorStrategy {
val forwardingTo = kryo.instantiatorStrategy
override fun <T> newInstantiatorOf(type: Class<T>): ObjectInstantiator<T> {
// If this is some object that isn't a state machine, use the default behaviour.
if (!ProtocolStateMachine::class.java.isAssignableFrom(type))
return forwardingTo.newInstantiatorOf(type)
// Otherwise, return an 'object instantiator' (i.e. factory) that uses the JavaFlow classloader.
@Suppress("UNCHECKED_CAST", "CAST_NEVER_SUCCEEDS")
return ObjectInstantiator<T> {
val p = loadContinuationClass(type as Class<out ProtocolStateMachine<*, *>>).first
// Pass the new object a pointer to the service hub where it can find objects that don't
// survive restarts.
p.serviceHub = serviceHub
_psm = p
p as T
}
}
}
val checkpoint = bytes.deserialize<Checkpoint>(kryo)
val continuation = checkpoint.continuation
// We know _psm can't be null here, because we always serialise a ProtocolStateMachine subclass, so the
// code above that does "_psm = p" will always run. But the Kotlin compiler can't know that so we have to
// forcibly cast away the nullness with the !! operator.
val psm = _psm!!
registerStateMachine(psm)
val logger = LoggerFactory.getLogger(checkpoint.loggerName)
val awaitingObjectOfType = Class.forName(checkpoint.awaitingObjectOfType)
// And now re-wire the deserialised continuation back up to the network service.
setupNextMessageHandler(logger, serviceHub.networkService, continuation, checkpoint.otherSide,
awaitingObjectOfType, checkpoint.awaitingTopic, bytes)
}
}
/**
* Kicks off a brand new state machine of the given class. It will send messages to the network node identified by
* the [otherSide] parameter, log with the named logger, and the [initialArgs] object will be passed to the call
* method of the [ProtocolStateMachine] object that is created. The state machine will be persisted when it suspends
* and will be removed once it completes.
*/
fun <T : ProtocolStateMachine<I, *>, I> add(otherSide: MessageRecipients, initialArgs: I, loggerName: String,
continuationClass: Class<out T>): T {
val logger = LoggerFactory.getLogger(loggerName)
val (sm, continuation) = loadContinuationClass(continuationClass)
sm.serviceHub = serviceHub
registerStateMachine(sm)
runInThread.execute {
// The current state of the continuation is held in the closure attached to the messaging system whenever
// the continuation suspends and tells us it expects a response.
iterateStateMachine(continuation, serviceHub.networkService, otherSide, initialArgs, logger, null)
}
@Suppress("UNCHECKED_CAST")
return sm as T
}
private fun registerStateMachine(psm: ProtocolStateMachine<*, *>) {
_stateMachines.add(psm)
psm.resultFuture.whenComplete(runInThread) {
_stateMachines.remove(psm)
}
}
@Suppress("UNCHECKED_CAST")
private fun loadContinuationClass(continuationClass: Class<out ProtocolStateMachine<*, *>>): Pair<ProtocolStateMachine<*, *>, Continuation> {
val url = continuationClass.protectionDomain.codeSource.location
val cl = ContinuationClassLoader(arrayOf(url), this.javaClass.classLoader)
val obj = cl.forceLoadClass(continuationClass.name).newInstance() as ProtocolStateMachine<*, *>
return Pair(obj, Continuation.startSuspendedWith(obj))
}
private fun persistCheckpoint(prev: ByteArray?, new: ByteArray) {
// It's OK for this to be unsynchronised, as the prev/new byte arrays are specific to a continuation instance,
// and the underlying map provided by the database layer is expected to be thread safe.
if (prev != null)
checkpointsMap.remove(SecureHash.sha256(prev))
checkpointsMap[SecureHash.sha256(new)] = new
}
private fun iterateStateMachine(c: Continuation, net: MessagingService, otherSide: MessageRecipients,
continuationInput: Any?, logger: Logger,
prevPersistedBytes: ByteArray?): Continuation {
// This will resume execution of the run() function inside the continuation at the place it left off.
val oldLogger = CONTINUATION_LOGGER.get()
val nextState: Continuation? = try {
CONTINUATION_LOGGER.set(logger)
Continuation.continueWith(c, continuationInput)
} catch (t: Throwable) {
logger.error("Caught error whilst invoking protocol state machine", t)
throw t
} finally {
CONTINUATION_LOGGER.set(oldLogger)
}
// If continuation returns null, it's finished and the result future has been set.
if (nextState == null)
return c
val req = nextState.value() as? ContinuationResult ?: return c
// Else, it wants us to do something: send, receive, or send-and-receive.
if (req is ContinuationResult.ExpectingResponse<*>) {
// Prepare a listener on the network that runs in the background thread when we received a message.
val topic = "${req.topic}.${req.sessionIDForReceive}"
setupNextMessageHandler(logger, net, nextState, otherSide, req.responseType, topic, prevPersistedBytes)
}
// If an object to send was provided (not null), send it now.
req.obj?.let {
val topic = "${req.topic}.${req.sessionIDForSend}"
logger.trace { "-> $topic : message of type ${it.javaClass.name}" }
net.send(net.createMessage(topic, it.serialize()), otherSide)
}
if (req is ContinuationResult.NotExpectingResponse) {
// We sent a message, but don't expect a response, so re-enter the continuation to let it keep going.
return iterateStateMachine(nextState, net, otherSide, null, logger, prevPersistedBytes)
} else {
return nextState
}
}
private fun setupNextMessageHandler(logger: Logger, net: MessagingService, nextState: Continuation,
otherSide: MessageRecipients, responseType: Class<*>,
topic: String, prevPersistedBytes: ByteArray?) {
val checkpoint = Checkpoint(nextState, otherSide, logger.name, topic, responseType.name)
val curPersistedBytes = checkpoint.serialize()
persistCheckpoint(prevPersistedBytes, curPersistedBytes)
net.runOnNextMessage(topic, runInThread) { netMsg ->
val obj: Any = THREAD_LOCAL_KRYO.get().readObject(Input(netMsg.data), responseType)
logger.trace { "<- $topic : message of type ${obj.javaClass.name}" }
iterateStateMachine(nextState, net, otherSide, obj, logger, curPersistedBytes)
}
}
}
val CONTINUATION_LOGGER = ThreadLocal<Logger>()
/**
* The base class that should be used by any object that wishes to act as a protocol state machine. Sub-classes should
* override the [call] method and return whatever the final result of the protocol is. Inside the call method,
* the rules of normal object oriented programming are a little different:
*
* - You can call send/receive/sendAndReceive in order to suspend the state machine and request network interaction.
* This does not block a thread and when a state machine is suspended like this, it will be serialised and written
* to stable storage. That means all objects on the stack and referenced from fields must be serialisable as well
* (with Kryo, so they don't have to implement the Java Serializable interface). The state machine may be resumed
* at some arbitrary later point.
* - Because of this, if you need access to data that might change over time, you should request it just-in-time
* via the [serviceHub] property which is provided. Don't try and keep data you got from a service across calls to
* send/receive/sendAndReceive because the world might change in arbitrary ways out from underneath you, for instance,
* if the node is restarted or reconfigured!
* - Don't pass initial data in using a constructor. This object will be instantiated using reflection so you cannot
* define your own constructor. Instead define a separate class that holds your initial arguments, and take it as
* the argument to [call].
*/
@Suppress("UNCHECKED_CAST")
abstract class ProtocolStateMachine<T, R> : Runnable {
protected fun logger(): Logger = CONTINUATION_LOGGER.get()
// These fields shouldn't be serialised.
@Transient private var _resultFuture: SettableFuture<R> = SettableFuture.create<R>()
/** This future will complete when the call method returns. */
val resultFuture: ListenableFuture<R> get() = _resultFuture
/** This field is initialised by the framework to point to various infrastructure submodules. */
@Transient lateinit var serviceHub: ServiceHub
abstract fun call(args: T): R
override fun run() {
// TODO: Catch any exceptions here and put them in the future.
val r = call(Continuation.getContext() as T)
if (r != null)
_resultFuture.set(r)
}
}
@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
inline fun <S : Any> ProtocolStateMachine<*, *>.send(topic: String, sessionID: Long, obj: S) =
Continuation.suspend(ContinuationResult.NotExpectingResponse(topic, sessionID, obj))
@Suppress("UNCHECKED_CAST")
inline fun <reified R : Any> ProtocolStateMachine<*, *>.sendAndReceive(
topic: String, sessionIDForSend: Long, sessionIDForReceive: Long, obj: Any): R {
return Continuation.suspend(ContinuationResult.ExpectingResponse(topic, sessionIDForSend, sessionIDForReceive,
obj, R::class.java)) as R
}
@Suppress("UNCHECKED_CAST")
inline fun <reified R : Any> ProtocolStateMachine<*, *>.receive(
topic: String, sessionIDForReceive: Long): R {
return Continuation.suspend(ContinuationResult.ExpectingResponse(topic, -1, sessionIDForReceive, null, R::class.java)) as R
}
open class ContinuationResult(val topic: String, val sessionIDForSend: Long, val sessionIDForReceive: Long, val obj: Any?) {
class ExpectingResponse<R : Any>(
topic: String,
sessionIDForSend: Long,
sessionIDForReceive: Long,
obj: Any?,
val responseType: Class<R>
) : ContinuationResult(topic, sessionIDForSend, sessionIDForReceive, obj)
class NotExpectingResponse(topic: String, sessionIDForSend: Long, obj: Any?) : ContinuationResult(topic, sessionIDForSend, -1, obj)
}

View File

@ -12,8 +12,10 @@ import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import core.OpaqueBytes
import de.javakaffee.kryoserializers.ArraysAsListSerializer
import org.objenesis.strategy.StdInstantiatorStrategy
import java.io.ByteArrayOutputStream
import java.util.*
/**
* Serialization utilities, using the Kryo framework with a custom serialiser for immutable data classes and a dead
@ -63,5 +65,7 @@ fun createKryo(): Kryo {
// Allow construction of objects using a JVM backdoor that skips invoking the constructors, if there is no
// no-arg constructor available.
instantiatorStrategy = Kryo.DefaultInstantiatorStrategy(StdInstantiatorStrategy())
register(Arrays.asList( "" ).javaClass, ArraysAsListSerializer());
}
}

View File

@ -65,10 +65,10 @@ class BriefLogFormatter : Formatter() {
handlers[0].formatter = BriefLogFormatter()
}
fun initVerbose() {
fun initVerbose(packageSpec: String = "") {
init()
loggerRef.level = Level.ALL
loggerRef.handlers[0].level = Level.ALL
Logger.getLogger(packageSpec).level = Level.ALL
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
* set forth therein.
*
* All other rights reserved.
*/
package core.utilities.continuations
import org.apache.commons.javaflow.Continuation
import org.apache.commons.javaflow.ContinuationClassLoader
/**
* A "continuation" is an object that represents a suspended execution of a function. They allow you to write code
* that suspends itself half way through, bundles up everything that was on the stack into a (potentially serialisable)
* object, and then be resumed from the exact same spot later. Continuations are not natively supported by the JVM
* but we can use the Apache JavaFlow library which implements them using bytecode rewriting.
*
* The primary benefit of using continuations is that state machine/protocol code that would otherwise be very
* convoluted and hard to read becomes very clear and straightforward.
*
* TODO: Document classloader interactions and gotchas here.
*/
inline fun <reified T : Runnable> loadContinuationClass(classLoader: ClassLoader): Continuation {
val klass = T::class.java
val url = klass.protectionDomain.codeSource.location
val cl = ContinuationClassLoader(arrayOf(url), classLoader)
val obj = cl.forceLoadClass(klass.name).newInstance() as Runnable
return Continuation.startSuspendedWith(obj)
}

View File

@ -107,7 +107,7 @@ class CommercialPaperTests {
val ptx = CommercialPaper().craftIssue(MINI_CORP.ref(123), 10000.DOLLARS, TEST_TX_TIME + 30.days)
ptx.signWith(MINI_CORP_KEY)
val stx = ptx.toSignedTransaction()
stx.verify().toLedgerTransaction(TEST_TX_TIME, TEST_KEYS_TO_CORP_MAP, SecureHash.randomSHA256())
stx.verify().toLedgerTransaction(TEST_TX_TIME, MockIdentityService, SecureHash.randomSHA256())
}
val (alicesWalletTX, alicesWallet) = cashOutputsToWallet(
@ -124,7 +124,7 @@ class CommercialPaperTests {
ptx.signWith(MINI_CORP_KEY)
ptx.signWith(ALICE_KEY)
val stx = ptx.toSignedTransaction()
stx.verify().toLedgerTransaction(TEST_TX_TIME, TEST_KEYS_TO_CORP_MAP, SecureHash.randomSHA256())
stx.verify().toLedgerTransaction(TEST_TX_TIME, MockIdentityService, SecureHash.randomSHA256())
}
// Won't be validated.
@ -138,7 +138,7 @@ class CommercialPaperTests {
CommercialPaper().craftRedeem(ptx, moveTX.outRef(1), corpWallet)
ptx.signWith(ALICE_KEY)
ptx.signWith(MINI_CORP_KEY)
return ptx.toSignedTransaction().verify().toLedgerTransaction(time, TEST_KEYS_TO_CORP_MAP, SecureHash.randomSHA256())
return ptx.toSignedTransaction().verify().toLedgerTransaction(time, MockIdentityService, SecureHash.randomSHA256())
}
val tooEarlyRedemption = makeRedeemTX(TEST_TX_TIME + 10.days)

View File

@ -105,7 +105,7 @@ class CrowdFundTests {
val ptx = CrowdFund().craftRegister(MINI_CORP.ref(123), 1000.DOLLARS, "crowd funding", TEST_TX_TIME + 7.days)
ptx.signWith(MINI_CORP_KEY)
val stx = ptx.toSignedTransaction()
stx.verify().toLedgerTransaction(TEST_TX_TIME, TEST_KEYS_TO_CORP_MAP, SecureHash.randomSHA256())
stx.verify().toLedgerTransaction(TEST_TX_TIME, MockIdentityService, SecureHash.randomSHA256())
}
// let's give Alice some funds that she can invest
@ -123,7 +123,7 @@ class CrowdFundTests {
ptx.signWith(ALICE_KEY)
val stx = ptx.toSignedTransaction()
// this verify passes - the transaction contains an output cash, necessary to verify the fund command
stx.verify().toLedgerTransaction(TEST_TX_TIME, TEST_KEYS_TO_CORP_MAP, SecureHash.randomSHA256())
stx.verify().toLedgerTransaction(TEST_TX_TIME, MockIdentityService, SecureHash.randomSHA256())
}
// Won't be validated.
@ -137,7 +137,7 @@ class CrowdFundTests {
CrowdFund().craftClose(ptx, pledgeTX.outRef(0), miniCorpWallet)
ptx.signWith(MINI_CORP_KEY)
val stx = ptx.toSignedTransaction()
return stx.verify().toLedgerTransaction(time, TEST_KEYS_TO_CORP_MAP, SecureHash.randomSHA256())
return stx.verify().toLedgerTransaction(time, MockIdentityService, SecureHash.randomSHA256())
}
val tooEarlyClose = makeFundedTX(TEST_TX_TIME + 6.days)

View File

@ -10,53 +10,54 @@
package core.messaging
import core.serialization.deserialize
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertFails
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class InMemoryMessagingTests {
val nodes: MutableMap<SingleMessageRecipient, InMemoryNetwork.Node> = HashMap()
open class TestWithInMemoryNetwork {
val nodes: MutableMap<InMemoryNetwork.Handle, InMemoryNetwork.Node> = HashMap()
lateinit var network: InMemoryNetwork
init {
// BriefLogFormatter.initVerbose()
}
fun makeNode(): Pair<SingleMessageRecipient, InMemoryNetwork.Node> {
fun makeNode(inBackground: Boolean = false): Pair<InMemoryNetwork.Handle, InMemoryNetwork.Node> {
// The manuallyPumped = true bit means that we must call the pump method on the system in order to
val (address, builder) = network.createNode(manuallyPumped = true)
val (address, builder) = network.createNode(!inBackground)
val node = builder.start().get()
nodes[address] = node
return Pair(address, node)
}
fun pumpAll() {
nodes.values.forEach { it.pump(false) }
}
// Utilities to help define messaging rounds.
fun roundWithPumpings(times: Int, body: () -> Unit) {
body()
repeat(times) { pumpAll() }
}
fun round(body: () -> Unit) = roundWithPumpings(1, body)
@Before
fun before() {
fun setupNetwork() {
network = InMemoryNetwork()
nodes.clear()
}
@After
fun after() {
fun stopNetwork() {
network.stop()
}
fun pumpAll(blocking: Boolean) = nodes.values.map { it.pump(blocking) }
// Keep calling "pump" in rounds until every node in the network reports that it had nothing to do
fun <T> runNetwork(body: () -> T): T {
val result = body()
while (pumpAll(false).any { it }) {}
return result
}
}
class InMemoryMessagingTests : TestWithInMemoryNetwork() {
init {
// BriefLogFormatter.initVerbose()
}
@Test
fun topicStringValidation() {
TopicStringValidator.check("this.is.ok")
@ -82,19 +83,19 @@ class InMemoryMessagingTests {
var finalDelivery: Message? = null
with(node2) {
addMessageHandler {
send(it, addr3)
addMessageHandler { msg, registration ->
send(msg, addr3)
}
}
with(node3) {
addMessageHandler {
finalDelivery = it
addMessageHandler { msg, registration ->
finalDelivery = msg
}
}
// Node 1 sends a message and it should end up in finalDelivery, after we pump each node.
roundWithPumpings(2) {
runNetwork {
node1.send(node1.createMessage("test.topic", bits), addr2)
}
@ -110,10 +111,46 @@ class InMemoryMessagingTests {
val bits = "test-content".toByteArray()
var counter = 0
listOf(node1, node2, node3).forEach { it.addMessageHandler { counter++ } }
round {
node1.send(node2.createMessage("test.topic", bits), network.entireNetwork)
listOf(node1, node2, node3).forEach { it.addMessageHandler { msg, registration -> counter++ } }
runNetwork {
node1.send(node2.createMessage("test.topic", bits), network.everyoneOnline)
}
assertEquals(3, counter)
}
@Test
fun downAndUp() {
// Test (re)delivery of messages to nodes that aren't created yet, or were stopped and then restarted.
// The purpose of this functionality is to simulate a reliable messaging system that keeps trying until
// messages are delivered.
val (addr1, node1) = makeNode()
var (addr2, node2) = makeNode()
node1.send("test.topic", addr2, "hello!")
node2.pump(false) // No handler registered, so the message goes into a holding area.
var runCount = 0
node2.addMessageHandler("test.topic") { msg, registration ->
if (msg.data.deserialize<String>() == "hello!")
runCount++
}
node2.pump(false) // Try again now the handler is registered
assertEquals(1, runCount)
// Shut node2 down for a while. Node 1 keeps sending it messages though.
node2.stop()
node1.send("test.topic", addr2, "are you there?")
node1.send("test.topic", addr2, "wake up!")
// Now re-create node2 with the same address as last time, and re-register a message handler.
// Check that the messages that were sent whilst it was gone are still there, waiting for it.
node2 = network.createNodeWithID(true, addr2.id).start().get()
node2.addMessageHandler("test.topic") { a, b -> runCount++ }
assertTrue(node2.pump(false))
assertEquals(2, runCount)
assertTrue(node2.pump(false))
assertEquals(3, runCount)
assertFalse(node2.pump(false))
assertEquals(3, runCount)
}
}

View File

@ -0,0 +1,191 @@
/*
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
* set forth therein.
*
* All other rights reserved.
*/
package core.messaging
import com.google.common.util.concurrent.MoreExecutors
import contracts.Cash
import contracts.CommercialPaper
import contracts.protocols.TwoPartyTradeProtocol
import core.*
import core.testutils.*
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.util.concurrent.Executors
import java.util.logging.Formatter
import java.util.logging.Level
import java.util.logging.LogRecord
import java.util.logging.Logger
import kotlin.test.assertEquals
import kotlin.test.assertTrue
/**
* In this example, Alice wishes to sell her commercial paper to Bob in return for $1,000,000 and they wish to do
* it on the ledger atomically. Therefore they must work together to build a transaction.
*
* We assume that Alice and Bob already found each other via some market, and have agreed the details already.
*/
class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
@Before
fun initLogging() {
Logger.getLogger("").handlers[0].level = Level.ALL
Logger.getLogger("").handlers[0].formatter = object : Formatter() {
override fun format(record: LogRecord) = "${record.threadID} ${record.loggerName}: ${record.message}\n"
}
Logger.getLogger("com.r3cev.protocols.trade").level = Level.ALL
}
@After
fun stopLogging() {
Logger.getLogger("com.r3cev.protocols.trade").level = Level.INFO
}
@Test
fun cashForCP() {
val backgroundThread = Executors.newSingleThreadExecutor()
transactionGroupFor<ContractState> {
// Bob (Buyer) has some cash, Alice (Seller) has some commercial paper she wants to sell to Bob.
roots {
transaction(CommercialPaper.State(MEGA_CORP.ref(1, 2, 3), ALICE, 1200.DOLLARS, TEST_TX_TIME + 7.days) label "alice's paper")
transaction(800.DOLLARS.CASH `owned by` BOB label "bob cash1")
transaction(300.DOLLARS.CASH `owned by` BOB label "bob cash2")
}
val bobsWallet = listOf<StateAndRef<Cash.State>>(lookup("bob cash1"), lookup("bob cash2"))
val (alicesAddress, alicesNode) = makeNode(inBackground = true)
val (bobsAddress, bobsNode) = makeNode(inBackground = true)
val alicesServices = MockServices(wallet = null, keyManagement = null, net = alicesNode)
val bobsServices = MockServices(
wallet = MockWalletService(bobsWallet),
keyManagement = MockKeyManagementService(mapOf(BOB to BOB_KEY.private)),
net = bobsNode
)
val tpSeller = TwoPartyTradeProtocol.create(StateMachineManager(alicesServices, backgroundThread))
val tpBuyer = TwoPartyTradeProtocol.create(StateMachineManager(bobsServices, backgroundThread))
val buyerSessionID = random63BitValue()
val aliceResult = tpSeller.runSeller(
bobsAddress,
TwoPartyTradeProtocol.SellerInitialArgs(
lookup("alice's paper"),
1000.DOLLARS,
ALICE_KEY,
buyerSessionID
)
)
val bobResult = tpBuyer.runBuyer(
alicesAddress,
TwoPartyTradeProtocol.BuyerInitialArgs(
1000.DOLLARS,
CommercialPaper.State::class.java,
buyerSessionID
)
)
assertEquals(aliceResult.resultFuture.get(), bobResult.resultFuture.get())
txns.add(aliceResult.resultFuture.get().second)
verify()
}
backgroundThread.shutdown()
}
@Test
fun serializeAndRestore() {
transactionGroupFor<ContractState> {
// Buyer Bob has some cash, Seller Alice has some commercial paper she wants to sell to Bob.
roots {
transaction(CommercialPaper.State(MEGA_CORP.ref(1, 2, 3), ALICE, 1200.DOLLARS, TEST_TX_TIME + 7.days) label "alice's paper")
transaction(800.DOLLARS.CASH `owned by` BOB label "bob cash1")
transaction(300.DOLLARS.CASH `owned by` BOB label "bob cash2")
}
val bobsWallet = listOf<StateAndRef<Cash.State>>(lookup("bob cash1"), lookup("bob cash2"))
val (alicesAddress, alicesNode) = makeNode(inBackground = false)
var (bobsAddress, bobsNode) = makeNode(inBackground = false)
val bobsStorage = MockStorageService()
val alicesServices = MockServices(wallet = null, keyManagement = null, net = alicesNode)
var bobsServices = MockServices(
wallet = MockWalletService(bobsWallet),
keyManagement = MockKeyManagementService(mapOf(BOB to BOB_KEY.private)),
net = bobsNode,
storage = bobsStorage
)
val tpSeller = TwoPartyTradeProtocol.create(StateMachineManager(alicesServices, MoreExecutors.directExecutor()))
val smmBuyer = StateMachineManager(bobsServices, MoreExecutors.directExecutor())
val tpBuyer = TwoPartyTradeProtocol.create(smmBuyer)
val buyerSessionID = random63BitValue()
tpSeller.runSeller(
bobsAddress,
TwoPartyTradeProtocol.SellerInitialArgs(
lookup("alice's paper"),
1000.DOLLARS,
ALICE_KEY,
buyerSessionID
)
)
tpBuyer.runBuyer(
alicesAddress,
TwoPartyTradeProtocol.BuyerInitialArgs(
1000.DOLLARS,
CommercialPaper.State::class.java,
buyerSessionID
)
)
// Everything is on this thread so we can now step through the protocol one step at a time.
// Seller Alice already sent a message to Buyer Bob. Pump once:
bobsNode.pump(false)
// OK, now Bob has sent the partial transaction back to Alice and is waiting for Alice's signature.
// Save the state machine to "disk" (i.e. a variable, here)
assertEquals(1, bobsStorage.getMap<Any, Any>("state machines").size)
// .. and let's imagine that Bob's computer has a power cut. He now has nothing now beyond what was on disk.
bobsNode.stop()
// Alice doesn't know that and sends Bob the now finalised transaction. Alice sends a message to a node
// that has gone offline.
alicesNode.pump(false)
// ... bring the node back up ... the act of constructing the SMM will re-register the message handlers
// that Bob was waiting on before the reboot occurred.
bobsNode = network.createNodeWithID(true, bobsAddress.id).start().get()
val smm = StateMachineManager(
MockServices(wallet = null, keyManagement = null, net = bobsNode, storage = bobsStorage),
MoreExecutors.directExecutor()
)
// Find the future representing the result of this state machine again.
assertEquals(1, smm.stateMachines.size)
var bobFuture = smm.stateMachines.filterIsInstance<TwoPartyTradeProtocol.Buyer>().first().resultFuture
// Let Bob process his mailbox.
assertTrue(bobsNode.pump(false))
// Bob is now finished and has the same transaction as Alice.
val tx = bobFuture.get()
txns.add(tx.second)
verify()
assertTrue(smm.stateMachines.isEmpty())
}
}
}

View File

@ -88,7 +88,7 @@ class TransactionSerializationTests {
fun timestamp() {
tx.signWith(TestUtils.keypair)
val ttx = tx.toSignedTransaction().toTimestampedTransactionWithoutTime()
val ltx = ttx.verifyToLedgerTransaction(DUMMY_TIMESTAMPER, TEST_KEYS_TO_CORP_MAP)
val ltx = ttx.verifyToLedgerTransaction(DUMMY_TIMESTAMPER, MockIdentityService)
assertEquals(tx.commands().map { it.command }, ltx.commands.map { it.value })
assertEquals(tx.inputStates(), ltx.inStateRefs)
assertEquals(tx.outputStates(), ltx.outStates)
@ -97,7 +97,7 @@ class TransactionSerializationTests {
val ltx2: LedgerTransaction = tx.
toSignedTransaction().
toTimestampedTransaction(DUMMY_TIMESTAMPER).
verifyToLedgerTransaction(DUMMY_TIMESTAMPER, TEST_KEYS_TO_CORP_MAP)
verifyToLedgerTransaction(DUMMY_TIMESTAMPER, MockIdentityService)
assertEquals(TEST_TX_TIME, ltx2.time)
}
}

View File

@ -13,15 +13,19 @@ package core.testutils
import com.google.common.io.BaseEncoding
import contracts.*
import core.*
import core.messaging.MessagingService
import core.visualiser.GraphVisualiser
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.DataInputStream
import java.io.DataOutputStream
import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.PrivateKey
import java.security.PublicKey
import java.time.Instant
import java.util.*
import javax.annotation.concurrent.ThreadSafe
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.fail
@ -89,6 +93,53 @@ class DummyTimestamper(private val time: Instant = TEST_TX_TIME) : TimestamperSe
val DUMMY_TIMESTAMPER = DummyTimestamper()
object MockIdentityService : IdentityService {
override fun partyFromKey(key: PublicKey): Party? = TEST_KEYS_TO_CORP_MAP[key]
}
class MockKeyManagementService(
override val keys: Map<PublicKey, PrivateKey>,
val nextKeys: MutableList<KeyPair> = arrayListOf(KeyPairGenerator.getInstance("EC").genKeyPair())
) : KeyManagementService {
override fun freshKey() = nextKeys.removeAt(nextKeys.lastIndex)
}
class MockWalletService(val states: List<StateAndRef<OwnableState>>) : WalletService {
override val currentWallet = Wallet(states)
}
@ThreadSafe
class MockStorageService : StorageService {
private val mapOfMaps = HashMap<String, MutableMap<Any, Any>>()
@Synchronized
override fun <K, V> getMap(tableName: String): MutableMap<K, V> {
return mapOfMaps.getOrPut(tableName) { Collections.synchronizedMap(HashMap<Any, Any>()) } as MutableMap<K, V>
}
}
class MockServices(
val wallet: WalletService?,
val keyManagement: KeyManagementService?,
val net: MessagingService?,
val identity: IdentityService? = MockIdentityService,
val storage: StorageService? = MockStorageService(),
val timestamping: TimestamperService? = DUMMY_TIMESTAMPER
) : ServiceHub {
override val walletService: WalletService
get() = wallet ?: throw UnsupportedOperationException()
override val keyManagementService: KeyManagementService
get() = keyManagement ?: throw UnsupportedOperationException()
override val identityService: IdentityService
get() = identity ?: throw UnsupportedOperationException()
override val timestampingService: TimestamperService
get() = timestamping ?: throw UnsupportedOperationException()
override val networkService: MessagingService
get() = net ?: throw UnsupportedOperationException()
override val storageService: StorageService
get() = storage ?: throw UnsupportedOperationException()
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Defines a simple DSL for building pseudo-transactions (not the same as the wire protocol) for testing purposes.
@ -220,7 +271,7 @@ class TransactionGroupDSL<T : ContractState>(private val stateType: Class<T>) {
private val inStates = ArrayList<ContractStateRef>()
fun input(label: String) {
inStates.add(labelToRefs[label] ?: throw IllegalArgumentException("Unknown label \"$label\""))
inStates.add(label.outputRef)
}
@ -230,11 +281,14 @@ class TransactionGroupDSL<T : ContractState>(private val stateType: Class<T>) {
*/
fun toLedgerTransaction(time: Instant): LedgerTransaction {
val wireCmds = commands.map { WireCommand(it.value, it.signers) }
return WireTransaction(inStates, outStates.map { it.state }, wireCmds).toLedgerTransaction(time, TEST_KEYS_TO_CORP_MAP, SecureHash.randomSHA256())
return WireTransaction(inStates, outStates.map { it.state }, wireCmds).toLedgerTransaction(time, MockIdentityService, SecureHash.randomSHA256())
}
}
val String.output: T get() = labelToOutputs[this] ?: throw IllegalArgumentException("State with label '$this' was not found")
val String.outputRef: ContractStateRef get() = labelToRefs[this] ?: throw IllegalArgumentException("Unknown label \"$this\"")
fun <C : ContractState> lookup(label: String) = StateAndRef(label.output as C, label.outputRef)
private inner class InternalLedgerTransactionDSL : LedgerTransactionDSL() {
fun finaliseAndInsertLabels(time: Instant): LedgerTransaction {
@ -263,11 +317,12 @@ class TransactionGroupDSL<T : ContractState>(private val stateType: Class<T>) {
fun transaction(vararg outputStates: LabeledOutput) {
val outs = outputStates.map { it.state }
val wtx = WireTransaction(emptyList(), outs, emptyList())
val ltx = wtx.toLedgerTransaction(TEST_TX_TIME, TEST_KEYS_TO_CORP_MAP, SecureHash.randomSHA256())
val ltx = wtx.toLedgerTransaction(TEST_TX_TIME, MockIdentityService, SecureHash.randomSHA256())
for ((index, state) in outputStates.withIndex()) {
val label = state.label!!
labelToRefs[label] = ContractStateRef(ltx.hash, index)
outputsToLabels[state.state] = label
labelToOutputs[label] = state.state as T
}
rootTxns.add(ltx)
}