mirror of
https://github.com/corda/corda.git
synced 2025-02-04 02:01:13 +00:00
Regen docsite
This commit is contained in:
parent
f32c83b8b3
commit
dfc15a6bab
2
docs/build/html/.buildinfo
vendored
2
docs/build/html/.buildinfo
vendored
@ -1,4 +1,4 @@
|
|||||||
# Sphinx build info version 1
|
# Sphinx build info version 1
|
||||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||||
config: b79bc8e267991c90208eba2e06b900db
|
config: ad139b05c69c728d506a9770157eaa25
|
||||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||||
|
355
docs/build/html/_sources/protocol-state-machines.txt
vendored
355
docs/build/html/_sources/protocol-state-machines.txt
vendored
@ -65,7 +65,7 @@ We use continuations for the following reasons:
|
|||||||
|
|
||||||
* It allows us to write code that is free of callbacks, that looks like ordinary sequential code.
|
* 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.
|
* 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.
|
In contrast a suspended Java thread stack can easily be 1mb in size.
|
||||||
* It frees the developer from thinking (much) about persistence and serialisation.
|
* 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
|
A *state machine* is a piece of code that moves through various *states*. These are not the same as states in the data
|
||||||
@ -86,10 +86,10 @@ Our protocol has two parties (B and S for buyer and seller) and will proceed as
|
|||||||
|
|
||||||
1. S sends a ``StateAndRef`` pointing to the state they want to sell to B, along with info about the price they require
|
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.
|
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
|
2. B sends to S a ``SignedTransaction`` 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
|
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.
|
it lacks a signature from S authorising movement of the asset.
|
||||||
3. S signs it and hands the now finalised ``SignedWireTransaction`` back to B.
|
3. S signs it and hands the now finalised ``SignedTransaction`` back to B.
|
||||||
|
|
||||||
You can find the implementation of this protocol in the file ``contracts/protocols/TwoPartyTradeProtocol.kt``.
|
You can find the implementation of this protocol in the file ``contracts/protocols/TwoPartyTradeProtocol.kt``.
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ each side.
|
|||||||
|
|
||||||
fun runSeller(smm: StateMachineManager, timestampingAuthority: LegallyIdentifiableNode,
|
fun runSeller(smm: StateMachineManager, timestampingAuthority: LegallyIdentifiableNode,
|
||||||
otherSide: SingleMessageRecipient, assetToSell: StateAndRef<OwnableState>, price: Amount,
|
otherSide: SingleMessageRecipient, assetToSell: StateAndRef<OwnableState>, price: Amount,
|
||||||
myKeyPair: KeyPair, buyerSessionID: Long): ListenableFuture<Pair<WireTransaction, LedgerTransaction>> {
|
myKeyPair: KeyPair, buyerSessionID: Long): ListenableFuture<SignedTransaction> {
|
||||||
val seller = Seller(otherSide, timestampingAuthority, assetToSell, price, myKeyPair, buyerSessionID)
|
val seller = Seller(otherSide, timestampingAuthority, assetToSell, price, myKeyPair, buyerSessionID)
|
||||||
smm.add("$TRADE_TOPIC.seller", seller)
|
smm.add("$TRADE_TOPIC.seller", seller)
|
||||||
return seller.resultFuture
|
return seller.resultFuture
|
||||||
@ -119,46 +119,46 @@ each side.
|
|||||||
|
|
||||||
fun runBuyer(smm: StateMachineManager, timestampingAuthority: LegallyIdentifiableNode,
|
fun runBuyer(smm: StateMachineManager, timestampingAuthority: LegallyIdentifiableNode,
|
||||||
otherSide: SingleMessageRecipient, acceptablePrice: Amount, typeToBuy: Class<out OwnableState>,
|
otherSide: SingleMessageRecipient, acceptablePrice: Amount, typeToBuy: Class<out OwnableState>,
|
||||||
sessionID: Long): ListenableFuture<Pair<WireTransaction, LedgerTransaction>> {
|
sessionID: Long): ListenableFuture<SignedTransaction> {
|
||||||
val buyer = Buyer(otherSide, timestampingAuthority.identity, acceptablePrice, typeToBuy, sessionID)
|
val buyer = Buyer(otherSide, timestampingAuthority.identity, acceptablePrice, typeToBuy, sessionID)
|
||||||
smm.add("$TRADE_TOPIC.buyer", buyer)
|
smm.add("$TRADE_TOPIC.buyer", buyer)
|
||||||
return buyer.resultFuture
|
return buyer.resultFuture
|
||||||
}
|
}
|
||||||
|
|
||||||
class Seller(val otherSide: SingleMessageRecipient,
|
|
||||||
val timestampingAuthority: LegallyIdentifiableNode,
|
|
||||||
val assetToSell: StateAndRef<OwnableState>,
|
|
||||||
val price: Amount,
|
|
||||||
val myKeyPair: KeyPair,
|
|
||||||
val buyerSessionID: Long) : ProtocolStateMachine<Pair<WireTransaction, LedgerTransaction>>() {
|
|
||||||
@Suspendable
|
|
||||||
override fun call(): Pair<WireTransaction, LedgerTransaction> {
|
|
||||||
TODO()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This object is serialised to the network and is the first protocol message the seller sends to the buyer.
|
// This object is serialised to the network and is the first protocol message the seller sends to the buyer.
|
||||||
private class SellerTradeInfo(
|
class SellerTradeInfo(
|
||||||
val assetForSale: StateAndRef<OwnableState>,
|
val assetForSale: StateAndRef<OwnableState>,
|
||||||
val price: Amount,
|
val price: Amount,
|
||||||
val sellerOwnerKey: PublicKey,
|
val sellerOwnerKey: PublicKey,
|
||||||
val sessionID: Long
|
val sessionID: Long
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class SignaturesFromSeller(val timestampAuthoritySig: DigitalSignature.WithKey, val sellerSig: DigitalSignature.WithKey)
|
||||||
|
|
||||||
|
class Seller(val otherSide: SingleMessageRecipient,
|
||||||
|
val timestampingAuthority: LegallyIdentifiableNode,
|
||||||
|
val assetToSell: StateAndRef<OwnableState>,
|
||||||
|
val price: Amount,
|
||||||
|
val myKeyPair: KeyPair,
|
||||||
|
val buyerSessionID: Long) : ProtocolLogic<SignedTransaction>() {
|
||||||
|
@Suspendable
|
||||||
|
override fun call(): SignedTransaction {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class UnacceptablePriceException(val givenPrice: Amount) : Exception()
|
class UnacceptablePriceException(val givenPrice: Amount) : Exception()
|
||||||
class AssetMismatchException(val expectedTypeName: String, val typeName: String) : 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"
|
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 Buyer(val otherSide: SingleMessageRecipient,
|
class Buyer(val otherSide: SingleMessageRecipient,
|
||||||
val timestampingAuthority: Party,
|
val timestampingAuthority: Party,
|
||||||
val acceptablePrice: Amount,
|
val acceptablePrice: Amount,
|
||||||
val typeToBuy: Class<out OwnableState>,
|
val typeToBuy: Class<out OwnableState>,
|
||||||
val sessionID: Long) : ProtocolStateMachine<Pair<WireTransaction, LedgerTransaction>>() {
|
val sessionID: Long) : ProtocolLogic<SignedTransaction>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): Pair<WireTransaction, LedgerTransaction> {
|
override fun call(): SignedTransaction {
|
||||||
TODO()
|
TODO()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,7 +167,7 @@ each side.
|
|||||||
Let's unpack what this code does:
|
Let's unpack what this code does:
|
||||||
|
|
||||||
- It defines a several classes nested inside the main ``TwoPartyTradeProtocol`` singleton, and a couple of methods, one
|
- It defines a several classes nested inside the main ``TwoPartyTradeProtocol`` singleton, and a couple of methods, one
|
||||||
to run the buyer side of the protocol and one to run the seller side.
|
to run the buyer side of the protocol and one to run the seller side. Some of the classes are simply protocol messages.
|
||||||
- It defines the "trade topic", which is just a string that namespaces this protocol. The prefix "platform." is reserved
|
- It defines the "trade topic", which is just a string that namespaces this protocol. The prefix "platform." is reserved
|
||||||
by the DLG, but you can define your own protocols using standard Java-style reverse DNS notation.
|
by the DLG, but you can define your own protocols using standard Java-style reverse DNS notation.
|
||||||
- The ``runBuyer`` and ``runSeller`` methods take a number of parameters that specialise the protocol for this run,
|
- The ``runBuyer`` and ``runSeller`` methods take a number of parameters that specialise the protocol for this run,
|
||||||
@ -205,7 +205,8 @@ to either runBuyer or runSeller, depending on who we are, and then call ``.get()
|
|||||||
block the calling thread until the protocol has finished. Or we could register a callback on the returned future that
|
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.
|
will be invoked when it's done, where we could e.g. update a user interface.
|
||||||
|
|
||||||
Finally, we define a couple of exceptions, and a class that will be used as a protocol message called ``SellerTradeInfo``.
|
Finally, we define a couple of exceptions, and two classes that will be used as a protocol message called
|
||||||
|
``SellerTradeInfo`` and ``SignaturesFromSeller``.
|
||||||
|
|
||||||
Suspendable methods
|
Suspendable methods
|
||||||
-------------------
|
-------------------
|
||||||
@ -244,13 +245,72 @@ Let's implement the ``Seller.call`` method. This will be invoked by the platform
|
|||||||
|
|
||||||
.. sourcecode:: kotlin
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
|
val partialTX: SignedTransaction = receiveAndCheckProposedTransaction()
|
||||||
|
|
||||||
|
// These two steps could be done in parallel, in theory. Our framework doesn't support that yet though.
|
||||||
|
val ourSignature = signWithOurKey(partialTX)
|
||||||
|
val tsaSig = subProtocol(TimestampingProtocol(timestampingAuthority, partialTX.txBits))
|
||||||
|
|
||||||
|
val stx: SignedTransaction = sendSignatures(partialTX, ourSignature, tsaSig)
|
||||||
|
|
||||||
|
return stx
|
||||||
|
|
||||||
|
Here we see the outline of the procedure. We receive a proposed trade transaction from the buyer and check that it's
|
||||||
|
valid. Then we sign with our own key, request a timestamping authority to assert with another signature that the
|
||||||
|
timestamp in the transaction (if any) is valid, and finally we send back both our signature and the TSA's signature.
|
||||||
|
Finally, we hand back to the code that invoked the protocol the finished transaction in a couple of different forms.
|
||||||
|
|
||||||
|
.. note:: ``ProtocolLogic`` classes can be composed together. Here, we see the use of the ``subProtocol`` method, which
|
||||||
|
is given an instance of ``TimestampingProtocol``. This protocol will run to completion and yield a result, almost
|
||||||
|
as if it's a regular method call. In fact, under the hood, all the ``subProtocol`` method does is pass the current
|
||||||
|
fiber object into the newly created object and then run ``call()`` on it ... so it basically _is_ just a method call.
|
||||||
|
This is where we can see the benefits of using continuations/fibers as a programming model.
|
||||||
|
|
||||||
|
Let's fill out the ``receiveAndCheckProposedTransaction()`` method.
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
open fun receiveAndCheckProposedTransaction(): SignedTransaction {
|
||||||
val sessionID = random63BitValue()
|
val sessionID = random63BitValue()
|
||||||
|
|
||||||
// Make the first message we'll send to kick off the protocol.
|
// Make the first message we'll send to kick off the protocol.
|
||||||
val hello = SellerTradeInfo(assetToSell, price, myKeyPair.public, sessionID)
|
val hello = SellerTradeInfo(assetToSell, price, myKeyPair.public, sessionID)
|
||||||
|
|
||||||
val partialTX = sendAndReceive(TRADE_TOPIC, buyerSessionID, sessionID, hello, SignedWireTransaction::class.java)
|
val maybeSTX = sendAndReceive<SignedTransaction>(TRADE_TOPIC, otherSide, buyerSessionID, sessionID, hello)
|
||||||
logger().trace { "Received partially signed transaction" }
|
|
||||||
|
maybeSTX.validate {
|
||||||
|
// Check that the tx proposed by the buyer is valid.
|
||||||
|
val missingSigs = it.verify(throwIfSignaturesAreMissing = false)
|
||||||
|
if (missingSigs != setOf(myKeyPair.public, timestampingAuthority.identity.owningKey))
|
||||||
|
throw SignatureException("The set of missing signatures is not as expected: $missingSigs")
|
||||||
|
|
||||||
|
val wtx: WireTransaction = it.tx
|
||||||
|
logger.trace { "Received partially signed transaction: ${it.id}" }
|
||||||
|
|
||||||
|
checkDependencies(it)
|
||||||
|
|
||||||
|
// This verifies that the transaction is contract-valid, even though it is missing signatures.
|
||||||
|
serviceHub.verifyTransaction(wtx.toLedgerTransaction(serviceHub.identityService))
|
||||||
|
|
||||||
|
if (wtx.outputs.sumCashBy(myKeyPair.public) != price)
|
||||||
|
throw IllegalArgumentException("Transaction is not sending us the right amounnt of cash")
|
||||||
|
|
||||||
|
// 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 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 (yet), but rather, just to find good ways to
|
||||||
|
// express protocol state machines on top of the messaging layer.
|
||||||
|
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
That's pretty straightforward. We generate a session ID to identify what's happening on the seller side, fill out
|
That's pretty straightforward. 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:
|
the initial protocol message, and then call ``sendAndReceive``. This function takes a few arguments:
|
||||||
@ -260,7 +320,7 @@ the initial protocol message, and then call ``sendAndReceive``. This function ta
|
|||||||
- The thing to send. It'll be serialised and sent automatically.
|
- The thing to send. It'll be serialised and sent automatically.
|
||||||
- Finally a type argument, which is the kind of object we're expecting to receive from the other side.
|
- Finally a type argument, which is the kind of object we're expecting to receive from the other side.
|
||||||
|
|
||||||
Once sendAndReceive is called, the call method will be suspended into a continuation. When it gets back we'll do a log
|
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
|
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.
|
cash put into the transaction and their signature on it authorising the movement of the cash.
|
||||||
|
|
||||||
@ -273,51 +333,70 @@ cash put into the transaction and their signature on it authorising the movement
|
|||||||
doing things like creating threads from inside these calls would be a bad idea. They should only contain business
|
doing things like creating threads from inside these calls would be a bad idea. They should only contain business
|
||||||
logic.
|
logic.
|
||||||
|
|
||||||
OK, let's keep going:
|
You get back a simple wrapper class, ``UntrustworthyData<SignedTransaction>``, which is just a marker class that reminds
|
||||||
|
us that the data came from a potentially malicious external source and may have been tampered with or be unexpected in
|
||||||
|
other ways. It doesn't add any functionality, but acts as a reminder to "scrub" the data before use. Here, our scrubbing
|
||||||
|
simply involves checking the signatures on it. Then we go ahead and check all the dependencies of this partial
|
||||||
|
transaction for validity. Here's the code to do that:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. sourcecode:: kotlin
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
partialTX.verifySignatures()
|
@Suspendable
|
||||||
val wtx = partialTX.txBits.deserialize<WireTransaction>()
|
private fun checkDependencies(stx: SignedTransaction) {
|
||||||
|
// Download and check all the transactions that this transaction depends on, but do not check this
|
||||||
requireThat {
|
// transaction itself.
|
||||||
"transaction sends us the right amount of cash" by (wtx.outputStates.sumCashBy(args.myKeyPair.public) == args.price)
|
val dependencyTxIDs = stx.tx.inputs.map { it.txhash }.toSet()
|
||||||
// There are all sorts of funny games a malicious secondary might play here, we should fix them:
|
subProtocol(ResolveTransactionsProtocol(dependencyTxIDs, otherSide))
|
||||||
//
|
|
||||||
// - 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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val ourSignature = args.myKeyPair.signWithECDSA(partialTX.txBits.bits)
|
This is simple enough: we mark the method as ``@Suspendable`` because we're going to invoke a sub-protocol, extract the
|
||||||
val fullySigned: SignedWireTransaction = partialTX.copy(sigs = partialTX.sigs + ourSignature)
|
IDs of the transactions the proposed transaction depends on, and then uses a protocol provided by the system to download
|
||||||
fullySigned.verify()
|
and check them all. This protocol does a breadth-first search over the dependency graph, bottoming out at issuance
|
||||||
val timestamped: TimestampedWireTransaction = fullySigned.toTimestampedTransaction(serviceHub.timestampingService)
|
transactions that don't have any inputs themselves. Once the node has audited the transaction history, all the dependencies
|
||||||
logger().trace { "Built finished transaction, sending back to secondary!" }
|
are committed to the node's local database so they won't be checked again next time.
|
||||||
|
|
||||||
send(TRADE_TOPIC, sessionID, timestamped)
|
.. note:: Transaction dependency resolution assumes that the peer you got the transaction from has all of the
|
||||||
|
dependencies itself. It must do, otherwise it could not have convinced itself that the dependencies were themselves
|
||||||
|
valid. It's important to realise that requesting only the transactions we require is a privacy leak, because if
|
||||||
|
we don't download a transaction from the peer, they know we must have already seen it before. Fixing this privacy
|
||||||
|
leak will come later.
|
||||||
|
|
||||||
return Pair(timestamped, timestamped.verifyToLedgerTransaction(serviceHub.timestampingService, serviceHub.identityService))
|
After the dependencies, we check the proposed trading transaction for validity by running the contracts for that as
|
||||||
|
well (but having handled the fact that some signatures are missing ourselves).
|
||||||
|
|
||||||
Here, we see some assertions and signature checking to satisfy ourselves that we're not about to sign something
|
Here's the rest of the code:
|
||||||
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
|
.. container:: codeset
|
||||||
transaction, in case the contracts governing the asset we're selling require it, and send the now finalised and
|
|
||||||
validated transaction back to the buyer.
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
|
open fun signWithOurKey(partialTX: SignedTransaction) = myKeyPair.signWithECDSA(partialTX.txBits)
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
open fun sendSignatures(partialTX: SignedTransaction, ourSignature: DigitalSignature.WithKey,
|
||||||
|
tsaSig: DigitalSignature.LegallyIdentifiable): SignedTransaction {
|
||||||
|
val fullySigned = partialTX + tsaSig + ourSignature
|
||||||
|
|
||||||
|
logger.trace { "Built finished transaction, sending back to secondary!" }
|
||||||
|
|
||||||
|
send(TRADE_TOPIC, otherSide, buyerSessionID, SignaturesFromSeller(tsaSig, ourSignature))
|
||||||
|
return fullySigned
|
||||||
|
}
|
||||||
|
|
||||||
|
It's should be all pretty straightforward: here, ``txBits`` is the raw byte array representing the transaction.
|
||||||
|
|
||||||
|
In ``sendSignatures``, we take the two signatures we calculated, then add them to the partial transaction we were sent.
|
||||||
|
We provide an overload for the + operator so signatures can be added to a SignedTransaction easily. Finally, we wrap the
|
||||||
|
two signatures in a simple wrapper message class and send it back. The send won't block waiting for an acknowledgement,
|
||||||
|
but the underlying message queue software will retry delivery if the other side has gone away temporarily.
|
||||||
|
|
||||||
.. warning:: This code is **not secure**. Other than not checking for all possible invalid constructions, if the
|
.. 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
|
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
|
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.
|
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
|
Implementing the buyer
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@ -328,82 +407,99 @@ OK, let's do the same for the buyer side:
|
|||||||
.. sourcecode:: kotlin
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): Pair<TimestampedWireTransaction, LedgerTransaction> {
|
override fun call(): SignedTransaction {
|
||||||
// Wait for a trade request to come in on our pre-provided session ID.
|
val tradeRequest = receiveAndValidateTradeRequest()
|
||||||
val tradeRequest = receive(TRADE_TOPIC, args.sessionID, SellerTradeInfo::class.java)
|
val (ptx, cashSigningPubKeys) = assembleSharedTX(tradeRequest)
|
||||||
|
val stx = signWithOurKeys(cashSigningPubKeys, ptx)
|
||||||
|
val signatures = swapSignaturesWithSeller(stx, tradeRequest.sessionID)
|
||||||
|
|
||||||
|
logger.trace { "Got signatures from seller, verifying ... "}
|
||||||
|
val fullySigned = stx + signatures.timestampAuthoritySig + signatures.sellerSig
|
||||||
|
fullySigned.verify()
|
||||||
|
|
||||||
|
logger.trace { "Fully signed transaction was valid. Trade complete! :-)" }
|
||||||
|
return fullySigned
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
open fun receiveAndValidateTradeRequest(): SellerTradeInfo {
|
||||||
|
// Wait for a trade request to come in on our pre-provided session ID.
|
||||||
|
val maybeTradeRequest = receive<SellerTradeInfo>(TRADE_TOPIC, sessionID)
|
||||||
|
|
||||||
|
maybeTradeRequest.validate {
|
||||||
// What is the seller trying to sell us?
|
// What is the seller trying to sell us?
|
||||||
val assetTypeName = tradeRequest.assetForSale.state.javaClass.name
|
val asset = it.assetForSale.state
|
||||||
logger().trace { "Got trade request for a $assetTypeName" }
|
val assetTypeName = asset.javaClass.name
|
||||||
|
logger.trace { "Got trade request for a $assetTypeName: ${it.assetForSale}" }
|
||||||
|
|
||||||
// Check the start message for acceptability.
|
// Check the start message for acceptability.
|
||||||
check(tradeRequest.sessionID > 0)
|
check(it.sessionID > 0)
|
||||||
if (tradeRequest.price > acceptablePrice)
|
if (it.price > acceptablePrice)
|
||||||
throw UnacceptablePriceException(tradeRequest.price)
|
throw UnacceptablePriceException(it.price)
|
||||||
if (!typeToBuy.isInstance(tradeRequest.assetForSale.state))
|
if (!typeToBuy.isInstance(asset))
|
||||||
throw AssetMismatchException(typeToBuy.name, assetTypeName)
|
throw AssetMismatchException(typeToBuy.name, assetTypeName)
|
||||||
|
|
||||||
// TODO: Either look up the stateref here in our local db, or accept a long chain
|
// Check the transaction that contains the state which is being resolved.
|
||||||
// of states and validate them to audit the other side and ensure it actually owns
|
// We only have a hash here, so if we don't know it already, we have to ask for it.
|
||||||
// the state we are being offered! For now, just assume validity!
|
subProtocol(ResolveTransactionsProtocol(setOf(it.assetForSale.ref.txhash), otherSide))
|
||||||
|
|
||||||
// Generate the shared transaction that both sides will sign, using the data we have.
|
return it
|
||||||
val ptx = TransactionBuilder()
|
}
|
||||||
// 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))
|
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
open fun swapSignaturesWithSeller(stx: SignedTransaction, theirSessionID: Long): SignaturesFromSeller {
|
||||||
|
logger.trace { "Sending partially signed transaction to seller" }
|
||||||
|
|
||||||
|
// TODO: Protect against the seller terminating here and leaving us in the lurch without the final tx.
|
||||||
|
|
||||||
|
return sendAndReceive(TRADE_TOPIC, otherSide, theirSessionID, sessionID, stx, SignaturesFromSeller::class.java).validate { it }
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun signWithOurKeys(cashSigningPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction {
|
||||||
// Now sign the transaction with whatever keys we need to move the cash.
|
// Now sign the transaction with whatever keys we need to move the cash.
|
||||||
for (k in cashSigningPubKeys) {
|
for (k in cashSigningPubKeys) {
|
||||||
val priv = serviceHub.keyManagementService.toPrivate(k)
|
val priv = serviceHub.keyManagementService.toPrivate(k)
|
||||||
ptx.signWith(KeyPair(k, priv))
|
ptx.signWith(KeyPair(k, priv))
|
||||||
}
|
}
|
||||||
|
|
||||||
val stx = ptx.toSignedTransaction(checkSufficientSignatures = false)
|
return 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.
|
open fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair<TransactionBuilder, List<PublicKey>> {
|
||||||
|
val ptx = TransactionBuilder()
|
||||||
|
// 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().generateSpend(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. This won't be linked with our identity in any way, which is what
|
||||||
|
// we want for privacy reasons: the key is here ONLY to manage and control ownership, it is not intended to
|
||||||
|
// reveal who the owner actually is. The key management service is expected to derive a unique key from some
|
||||||
|
// initial seed in order to provide privacy protection.
|
||||||
|
val freshKey = serviceHub.keyManagementService.freshKey()
|
||||||
|
val (command, state) = tradeRequest.assetForSale.state.withNewOwner(freshKey.public)
|
||||||
|
ptx.addOutputState(state)
|
||||||
|
ptx.addCommand(command, tradeRequest.assetForSale.state.owner)
|
||||||
|
|
||||||
logger().trace { "Sending partially signed transaction to seller" }
|
// And add a request for timestamping: it may be that none of the contracts need this! But it can't hurt
|
||||||
|
// to have one.
|
||||||
// TODO: Protect against the buyer terminating here and leaving us in the lurch without
|
ptx.setTime(Instant.now(), timestampingAuthority, 30.seconds)
|
||||||
// the final tx.
|
return Pair(ptx, cashSigningPubKeys)
|
||||||
// TODO: Protect against a malicious buyer sending us back a different transaction to
|
|
||||||
// the one we built.
|
|
||||||
val fullySigned = sendAndReceive(TRADE_TOPIC, tradeRequest.sessionID, sessionID, stx,
|
|
||||||
TimestampedWireTransaction::class.java)
|
|
||||||
|
|
||||||
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 longer but still fairly straightforward. Here are some things to pay attention to:
|
This code is longer but still 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.
|
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``. See the contracts tutorial if this isn't
|
2. We create a cash spend in the normal way, by using ``Cash().generateSpend``. See the contracts tutorial if this isn't
|
||||||
clear.
|
clear.
|
||||||
3. We access the *service hub* when we need it to access things that are transient and may change or be recreated
|
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
|
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
|
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'.
|
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
|
4. Finally, we send the unfinished, 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.
|
back to us a ``SignaturesFromSeller``, 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
|
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.
|
the fact that it takes minimal resources and can survive node restarts.
|
||||||
@ -415,3 +511,52 @@ the fact that it takes minimal resources and can survive node restarts.
|
|||||||
this problem doesn't occur. It's also restored for you when a protocol state machine is restored after a node
|
this problem doesn't occur. It's also restored for you when a protocol state machine is restored after a node
|
||||||
restart.
|
restart.
|
||||||
|
|
||||||
|
Progress tracking
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Not shown in the code snippets above is the usage of the ``ProgressTracker`` API. Progress tracking exports information
|
||||||
|
from a protocol about where it's got up to in such a way that observers can render it in a useful manner to humans who
|
||||||
|
may need to be informed. It may be rendered via an API, in a GUI, onto a terminal window, etc.
|
||||||
|
|
||||||
|
A ``ProgressTracker`` is constructed with a series of ``Step`` objects, where each step is an object representing a
|
||||||
|
stage in a piece of work. It is therefore typical to use singletons that subclass ``Step``, which may be defined easily
|
||||||
|
in one line when using Kotlin. Typical steps might be "Waiting for response from peer", "Waiting for signature to be
|
||||||
|
approved", "Downloading and verifying data" etc.
|
||||||
|
|
||||||
|
Each step exposes a label. By default labels are fixed, but by subclassing ``RelabelableStep``
|
||||||
|
you can make a step that can update its label on the fly. That's useful for steps that want to expose non-structured
|
||||||
|
progress information like the current file being downloaded. By defining your own step types, you can export progress
|
||||||
|
in a way that's both human readable and machine readable.
|
||||||
|
|
||||||
|
Progress trackers are hierarchical. Each step can be the parent for another tracker. By altering the
|
||||||
|
``ProgressTracker.childrenFor[step] = tracker`` map, a tree of steps can be created. It's allowed to alter the hierarchy
|
||||||
|
at runtime, on the fly, and the progress renderers will adapt to that properly. This can be helpful when you don't
|
||||||
|
fully know ahead of time what steps will be required. If you _do_ know what is required, configuring as much of the
|
||||||
|
hierarchy ahead of time is a good idea, as that will help the users see what is coming up.
|
||||||
|
|
||||||
|
Every tracker has not only the steps given to it at construction time, but also the singleton
|
||||||
|
``ProgressTracker.UNSTARTED`` step and the ``ProgressTracker.DONE`` step. Once a tracker has become ``DONE`` its
|
||||||
|
position may not be modified again (because e.g. the UI may have been removed/cleaned up), but until that point, the
|
||||||
|
position can be set to any arbitrary set both forwards and backwards. Steps may be skipped, repeated, etc. Note that
|
||||||
|
rolling the current step backwards will delete any progress trackers that are children of the steps being reversed, on
|
||||||
|
the assumption that those subtasks will have to be repeated.
|
||||||
|
|
||||||
|
Trackers provide an `Rx observable <http://reactivex.io/>`_ which streams changes to the hierarchy. The top level
|
||||||
|
observable exposes all the events generated by its children as well. The changes are represented by objects indicating
|
||||||
|
whether the change is one of position (i.e. progress), structure (i.e. new subtasks being added/removed) or some other
|
||||||
|
aspect of rendering (i.e. a step has changed in some way and is requesting a re-render).
|
||||||
|
|
||||||
|
The protocol framework is somewhat integrated with this API. Each ``ProtocolLogic`` may optionally provide a tracker by
|
||||||
|
overriding the ``protocolTracker`` property (``getProtocolTracker`` method in Java). If the
|
||||||
|
``ProtocolLogic.subProtocol`` method is used, then the tracker of the sub-protocol will be made a child of the current
|
||||||
|
step in the parent protocol automatically, if the parent is using tracking in the first place. The framework will also
|
||||||
|
automatically set the current step to ``DONE`` for you, when the protocol is finished.
|
||||||
|
|
||||||
|
Because a protocol may sometimes wish to configure the children in its progress hierarchy _before_ the sub-protocol
|
||||||
|
is constructed, for sub-protocols that always follow the same outline regardless of their parameters it's conventional
|
||||||
|
to define a companion object/static method (for Kotlin/Java respectively) that constructs a tracker, and then allow
|
||||||
|
the sub-protocol to have the tracker it will use be passed in as a parameter. This allows all trackers to be built
|
||||||
|
and linked ahead of time.
|
||||||
|
|
||||||
|
In future, the progress tracking framework will become a vital part of how exceptions, errors, and other faults are
|
||||||
|
surfaced to human operators for investigation and resolution.
|
@ -9,20 +9,20 @@ let us know.
|
|||||||
|
|
||||||
Now, open two terminals, and in the first run:::
|
Now, open two terminals, and in the first run:::
|
||||||
|
|
||||||
./gradlew runDemoBuyer
|
./scripts/trader-demo.sh buyer
|
||||||
|
|
||||||
It will create a directory named "buyer" and ask you to edit the configuration file inside. Open up ``buyer/config``
|
It will compile things, if necessary, then create a directory named "buyer" with a bunch of files inside and start
|
||||||
in your favourite text editor and give the node a legal identity of "Big Buyer Corp, Inc" or whatever else you feel like.
|
the node. You should see it waiting for a trade to begin.
|
||||||
The actual text string is not important. Now run the gradle command again, and it should start up and wait for
|
|
||||||
a seller to connect.
|
|
||||||
|
|
||||||
In the second terminal, run::
|
In the second terminal, run::
|
||||||
|
|
||||||
./gradlew runDemoSeller
|
./scripts/trader-demo.sh seller
|
||||||
|
|
||||||
and repeat the process, this time calling the node ... something else.
|
|
||||||
|
|
||||||
You should see some log lines scroll past, and within a few seconds the messages "Purchase complete - we are a
|
You should see some log lines scroll past, and within a few seconds the messages "Purchase complete - we are a
|
||||||
happy customer!" and "Sale completed - we have a happy customer!" should be printed.
|
happy customer!" and "Sale completed - we have a happy customer!" should be printed.
|
||||||
|
|
||||||
If it doesn't work, jump on the mailing list and let us know.
|
If it doesn't work, jump on the mailing list and let us know.
|
||||||
|
|
||||||
|
For Windows users, the contents of the shell script are very trivial and can easily be done by hand from a command
|
||||||
|
window. Essentially, it just runs Gradle to create the startup scripts, and then starts the node with one set of
|
||||||
|
flags or another.
|
29
docs/build/html/_sources/tutorial.txt
vendored
29
docs/build/html/_sources/tutorial.txt
vendored
@ -636,22 +636,22 @@ again to ensure the third transaction fails with a message that contains "must h
|
|||||||
the exact message).
|
the exact message).
|
||||||
|
|
||||||
|
|
||||||
Adding a crafting API to your contract
|
Adding a generation API to your contract
|
||||||
--------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
Contract classes **must** provide a verify function, but they may optionally also provide helper functions to simplify
|
Contract classes **must** provide a verify function, but they may optionally also provide helper functions to simplify
|
||||||
their usage. A simple class of functions most contracts provide are *crafting functions*, which either generate or
|
their usage. A simple class of functions most contracts provide are *generation functions*, which either create or
|
||||||
modify a transaction to perform certain actions (an action is normally mappable 1:1 to a command, but doesn't have to
|
modify a transaction to perform certain actions (an action is normally mappable 1:1 to a command, but doesn't have to
|
||||||
be so).
|
be so).
|
||||||
|
|
||||||
Crafting may involve complex logic. For example, the cash contract has a ``craftSpend`` method that is given a set of
|
Generation may involve complex logic. For example, the cash contract has a ``generateSpend`` method that is given a set of
|
||||||
cash states and chooses a way to combine them together to satisfy the amount of money that is being sent. In the
|
cash states and chooses a way to combine them together to satisfy the amount of money that is being sent. In the
|
||||||
immutable-state model that we are using ledger entries (states) can only be created and deleted, but never modified.
|
immutable-state model that we are using ledger entries (states) can only be created and deleted, but never modified.
|
||||||
Therefore to send $1200 when we have only $900 and $500 requires combining both states together, and then creating
|
Therefore to send $1200 when we have only $900 and $500 requires combining both states together, and then creating
|
||||||
two new output states of $1200 and $200 back to ourselves. This latter state is called the *change* and is a concept
|
two new output states of $1200 and $200 back to ourselves. This latter state is called the *change* and is a concept
|
||||||
that should be familiar to anyone who has worked with Bitcoin.
|
that should be familiar to anyone who has worked with Bitcoin.
|
||||||
|
|
||||||
As another example, we can imagine code that implements a netting algorithm may craft complex transactions that must
|
As another example, we can imagine code that implements a netting algorithm may generate complex transactions that must
|
||||||
be signed by many people. Whilst such code might be too big for a single utility method (it'd probably be sized more
|
be signed by many people. Whilst such code might be too big for a single utility method (it'd probably be sized more
|
||||||
like a module), the basic concept is the same: preparation of a transaction using complex logic.
|
like a module), the basic concept is the same: preparation of a transaction using complex logic.
|
||||||
|
|
||||||
@ -662,7 +662,7 @@ a method to wrap up the issuance process:
|
|||||||
|
|
||||||
.. sourcecode:: kotlin
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
fun craftIssue(issuance: InstitutionReference, faceValue: Amount, maturityDate: Instant): TransactionBuilder {
|
fun generateIssue(issuance: InstitutionReference, faceValue: Amount, maturityDate: Instant): TransactionBuilder {
|
||||||
val state = State(issuance, issuance.party.owningKey, faceValue, maturityDate)
|
val state = State(issuance, issuance.party.owningKey, faceValue, maturityDate)
|
||||||
return TransactionBuilder(state, WireCommand(Commands.Issue, issuance.party.owningKey))
|
return TransactionBuilder(state, WireCommand(Commands.Issue, issuance.party.owningKey))
|
||||||
}
|
}
|
||||||
@ -673,7 +673,7 @@ returns a ``TransactionBuilder``. A ``TransactionBuilder`` is one of the few mut
|
|||||||
It allows you to add inputs, outputs and commands to it and is designed to be passed around, potentially between
|
It allows you to add inputs, outputs and commands to it and is designed to be passed around, potentially between
|
||||||
multiple contracts.
|
multiple contracts.
|
||||||
|
|
||||||
.. note:: Crafting methods should ideally be written to compose with each other, that is, they should take a
|
.. note:: Generation methods should ideally be written to compose with each other, that is, they should take a
|
||||||
``TransactionBuilder`` as an argument instead of returning one, unless you are sure it doesn't make sense to
|
``TransactionBuilder`` as an argument instead of returning one, unless you are sure it doesn't make sense to
|
||||||
combine this type of transaction with others. In this case, issuing CP at the same time as doing other things
|
combine this type of transaction with others. In this case, issuing CP at the same time as doing other things
|
||||||
would just introduce complexity that isn't likely to be worth it, so we return a fresh object each time: instead,
|
would just introduce complexity that isn't likely to be worth it, so we return a fresh object each time: instead,
|
||||||
@ -697,7 +697,7 @@ What about moving the paper, i.e. reassigning ownership to someone else?
|
|||||||
|
|
||||||
.. sourcecode:: kotlin
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
fun craftMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) {
|
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) {
|
||||||
tx.addInputState(paper.ref)
|
tx.addInputState(paper.ref)
|
||||||
tx.addOutputState(paper.state.copy(owner = newOwner))
|
tx.addOutputState(paper.state.copy(owner = newOwner))
|
||||||
tx.addArg(WireCommand(Commands.Move, paper.state.owner))
|
tx.addArg(WireCommand(Commands.Move, paper.state.owner))
|
||||||
@ -705,7 +705,7 @@ What about moving the paper, i.e. reassigning ownership to someone else?
|
|||||||
|
|
||||||
Here, the method takes a pre-existing ``TransactionBuilder`` and adds to it. This is correct because typically
|
Here, the method takes a pre-existing ``TransactionBuilder`` and adds to it. This is correct because typically
|
||||||
you will want to combine a sale of CP atomically with the movement of some other asset, such as cash. So both
|
you will want to combine a sale of CP atomically with the movement of some other asset, such as cash. So both
|
||||||
craft methods should operate on the same transaction. You can see an example of this being done in the unit tests
|
generate methods should operate on the same transaction. You can see an example of this being done in the unit tests
|
||||||
for the commercial paper contract.
|
for the commercial paper contract.
|
||||||
|
|
||||||
The paper is given to us as a ``StateAndRef<CommercialPaper.State>`` object. This is exactly what it sounds like:
|
The paper is given to us as a ``StateAndRef<CommercialPaper.State>`` object. This is exactly what it sounds like:
|
||||||
@ -719,9 +719,9 @@ Finally, we can do redemption.
|
|||||||
.. sourcecode:: kotlin
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
@Throws(InsufficientBalanceException::class)
|
@Throws(InsufficientBalanceException::class)
|
||||||
fun craftRedeem(tx: TransactionBuilder, paper: StateAndRef<State>, wallet: List<StateAndRef<Cash.State>>) {
|
fun generateRedeem(tx: TransactionBuilder, paper: StateAndRef<State>, wallet: List<StateAndRef<Cash.State>>) {
|
||||||
// Add the cash movement using the states in our wallet.
|
// Add the cash movement using the states in our wallet.
|
||||||
Cash().craftSpend(tx, paper.state.faceValue, paper.state.owner, wallet)
|
Cash().generateSpend(tx, paper.state.faceValue, paper.state.owner, wallet)
|
||||||
tx.addInputState(paper.ref)
|
tx.addInputState(paper.ref)
|
||||||
tx.addArg(WireCommand(CommercialPaper.Commands.Redeem, paper.state.owner))
|
tx.addArg(WireCommand(CommercialPaper.Commands.Redeem, paper.state.owner))
|
||||||
}
|
}
|
||||||
@ -744,10 +744,9 @@ A ``TransactionBuilder`` is not by itself ready to be used anywhere, so first, w
|
|||||||
is recognised by the network. The most important next step is for the participating entities to sign it using the
|
is recognised by the network. The most important next step is for the participating entities to sign it using the
|
||||||
``signWith()`` method. This takes a keypair, serialises the transaction, signs the serialised form and then stores the
|
``signWith()`` method. This takes a keypair, serialises the transaction, signs the serialised form and then stores the
|
||||||
signature inside the ``TransactionBuilder``. Once all parties have signed, you can call ``TransactionBuilder.toSignedTransaction()``
|
signature inside the ``TransactionBuilder``. Once all parties have signed, you can call ``TransactionBuilder.toSignedTransaction()``
|
||||||
to get a ``SignedWireTransaction`` object. This is an immutable form of the transaction that's ready for *timestamping*.
|
to get a ``SignedTransaction`` object. This is an immutable form of the transaction that's ready for *timestamping*,
|
||||||
|
which can be done using a ``TimestamperClient``. To learn more about that, please refer to the
|
||||||
.. note:: Timestamping and passing around of partial transactions for group signing is not yet fully implemented.
|
:doc:`protocol-state-machines` document.
|
||||||
This tutorial will be updated once it is.
|
|
||||||
|
|
||||||
You can see how transactions flow through the different stages of construction by examining the commercial paper
|
You can see how transactions flow through the different stages of construction by examining the commercial paper
|
||||||
unit tests.
|
unit tests.
|
||||||
|
4
docs/build/html/_static/css/custom.css
vendored
4
docs/build/html/_static/css/custom.css
vendored
@ -25,3 +25,7 @@
|
|||||||
.codeset > .highlight-java {
|
.codeset > .highlight-java {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wy-nav-content {
|
||||||
|
max-width: 1000px;
|
||||||
|
}
|
8
docs/build/html/codestyle.html
vendored
8
docs/build/html/codestyle.html
vendored
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>Code style guide — R3 Prototyping 0.1 documentation</title>
|
<title>Code style guide — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
<link rel="prev" title="Roadmap" href="roadmap.html"/>
|
<link rel="prev" title="Roadmap" href="roadmap.html"/>
|
||||||
|
|
||||||
|
|
||||||
@ -59,7 +59,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -326,7 +326,7 @@ really necessary. In other words, don’t do this:</p>
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
9
docs/build/html/data-model.html
vendored
9
docs/build/html/data-model.html
vendored
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>Data model — R3 Prototyping 0.1 documentation</title>
|
<title>Data model — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
<link rel="next" title="Networking and messaging" href="messaging.html"/>
|
<link rel="next" title="Networking and messaging" href="messaging.html"/>
|
||||||
<link rel="prev" title="Getting set up" href="getting-set-up.html"/>
|
<link rel="prev" title="Getting set up" href="getting-set-up.html"/>
|
||||||
|
|
||||||
@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -92,6 +92,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</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="messaging.html">Networking and messaging</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="running-the-trading-demo.html">Running the trading demo</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
||||||
<ul>
|
<ul>
|
||||||
@ -292,7 +293,7 @@ platform considers non-financial applications to be out of scope.</li>
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
8
docs/build/html/genindex.html
vendored
8
docs/build/html/genindex.html
vendored
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>Index — R3 Prototyping 0.1 documentation</title>
|
<title>Index — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
|
|
||||||
|
|
||||||
<script src="_static/js/modernizr.min.js"></script>
|
<script src="_static/js/modernizr.min.js"></script>
|
||||||
@ -59,7 +59,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -179,7 +179,7 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
9
docs/build/html/getting-set-up.html
vendored
9
docs/build/html/getting-set-up.html
vendored
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>Getting set up — R3 Prototyping 0.1 documentation</title>
|
<title>Getting set up — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
<link rel="next" title="Data model" href="data-model.html"/>
|
<link rel="next" title="Data model" href="data-model.html"/>
|
||||||
<link rel="prev" title="What’s included?" href="inthebox.html"/>
|
<link rel="prev" title="What’s included?" href="inthebox.html"/>
|
||||||
|
|
||||||
@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -91,6 +91,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</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="messaging.html">Networking and messaging</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="running-the-trading-demo.html">Running the trading demo</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
||||||
<ul>
|
<ul>
|
||||||
@ -227,7 +228,7 @@ found at something like</p>
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
11
docs/build/html/index.html
vendored
11
docs/build/html/index.html
vendored
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>Welcome to the R3 prototyping repository! — R3 Prototyping 0.1 documentation</title>
|
<title>Welcome to the R3 prototyping repository! — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="#"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="#"/>
|
||||||
<link rel="next" title="What’s included?" href="inthebox.html"/>
|
<link rel="next" title="What’s included?" href="inthebox.html"/>
|
||||||
|
|
||||||
|
|
||||||
@ -59,7 +59,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ prove or disprove the following hypothesis:</p>
|
|||||||
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#understanding-fungibility">Understanding fungibility</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#understanding-fungibility">Understanding fungibility</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#checking-the-requirements">Checking the requirements</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#checking-the-requirements">Checking the requirements</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#how-to-test-your-contract">How to test your contract</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#how-to-test-your-contract">How to test your contract</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#adding-a-crafting-api-to-your-contract">Adding a crafting API to your contract</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="tutorial.html#adding-a-generation-api-to-your-contract">Adding a generation API to your contract</a></li>
|
||||||
<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>
|
<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>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@ -208,6 +208,7 @@ prove or disprove the following hypothesis:</p>
|
|||||||
<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#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-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>
|
<li class="toctree-l2"><a class="reference internal" href="protocol-state-machines.html#implementing-the-buyer">Implementing the buyer</a></li>
|
||||||
|
<li class="toctree-l2"><a class="reference internal" href="protocol-state-machines.html#progress-tracking">Progress tracking</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -267,7 +268,7 @@ prove or disprove the following hypothesis:</p>
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
8
docs/build/html/inthebox.html
vendored
8
docs/build/html/inthebox.html
vendored
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>What’s included? — R3 Prototyping 0.1 documentation</title>
|
<title>What’s included? — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
<link rel="next" title="Getting set up" href="getting-set-up.html"/>
|
<link rel="next" title="Getting set up" href="getting-set-up.html"/>
|
||||||
<link rel="prev" title="Welcome to the R3 prototyping repository!" href="index.html"/>
|
<link rel="prev" title="Welcome to the R3 prototyping repository!" href="index.html"/>
|
||||||
|
|
||||||
@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ to write contracts in both Kotlin and Java. You can <a class="reference external
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
13
docs/build/html/messaging.html
vendored
13
docs/build/html/messaging.html
vendored
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>Networking and messaging — R3 Prototyping 0.1 documentation</title>
|
<title>Networking and messaging — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,8 +30,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
<link rel="next" title="Writing a contract" href="tutorial.html"/>
|
<link rel="next" title="Running the trading demo" href="running-the-trading-demo.html"/>
|
||||||
<link rel="prev" title="Data model" href="data-model.html"/>
|
<link rel="prev" title="Data model" href="data-model.html"/>
|
||||||
|
|
||||||
|
|
||||||
@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -92,6 +92,7 @@
|
|||||||
<li class="toctree-l2"><a class="reference internal" href="#in-memory-implementation">In memory implementation</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#in-memory-implementation">In memory implementation</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="running-the-trading-demo.html">Running the trading demo</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
||||||
<ul>
|
<ul>
|
||||||
@ -239,7 +240,7 @@ nodes run in parallel, just as they would on a real network spread over multiple
|
|||||||
|
|
||||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||||
|
|
||||||
<a href="tutorial.html" class="btn btn-neutral float-right" title="Writing a contract" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
|
<a href="running-the-trading-demo.html" class="btn btn-neutral float-right" title="Running the trading demo" accesskey="n">Next <span class="fa fa-arrow-circle-right"></span></a>
|
||||||
|
|
||||||
|
|
||||||
<a href="data-model.html" class="btn btn-neutral" title="Data model" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
<a href="data-model.html" class="btn btn-neutral" title="Data model" accesskey="p"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||||
@ -273,7 +274,7 @@ nodes run in parallel, just as they would on a real network spread over multiple
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
BIN
docs/build/html/objects.inv
vendored
BIN
docs/build/html/objects.inv
vendored
Binary file not shown.
359
docs/build/html/protocol-state-machines.html
vendored
359
docs/build/html/protocol-state-machines.html
vendored
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>Protocol state machines — R3 Prototyping 0.1 documentation</title>
|
<title>Protocol state machines — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
<link rel="next" title="Using the visualiser" href="visualiser.html"/>
|
<link rel="next" title="Using the visualiser" href="visualiser.html"/>
|
||||||
<link rel="prev" title="Writing a contract" href="tutorial.html"/>
|
<link rel="prev" title="Writing a contract" href="tutorial.html"/>
|
||||||
|
|
||||||
@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -100,6 +100,7 @@
|
|||||||
<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="#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-seller">Implementing the seller</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#implementing-the-buyer">Implementing the buyer</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#implementing-the-buyer">Implementing the buyer</a></li>
|
||||||
|
<li class="toctree-l2"><a class="reference internal" href="#progress-tracking">Progress tracking</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -205,7 +206,7 @@ bytecode rewriting. You don’t have to know how this works to benefit from
|
|||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li>It allows us to write code that is free of callbacks, that looks like ordinary sequential code.</li>
|
<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.
|
<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>
|
In contrast a suspended Java thread stack can easily be 1mb in size.</li>
|
||||||
<li>It frees the developer from thinking (much) about persistence and serialisation.</li>
|
<li>It frees the developer from thinking (much) about persistence and serialisation.</li>
|
||||||
</ul>
|
</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
|
<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
|
||||||
@ -224,10 +225,10 @@ the trade is arranged isn’t covered in this article.</p>
|
|||||||
<ol class="arabic simple">
|
<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
|
<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>
|
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’s cash as input, the state with the new
|
<li>B sends to S a <code class="docutils literal"><span class="pre">SignedTransaction</span></code> that includes the state as input, B’s cash as input, the state with the new
|
||||||
owner key as output, and any change cash as output. It contains a single signature from B but isn’t valid because
|
owner key as output, and any change cash as output. It contains a single signature from B but isn’t valid because
|
||||||
it lacks a signature from S authorising movement of the asset.</li>
|
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>
|
<li>S signs it and hands the now finalised <code class="docutils literal"><span class="pre">SignedTransaction</span></code> back to B.</li>
|
||||||
</ol>
|
</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>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
|
<p>Assuming no malicious termination, they both end the protocol being in posession of a valid, signed transaction that
|
||||||
@ -242,7 +243,7 @@ each side.</p>
|
|||||||
|
|
||||||
<span class="k">fun</span> <span class="nf">runSeller</span><span class="p">(</span><span class="n">smm</span><span class="p">:</span> <span class="n">StateMachineManager</span><span class="p">,</span> <span class="n">timestampingAuthority</span><span class="p">:</span> <span class="n">LegallyIdentifiableNode</span><span class="p">,</span>
|
<span class="k">fun</span> <span class="nf">runSeller</span><span class="p">(</span><span class="n">smm</span><span class="p">:</span> <span class="n">StateMachineManager</span><span class="p">,</span> <span class="n">timestampingAuthority</span><span class="p">:</span> <span class="n">LegallyIdentifiableNode</span><span class="p">,</span>
|
||||||
<span class="n">otherSide</span><span class="p">:</span> <span class="n">SingleMessageRecipient</span><span class="p">,</span> <span class="n">assetToSell</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">OwnableState</span><span class="p">>,</span> <span class="n">price</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span>
|
<span class="n">otherSide</span><span class="p">:</span> <span class="n">SingleMessageRecipient</span><span class="p">,</span> <span class="n">assetToSell</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">OwnableState</span><span class="p">>,</span> <span class="n">price</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span>
|
||||||
<span class="n">myKeyPair</span><span class="p">:</span> <span class="n">KeyPair</span><span class="p">,</span> <span class="n">buyerSessionID</span><span class="p">:</span> <span class="n">Long</span><span class="p">):</span> <span class="n">ListenableFuture</span><span class="p"><</span><span class="n">Pair</span><span class="p"><</span><span class="n">WireTransaction</span><span class="p">,</span> <span class="n">LedgerTransaction</span><span class="p">>></span> <span class="p">{</span>
|
<span class="n">myKeyPair</span><span class="p">:</span> <span class="n">KeyPair</span><span class="p">,</span> <span class="n">buyerSessionID</span><span class="p">:</span> <span class="n">Long</span><span class="p">):</span> <span class="n">ListenableFuture</span><span class="p"><</span><span class="n">SignedTransaction</span><span class="p">></span> <span class="p">{</span>
|
||||||
<span class="k">val</span> <span class="py">seller</span> <span class="p">=</span> <span class="n">Seller</span><span class="p">(</span><span class="n">otherSide</span><span class="p">,</span> <span class="n">timestampingAuthority</span><span class="p">,</span> <span class="n">assetToSell</span><span class="p">,</span> <span class="n">price</span><span class="p">,</span> <span class="n">myKeyPair</span><span class="p">,</span> <span class="n">buyerSessionID</span><span class="p">)</span>
|
<span class="k">val</span> <span class="py">seller</span> <span class="p">=</span> <span class="n">Seller</span><span class="p">(</span><span class="n">otherSide</span><span class="p">,</span> <span class="n">timestampingAuthority</span><span class="p">,</span> <span class="n">assetToSell</span><span class="p">,</span> <span class="n">price</span><span class="p">,</span> <span class="n">myKeyPair</span><span class="p">,</span> <span class="n">buyerSessionID</span><span class="p">)</span>
|
||||||
<span class="n">smm</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="s">"$TRADE_TOPIC.seller"</span><span class="p">,</span> <span class="n">seller</span><span class="p">)</span>
|
<span class="n">smm</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="s">"$TRADE_TOPIC.seller"</span><span class="p">,</span> <span class="n">seller</span><span class="p">)</span>
|
||||||
<span class="k">return</span> <span class="n">seller</span><span class="p">.</span><span class="n">resultFuture</span>
|
<span class="k">return</span> <span class="n">seller</span><span class="p">.</span><span class="n">resultFuture</span>
|
||||||
@ -250,46 +251,46 @@ each side.</p>
|
|||||||
|
|
||||||
<span class="k">fun</span> <span class="nf">runBuyer</span><span class="p">(</span><span class="n">smm</span><span class="p">:</span> <span class="n">StateMachineManager</span><span class="p">,</span> <span class="n">timestampingAuthority</span><span class="p">:</span> <span class="n">LegallyIdentifiableNode</span><span class="p">,</span>
|
<span class="k">fun</span> <span class="nf">runBuyer</span><span class="p">(</span><span class="n">smm</span><span class="p">:</span> <span class="n">StateMachineManager</span><span class="p">,</span> <span class="n">timestampingAuthority</span><span class="p">:</span> <span class="n">LegallyIdentifiableNode</span><span class="p">,</span>
|
||||||
<span class="n">otherSide</span><span class="p">:</span> <span class="n">SingleMessageRecipient</span><span class="p">,</span> <span class="n">acceptablePrice</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span> <span class="n">typeToBuy</span><span class="p">:</span> <span class="n">Class</span><span class="p"><</span><span class="k">out</span> <span class="n">OwnableState</span><span class="p">>,</span>
|
<span class="n">otherSide</span><span class="p">:</span> <span class="n">SingleMessageRecipient</span><span class="p">,</span> <span class="n">acceptablePrice</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span> <span class="n">typeToBuy</span><span class="p">:</span> <span class="n">Class</span><span class="p"><</span><span class="k">out</span> <span class="n">OwnableState</span><span class="p">>,</span>
|
||||||
<span class="n">sessionID</span><span class="p">:</span> <span class="n">Long</span><span class="p">):</span> <span class="n">ListenableFuture</span><span class="p"><</span><span class="n">Pair</span><span class="p"><</span><span class="n">WireTransaction</span><span class="p">,</span> <span class="n">LedgerTransaction</span><span class="p">>></span> <span class="p">{</span>
|
<span class="n">sessionID</span><span class="p">:</span> <span class="n">Long</span><span class="p">):</span> <span class="n">ListenableFuture</span><span class="p"><</span><span class="n">SignedTransaction</span><span class="p">></span> <span class="p">{</span>
|
||||||
<span class="k">val</span> <span class="py">buyer</span> <span class="p">=</span> <span class="n">Buyer</span><span class="p">(</span><span class="n">otherSide</span><span class="p">,</span> <span class="n">timestampingAuthority</span><span class="p">.</span><span class="n">identity</span><span class="p">,</span> <span class="n">acceptablePrice</span><span class="p">,</span> <span class="n">typeToBuy</span><span class="p">,</span> <span class="n">sessionID</span><span class="p">)</span>
|
<span class="k">val</span> <span class="py">buyer</span> <span class="p">=</span> <span class="n">Buyer</span><span class="p">(</span><span class="n">otherSide</span><span class="p">,</span> <span class="n">timestampingAuthority</span><span class="p">.</span><span class="n">identity</span><span class="p">,</span> <span class="n">acceptablePrice</span><span class="p">,</span> <span class="n">typeToBuy</span><span class="p">,</span> <span class="n">sessionID</span><span class="p">)</span>
|
||||||
<span class="n">smm</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="s">"$TRADE_TOPIC.buyer"</span><span class="p">,</span> <span class="n">buyer</span><span class="p">)</span>
|
<span class="n">smm</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="s">"$TRADE_TOPIC.buyer"</span><span class="p">,</span> <span class="n">buyer</span><span class="p">)</span>
|
||||||
<span class="k">return</span> <span class="n">buyer</span><span class="p">.</span><span class="n">resultFuture</span>
|
<span class="k">return</span> <span class="n">buyer</span><span class="p">.</span><span class="n">resultFuture</span>
|
||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
|
|
||||||
<span class="k">class</span> <span class="nc">Seller</span><span class="p">(</span><span class="k">val</span> <span class="py">otherSide</span><span class="p">:</span> <span class="n">SingleMessageRecipient</span><span class="p">,</span>
|
|
||||||
<span class="k">val</span> <span class="py">timestampingAuthority</span><span class="p">:</span> <span class="n">LegallyIdentifiableNode</span><span class="p">,</span>
|
|
||||||
<span class="k">val</span> <span class="py">assetToSell</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">OwnableState</span><span class="p">>,</span>
|
|
||||||
<span class="k">val</span> <span class="py">price</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span>
|
|
||||||
<span class="k">val</span> <span class="py">myKeyPair</span><span class="p">:</span> <span class="n">KeyPair</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> <span class="p">:</span> <span class="n">ProtocolStateMachine</span><span class="p"><</span><span class="n">Pair</span><span class="p"><</span><span class="n">WireTransaction</span><span class="p">,</span> <span class="n">LedgerTransaction</span><span class="p">>>()</span> <span class="p">{</span>
|
|
||||||
<span class="n">@Suspendable</span>
|
|
||||||
<span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">():</span> <span class="n">Pair</span><span class="p"><</span><span class="n">WireTransaction</span><span class="p">,</span> <span class="n">LedgerTransaction</span><span class="p">></span> <span class="p">{</span>
|
|
||||||
<span class="n">TODO</span><span class="p">()</span>
|
|
||||||
<span class="p">}</span>
|
|
||||||
<span class="p">}</span>
|
|
||||||
|
|
||||||
<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="c1">// This object is serialised to the network and is the first protocol message the seller sends to the buyer.</span>
|
||||||
<span class="k">private</span> <span class="k">class</span> <span class="nc">SellerTradeInfo</span><span class="p">(</span>
|
<span class="k">class</span> <span class="nc">SellerTradeInfo</span><span class="p">(</span>
|
||||||
<span class="k">val</span> <span class="py">assetForSale</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">OwnableState</span><span class="p">>,</span>
|
<span class="k">val</span> <span class="py">assetForSale</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">OwnableState</span><span class="p">>,</span>
|
||||||
<span class="k">val</span> <span class="py">price</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span>
|
<span class="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">sellerOwnerKey</span><span class="p">:</span> <span class="n">PublicKey</span><span class="p">,</span>
|
||||||
<span class="k">val</span> <span class="py">sessionID</span><span class="p">:</span> <span class="n">Long</span>
|
<span class="k">val</span> <span class="py">sessionID</span><span class="p">:</span> <span class="n">Long</span>
|
||||||
<span class="p">)</span>
|
<span class="p">)</span>
|
||||||
|
|
||||||
|
<span class="k">class</span> <span class="nc">SignaturesFromSeller</span><span class="p">(</span><span class="k">val</span> <span class="py">timestampAuthoritySig</span><span class="p">:</span> <span class="n">DigitalSignature</span><span class="p">.</span><span class="n">WithKey</span><span class="p">,</span> <span class="k">val</span> <span class="py">sellerSig</span><span class="p">:</span> <span class="n">DigitalSignature</span><span class="p">.</span><span class="n">WithKey</span><span class="p">)</span>
|
||||||
|
|
||||||
|
<span class="k">class</span> <span class="nc">Seller</span><span class="p">(</span><span class="k">val</span> <span class="py">otherSide</span><span class="p">:</span> <span class="n">SingleMessageRecipient</span><span class="p">,</span>
|
||||||
|
<span class="k">val</span> <span class="py">timestampingAuthority</span><span class="p">:</span> <span class="n">LegallyIdentifiableNode</span><span class="p">,</span>
|
||||||
|
<span class="k">val</span> <span class="py">assetToSell</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">OwnableState</span><span class="p">>,</span>
|
||||||
|
<span class="k">val</span> <span class="py">price</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span>
|
||||||
|
<span class="k">val</span> <span class="py">myKeyPair</span><span class="p">:</span> <span class="n">KeyPair</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> <span class="p">:</span> <span class="n">ProtocolLogic</span><span class="p"><</span><span class="n">SignedTransaction</span><span class="p">>()</span> <span class="p">{</span>
|
||||||
|
<span class="n">@Suspendable</span>
|
||||||
|
<span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">():</span> <span class="n">SignedTransaction</span> <span class="p">{</span>
|
||||||
|
<span class="n">TODO</span><span class="p">()</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
|
||||||
<span class="k">class</span> <span class="nc">UnacceptablePriceException</span><span class="p">(</span><span class="k">val</span> <span class="py">givenPrice</span><span class="p">:</span> <span class="n">Amount</span><span class="p">)</span> <span class="p">:</span> <span class="n">Exception</span><span class="p">()</span>
|
<span class="k">class</span> <span class="nc">UnacceptablePriceException</span><span class="p">(</span><span class="k">val</span> <span class="py">givenPrice</span><span class="p">:</span> <span class="n">Amount</span><span class="p">)</span> <span class="p">:</span> <span class="n">Exception</span><span class="p">()</span>
|
||||||
<span class="k">class</span> <span class="nc">AssetMismatchException</span><span class="p">(</span><span class="k">val</span> <span class="py">expectedTypeName</span><span class="p">:</span> <span class="n">String</span><span class="p">,</span> <span class="k">val</span> <span class="py">typeName</span><span class="p">:</span> <span class="n">String</span><span class="p">)</span> <span class="p">:</span> <span class="n">Exception</span><span class="p">()</span> <span class="p">{</span>
|
<span class="k">class</span> <span class="nc">AssetMismatchException</span><span class="p">(</span><span class="k">val</span> <span class="py">expectedTypeName</span><span class="p">:</span> <span class="n">String</span><span class="p">,</span> <span class="k">val</span> <span class="py">typeName</span><span class="p">:</span> <span class="n">String</span><span class="p">)</span> <span class="p">:</span> <span class="n">Exception</span><span class="p">()</span> <span class="p">{</span>
|
||||||
<span class="k">override</span> <span class="k">fun</span> <span class="nf">toString</span><span class="p">()</span> <span class="p">=</span> <span class="s">"The submitted asset didn't match the expected type: $expectedTypeName vs $typeName"</span>
|
<span class="k">override</span> <span class="k">fun</span> <span class="nf">toString</span><span class="p">()</span> <span class="p">=</span> <span class="s">"The submitted asset didn't match the expected type: $expectedTypeName vs $typeName"</span>
|
||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
|
|
||||||
<span class="c1">// The buyer's side of the protocol. See note above Seller to learn about the caveats here.</span>
|
|
||||||
<span class="k">class</span> <span class="nc">Buyer</span><span class="p">(</span><span class="k">val</span> <span class="py">otherSide</span><span class="p">:</span> <span class="n">SingleMessageRecipient</span><span class="p">,</span>
|
<span class="k">class</span> <span class="nc">Buyer</span><span class="p">(</span><span class="k">val</span> <span class="py">otherSide</span><span class="p">:</span> <span class="n">SingleMessageRecipient</span><span class="p">,</span>
|
||||||
<span class="k">val</span> <span class="py">timestampingAuthority</span><span class="p">:</span> <span class="n">Party</span><span class="p">,</span>
|
<span class="k">val</span> <span class="py">timestampingAuthority</span><span class="p">:</span> <span class="n">Party</span><span class="p">,</span>
|
||||||
<span class="k">val</span> <span class="py">acceptablePrice</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span>
|
<span class="k">val</span> <span class="py">acceptablePrice</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span>
|
||||||
<span class="k">val</span> <span class="py">typeToBuy</span><span class="p">:</span> <span class="n">Class</span><span class="p"><</span><span class="k">out</span> <span class="n">OwnableState</span><span class="p">>,</span>
|
<span class="k">val</span> <span class="py">typeToBuy</span><span class="p">:</span> <span class="n">Class</span><span class="p"><</span><span class="k">out</span> <span class="n">OwnableState</span><span class="p">>,</span>
|
||||||
<span class="k">val</span> <span class="py">sessionID</span><span class="p">:</span> <span class="n">Long</span><span class="p">)</span> <span class="p">:</span> <span class="n">ProtocolStateMachine</span><span class="p"><</span><span class="n">Pair</span><span class="p"><</span><span class="n">WireTransaction</span><span class="p">,</span> <span class="n">LedgerTransaction</span><span class="p">>>()</span> <span class="p">{</span>
|
<span class="k">val</span> <span class="py">sessionID</span><span class="p">:</span> <span class="n">Long</span><span class="p">)</span> <span class="p">:</span> <span class="n">ProtocolLogic</span><span class="p"><</span><span class="n">SignedTransaction</span><span class="p">>()</span> <span class="p">{</span>
|
||||||
<span class="n">@Suspendable</span>
|
<span class="n">@Suspendable</span>
|
||||||
<span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">():</span> <span class="n">Pair</span><span class="p"><</span><span class="n">WireTransaction</span><span class="p">,</span> <span class="n">LedgerTransaction</span><span class="p">></span> <span class="p">{</span>
|
<span class="k">override</span> <span class="k">fun</span> <span class="nf">call</span><span class="p">():</span> <span class="n">SignedTransaction</span> <span class="p">{</span>
|
||||||
<span class="n">TODO</span><span class="p">()</span>
|
<span class="n">TODO</span><span class="p">()</span>
|
||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
@ -300,7 +301,7 @@ each side.</p>
|
|||||||
<p>Let’s unpack what this code does:</p>
|
<p>Let’s unpack what this code does:</p>
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li>It defines a several classes nested inside the main <code class="docutils literal"><span class="pre">TwoPartyTradeProtocol</span></code> singleton, and a couple of methods, one
|
<li>It defines a several classes nested inside the main <code class="docutils literal"><span class="pre">TwoPartyTradeProtocol</span></code> singleton, and a couple of methods, one
|
||||||
to run the buyer side of the protocol and one to run the seller side.</li>
|
to run the buyer side of the protocol and one to run the seller side. Some of the classes are simply protocol messages.</li>
|
||||||
<li>It defines the “trade topic”, which is just a string that namespaces this protocol. The prefix “platform.” is reserved
|
<li>It defines the “trade topic”, which is just a string that namespaces this protocol. The prefix “platform.” is reserved
|
||||||
by the DLG, but you can define your own protocols using standard Java-style reverse DNS notation.</li>
|
by the DLG, but you can define your own protocols using standard Java-style reverse DNS notation.</li>
|
||||||
<li>The <code class="docutils literal"><span class="pre">runBuyer</span></code> and <code class="docutils literal"><span class="pre">runSeller</span></code> methods take a number of parameters that specialise the protocol for this run,
|
<li>The <code class="docutils literal"><span class="pre">runBuyer</span></code> and <code class="docutils literal"><span class="pre">runSeller</span></code> methods take a number of parameters that specialise the protocol for this run,
|
||||||
@ -338,7 +339,8 @@ sell side of the protocol isn’t trying to sell us the wrong thing, whether
|
|||||||
to either runBuyer or runSeller, depending on who we are, and then call <code class="docutils literal"><span class="pre">.get()</span></code> on resulting object to
|
to either runBuyer or runSeller, depending on who we are, and then call <code class="docutils literal"><span class="pre">.get()</span></code> on resulting object to
|
||||||
block the calling thread until the protocol has finished. Or we could register a callback on the returned future that
|
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.</p>
|
will be invoked when it’s done, where we could e.g. update a user interface.</p>
|
||||||
<p>Finally, we define a couple of exceptions, and a class that will be used as a protocol message called <code class="docutils literal"><span class="pre">SellerTradeInfo</span></code>.</p>
|
<p>Finally, we define a couple of exceptions, and two classes that will be used as a protocol message called
|
||||||
|
<code class="docutils literal"><span class="pre">SellerTradeInfo</span></code> and <code class="docutils literal"><span class="pre">SignaturesFromSeller</span></code>.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="suspendable-methods">
|
<div class="section" id="suspendable-methods">
|
||||||
<h2>Suspendable methods<a class="headerlink" href="#suspendable-methods" title="Permalink to this headline">¶</a></h2>
|
<h2>Suspendable methods<a class="headerlink" href="#suspendable-methods" title="Permalink to this headline">¶</a></h2>
|
||||||
@ -370,13 +372,71 @@ unit tests to see how it’s done.</p>
|
|||||||
<p>Let’s implement the <code class="docutils literal"><span class="pre">Seller.call</span></code> method. This will be invoked by the platform when the protocol is started by the
|
<p>Let’s implement the <code class="docutils literal"><span class="pre">Seller.call</span></code> method. This will be invoked by the platform when the protocol is started by the
|
||||||
<code class="docutils literal"><span class="pre">StateMachineManager</span></code>.</p>
|
<code class="docutils literal"><span class="pre">StateMachineManager</span></code>.</p>
|
||||||
<div class="codeset container">
|
<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>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">val</span> <span class="py">partialTX</span><span class="p">:</span> <span class="n">SignedTransaction</span> <span class="p">=</span> <span class="n">receiveAndCheckProposedTransaction</span><span class="p">()</span>
|
||||||
|
|
||||||
|
<span class="c1">// These two steps could be done in parallel, in theory. Our framework doesn't support that yet though.</span>
|
||||||
|
<span class="k">val</span> <span class="py">ourSignature</span> <span class="p">=</span> <span class="n">signWithOurKey</span><span class="p">(</span><span class="n">partialTX</span><span class="p">)</span>
|
||||||
|
<span class="k">val</span> <span class="py">tsaSig</span> <span class="p">=</span> <span class="n">subProtocol</span><span class="p">(</span><span class="n">TimestampingProtocol</span><span class="p">(</span><span class="n">timestampingAuthority</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="k">val</span> <span class="py">stx</span><span class="p">:</span> <span class="n">SignedTransaction</span> <span class="p">=</span> <span class="n">sendSignatures</span><span class="p">(</span><span class="n">partialTX</span><span class="p">,</span> <span class="n">ourSignature</span><span class="p">,</span> <span class="n">tsaSig</span><span class="p">)</span>
|
||||||
|
|
||||||
|
<span class="k">return</span> <span class="n">stx</span>
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>Here we see the outline of the procedure. We receive a proposed trade transaction from the buyer and check that it’s
|
||||||
|
valid. Then we sign with our own key, request a timestamping authority to assert with another signature that the
|
||||||
|
timestamp in the transaction (if any) is valid, and finally we send back both our signature and the TSA’s signature.
|
||||||
|
Finally, we hand back to the code that invoked the protocol the finished transaction in a couple of different forms.</p>
|
||||||
|
<div class="admonition note">
|
||||||
|
<p class="first admonition-title">Note</p>
|
||||||
|
<p class="last"><code class="docutils literal"><span class="pre">ProtocolLogic</span></code> classes can be composed together. Here, we see the use of the <code class="docutils literal"><span class="pre">subProtocol</span></code> method, which
|
||||||
|
is given an instance of <code class="docutils literal"><span class="pre">TimestampingProtocol</span></code>. This protocol will run to completion and yield a result, almost
|
||||||
|
as if it’s a regular method call. In fact, under the hood, all the <code class="docutils literal"><span class="pre">subProtocol</span></code> method does is pass the current
|
||||||
|
fiber object into the newly created object and then run <code class="docutils literal"><span class="pre">call()</span></code> on it ... so it basically _is_ just a method call.
|
||||||
|
This is where we can see the benefits of using continuations/fibers as a programming model.</p>
|
||||||
|
</div>
|
||||||
|
<p>Let’s fill out the <code class="docutils literal"><span class="pre">receiveAndCheckProposedTransaction()</span></code> method.</p>
|
||||||
|
<div class="codeset container">
|
||||||
|
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">@Suspendable</span>
|
||||||
|
<span class="k">open</span> <span class="k">fun</span> <span class="nf">receiveAndCheckProposedTransaction</span><span class="p">():</span> <span class="n">SignedTransaction</span> <span class="p">{</span>
|
||||||
|
<span class="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'll send to kick off the protocol.</span>
|
<span class="c1">// Make the first message we'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">assetToSell</span><span class="p">,</span> <span class="n">price</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="k">val</span> <span class="py">hello</span> <span class="p">=</span> <span class="n">SellerTradeInfo</span><span class="p">(</span><span class="n">assetToSell</span><span class="p">,</span> <span class="n">price</span><span class="p">,</span> <span class="n">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="k">val</span> <span class="py">partialTX</span> <span class="p">=</span> <span class="n">sendAndReceive</span><span class="p">(</span><span class="n">TRADE_TOPIC</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">SignedWireTransaction</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
|
<span class="k">val</span> <span class="py">maybeSTX</span> <span class="p">=</span> <span class="n">sendAndReceive</span><span class="p"><</span><span class="n">SignedTransaction</span><span class="p">>(</span><span class="n">TRADE_TOPIC</span><span class="p">,</span> <span class="n">otherSide</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">"Received partially signed transaction"</span> <span class="p">}</span>
|
|
||||||
|
<span class="n">maybeSTX</span><span class="p">.</span><span class="n">validate</span> <span class="p">{</span>
|
||||||
|
<span class="c1">// Check that the tx proposed by the buyer is valid.</span>
|
||||||
|
<span class="k">val</span> <span class="py">missingSigs</span> <span class="p">=</span> <span class="n">it</span><span class="p">.</span><span class="n">verify</span><span class="p">(</span><span class="n">throwIfSignaturesAreMissing</span> <span class="p">=</span> <span class="k">false</span><span class="p">)</span>
|
||||||
|
<span class="k">if</span> <span class="p">(</span><span class="n">missingSigs</span> <span class="p">!=</span> <span class="n">setOf</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">timestampingAuthority</span><span class="p">.</span><span class="n">identity</span><span class="p">.</span><span class="n">owningKey</span><span class="p">))</span>
|
||||||
|
<span class="k">throw</span> <span class="n">SignatureException</span><span class="p">(</span><span class="s">"The set of missing signatures is not as expected: $missingSigs"</span><span class="p">)</span>
|
||||||
|
|
||||||
|
<span class="k">val</span> <span class="py">wtx</span><span class="p">:</span> <span class="n">WireTransaction</span> <span class="p">=</span> <span class="n">it</span><span class="p">.</span><span class="n">tx</span>
|
||||||
|
<span class="n">logger</span><span class="p">.</span><span class="n">trace</span> <span class="p">{</span> <span class="s">"Received partially signed transaction: ${it.id}"</span> <span class="p">}</span>
|
||||||
|
|
||||||
|
<span class="n">checkDependencies</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
|
||||||
|
|
||||||
|
<span class="c1">// This verifies that the transaction is contract-valid, even though it is missing signatures.</span>
|
||||||
|
<span class="n">serviceHub</span><span class="p">.</span><span class="n">verifyTransaction</span><span class="p">(</span><span class="n">wtx</span><span class="p">.</span><span class="n">toLedgerTransaction</span><span class="p">(</span><span class="n">serviceHub</span><span class="p">.</span><span class="n">identityService</span><span class="p">))</span>
|
||||||
|
|
||||||
|
<span class="k">if</span> <span class="p">(</span><span class="n">wtx</span><span class="p">.</span><span class="n">outputs</span><span class="p">.</span><span class="n">sumCashBy</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">price</span><span class="p">)</span>
|
||||||
|
<span class="k">throw</span> <span class="n">IllegalArgumentException</span><span class="p">(</span><span class="s">"Transaction is not sending us the right amounnt of cash"</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't intending to sell to the secondary, if</span>
|
||||||
|
<span class="c1">// we're reusing keys! So don't reuse keys!</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 (yet), 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="k">return</span> <span class="n">it</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
<span class="p">}</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -388,7 +448,7 @@ the initial protocol message, and then call <code class="docutils literal"><span
|
|||||||
<li>The thing to send. It’ll be serialised and sent automatically.</li>
|
<li>The thing to send. It’ll be serialised and sent automatically.</li>
|
||||||
<li>Finally a type argument, which is the kind of object we’re expecting to receive from the other side.</li>
|
<li>Finally a type argument, which is the kind of object we’re expecting to receive from the other side.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Once sendAndReceive is called, the call method will be suspended into a continuation. When it gets back we’ll do a log
|
<p>Once <code class="docutils literal"><span class="pre">sendAndReceive</span></code> 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
|
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>
|
cash put into the transaction and their signature on it authorising the movement of the cash.</p>
|
||||||
<div class="admonition note">
|
<div class="admonition note">
|
||||||
@ -401,40 +461,59 @@ of the function call may be resurrected much later! Kryo doesn’t require o
|
|||||||
doing things like creating threads from inside these calls would be a bad idea. They should only contain business
|
doing things like creating threads from inside these calls would be a bad idea. They should only contain business
|
||||||
logic.</p>
|
logic.</p>
|
||||||
</div>
|
</div>
|
||||||
<p>OK, let’s keep going:</p>
|
<p>You get back a simple wrapper class, <code class="docutils literal"><span class="pre">UntrustworthyData<SignedTransaction></span></code>, which is just a marker class that reminds
|
||||||
|
us that the data came from a potentially malicious external source and may have been tampered with or be unexpected in
|
||||||
|
other ways. It doesn’t add any functionality, but acts as a reminder to “scrub” the data before use. Here, our scrubbing
|
||||||
|
simply involves checking the signatures on it. Then we go ahead and check all the dependencies of this partial
|
||||||
|
transaction for validity. Here’s the code to do that:</p>
|
||||||
<div class="codeset container">
|
<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>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">@Suspendable</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"><</span><span class="n">WireTransaction</span><span class="p">>()</span>
|
<span class="k">private</span> <span class="k">fun</span> <span class="nf">checkDependencies</span><span class="p">(</span><span class="n">stx</span><span class="p">:</span> <span class="n">SignedTransaction</span><span class="p">)</span> <span class="p">{</span>
|
||||||
|
<span class="c1">// Download and check all the transactions that this transaction depends on, but do not check this</span>
|
||||||
<span class="n">requireThat</span> <span class="p">{</span>
|
<span class="c1">// transaction itself.</span>
|
||||||
<span class="s">"transaction sends us the right amount of cash"</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="k">val</span> <span class="py">dependencyTxIDs</span> <span class="p">=</span> <span class="n">stx</span><span class="p">.</span><span class="n">tx</span><span class="p">.</span><span class="n">inputs</span><span class="p">.</span><span class="n">map</span> <span class="p">{</span> <span class="n">it</span><span class="p">.</span><span class="n">txhash</span> <span class="p">}.</span><span class="n">toSet</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="n">subProtocol</span><span class="p">(</span><span class="n">ResolveTransactionsProtocol</span><span class="p">(</span><span class="n">dependencyTxIDs</span><span class="p">,</span> <span class="n">otherSide</span><span class="p">))</span>
|
||||||
<span class="c1">//</span>
|
|
||||||
<span class="c1">// - This tx may attempt to send some assets we aren't intending to sell to the secondary, if</span>
|
|
||||||
<span class="c1">// we're reusing keys! So don'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="p">}</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="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">"Built finished transaction, sending back to secondary!"</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>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>Here, we see some assertions and signature checking to satisfy ourselves that we’re not about to sign something
|
<p>This is simple enough: we mark the method as <code class="docutils literal"><span class="pre">@Suspendable</span></code> because we’re going to invoke a sub-protocol, extract the
|
||||||
incorrect. Once we’re happy, we calculate a signature over the transaction to authorise the movement of the asset
|
IDs of the transactions the proposed transaction depends on, and then uses a protocol provided by the system to download
|
||||||
we are selling, and then we verify things to make sure it’s all OK. Finally, we request timestamping of the
|
and check them all. This protocol does a breadth-first search over the dependency graph, bottoming out at issuance
|
||||||
transaction, in case the contracts governing the asset we’re selling require it, and send the now finalised and
|
transactions that don’t have any inputs themselves. Once the node has audited the transaction history, all the dependencies
|
||||||
validated transaction back to the buyer.</p>
|
are committed to the node’s local database so they won’t be checked again next time.</p>
|
||||||
|
<div class="admonition note">
|
||||||
|
<p class="first admonition-title">Note</p>
|
||||||
|
<p class="last">Transaction dependency resolution assumes that the peer you got the transaction from has all of the
|
||||||
|
dependencies itself. It must do, otherwise it could not have convinced itself that the dependencies were themselves
|
||||||
|
valid. It’s important to realise that requesting only the transactions we require is a privacy leak, because if
|
||||||
|
we don’t download a transaction from the peer, they know we must have already seen it before. Fixing this privacy
|
||||||
|
leak will come later.</p>
|
||||||
|
</div>
|
||||||
|
<p>After the dependencies, we check the proposed trading transaction for validity by running the contracts for that as
|
||||||
|
well (but having handled the fact that some signatures are missing ourselves).</p>
|
||||||
|
<p>Here’s the rest of the code:</p>
|
||||||
|
<div class="codeset container">
|
||||||
|
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">open</span> <span class="k">fun</span> <span class="nf">signWithOurKey</span><span class="p">(</span><span class="n">partialTX</span><span class="p">:</span> <span class="n">SignedTransaction</span><span class="p">)</span> <span class="p">=</span> <span class="n">myKeyPair</span><span class="p">.</span><span class="n">signWithECDSA</span><span class="p">(</span><span class="n">partialTX</span><span class="p">.</span><span class="n">txBits</span><span class="p">)</span>
|
||||||
|
|
||||||
|
<span class="n">@Suspendable</span>
|
||||||
|
<span class="k">open</span> <span class="k">fun</span> <span class="nf">sendSignatures</span><span class="p">(</span><span class="n">partialTX</span><span class="p">:</span> <span class="n">SignedTransaction</span><span class="p">,</span> <span class="n">ourSignature</span><span class="p">:</span> <span class="n">DigitalSignature</span><span class="p">.</span><span class="n">WithKey</span><span class="p">,</span>
|
||||||
|
<span class="n">tsaSig</span><span class="p">:</span> <span class="n">DigitalSignature</span><span class="p">.</span><span class="n">LegallyIdentifiable</span><span class="p">):</span> <span class="n">SignedTransaction</span> <span class="p">{</span>
|
||||||
|
<span class="k">val</span> <span class="py">fullySigned</span> <span class="p">=</span> <span class="n">partialTX</span> <span class="p">+</span> <span class="n">tsaSig</span> <span class="p">+</span> <span class="n">ourSignature</span>
|
||||||
|
|
||||||
|
<span class="n">logger</span><span class="p">.</span><span class="n">trace</span> <span class="p">{</span> <span class="s">"Built finished transaction, sending back to secondary!"</span> <span class="p">}</span>
|
||||||
|
|
||||||
|
<span class="n">send</span><span class="p">(</span><span class="n">TRADE_TOPIC</span><span class="p">,</span> <span class="n">otherSide</span><span class="p">,</span> <span class="n">buyerSessionID</span><span class="p">,</span> <span class="n">SignaturesFromSeller</span><span class="p">(</span><span class="n">tsaSig</span><span class="p">,</span> <span class="n">ourSignature</span><span class="p">))</span>
|
||||||
|
<span class="k">return</span> <span class="n">fullySigned</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
</pre></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>It’s should be all pretty straightforward: here, <code class="docutils literal"><span class="pre">txBits</span></code> is the raw byte array representing the transaction.</p>
|
||||||
|
<p>In <code class="docutils literal"><span class="pre">sendSignatures</span></code>, we take the two signatures we calculated, then add them to the partial transaction we were sent.
|
||||||
|
We provide an overload for the + operator so signatures can be added to a SignedTransaction easily. Finally, we wrap the
|
||||||
|
two signatures in a simple wrapper message class and send it back. The send won’t block waiting for an acknowledgement,
|
||||||
|
but the underlying message queue software will retry delivery if the other side has gone away temporarily.</p>
|
||||||
<div class="admonition warning">
|
<div class="admonition warning">
|
||||||
<p class="first admonition-title">Warning</p>
|
<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
|
<p class="last">This code is <strong>not secure</strong>. Other than not checking for all possible invalid constructions, if the
|
||||||
@ -442,77 +521,92 @@ seller stops before sending the finalised transaction to the buyer, the seller i
|
|||||||
but the buyer isn’t, so they can’t spend the asset they just purchased! This sort of thing will be fixed in a
|
but the buyer isn’t, so they can’t spend the asset they just purchased! This sort of thing will be fixed in a
|
||||||
future version of the code.</p>
|
future version of the code.</p>
|
||||||
</div>
|
</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>
|
||||||
<div class="section" id="implementing-the-buyer">
|
<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>
|
<h2>Implementing the buyer<a class="headerlink" href="#implementing-the-buyer" title="Permalink to this headline">¶</a></h2>
|
||||||
<p>OK, let’s do the same for the buyer side:</p>
|
<p>OK, let’s do the same for the buyer side:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre>@Suspendable
|
<div class="highlight-kotlin"><div class="highlight"><pre>@Suspendable
|
||||||
override fun call(): Pair<TimestampedWireTransaction, LedgerTransaction> {
|
override fun call(): SignedTransaction {
|
||||||
// Wait for a trade request to come in on our pre-provided session ID.
|
val tradeRequest = receiveAndValidateTradeRequest()
|
||||||
val tradeRequest = receive(TRADE_TOPIC, args.sessionID, SellerTradeInfo::class.java)
|
val (ptx, cashSigningPubKeys) = assembleSharedTX(tradeRequest)
|
||||||
|
val stx = signWithOurKeys(cashSigningPubKeys, ptx)
|
||||||
|
val signatures = swapSignaturesWithSeller(stx, tradeRequest.sessionID)
|
||||||
|
|
||||||
|
logger.trace { "Got signatures from seller, verifying ... "}
|
||||||
|
val fullySigned = stx + signatures.timestampAuthoritySig + signatures.sellerSig
|
||||||
|
fullySigned.verify()
|
||||||
|
|
||||||
|
logger.trace { "Fully signed transaction was valid. Trade complete! :-)" }
|
||||||
|
return fullySigned
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
open fun receiveAndValidateTradeRequest(): SellerTradeInfo {
|
||||||
|
// Wait for a trade request to come in on our pre-provided session ID.
|
||||||
|
val maybeTradeRequest = receive<SellerTradeInfo>(TRADE_TOPIC, sessionID)
|
||||||
|
|
||||||
|
maybeTradeRequest.validate {
|
||||||
// What is the seller trying to sell us?
|
// What is the seller trying to sell us?
|
||||||
val assetTypeName = tradeRequest.assetForSale.state.javaClass.name
|
val asset = it.assetForSale.state
|
||||||
logger().trace { "Got trade request for a $assetTypeName" }
|
val assetTypeName = asset.javaClass.name
|
||||||
|
logger.trace { "Got trade request for a $assetTypeName: ${it.assetForSale}" }
|
||||||
|
|
||||||
// Check the start message for acceptability.
|
// Check the start message for acceptability.
|
||||||
check(tradeRequest.sessionID > 0)
|
check(it.sessionID > 0)
|
||||||
if (tradeRequest.price > acceptablePrice)
|
if (it.price > acceptablePrice)
|
||||||
throw UnacceptablePriceException(tradeRequest.price)
|
throw UnacceptablePriceException(it.price)
|
||||||
if (!typeToBuy.isInstance(tradeRequest.assetForSale.state))
|
if (!typeToBuy.isInstance(asset))
|
||||||
throw AssetMismatchException(typeToBuy.name, assetTypeName)
|
throw AssetMismatchException(typeToBuy.name, assetTypeName)
|
||||||
|
|
||||||
// TODO: Either look up the stateref here in our local db, or accept a long chain
|
// Check the transaction that contains the state which is being resolved.
|
||||||
// of states and validate them to audit the other side and ensure it actually owns
|
// We only have a hash here, so if we don't know it already, we have to ask for it.
|
||||||
// the state we are being offered! For now, just assume validity!
|
subProtocol(ResolveTransactionsProtocol(setOf(it.assetForSale.ref.txhash), otherSide))
|
||||||
|
|
||||||
// Generate the shared transaction that both sides will sign, using the data we have.
|
return it
|
||||||
val ptx = TransactionBuilder()
|
}
|
||||||
// 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))
|
|
||||||
|
|
||||||
|
@Suspendable
|
||||||
|
open fun swapSignaturesWithSeller(stx: SignedTransaction, theirSessionID: Long): SignaturesFromSeller {
|
||||||
|
logger.trace { "Sending partially signed transaction to seller" }
|
||||||
|
|
||||||
|
// TODO: Protect against the seller terminating here and leaving us in the lurch without the final tx.
|
||||||
|
|
||||||
|
return sendAndReceive(TRADE_TOPIC, otherSide, theirSessionID, sessionID, stx, SignaturesFromSeller::class.java).validate { it }
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun signWithOurKeys(cashSigningPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction {
|
||||||
// Now sign the transaction with whatever keys we need to move the cash.
|
// Now sign the transaction with whatever keys we need to move the cash.
|
||||||
for (k in cashSigningPubKeys) {
|
for (k in cashSigningPubKeys) {
|
||||||
val priv = serviceHub.keyManagementService.toPrivate(k)
|
val priv = serviceHub.keyManagementService.toPrivate(k)
|
||||||
ptx.signWith(KeyPair(k, priv))
|
ptx.signWith(KeyPair(k, priv))
|
||||||
}
|
}
|
||||||
|
|
||||||
val stx = ptx.toSignedTransaction(checkSufficientSignatures = false)
|
return 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.
|
open fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair<TransactionBuilder, List<PublicKey>> {
|
||||||
|
val ptx = TransactionBuilder()
|
||||||
|
// 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().generateSpend(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. This won't be linked with our identity in any way, which is what
|
||||||
|
// we want for privacy reasons: the key is here ONLY to manage and control ownership, it is not intended to
|
||||||
|
// reveal who the owner actually is. The key management service is expected to derive a unique key from some
|
||||||
|
// initial seed in order to provide privacy protection.
|
||||||
|
val freshKey = serviceHub.keyManagementService.freshKey()
|
||||||
|
val (command, state) = tradeRequest.assetForSale.state.withNewOwner(freshKey.public)
|
||||||
|
ptx.addOutputState(state)
|
||||||
|
ptx.addCommand(command, tradeRequest.assetForSale.state.owner)
|
||||||
|
|
||||||
logger().trace { "Sending partially signed transaction to seller" }
|
// And add a request for timestamping: it may be that none of the contracts need this! But it can't hurt
|
||||||
|
// to have one.
|
||||||
// TODO: Protect against the buyer terminating here and leaving us in the lurch without
|
ptx.setTime(Instant.now(), timestampingAuthority, 30.seconds)
|
||||||
// the final tx.
|
return Pair(ptx, cashSigningPubKeys)
|
||||||
// TODO: Protect against a malicious buyer sending us back a different transaction to
|
|
||||||
// the one we built.
|
|
||||||
val fullySigned = sendAndReceive(TRADE_TOPIC, tradeRequest.sessionID, sessionID, stx,
|
|
||||||
TimestampedWireTransaction::class.java)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
@ -520,14 +614,14 @@ override fun call(): Pair<TimestampedWireTransaction, LedgerTransaction> {
|
|||||||
<p>This code is longer but still fairly straightforward. Here are some things to pay attention to:</p>
|
<p>This code is longer but still fairly straightforward. Here are some things to pay attention to:</p>
|
||||||
<ol class="arabic simple">
|
<ol class="arabic simple">
|
||||||
<li>We do some sanity checking on the received message to ensure we’re being offered what we expected to be offered.</li>
|
<li>We do some sanity checking on the received message to ensure we’re being offered what we expected to be offered.</li>
|
||||||
<li>We create a cash spend in the normal way, by using <code class="docutils literal"><span class="pre">Cash().craftSpend</span></code>. See the contracts tutorial if this isn’t
|
<li>We create a cash spend in the normal way, by using <code class="docutils literal"><span class="pre">Cash().generateSpend</span></code>. See the contracts tutorial if this isn’t
|
||||||
clear.</li>
|
clear.</li>
|
||||||
<li>We access the <em>service hub</em> when we need it to access things that are transient and may change or be recreated
|
<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
|
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
|
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’.</li>
|
or data which may frequently change should be accessed ‘just in time’.</li>
|
||||||
<li>Finally, we send the unfinsished, invalid transaction to the seller so they can sign it. They are expected to send
|
<li>Finally, we send the unfinished, invalid transaction to the seller so they can sign it. They are expected to send
|
||||||
back to us a <code class="docutils literal"><span class="pre">TimestampedWireTransaction</span></code>, which once we verify it, should be the final outcome of the trade.</li>
|
back to us a <code class="docutils literal"><span class="pre">SignaturesFromSeller</span></code>, which once we verify it, should be the final outcome of the trade.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<p>As you can see, the protocol logic is straightforward and does not contain any callbacks or network glue code, despite
|
<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>
|
the fact that it takes minimal resources and can survive node restarts.</p>
|
||||||
@ -541,6 +635,47 @@ this problem doesn’t occur. It’s also restored for you when a protoc
|
|||||||
restart.</p>
|
restart.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="section" id="progress-tracking">
|
||||||
|
<h2>Progress tracking<a class="headerlink" href="#progress-tracking" title="Permalink to this headline">¶</a></h2>
|
||||||
|
<p>Not shown in the code snippets above is the usage of the <code class="docutils literal"><span class="pre">ProgressTracker</span></code> API. Progress tracking exports information
|
||||||
|
from a protocol about where it’s got up to in such a way that observers can render it in a useful manner to humans who
|
||||||
|
may need to be informed. It may be rendered via an API, in a GUI, onto a terminal window, etc.</p>
|
||||||
|
<p>A <code class="docutils literal"><span class="pre">ProgressTracker</span></code> is constructed with a series of <code class="docutils literal"><span class="pre">Step</span></code> objects, where each step is an object representing a
|
||||||
|
stage in a piece of work. It is therefore typical to use singletons that subclass <code class="docutils literal"><span class="pre">Step</span></code>, which may be defined easily
|
||||||
|
in one line when using Kotlin. Typical steps might be “Waiting for response from peer”, “Waiting for signature to be
|
||||||
|
approved”, “Downloading and verifying data” etc.</p>
|
||||||
|
<p>Each step exposes a label. By default labels are fixed, but by subclassing <code class="docutils literal"><span class="pre">RelabelableStep</span></code>
|
||||||
|
you can make a step that can update its label on the fly. That’s useful for steps that want to expose non-structured
|
||||||
|
progress information like the current file being downloaded. By defining your own step types, you can export progress
|
||||||
|
in a way that’s both human readable and machine readable.</p>
|
||||||
|
<p>Progress trackers are hierarchical. Each step can be the parent for another tracker. By altering the
|
||||||
|
<code class="docutils literal"><span class="pre">ProgressTracker.childrenFor[step]</span> <span class="pre">=</span> <span class="pre">tracker</span></code> map, a tree of steps can be created. It’s allowed to alter the hierarchy
|
||||||
|
at runtime, on the fly, and the progress renderers will adapt to that properly. This can be helpful when you don’t
|
||||||
|
fully know ahead of time what steps will be required. If you _do_ know what is required, configuring as much of the
|
||||||
|
hierarchy ahead of time is a good idea, as that will help the users see what is coming up.</p>
|
||||||
|
<p>Every tracker has not only the steps given to it at construction time, but also the singleton
|
||||||
|
<code class="docutils literal"><span class="pre">ProgressTracker.UNSTARTED</span></code> step and the <code class="docutils literal"><span class="pre">ProgressTracker.DONE</span></code> step. Once a tracker has become <code class="docutils literal"><span class="pre">DONE</span></code> its
|
||||||
|
position may not be modified again (because e.g. the UI may have been removed/cleaned up), but until that point, the
|
||||||
|
position can be set to any arbitrary set both forwards and backwards. Steps may be skipped, repeated, etc. Note that
|
||||||
|
rolling the current step backwards will delete any progress trackers that are children of the steps being reversed, on
|
||||||
|
the assumption that those subtasks will have to be repeated.</p>
|
||||||
|
<p>Trackers provide an <a class="reference external" href="http://reactivex.io/">Rx observable</a> which streams changes to the hierarchy. The top level
|
||||||
|
observable exposes all the events generated by its children as well. The changes are represented by objects indicating
|
||||||
|
whether the change is one of position (i.e. progress), structure (i.e. new subtasks being added/removed) or some other
|
||||||
|
aspect of rendering (i.e. a step has changed in some way and is requesting a re-render).</p>
|
||||||
|
<p>The protocol framework is somewhat integrated with this API. Each <code class="docutils literal"><span class="pre">ProtocolLogic</span></code> may optionally provide a tracker by
|
||||||
|
overriding the <code class="docutils literal"><span class="pre">protocolTracker</span></code> property (<code class="docutils literal"><span class="pre">getProtocolTracker</span></code> method in Java). If the
|
||||||
|
<code class="docutils literal"><span class="pre">ProtocolLogic.subProtocol</span></code> method is used, then the tracker of the sub-protocol will be made a child of the current
|
||||||
|
step in the parent protocol automatically, if the parent is using tracking in the first place. The framework will also
|
||||||
|
automatically set the current step to <code class="docutils literal"><span class="pre">DONE</span></code> for you, when the protocol is finished.</p>
|
||||||
|
<p>Because a protocol may sometimes wish to configure the children in its progress hierarchy _before_ the sub-protocol
|
||||||
|
is constructed, for sub-protocols that always follow the same outline regardless of their parameters it’s conventional
|
||||||
|
to define a companion object/static method (for Kotlin/Java respectively) that constructs a tracker, and then allow
|
||||||
|
the sub-protocol to have the tracker it will use be passed in as a parameter. This allows all trackers to be built
|
||||||
|
and linked ahead of time.</p>
|
||||||
|
<p>In future, the progress tracking framework will become a vital part of how exceptions, errors, and other faults are
|
||||||
|
surfaced to human operators for investigation and resolution.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -584,7 +719,7 @@ restart.</p>
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
9
docs/build/html/roadmap.html
vendored
9
docs/build/html/roadmap.html
vendored
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>Roadmap — R3 Prototyping 0.1 documentation</title>
|
<title>Roadmap — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
<link rel="next" title="Code style guide" href="codestyle.html"/>
|
<link rel="next" title="Code style guide" href="codestyle.html"/>
|
||||||
<link rel="prev" title="Using the visualiser" href="visualiser.html"/>
|
<link rel="prev" title="Using the visualiser" href="visualiser.html"/>
|
||||||
|
|
||||||
@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -87,6 +87,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</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="messaging.html">Networking and messaging</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="running-the-trading-demo.html">Running the trading demo</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
||||||
<ul>
|
<ul>
|
||||||
@ -220,7 +221,7 @@ Thread.stop() unsafe to use), and to measure and enforce runtime limits to handl
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
22
docs/build/html/running-the-trading-demo.html
vendored
22
docs/build/html/running-the-trading-demo.html
vendored
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>Running the trading demo — R3 Prototyping 0.1 documentation</title>
|
<title>Running the trading demo — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
<link rel="next" title="Writing a contract" href="tutorial.html"/>
|
<link rel="next" title="Writing a contract" href="tutorial.html"/>
|
||||||
<link rel="prev" title="Networking and messaging" href="messaging.html"/>
|
<link rel="prev" title="Networking and messaging" href="messaging.html"/>
|
||||||
|
|
||||||
@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -150,21 +150,21 @@ can learn about in <a class="reference internal" href="protocol-state-machines.h
|
|||||||
<p>The node has only currently been tested on MacOS X and Ubuntu Linux. If you have success on other platforms, please
|
<p>The node has only currently been tested on MacOS X and Ubuntu Linux. If you have success on other platforms, please
|
||||||
let us know.</p>
|
let us know.</p>
|
||||||
<p>Now, open two terminals, and in the first run::</p>
|
<p>Now, open two terminals, and in the first run::</p>
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="p">./</span><span class="n">gradlew</span> <span class="n">runDemoBuyer</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span class="p">./</span><span class="n">scripts</span><span class="p">/</span><span class="n">trader</span><span class="p">-</span><span class="n">demo</span><span class="p">.</span><span class="n">sh</span> <span class="n">buyer</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>It will create a directory named “buyer” and ask you to edit the configuration file inside. Open up <code class="docutils literal"><span class="pre">buyer/config</span></code>
|
<p>It will compile things, if necessary, then create a directory named “buyer” with a bunch of files inside and start
|
||||||
in your favourite text editor and give the node a legal identity of “Big Buyer Corp, Inc” or whatever else you feel like.
|
the node. You should see it waiting for a trade to begin.</p>
|
||||||
The actual text string is not important. Now run the gradle command again, and it should start up and wait for
|
|
||||||
a seller to connect.</p>
|
|
||||||
<p>In the second terminal, run:</p>
|
<p>In the second terminal, run:</p>
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="p">./</span><span class="n">gradlew</span> <span class="n">runDemoSeller</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span class="p">./</span><span class="n">scripts</span><span class="p">/</span><span class="n">trader</span><span class="p">-</span><span class="n">demo</span><span class="p">.</span><span class="n">sh</span> <span class="n">seller</span>
|
||||||
</pre></div>
|
</pre></div>
|
||||||
</div>
|
</div>
|
||||||
<p>and repeat the process, this time calling the node ... something else.</p>
|
|
||||||
<p>You should see some log lines scroll past, and within a few seconds the messages “Purchase complete - we are a
|
<p>You should see some log lines scroll past, and within a few seconds the messages “Purchase complete - we are a
|
||||||
happy customer!” and “Sale completed - we have a happy customer!” should be printed.</p>
|
happy customer!” and “Sale completed - we have a happy customer!” should be printed.</p>
|
||||||
<p>If it doesn’t work, jump on the mailing list and let us know.</p>
|
<p>If it doesn’t work, jump on the mailing list and let us know.</p>
|
||||||
|
<p>For Windows users, the contents of the shell script are very trivial and can easily be done by hand from a command
|
||||||
|
window. Essentially, it just runs Gradle to create the startup scripts, and then starts the node with one set of
|
||||||
|
flags or another.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -208,7 +208,7 @@ happy customer!” and “Sale completed - we have a happy customer!R
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
8
docs/build/html/search.html
vendored
8
docs/build/html/search.html
vendored
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>Search — R3 Prototyping 0.1 documentation</title>
|
<title>Search — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
|
|
||||||
|
|
||||||
<script src="_static/js/modernizr.min.js"></script>
|
<script src="_static/js/modernizr.min.js"></script>
|
||||||
@ -58,7 +58,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -182,7 +182,7 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
2
docs/build/html/searchindex.js
vendored
2
docs/build/html/searchindex.js
vendored
File diff suppressed because one or more lines are too long
41
docs/build/html/tutorial.html
vendored
41
docs/build/html/tutorial.html
vendored
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>Writing a contract — R3 Prototyping 0.1 documentation</title>
|
<title>Writing a contract — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
<link rel="next" title="Protocol state machines" href="protocol-state-machines.html"/>
|
<link rel="next" title="Protocol state machines" href="protocol-state-machines.html"/>
|
||||||
<link rel="prev" title="Running the trading demo" href="running-the-trading-demo.html"/>
|
<link rel="prev" title="Running the trading demo" href="running-the-trading-demo.html"/>
|
||||||
|
|
||||||
@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -99,7 +99,7 @@
|
|||||||
<li class="toctree-l2"><a class="reference internal" href="#understanding-fungibility">Understanding fungibility</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#understanding-fungibility">Understanding fungibility</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#checking-the-requirements">Checking the requirements</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#checking-the-requirements">Checking the requirements</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#how-to-test-your-contract">How to test your contract</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#how-to-test-your-contract">How to test your contract</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#adding-a-crafting-api-to-your-contract">Adding a crafting API to your contract</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#adding-a-generation-api-to-your-contract">Adding a generation API to your contract</a></li>
|
||||||
<li class="toctree-l2"><a class="reference internal" href="#non-asset-oriented-based-smart-contracts">Non-asset-oriented based smart contracts</a></li>
|
<li class="toctree-l2"><a class="reference internal" href="#non-asset-oriented-based-smart-contracts">Non-asset-oriented based smart contracts</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@ -725,25 +725,25 @@ an exception will be thrown indicating which transaction failed and why. In the
|
|||||||
again to ensure the third transaction fails with a message that contains “must have matured” (it doesn’t have to be
|
again to ensure the third transaction fails with a message that contains “must have matured” (it doesn’t have to be
|
||||||
the exact message).</p>
|
the exact message).</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="adding-a-crafting-api-to-your-contract">
|
<div class="section" id="adding-a-generation-api-to-your-contract">
|
||||||
<h2>Adding a crafting API to your contract<a class="headerlink" href="#adding-a-crafting-api-to-your-contract" title="Permalink to this headline">¶</a></h2>
|
<h2>Adding a generation API to your contract<a class="headerlink" href="#adding-a-generation-api-to-your-contract" title="Permalink to this headline">¶</a></h2>
|
||||||
<p>Contract classes <strong>must</strong> provide a verify function, but they may optionally also provide helper functions to simplify
|
<p>Contract classes <strong>must</strong> provide a verify function, but they may optionally also provide helper functions to simplify
|
||||||
their usage. A simple class of functions most contracts provide are <em>crafting functions</em>, which either generate or
|
their usage. A simple class of functions most contracts provide are <em>generation functions</em>, which either create or
|
||||||
modify a transaction to perform certain actions (an action is normally mappable 1:1 to a command, but doesn’t have to
|
modify a transaction to perform certain actions (an action is normally mappable 1:1 to a command, but doesn’t have to
|
||||||
be so).</p>
|
be so).</p>
|
||||||
<p>Crafting may involve complex logic. For example, the cash contract has a <code class="docutils literal"><span class="pre">craftSpend</span></code> method that is given a set of
|
<p>Generation may involve complex logic. For example, the cash contract has a <code class="docutils literal"><span class="pre">generateSpend</span></code> method that is given a set of
|
||||||
cash states and chooses a way to combine them together to satisfy the amount of money that is being sent. In the
|
cash states and chooses a way to combine them together to satisfy the amount of money that is being sent. In the
|
||||||
immutable-state model that we are using ledger entries (states) can only be created and deleted, but never modified.
|
immutable-state model that we are using ledger entries (states) can only be created and deleted, but never modified.
|
||||||
Therefore to send $1200 when we have only $900 and $500 requires combining both states together, and then creating
|
Therefore to send $1200 when we have only $900 and $500 requires combining both states together, and then creating
|
||||||
two new output states of $1200 and $200 back to ourselves. This latter state is called the <em>change</em> and is a concept
|
two new output states of $1200 and $200 back to ourselves. This latter state is called the <em>change</em> and is a concept
|
||||||
that should be familiar to anyone who has worked with Bitcoin.</p>
|
that should be familiar to anyone who has worked with Bitcoin.</p>
|
||||||
<p>As another example, we can imagine code that implements a netting algorithm may craft complex transactions that must
|
<p>As another example, we can imagine code that implements a netting algorithm may generate complex transactions that must
|
||||||
be signed by many people. Whilst such code might be too big for a single utility method (it’d probably be sized more
|
be signed by many people. Whilst such code might be too big for a single utility method (it’d probably be sized more
|
||||||
like a module), the basic concept is the same: preparation of a transaction using complex logic.</p>
|
like a module), the basic concept is the same: preparation of a transaction using complex logic.</p>
|
||||||
<p>For our commercial paper contract however, the things that can be done with it are quite simple. Let’s start with
|
<p>For our commercial paper contract however, the things that can be done with it are quite simple. Let’s start with
|
||||||
a method to wrap up the issuance process:</p>
|
a method to wrap up the issuance process:</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">fun</span> <span class="nf">craftIssue</span><span class="p">(</span><span class="n">issuance</span><span class="p">:</span> <span class="n">InstitutionReference</span><span class="p">,</span> <span class="n">faceValue</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span> <span class="n">maturityDate</span><span class="p">:</span> <span class="n">Instant</span><span class="p">):</span> <span class="n">TransactionBuilder</span> <span class="p">{</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">fun</span> <span class="nf">generateIssue</span><span class="p">(</span><span class="n">issuance</span><span class="p">:</span> <span class="n">InstitutionReference</span><span class="p">,</span> <span class="n">faceValue</span><span class="p">:</span> <span class="n">Amount</span><span class="p">,</span> <span class="n">maturityDate</span><span class="p">:</span> <span class="n">Instant</span><span class="p">):</span> <span class="n">TransactionBuilder</span> <span class="p">{</span>
|
||||||
<span class="k">val</span> <span class="py">state</span> <span class="p">=</span> <span class="n">State</span><span class="p">(</span><span class="n">issuance</span><span class="p">,</span> <span class="n">issuance</span><span class="p">.</span><span class="n">party</span><span class="p">.</span><span class="n">owningKey</span><span class="p">,</span> <span class="n">faceValue</span><span class="p">,</span> <span class="n">maturityDate</span><span class="p">)</span>
|
<span class="k">val</span> <span class="py">state</span> <span class="p">=</span> <span class="n">State</span><span class="p">(</span><span class="n">issuance</span><span class="p">,</span> <span class="n">issuance</span><span class="p">.</span><span class="n">party</span><span class="p">.</span><span class="n">owningKey</span><span class="p">,</span> <span class="n">faceValue</span><span class="p">,</span> <span class="n">maturityDate</span><span class="p">)</span>
|
||||||
<span class="k">return</span> <span class="n">TransactionBuilder</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">WireCommand</span><span class="p">(</span><span class="n">Commands</span><span class="p">.</span><span class="n">Issue</span><span class="p">,</span> <span class="n">issuance</span><span class="p">.</span><span class="n">party</span><span class="p">.</span><span class="n">owningKey</span><span class="p">))</span>
|
<span class="k">return</span> <span class="n">TransactionBuilder</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">WireCommand</span><span class="p">(</span><span class="n">Commands</span><span class="p">.</span><span class="n">Issue</span><span class="p">,</span> <span class="n">issuance</span><span class="p">.</span><span class="n">party</span><span class="p">.</span><span class="n">owningKey</span><span class="p">))</span>
|
||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
@ -757,7 +757,7 @@ It allows you to add inputs, outputs and commands to it and is designed to be pa
|
|||||||
multiple contracts.</p>
|
multiple contracts.</p>
|
||||||
<div class="admonition note">
|
<div class="admonition note">
|
||||||
<p class="first admonition-title">Note</p>
|
<p class="first admonition-title">Note</p>
|
||||||
<p class="last">Crafting methods should ideally be written to compose with each other, that is, they should take a
|
<p class="last">Generation methods should ideally be written to compose with each other, that is, they should take a
|
||||||
<code class="docutils literal"><span class="pre">TransactionBuilder</span></code> as an argument instead of returning one, unless you are sure it doesn’t make sense to
|
<code class="docutils literal"><span class="pre">TransactionBuilder</span></code> as an argument instead of returning one, unless you are sure it doesn’t make sense to
|
||||||
combine this type of transaction with others. In this case, issuing CP at the same time as doing other things
|
combine this type of transaction with others. In this case, issuing CP at the same time as doing other things
|
||||||
would just introduce complexity that isn’t likely to be worth it, so we return a fresh object each time: instead,
|
would just introduce complexity that isn’t likely to be worth it, so we return a fresh object each time: instead,
|
||||||
@ -774,7 +774,7 @@ any <code class="docutils literal"><span class="pre">ContractStateRef</span></co
|
|||||||
for you.</p>
|
for you.</p>
|
||||||
<p>What about moving the paper, i.e. reassigning ownership to someone else?</p>
|
<p>What about moving the paper, i.e. reassigning ownership to someone else?</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">fun</span> <span class="nf">craftMove</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionBuilder</span><span class="p">,</span> <span class="n">paper</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">State</span><span class="p">>,</span> <span class="n">newOwner</span><span class="p">:</span> <span class="n">PublicKey</span><span class="p">)</span> <span class="p">{</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span class="k">fun</span> <span class="nf">generateMove</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionBuilder</span><span class="p">,</span> <span class="n">paper</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">State</span><span class="p">>,</span> <span class="n">newOwner</span><span class="p">:</span> <span class="n">PublicKey</span><span class="p">)</span> <span class="p">{</span>
|
||||||
<span class="n">tx</span><span class="p">.</span><span class="n">addInputState</span><span class="p">(</span><span class="n">paper</span><span class="p">.</span><span class="n">ref</span><span class="p">)</span>
|
<span class="n">tx</span><span class="p">.</span><span class="n">addInputState</span><span class="p">(</span><span class="n">paper</span><span class="p">.</span><span class="n">ref</span><span class="p">)</span>
|
||||||
<span class="n">tx</span><span class="p">.</span><span class="n">addOutputState</span><span class="p">(</span><span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">copy</span><span class="p">(</span><span class="n">owner</span> <span class="p">=</span> <span class="n">newOwner</span><span class="p">))</span>
|
<span class="n">tx</span><span class="p">.</span><span class="n">addOutputState</span><span class="p">(</span><span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">copy</span><span class="p">(</span><span class="n">owner</span> <span class="p">=</span> <span class="n">newOwner</span><span class="p">))</span>
|
||||||
<span class="n">tx</span><span class="p">.</span><span class="n">addArg</span><span class="p">(</span><span class="n">WireCommand</span><span class="p">(</span><span class="n">Commands</span><span class="p">.</span><span class="n">Move</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">owner</span><span class="p">))</span>
|
<span class="n">tx</span><span class="p">.</span><span class="n">addArg</span><span class="p">(</span><span class="n">WireCommand</span><span class="p">(</span><span class="n">Commands</span><span class="p">.</span><span class="n">Move</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">owner</span><span class="p">))</span>
|
||||||
@ -784,7 +784,7 @@ for you.</p>
|
|||||||
</div>
|
</div>
|
||||||
<p>Here, the method takes a pre-existing <code class="docutils literal"><span class="pre">TransactionBuilder</span></code> and adds to it. This is correct because typically
|
<p>Here, the method takes a pre-existing <code class="docutils literal"><span class="pre">TransactionBuilder</span></code> and adds to it. This is correct because typically
|
||||||
you will want to combine a sale of CP atomically with the movement of some other asset, such as cash. So both
|
you will want to combine a sale of CP atomically with the movement of some other asset, such as cash. So both
|
||||||
craft methods should operate on the same transaction. You can see an example of this being done in the unit tests
|
generate methods should operate on the same transaction. You can see an example of this being done in the unit tests
|
||||||
for the commercial paper contract.</p>
|
for the commercial paper contract.</p>
|
||||||
<p>The paper is given to us as a <code class="docutils literal"><span class="pre">StateAndRef<CommercialPaper.State></span></code> object. This is exactly what it sounds like:
|
<p>The paper is given to us as a <code class="docutils literal"><span class="pre">StateAndRef<CommercialPaper.State></span></code> object. This is exactly what it sounds like:
|
||||||
a small object that has a (copy of) a state object, and also the (txhash, index) that indicates the location of this
|
a small object that has a (copy of) a state object, and also the (txhash, index) that indicates the location of this
|
||||||
@ -792,9 +792,9 @@ state on the ledger.</p>
|
|||||||
<p>Finally, we can do redemption.</p>
|
<p>Finally, we can do redemption.</p>
|
||||||
<div class="codeset container">
|
<div class="codeset container">
|
||||||
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">@Throws</span><span class="p">(</span><span class="n">InsufficientBalanceException</span><span class="o">::</span><span class="k">class</span><span class="p">)</span>
|
<div class="highlight-kotlin"><div class="highlight"><pre><span class="n">@Throws</span><span class="p">(</span><span class="n">InsufficientBalanceException</span><span class="o">::</span><span class="k">class</span><span class="p">)</span>
|
||||||
<span class="k">fun</span> <span class="nf">craftRedeem</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionBuilder</span><span class="p">,</span> <span class="n">paper</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">State</span><span class="p">>,</span> <span class="n">wallet</span><span class="p">:</span> <span class="n">List</span><span class="p"><</span><span class="n">StateAndRef</span><span class="p"><</span><span class="n">Cash</span><span class="p">.</span><span class="n">State</span><span class="p">>>)</span> <span class="p">{</span>
|
<span class="k">fun</span> <span class="nf">generateRedeem</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionBuilder</span><span class="p">,</span> <span class="n">paper</span><span class="p">:</span> <span class="n">StateAndRef</span><span class="p"><</span><span class="n">State</span><span class="p">>,</span> <span class="n">wallet</span><span class="p">:</span> <span class="n">List</span><span class="p"><</span><span class="n">StateAndRef</span><span class="p"><</span><span class="n">Cash</span><span class="p">.</span><span class="n">State</span><span class="p">>>)</span> <span class="p">{</span>
|
||||||
<span class="c1">// Add the cash movement using the states in our wallet.</span>
|
<span class="c1">// Add the cash movement using the states in our wallet.</span>
|
||||||
<span class="n">Cash</span><span class="p">().</span><span class="n">craftSpend</span><span class="p">(</span><span class="n">tx</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">faceValue</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">owner</span><span class="p">,</span> <span class="n">wallet</span><span class="p">)</span>
|
<span class="n">Cash</span><span class="p">().</span><span class="n">generateSpend</span><span class="p">(</span><span class="n">tx</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">faceValue</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">owner</span><span class="p">,</span> <span class="n">wallet</span><span class="p">)</span>
|
||||||
<span class="n">tx</span><span class="p">.</span><span class="n">addInputState</span><span class="p">(</span><span class="n">paper</span><span class="p">.</span><span class="n">ref</span><span class="p">)</span>
|
<span class="n">tx</span><span class="p">.</span><span class="n">addInputState</span><span class="p">(</span><span class="n">paper</span><span class="p">.</span><span class="n">ref</span><span class="p">)</span>
|
||||||
<span class="n">tx</span><span class="p">.</span><span class="n">addArg</span><span class="p">(</span><span class="n">WireCommand</span><span class="p">(</span><span class="n">CommercialPaper</span><span class="p">.</span><span class="n">Commands</span><span class="p">.</span><span class="n">Redeem</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">owner</span><span class="p">))</span>
|
<span class="n">tx</span><span class="p">.</span><span class="n">addArg</span><span class="p">(</span><span class="n">WireCommand</span><span class="p">(</span><span class="n">CommercialPaper</span><span class="p">.</span><span class="n">Commands</span><span class="p">.</span><span class="n">Redeem</span><span class="p">,</span> <span class="n">paper</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">owner</span><span class="p">))</span>
|
||||||
<span class="p">}</span>
|
<span class="p">}</span>
|
||||||
@ -819,12 +819,9 @@ paper.</p>
|
|||||||
is recognised by the network. The most important next step is for the participating entities to sign it using the
|
is recognised by the network. The most important next step is for the participating entities to sign it using the
|
||||||
<code class="docutils literal"><span class="pre">signWith()</span></code> method. This takes a keypair, serialises the transaction, signs the serialised form and then stores the
|
<code class="docutils literal"><span class="pre">signWith()</span></code> method. This takes a keypair, serialises the transaction, signs the serialised form and then stores the
|
||||||
signature inside the <code class="docutils literal"><span class="pre">TransactionBuilder</span></code>. Once all parties have signed, you can call <code class="docutils literal"><span class="pre">TransactionBuilder.toSignedTransaction()</span></code>
|
signature inside the <code class="docutils literal"><span class="pre">TransactionBuilder</span></code>. Once all parties have signed, you can call <code class="docutils literal"><span class="pre">TransactionBuilder.toSignedTransaction()</span></code>
|
||||||
to get a <code class="docutils literal"><span class="pre">SignedWireTransaction</span></code> object. This is an immutable form of the transaction that’s ready for <em>timestamping</em>.</p>
|
to get a <code class="docutils literal"><span class="pre">SignedTransaction</span></code> object. This is an immutable form of the transaction that’s ready for <em>timestamping</em>,
|
||||||
<div class="admonition note">
|
which can be done using a <code class="docutils literal"><span class="pre">TimestamperClient</span></code>. To learn more about that, please refer to the
|
||||||
<p class="first admonition-title">Note</p>
|
<a class="reference internal" href="protocol-state-machines.html"><em>Protocol state machines</em></a> document.</p>
|
||||||
<p class="last">Timestamping and passing around of partial transactions for group signing is not yet fully implemented.
|
|
||||||
This tutorial will be updated once it is.</p>
|
|
||||||
</div>
|
|
||||||
<p>You can see how transactions flow through the different stages of construction by examining the commercial paper
|
<p>You can see how transactions flow through the different stages of construction by examining the commercial paper
|
||||||
unit tests.</p>
|
unit tests.</p>
|
||||||
</div>
|
</div>
|
||||||
@ -902,7 +899,7 @@ be implemented once in a separate contract, with the controlling data being held
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
9
docs/build/html/visualiser.html
vendored
9
docs/build/html/visualiser.html
vendored
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>Using the visualiser — R3 Prototyping 0.1 documentation</title>
|
<title>Using the visualiser — R3 Prototyping latest documentation</title>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="top" title="R3 Prototyping 0.1 documentation" href="index.html"/>
|
<link rel="top" title="R3 Prototyping latest documentation" href="index.html"/>
|
||||||
<link rel="next" title="Roadmap" href="roadmap.html"/>
|
<link rel="next" title="Roadmap" href="roadmap.html"/>
|
||||||
<link rel="prev" title="Protocol state machines" href="protocol-state-machines.html"/>
|
<link rel="prev" title="Protocol state machines" href="protocol-state-machines.html"/>
|
||||||
|
|
||||||
@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
0.1
|
latest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@ -87,6 +87,7 @@
|
|||||||
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="getting-set-up.html">Getting set up</a></li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</a></li>
|
<li class="toctree-l1"><a class="reference internal" href="data-model.html">Data model</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="messaging.html">Networking and messaging</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="running-the-trading-demo.html">Running the trading demo</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
<p class="caption"><span class="caption-text">Tutorials</span></p>
|
||||||
<ul>
|
<ul>
|
||||||
@ -250,7 +251,7 @@ feature).</li>
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var DOCUMENTATION_OPTIONS = {
|
var DOCUMENTATION_OPTIONS = {
|
||||||
URL_ROOT:'./',
|
URL_ROOT:'./',
|
||||||
VERSION:'0.1',
|
VERSION:'latest',
|
||||||
COLLAPSE_INDEX:false,
|
COLLAPSE_INDEX:false,
|
||||||
FILE_SUFFIX:'.html',
|
FILE_SUFFIX:'.html',
|
||||||
HAS_SOURCE: true
|
HAS_SOURCE: true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user