Rename protocol to flow in docs

This commit is contained in:
rick.parker 2016-11-22 16:30:17 +00:00
parent 1f148cfc95
commit 6c3476e89f
17 changed files with 157 additions and 157 deletions

View File

@ -59,18 +59,18 @@ Ensuring that all input states point to the same notary is the responsibility of
Changing notaries
~~~~~~~~~~~~~~~~~
To change the notary for an input state, use the ``NotaryChangeProtocol``. For example:
To change the notary for an input state, use the ``NotaryChangeFlow``. For example:
.. sourcecode:: kotlin
@Suspendable
fun changeNotary(originalState: StateAndRef<ContractState>,
newNotary: Party): StateAndRef<ContractState> {
val protocol = NotaryChangeProtocol.Instigator(originalState, newNotary)
return subProtocol(protocol)
val flow = NotaryChangeFlow.Instigator(originalState, newNotary)
return subFlow(flow)
}
The protocol will:
The flow will:
1. Construct a transaction with the old state as the input and the new state as the output
@ -152,10 +152,10 @@ At present we have two basic implementations that store committed input states i
Obtaining a signature
---------------------
Once a transaction is built and ready to be finalised, normally you would call ``FinalityProtocol`` passing in a
Once a transaction is built and ready to be finalised, normally you would call ``FinalityFlow`` passing in a
``SignedTransaction`` (including signatures from the participants) and a list of participants to notify. This requests a
notary signature if needed, and then sends a copy of the notarised transaction to all participants for them to store.
``FinalityProtocol`` delegates to ``NotaryProtocol.Client`` followed by ``BroadcastTransactionProtocol`` to do the
``FinalityFlow`` delegates to ``NotaryFlow.Client`` followed by ``BroadcastTransactionFlow`` to do the
actual work of notarising and broadcasting the transaction. For example:
.. sourcecode:: kotlin
@ -165,21 +165,21 @@ actual work of notarising and broadcasting the transaction. For example:
// We conclusively cannot have all the signatures, as the notary has not signed yet
val tx = ptx.toSignedTransaction(checkSufficientSignatures = false)
// The empty set would be the trigger events, which are not used here
val protocol = FinalityProtocol(tx, emptySet(), participants)
return serviceHub.startProtocol("protocol.finalisation", protocol)
val flow = FinalityFlow(tx, emptySet(), participants)
return serviceHub.startFlow("flow.finalisation", flow)
}
To manually obtain a signature from a notary you can call ``NotaryProtocol.Client`` directly. The protocol will work out
To manually obtain a signature from a notary you can call ``NotaryFlow.Client`` directly. The flow will work out
which notary needs to be called based on the input states and the timestamp command. For example, the following snippet
can be used when writing a custom protocol:
can be used when writing a custom flow:
.. sourcecode:: kotlin
fun getNotarySignature(wtx: WireTransaction): DigitalSignature.LegallyIdentifiable {
return subProtocol(NotaryProtocol.Client(wtx))
return subFlow(NotaryFlow.Client(wtx))
}
On conflict the ``NotaryProtocol`` with throw a ``NotaryException`` containing the conflict details:
On conflict the ``NotaryFlow`` with throw a ``NotaryException`` containing the conflict details:
.. sourcecode:: kotlin
@ -192,4 +192,4 @@ On conflict the ``NotaryProtocol`` with throw a ``NotaryException`` containing t
*/
data class ConsumingTx(val id: SecureHash, val inputIndex: Int, val requestingParty: Party)
Conflict handling and resolution is currently the responsibility of the protocol author.
Conflict handling and resolution is currently the responsibility of the flow author.

View File

@ -12,7 +12,7 @@ App Plugins
To create an app plugin you must you must extend from `CordaPluginRegistry`_. The JavaDoc contains
specific details of the implementation, but you can extend the server in the following ways:
1. Required protocols: Specify which protocols will be whitelisted for use in your web APIs.
1. Required flows: Specify which flows will be whitelisted for use in your web APIs.
2. Service plugins: Register your services (see below).
3. Web APIs: You may register your own endpoints under /api/ of the built-in web server.
4. Static web endpoints: You may register your own static serving directories for serving web content.
@ -22,7 +22,7 @@ Services
--------
Services are classes which are constructed after the node has started. It is provided a `ServiceHubInternal`_ which
allows a richer API than the `ServiceHub`_ exposed to contracts. It enables adding protocols, registering
allows a richer API than the `ServiceHub`_ exposed to contracts. It enables adding flows, registering
message handlers and more. The service does not run in a separate thread, so the only entry point to the service is during
construction, where message handlers should be registered and threads started.

View File

@ -254,7 +254,7 @@ including things like the shape of the transaction. It may seem that it's an ove
just immediately respend the big output back to yourself in order to split it? And yes, you could, until you hit
scenarios like "the machine requesting the payment doesn't have the keys needed to spend it",
which turn out to be very common. So it's really more effective for a recipient to be able to say to the
sender, "here's the kind of transaction I want you to send me". The :doc:`protocol framework <protocol-state-machines>`
sender, "here's the kind of transaction I want you to send me". The :doc:`flow framework <flow-state-machines>`
may provide a vehicle to make such negotiations simpler.
A further challenge is privacy. Whilst our goal of not sending transactions to nodes that don't "need to know"

View File

@ -28,8 +28,8 @@ due. If a contract state is consumed in the UTXO model, then what *was* the nex
and the next time sensitive event is determined by any successor contract state.
Knowing when the next time sensitive event is due to occur is useful, but typically some *activity* is expected to take
place when this event occurs. We already have a model for business processes in the form of :doc:`protocols <protocol-state-machines>`,
so in the platform we have introduced the concept of *scheduled activities* that can invoke protocol state machines
place when this event occurs. We already have a model for business processes in the form of :doc:`flows <flow-state-machines>`,
so in the platform we have introduced the concept of *scheduled activities* that can invoke flow state machines
at a scheduled time. A contract state can optionally described the next scheduled activity for itself. If it omits
to do so, then nothing will be scheduled.
@ -40,18 +40,18 @@ There are two main steps to implementing scheduled events:
* Have your ``ContractState`` implementation also implement ``SchedulableState``. This requires a method named
``nextScheduledActivity`` to be implemented which returns an optional ``ScheduledActivity`` instance.
``ScheduledActivity`` captures what ``ProtocolLogic`` instance each node will run, to perform the activity, and when it
``ScheduledActivity`` captures what ``FlowLogic`` instance each node will run, to perform the activity, and when it
will run is described by a ``java.time.Instant``. Once your state implements this interface and is tracked by the
wallet, it can expect to be queried for the next activity when committed to the wallet.
* If nothing suitable exists, implement a ``ProtocolLogic`` to be executed by each node as the activity itself.
* If nothing suitable exists, implement a ``FlowLogic`` to be executed by each node as the activity itself.
The important thing to remember is that in the current implementation, each node that is party to the transaction
will execute the same ``ProtocolLogic``, so it needs to establish roles in the business process based on the contract
will execute the same ``FlowLogic``, so it needs to establish roles in the business process based on the contract
state and the node it is running on. Each side will follow different but complementary paths through the business logic.
.. note:: The scheduler's clock always operates in the UTC time zone for uniformity, so any time zone logic must be
performed by the contract, using ``ZonedDateTime``.
In the short term, until we have automatic protocol session set up, you will also likely need to install a network
In the short term, until we have automatic flow session set up, you will also likely need to install a network
handler to help with obtaining a unqiue and secure random session. An example is described below.
The production and consumption of ``ContractStates`` is observed by the scheduler and the activities associated with
@ -73,30 +73,30 @@ Let's take an example of the interest rate swap fixings for our scheduled events
.. sourcecode:: kotlin
override fun nextScheduledActivity(thisStateRef: StateRef,
protocolLogicRefFactory: ProtocolLogicRefFactory): ScheduledActivity? {
flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
val nextFixingOf = nextFixingOf() ?: return null
val (instant, duration) = suggestInterestRateAnnouncementTimeWindow(index = nextFixingOf.name,
source = floatingLeg.indexSource,
date = nextFixingOf.forDay)
return ScheduledActivity(protocolLogicRefFactory.create(TwoPartyDealProtocol.FixingRoleDecider::class.java,
return ScheduledActivity(flowLogicRefFactory.create(TwoPartyDealFlow.FixingRoleDecider::class.java,
thisStateRef, duration), instant)
}
The first thing this does is establish if there are any remaining fixings. If there are none, then it returns ``null``
to indicate that there is no activity to schedule. Otherwise it calculates the ``Instant`` at which the interest rate
should become available and schedules an activity at that time to work out what roles each node will take in the fixing
business process and to take on those roles. That ``ProtocolLogic`` will be handed the ``StateRef`` for the interest
business process and to take on those roles. That ``FlowLogic`` will be handed the ``StateRef`` for the interest
rate swap ``State`` in question, as well as a tolerance ``Duration`` of how long to wait after the activity is triggered
for the interest rate before indicating an error.
.. note:: This is a way to create a reference to the ProtocolLogic class and its constructor parameters to
.. note:: This is a way to create a reference to the FlowLogic class and its constructor parameters to
instantiate. The reference can be checked against a per-node whitelist of approved and allowable types as
part of our overall security sandboxing.
As previously mentioned, we currently need a small network handler to assist with session setup until the work to
automate that is complete. See the interest rate swap specific implementation ``FixingSessionInitiationHandler`` which
is responsible for starting a ``ProtocolLogic`` to perform one role in the fixing protocol with the ``sessionID`` sent
by the ``FixingRoleDecider`` on the other node which then launches the other role in the fixing protocol. Currently
is responsible for starting a ``FlowLogic`` to perform one role in the fixing flow with the ``sessionID`` sent
by the ``FixingRoleDecider`` on the other node which then launches the other role in the fixing flow. Currently
the handler needs to be manually installed in the node.

View File

@ -4,11 +4,11 @@
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/codesets.js"></script>
Protocol state machines
=======================
Flow state machines
===================
This article explains our experimental approach to modelling financial protocols in code. It explains how the
platform's state machine framework is used, and takes you through the code for a simple 2-party asset trading protocol
This article explains our experimental approach to modelling financial flows in code. It explains how the
platform's state machine framework is used, and takes you through the code for a simple 2-party asset trading flow
which is included in the source.
Introduction
@ -21,23 +21,23 @@ shared ledger, and transactions may alter many states simultaneously and atomica
Blockchain systems such as Bitcoin support the idea of building up a finished, signed transaction by passing around
partially signed invalid transactions outside of the main network, and by doing this you can implement
*delivery versus payment* such that there is no chance of settlement failure, because the movement of cash and the
traded asset are performed atomically by the same transaction. To perform such a trade involves a multi-step protocol
traded asset are performed atomically by the same transaction. To perform such a trade involves a multi-step flow
in which messages are passed back and forth privately between parties, checked, signed and so on.
Despite how useful these protocols are, platforms such as Bitcoin and Ethereum do not assist the developer with the rather
Despite how useful these flows are, platforms such as Bitcoin and Ethereum do not assist the developer with the rather
tricky task of actually building them. That is unfortunate. There are many awkward problems in their implementation
that a good platform would take care of for you, problems like:
* Avoiding "callback hell" in which code that should ideally be sequential is turned into an unreadable mess due to the
desire to avoid using up a thread for every protocol instantiation.
* Surviving node shutdowns/restarts that may occur in the middle of the protocol without complicating things. This
implies that the state of the protocol must be persisted to disk.
desire to avoid using up a thread for every flow instantiation.
* Surviving node shutdowns/restarts that may occur in the middle of the flow without complicating things. This
implies that the state of the flow must be persisted to disk.
* Error handling.
* Message routing.
* Serialisation.
* Catching type errors, in which the developer gets temporarily confused and expects to receive/send one type of message
when actually they need to receive/send another.
* Unit testing of the finished protocol.
* Unit testing of the finished flow.
Actor frameworks can solve some of the above but they are often tightly bound to a particular messaging layer, and
we would like to keep a clean separation. Additionally, they are typically not type safe, and don't make persistence or
@ -49,7 +49,7 @@ Java and took over a month of full time work to develop. Most of that code is co
message passing, lifecycle management, error handling and callback management. Because the business logic is quite
spread out the code can be difficult to read and debug.
As small contract-specific trading protocols are a common occurence in finance, we provide a framework for the
As small contract-specific trading flows are a common occurence in finance, we provide a framework for the
construction of them that automatically handles many of the concerns outlined above.
Theory
@ -70,19 +70,19 @@ We use continuations for the following reasons:
A *state machine* is a piece of code that moves through various *states*. These are not the same as states in the data
model (that represent facts about the world on the ledger), but rather indicate different stages in the progression
of a multi-stage protocol. Typically writing a state machine would require the use of a big switch statement and some
of a multi-stage flow. Typically writing a state machine would require the use of a big switch statement and some
explicit variables to keep track of where you're up to. The use of continuations avoids this hassle.
A two party trading protocol
----------------------------
A two party trading flow
------------------------
We would like to implement the "hello world" of shared transaction building protocols: a seller wishes to sell some
We would like to implement the "hello world" of shared transaction building flows: a seller wishes to sell some
*asset* (e.g. some commercial paper) in return for *cash*. The buyer wishes to purchase the asset using his cash. They
want the trade to be atomic so neither side is exposed to the risk of settlement failure. We assume that the buyer
and seller have found each other and arranged the details on some exchange, or over the counter. The details of how
the trade is arranged isn't covered in this article.
Our protocol has two parties (B and S for buyer and seller) and will proceed as follows:
Our flow has two parties (B and S for buyer and seller) and will proceed as follows:
1. S sends a ``StateAndRef`` pointing to the state they want to sell to B, along with info about the price they require
B to pay.
@ -91,15 +91,15 @@ Our protocol has two parties (B and S for buyer and seller) and will proceed as
it lacks a signature from S authorising movement of the asset.
3. S signs it and hands the now finalised ``SignedTransaction`` back to B.
You can find the implementation of this protocol in the file ``finance/src/main/kotlin/net.corda.protocols/TwoPartyTradeProtocol.kt``.
You can find the implementation of this flow in the file ``finance/src/main/kotlin/net.corda.flows/TwoPartyTradeFlow.kt``.
Assuming no malicious termination, they both end the protocol being in posession of a valid, signed transaction that
Assuming no malicious termination, they both end the flow being in posession of a valid, signed transaction that
represents an atomic asset swap.
Note that it's the *seller* who initiates contact with the buyer, not vice-versa as you might imagine.
We start by defining a wrapper that namespaces the protocol code, two functions to start either the buy or sell side
of the protocol, and two classes that will contain the protocol definition. We also pick what data will be used by
We start by defining a wrapper that namespaces the flow code, two functions to start either the buy or sell side
of the flow, and two classes that will contain the flow definition. We also pick what data will be used by
each side.
.. note:: The code samples in this tutorial are only available in Kotlin, but you can use any JVM language to
@ -109,14 +109,14 @@ each side.
.. sourcecode:: kotlin
object TwoPartyTradeProtocol {
object TwoPartyTradeFlow {
class UnacceptablePriceException(val givenPrice: Amount<Currency>) : Exception("Unacceptable price: $givenPrice")
class AssetMismatchException(val expectedTypeName: String, val typeName: String) : Exception() {
override fun toString() = "The submitted asset didn't match the expected type: $expectedTypeName vs $typeName"
}
// 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 flow message the seller sends to the buyer.
data class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>,
val price: Amount<Currency>,
@ -131,7 +131,7 @@ each side.
val assetToSell: StateAndRef<OwnableState>,
val price: Amount<Currency>,
val myKeyPair: KeyPair,
override val progressTracker: ProgressTracker = Seller.tracker()) : ProtocolLogic<SignedTransaction>() {
override val progressTracker: ProgressTracker = Seller.tracker()) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
TODO()
@ -141,7 +141,7 @@ each side.
open class Buyer(val otherSide: Party,
val notary: Party,
val acceptablePrice: Amount<Currency>,
val typeToBuy: Class<out OwnableState>) : ProtocolLogic<SignedTransaction>() {
val typeToBuy: Class<out OwnableState>) : FlowLogic<SignedTransaction>() {
@Suspendable
override fun call(): SignedTransaction {
TODO()
@ -149,8 +149,8 @@ each side.
}
}
This code defines several classes nested inside the main ``TwoPartyTradeProtocol`` singleton. Some of the classes are
simply protocol messages or exceptions. The other two represent the buyer and seller side of the protocol.
This code defines several classes nested inside the main ``TwoPartyTradeFlow`` singleton. Some of the classes are
simply flow messages or exceptions. The other two represent the buyer and seller side of the flow.
Going through the data needed to become a seller, we have:
@ -166,11 +166,11 @@ And for the buyer:
- ``acceptablePrice: Amount<Currency>`` - the price that was agreed upon out of band. If the seller specifies
a price less than or equal to this, then the trade will go ahead.
- ``typeToBuy: Class<out OwnableState>`` - the type of state that is being purchased. This is used to check that the
sell side of the protocol isn't trying to sell us the wrong thing, whether by accident or on purpose.
sell side of the flow isn't trying to sell us the wrong thing, whether by accident or on purpose.
Alright, so using this protocol shouldn't be too hard: in the simplest case we can just create a Buyer or Seller
with the details of the trade, depending on who we are. We then have to start the protocol in some way. Just
calling the ``call`` function ourselves won't work: instead we need to ask the framework to start the protocol for
Alright, so using this flow shouldn't be too hard: in the simplest case we can just create a Buyer or Seller
with the details of the trade, depending on who we are. We then have to start the flow in some way. Just
calling the ``call`` function ourselves won't work: instead we need to ask the framework to start the flow for
us. More on that in a moment.
Suspendable functions
@ -178,9 +178,9 @@ Suspendable functions
The ``call`` function of the buyer/seller classes is marked with the ``@Suspendable`` annotation. What does this mean?
As mentioned above, our protocol framework will at points suspend the code and serialise it to disk. For this to work,
As mentioned above, our flow framework will at points suspend the code and serialise it to disk. For this to work,
any methods on the call stack must have been pre-marked as ``@Suspendable`` so the bytecode rewriter knows to modify
the underlying code to support this new feature. A protocol is suspended when calling either ``receive``, ``send`` or
the underlying code to support this new feature. A flow is suspended when calling either ``receive``, ``send`` or
``sendAndReceive`` which we will learn more about below. For now, just be aware that when one of these methods is
invoked, all methods on the stack must have been marked. If you forget, then in the unit test environment you will
get a useful error message telling you which methods you didn't mark. The fix is simple enough: just add the annotation
@ -188,30 +188,30 @@ and try again.
.. note:: Java 9 is likely to remove this pre-marking requirement completely.
Starting your protocol
----------------------
Starting your flow
------------------
The ``StateMachineManager`` is the class responsible for taking care of all running protocols in a node. It knows
The ``StateMachineManager`` is the class responsible for taking care of all running flows in a node. It knows
how to register handlers with the messaging system (see ":doc:`messaging`") and iterate the right state machine
when messages arrive. It provides the send/receive/sendAndReceive calls that let the code request network
interaction and it will save/restore serialised versions of the fiber at the right times.
Protocols can be invoked in several ways. For instance, they can be triggered by scheduled events,
Flows can be invoked in several ways. For instance, they can be triggered by scheduled events,
see ":doc:`event-scheduling`" to learn more about this. Or they can be triggered via the HTTP API. Or they can
be triggered directly via the Java-level node APIs from your app code.
You request a protocol to be invoked by using the ``ServiceHub.invokeProtocolAsync`` method. This takes a
Java reflection ``Class`` object that describes the protocol class to use (in this case, either ``Buyer`` or ``Seller``).
It also takes a set of arguments to pass to the constructor. Because it's possible for protocol invocations to
You request a flow to be invoked by using the ``ServiceHub.invokeFlowAsync`` method. This takes a
Java reflection ``Class`` object that describes the flow class to use (in this case, either ``Buyer`` or ``Seller``).
It also takes a set of arguments to pass to the constructor. Because it's possible for flow invocations to
be requested by untrusted code (e.g. a state that you have been sent), the types that can be passed into the
protocol are checked against a whitelist, which can be extended by apps themselves at load time.
flow are checked against a whitelist, which can be extended by apps themselves at load time.
The process of starting a protocol returns a ``ListenableFuture`` that you can use to either block waiting for
The process of starting a flow returns a ``ListenableFuture`` that you can use to either block waiting for
the result, or register a callback that will be invoked when the result is ready.
In a two party protocol only one side is to be manually started using ``ServiceHub.invokeProtocolAsync``. The other side
has to be registered by its node to respond to the initiating protocol via ``ServiceHubInternal.registerProtocolInitiator``.
In our example it doesn't matter which protocol is the initiator and which is the initiated. For example, if we are to
In a two party flow only one side is to be manually started using ``ServiceHub.invokeFlowAsync``. The other side
has to be registered by its node to respond to the initiating flow via ``ServiceHubInternal.registerFlowInitiator``.
In our example it doesn't matter which flow is the initiator and which is the initiated. For example, if we are to
take the seller as the initiator then we would register the buyer as such:
.. container:: codeset
@ -220,20 +220,20 @@ take the seller as the initiator then we would register the buyer as such:
val services: ServiceHubInternal = TODO()
services.registerProtocolInitiator(Seller::class) { otherParty ->
services.registerFlowInitiator(Seller::class) { otherParty ->
val notary = services.networkMapCache.notaryNodes[0]
val acceptablePrice = TODO()
val typeToBuy = TODO()
Buyer(otherParty, notary, acceptablePrice, typeToBuy)
}
This is telling the buyer node to fire up an instance of ``Buyer`` (the code in the lambda) when the initiating protocol
This is telling the buyer node to fire up an instance of ``Buyer`` (the code in the lambda) when the initiating flow
is a seller (``Seller::class``).
Implementing the seller
-----------------------
Let's implement the ``Seller.call`` method. This will be run when the protocol is invoked.
Let's implement the ``Seller.call`` method. This will be run when the flow is invoked.
.. container:: codeset
@ -255,7 +255,7 @@ now signed by both the buyer and the seller. We then send this request to a nota
timestamp in the transaction (if any) is valid and there are no double spends, and send back both
our signature and the notaries signature. Note we should not send to the notary until all other required signatures have been appended
as the notary may validate the signatures as well as verifying for itself the transactional integrity.
Finally, we hand back to the code that invoked the protocol the finished transaction.
Finally, we hand back to the code that invoked the flow the finished transaction.
Let's fill out the ``receiveAndCheckProposedTransaction()`` method.
@ -265,7 +265,7 @@ Let's fill out the ``receiveAndCheckProposedTransaction()`` method.
@Suspendable
private fun receiveAndCheckProposedTransaction(): SignedTransaction {
// Make the first message we'll send to kick off the protocol.
// Make the first message we'll send to kick off the flow.
val hello = SellerTradeInfo(assetToSell, price, myKeyPair.public)
val maybeSTX = sendAndReceive<SignedTransaction>(otherSide, hello)
@ -282,7 +282,7 @@ Let's fill out the ``receiveAndCheckProposedTransaction()`` method.
// Download and check all the things that this transaction depends on and verify it is contract-valid,
// even though it is missing signatures.
subProtocol(ResolveTransactionsProtocol(wtx, otherSide))
subFlow(ResolveTransactionsFlow(wtx, otherSide))
if (wtx.outputs.map { it.data }.sumCashBy(myKeyPair.public).withoutIssuer() != price)
throw IllegalArgumentException("Transaction is not sending us the right amount of cash")
@ -291,7 +291,7 @@ Let's fill out the ``receiveAndCheckProposedTransaction()`` method.
}
}
Let's break this down. We fill out the initial protocol message with the trade info, and then call ``sendAndReceive``.
Let's break this down. We fill out the initial flow message with the trade info, and then call ``sendAndReceive``.
This function takes a few arguments:
- The party on the other side.
@ -300,8 +300,8 @@ This function takes a few arguments:
back something else an exception is thrown.
Once ``sendAndReceive`` is called, the call method will be suspended into a continuation and saved to persistent
storage. If the node crashes or is restarted, the protocol will effectively continue as if nothing had happened. Your
code may remain blocked inside such a call for seconds, minutes, hours or even days in the case of a protocol that
storage. If the node crashes or is restarted, the flow will effectively continue as if nothing had happened. Your
code may remain blocked inside such a call for seconds, minutes, hours or even days in the case of a flow that
needs human interaction!
.. note:: There are a couple of rules you need to bear in mind when writing a class that will be used as a continuation.
@ -311,7 +311,7 @@ needs human interaction!
The second is that as well as being kept on the heap, objects reachable from the stack will be serialised. The state
of the function call may be resurrected much later! Kryo doesn't require objects be marked as serialisable, but even so,
doing things like creating threads from inside these calls would be a bad idea. They should only contain business
logic and only do I/O via the methods exposed by the protocol framework.
logic and only do I/O via the methods exposed by the flow framework.
It's OK to keep references around to many large internal node services though: these will be serialised using a
special token that's recognised by the platform, and wired up to the right instance when the continuation is
@ -331,10 +331,10 @@ Our "scrubbing" has three parts:
2. We resolve the transaction, which we will cover below.
3. We verify that the transaction is paying us the demanded price.
Subprotocols
------------
Sub-flows
---------
Protocols can be composed via nesting. Invoking a sub-protocol looks similar to an ordinary function call:
Flows can be composed via nesting. Invoking a sub-flow looks similar to an ordinary function call:
.. container:: codeset
@ -343,18 +343,18 @@ Protocols can be composed via nesting. Invoking a sub-protocol looks similar to
@Suspendable
private fun getNotarySignature(stx: SignedTransaction): DigitalSignature.LegallyIdentifiable {
progressTracker.currentStep = NOTARY
return subProtocol(NotaryProtocol.Client(stx))
return subFlow(NotaryFlow.Client(stx))
}
In this code snippet we are using the ``NotaryProtocol.Client`` to request notarisation of the transaction.
We simply create the protocol object via its constructor, and then pass it to the ``subProtocol`` method which
returns the result of the protocol's execution directly. Behind the scenes all this is doing is wiring up progress
In this code snippet we are using the ``NotaryFlow.Client`` to request notarisation of the transaction.
We simply create the flow object via its constructor, and then pass it to the ``subFlow`` method which
returns the result of the flow's execution directly. Behind the scenes all this is doing is wiring up progress
tracking (discussed more below) and then running the objects ``call`` method. Because this little helper method can
be on the stack when network IO takes place, we mark it as ``@Suspendable``.
Going back to the previous code snippet, we use a subprotocol called ``ResolveTransactionsProtocol``. This is
Going back to the previous code snippet, we use a sub-flow called ``ResolveTransactionsFlow``. This is
responsible for downloading and checking all the dependencies of a transaction, which in Corda are always retrievable
from the party that sent you a transaction that uses them. This protocol returns a list of ``LedgerTransaction``
from the party that sent you a transaction that uses them. This flow returns a list of ``LedgerTransaction``
objects, but we don't need them here so we just ignore the return value.
.. note:: Transaction dependency resolution assumes that the peer you got the transaction from has all of the
@ -392,7 +392,7 @@ There is an overload for the + operator so signatures can be added to a SignedTr
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.
You can also see that every protocol instance has a logger (using the SLF4J API) which you can use to log progress
You can also see that every flow instance has a logger (using the SLF4J API) which you can use to log progress
messages.
.. warning:: This sample code is **not secure**. Other than not checking for all possible invalid constructions, if the
@ -443,7 +443,7 @@ OK, let's do the same for the buyer side:
// Check the transaction that contains the state which is being resolved.
// We only have a hash here, so if we don't know it already, we have to ask for it.
subProtocol(ResolveTransactionsProtocol(setOf(it.assetForSale.ref.txhash), otherSide))
subFlow(ResolveTransactionsFlow(setOf(it.assetForSale.ref.txhash), otherSide))
return it
}
@ -499,21 +499,21 @@ This code is longer but no more complicated. Here are some things to pay attenti
2. We create a cash spend in the normal way, by using ``Cash().generateSpend``. See the contracts tutorial if this
part isn't clear.
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 network map.
whilst a flow is suspended, things like the wallet or the network map.
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 ``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 flow logic is straightforward and does not contain any callbacks or network glue code, despite
the fact that it takes minimal resources and can survive node restarts.
.. warning:: In the current version of the platform, exceptions thrown during protocol execution are not propagated
.. warning:: In the current version of the platform, exceptions thrown during flow execution are not propagated
back to the sender. A thorough error handling and exceptions framework will be in a future version of the platform.
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
from a flow about where it's got up to in such a way that observers can render it in a useful manner to humans who
may need to be informed. It may be rendered via an API, in a GUI, onto a terminal window, etc.
A ``ProgressTracker`` is constructed with a series of ``Step`` objects, where each step is an object representing a
@ -544,16 +544,16 @@ observable exposes all the events generated by its children as well. The changes
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.
The flow framework is somewhat integrated with this API. Each ``FlowLogic`` may optionally provide a tracker by
overriding the ``flowTracker`` property (``getFlowTracker`` method in Java). If the
``FlowLogic.subFlow`` method is used, then the tracker of the sub-flow will be made a child of the current
step in the parent flow automatically, if the parent is using tracking in the first place. The framework will also
automatically set the current step to ``DONE`` for you, when the flow 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
Because a flow may sometimes wish to configure the children in its progress hierarchy _before_ the sub-flow
is constructed, for sub-flows that always follow the same outline regardless of their parameters it's conventional
to define a companion object/static method (for Kotlin/Java respectively) that constructs a tracker, and then allow
the sub-protocol to have the tracker it will use be passed in as a parameter. This allows all trackers to be built
the sub-flow to have the tracker it will use be passed in as a parameter. This allows all trackers to be built
and linked ahead of time.
In future, the progress tracking framework will become a vital part of how exceptions, errors, and other faults are
@ -562,19 +562,19 @@ surfaced to human operators for investigation and resolution.
Unit testing
------------
A protocol can be a fairly complex thing that interacts with many services and other parties over the network. That
A flow can be a fairly complex thing that interacts with many services and other parties over the network. That
means unit testing one requires some infrastructure to provide lightweight mock implementations. The MockNetwork
provides this testing infrastructure layer; you can find this class in the node module
A good example to examine for learning how to unit test protocols is the ``ResolveTransactionsProtocol`` tests. This
protocol takes care of downloading and verifying transaction graphs, with all the needed dependencies. We start
A good example to examine for learning how to unit test flows is the ``ResolveTransactionsFlow`` tests. This
flow takes care of downloading and verifying transaction graphs, with all the needed dependencies. We start
with this basic skeleton:
.. container:: codeset
.. sourcecode:: kotlin
class ResolveTransactionsProtocolTest {
class ResolveTransactionsFlowTest {
lateinit var net: MockNetwork
lateinit var a: MockNetwork.MockNode
lateinit var b: MockNetwork.MockNode
@ -608,8 +608,8 @@ Next, we write a test case:
@Test
fun resolveFromTwoHashes() {
val (stx1, stx2) = makeTransactions()
val p = ResolveTransactionsProtocol(setOf(stx2.id), a.info.identity)
val future = b.services.startProtocol("resolve", p)
val p = ResolveTransactionsFlow(setOf(stx2.id), a.info.identity)
val future = b.services.startFlow("resolve", p)
net.runNetwork()
val results = future.get()
assertEquals(listOf(stx1.id, stx2.id), results.map { it.id })
@ -621,11 +621,11 @@ We'll take a look at the ``makeTransactions`` function in a moment. For now, it'
``SignedTransaction`` objects, the second of which spends the first. Both transactions are known by node A
but not node B.
The test logic is simple enough: we create the protocol, giving it node A's identity as the target to talk to.
The test logic is simple enough: we create the flow, giving it node A's identity as the target to talk to.
Then we start it on node B and use the ``net.runNetwork()`` method to bounce messages around until things have
settled (i.e. there are no more messages waiting to be delivered). All this is done using an in memory message
routing implementation that is fast to initialise and use. Finally, we obtain the result of the protocol and do
some tests on it. We also check the contents of node B's database to see that the protocol had the intended effect
routing implementation that is fast to initialise and use. Finally, we obtain the result of the flow and do
some tests on it. We also check the contents of node B's database to see that the flow had the intended effect
on the node's persistent state.
Here's what ``makeTransactions`` looks like:
@ -665,30 +665,30 @@ Versioning
----------
Fibers involve persisting object-serialised stack frames to disk. Although we may do some R&D into in-place upgrades
in future, for now the upgrade process for protocols is simple: you duplicate the code and rename it so it has a
new set of class names. Old versions of the protocol can then drain out of the system whilst new versions are
in future, for now the upgrade process for flows is simple: you duplicate the code and rename it so it has a
new set of class names. Old versions of the flow can then drain out of the system whilst new versions are
initiated. When enough time has passed that no old versions are still waiting for anything to happen, the previous
copy of the code can be deleted.
Whilst kind of ugly, this is a very simple approach that should suffice for now.
.. warning:: Protocols are not meant to live for months or years, and by implication they are not meant to implement entire deal
lifecycles. For instance, implementing the entire life cycle of an interest rate swap as a single protocol - whilst
.. warning:: Flows are not meant to live for months or years, and by implication they are not meant to implement entire deal
lifecycles. For instance, implementing the entire life cycle of an interest rate swap as a single flow - whilst
technically possible - would not be a good idea. The platform provides a job scheduler tool that can invoke
protocols for this reason (see ":doc:`event-scheduling`")
flows for this reason (see ":doc:`event-scheduling`")
Future features
---------------
The protocol framework is a key part of the platform and will be extended in major ways in future. Here are some of
The flow framework is a key part of the platform and will be extended in major ways in future. Here are some of
the features we have planned:
* Identity based addressing
* Exposing progress trackers to local (inside the firewall) clients using message queues and/or WebSockets
* Exception propagation and management, with a "protocol hospital" tool to manually provide solutions to unavoidable
* Exception propagation and management, with a "flow hospital" tool to manually provide solutions to unavoidable
problems (e.g. the other side doesn't know the trade)
* Being able to interact with internal apps and tools via HTTP and similar
* Being able to interact with people, either via some sort of external ticketing system, or email, or a custom UI.
For example to implement human transaction authorisations.
* A standard library of protocols that can be easily sub-classed by local developers in order to integrate internal
* A standard library of flows that can be easily sub-classed by local developers in order to integrate internal
reporting logic, or anything else that might be required as part of a communications lifecycle.

View File

@ -16,7 +16,7 @@ Contract
Corda
A Distributed Ledger for recording and managing financial agreements
CorDapp
A Corda Distributed Application. A shared ledger application on Corda consisting of components from: State objects (data), Contract Code (allowable operations), Flows (aka Transaction Protocols, the business logic choreography), any necessary APIs, wallet plugins, and UI components.
A Corda Distributed Application. A shared ledger application on Corda consisting of components from: State objects (data), Contract Code (allowable operations), Flows (aka Transaction Flows, the business logic choreography), any necessary APIs, wallet plugins, and UI components.
Cordformation
A gradle plugin that can be configured via your gradle buildscripts to locally deploy a set of Corda nodes
Counterparty

View File

@ -65,7 +65,7 @@ Read on to learn:
tutorial-contract-clauses
tutorial-test-dsl
tutorial-clientrpc-api
protocol-state-machines
flow-state-machines
oracles
tutorial-attachments
event-scheduling

View File

@ -43,7 +43,7 @@ Preliminaries
ISDA SIMM agreement (if none, then use the demo to enter some simple trades as described below).
Initial Margin Agreement Process
- Agree that one will be performing the margining calculation against a portfolio of trades with another party, and agree the trades in that portfolio. In practice, one node will start the protocol but it does not matter which node does.
- Agree that one will be performing the margining calculation against a portfolio of trades with another party, and agree the trades in that portfolio. In practice, one node will start the flow but it does not matter which node does.
- Individually (at the node level), identify the data (static, reference etc) one will need in order to be able to calculate the metrics on those trades
- Confirm with the other counterparty the dataset from the above set
- Calculate any intermediary steps and values needed for the margin calculation (ie sensitivities to risk factors)

View File

@ -73,13 +73,13 @@ commands of type ``Fix`` as in IRSDemo example. Then we can construct ``Filtered
val wtx: WireTransaction = partialTx.toWireTransaction()
val ftx = FilteredTransaction.buildMerkleTransaction(wtx, filterFuns)
In the Oracle example this step takes place in ``RatesFixProtocol``:
In the Oracle example this step takes place in ``RatesFixFlow``:
.. container:: codeset
.. sourcecode:: kotlin
val protocol = RatesFixProtocol(partialTx, filterFuns, oracle, fixOf, "0.675".bd, "0.1".bd)
val flow = RatesFixFlow(partialTx, filterFuns, oracle, fixOf, "0.675".bd, "0.1".bd)
``FilteredTransaction`` holds ``filteredLeaves`` (data that we wanted to reveal) and Merkle branch for them.

View File

@ -13,8 +13,8 @@ unit tests and visualisation tools.
out for alternative implementations.
There are multiple ways of interacting with the network. When writing an application you typically won't use the
messaging subsystem directly. Instead you will build on top of the :doc:`protocol framework <protocol-state-machines>`,
which adds a layer on top of raw messaging to manage multi-step protocols and let you think in terms of identities
messaging subsystem directly. Instead you will build on top of the :doc:`flow framework <flow-state-machines>`,
which adds a layer on top of raw messaging to manage multi-step flows and let you think in terms of identities
rather than specific network endpoints.
Messaging types

View File

@ -5,7 +5,7 @@ The repository contains a small number of demo programs that run two-node networ
so far. We have:
1. The trader demo, which shows a delivery-vs-payment atomic swap of commercial paper for cash. You can learn more about
how this works in :doc:`protocol-state-machines`.
how this works in :doc:`flow-state-machines`.
2. The IRS demo, which shows two nodes establishing an interest rate swap between them and performing fixings with a
rates oracle, all driven via the HTTP API.
3. The IRS demo web interface - a web interface to the IRS demo.

View File

@ -4,22 +4,22 @@ Secure coding guidelines
The platform does what it can to be secure by default and safe by design. Unfortunately the platform cannot
prevent every kind of security mistake. This document describes what to think about when writing applications
to block various kinds of attack. Whilst it may be tempting to just assume no reasonable counterparty would
attempt to subvert your trades using protocol level attacks, relying on trust for software security makes it
attempt to subvert your trades using flow level attacks, relying on trust for software security makes it
harder to scale up your operations later when you might want to add counterparties quickly and without
extensive vetting.
Protocols
---------
Flows
-----
:doc:`protocol-state-machines` are how your app communicates with other parties on the network. Therefore they
:doc:`flow-state-machines` are how your app communicates with other parties on the network. Therefore they
are the typical entry point for malicious data into your app and must be treated with care.
The ``receive`` methods return data wrapped in the ``UntrustworthyData<T>`` marker type. This type doesn't add
any functionality, it's only there to remind you to properly validate everything that you get from the network.
Remember that the other side may *not* be running the code you provide to take part in the protocol: they are
Remember that the other side may *not* be running the code you provide to take part in the flow: they are
allowed to do anything! Things to watch out for:
* A transaction that doesn't match a partial transaction built or proposed earlier in the protocol, for instance,
* A transaction that doesn't match a partial transaction built or proposed earlier in the flow, for instance,
if you propose to trade a cash state worth $100 for an asset, and the transaction to sign comes back from the
other side, you must check that it points to the state you actually requested. Otherwise the attacker could
get you to sign a transaction that spends a much larger state to you, if they know the ID of one!
@ -30,7 +30,7 @@ allowed to do anything! Things to watch out for:
could re-run the builder logic and do a comparison of the resulting states to ensure that it's what you expected.
For instance if the data needed to construct the next state is available to both parties, the function to
calculate the transaction you want to mutually agree could be shared between both classes implementing both
sides of the protocol.
sides of the flow.
The theme should be clear: signing is a very sensitive operation, so you need to be sure you know what it is you
are about to sign, and that nothing has changed in the small print!

View File

@ -64,7 +64,7 @@ of state:
``DealState``
A LinearState representing an agreement between two or more parties. Intended to simplify implementing generic
protocols that manipulate many agreement types.
flows that manipulate many agreement types.
``FixableDealState``
A deal state, with further functions exposed to support fixing of interest rates.

View File

@ -17,10 +17,10 @@ which returns a unique ID that can be added using ``TransactionBuilder.addAttach
uploaded and downloaded via HTTP, to enable integration with external systems. For instructions on HTTP upload/download
please see ":doc:`node-administration`".
Normally attachments on transactions are fetched automatically via the ``ResolveTransactionsProtocol`` when verifying
Normally attachments on transactions are fetched automatically via the ``ResolveTransactionsFlow`` when verifying
received transactions. Attachments are needed in order to validate a transaction (they include, for example, the
contract code), so must be fetched before the validation process can run. ``ResolveTransactionsProtocol`` calls
``FetchTransactionsProtocol`` to perform the actual retrieval.
contract code), so must be fetched before the validation process can run. ``ResolveTransactionsFlow`` calls
``FetchTransactionsFlow`` to perform the actual retrieval.
It is encouraged that where possible attachments are reusable data, so that nodes can meaningfully cache them.
@ -28,13 +28,13 @@ Attachments demo
----------------
There is a worked example of attachments, which relays a simple document from one node to another. The "two party
trade protocol" also includes an attachment, however it is a significantly more complex demo, and less well suited
trade flow" also includes an attachment, however it is a significantly more complex demo, and less well suited
for a tutorial.
The demo code is in the file "src/main/kotlin/net.corda.demos/attachment/AttachmentDemo.kt", with the core logic
contained within the two functions ``runRecipient()`` and ``runSender()``. We'll look at the recipient function first;
this subscribes to notifications of new validated transactions, and if it receives a transaction containing attachments,
loads the first attachment from storage, and checks it matches the expected attachment ID. ``ResolveTransactionsProtocol``
loads the first attachment from storage, and checks it matches the expected attachment ID. ``ResolveTransactionsFlow``
has already fetched all attachments from the remote node, and as such the attachments are available from the node's
storage service. Once the attachment is verified, the node shuts itself down.
@ -43,10 +43,10 @@ storage service. Once the attachment is verified, the node shuts itself down.
private fun runRecipient(node: Node) {
val serviceHub = node.services
// Normally we would receive the transaction from a more specific protocol, but in this case we let [FinalityProtocol]
// Normally we would receive the transaction from a more specific flow, but in this case we let [FinalityFlow]
// handle receiving it for us.
serviceHub.storageService.validatedTransactions.updates.subscribe { event ->
// When the transaction is received, it's passed through [ResolveTransactionsProtocol], which first fetches any
// When the transaction is received, it's passed through [ResolveTransactionsFlow], which first fetches any
// attachments for us, then verifies the transaction. As such, by the time it hits the validated transaction store,
// we have a copy of the attachment.
val tx = event.tx
@ -62,7 +62,7 @@ storage service. Once the attachment is verified, the node shuts itself down.
}
}
The sender correspondingly builds a transaction with the attachment, then calls ``FinalityProtocol`` to complete the
The sender correspondingly builds a transaction with the attachment, then calls ``FinalityFlow`` to complete the
transaction and send it to the recipient node:
@ -88,7 +88,7 @@ transaction and send it to the recipient node:
// Send the transaction to the other recipient
val tx = ptx.toSignedTransaction()
serviceHub.startProtocol(LOG_SENDER, FinalityProtocol(tx, emptySet(), setOf(otherSide))).success {
serviceHub.startFlow(LOG_SENDER, FinalityFlow(tx, emptySet(), setOf(otherSide))).success {
thread {
Thread.sleep(1000L) // Give the other side time to request the attachment
node.stop()

View File

@ -11,7 +11,7 @@ see :doc:`clientrpc`.
We start off by connecting to the node itself. For the purposes of the tutorial we will use the Driver to start up a notary and a node that issues/exits and moves Cash around for herself. To authenticate we will use the certificates of the nodes directly.
Note how we configure the node to create a user that has permission to start the CashProtocol.
Note how we configure the node to create a user that has permission to start the CashFlow.
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt
:language: kotlin
@ -62,7 +62,7 @@ We utilise several RPC functions here to query things like the notaries in the n
Then in a loop we generate randomly either an Issue, a Pay or an Exit transaction.
The RPC we need to initiate a Cash transaction is ``startProtocolDynamic`` which may start an arbitrary protocol, given sufficient permissions to do so. We won't use this function directly, but rather a type-safe wrapper around it ``startProtocol`` that type-checks the arguments for us.
The RPC we need to initiate a Cash transaction is ``startFlowDynamic`` which may start an arbitrary flow, given sufficient permissions to do so. We won't use this function directly, but rather a type-safe wrapper around it ``startFlow`` that type-checks the arguments for us.
Finally we have everything in place: we start a couple of nodes, connect to them, and start creating transactions while listening on successfully created ones, which are dumped to the console. We just need to run it!:
@ -81,7 +81,7 @@ Now let's try to visualise the transaction graph. We will use a graph drawing li
If we run the client with ``Visualise`` we should see a simple random graph being drawn as new transactions are being created.
Registering classes from your Cordapp with RPC Kryo
--------------------------------------------------
---------------------------------------------------
As described in :doc:`clientrpc`, you currently have to register any additional classes you add that are needed in RPC
requests or responses with the `Kryo` instance RPC uses. Here's an example of how you do this for an example class.

View File

@ -19,7 +19,7 @@ Kotlin syntax works.
Where to put your code
----------------------
A CorDapp is a collection of contracts, state definitions, protocols and other ways to extend the server. To create
A CorDapp is a collection of contracts, state definitions, flows and other ways to extend the server. To create
one you would just create a Java-style project as normal, with your choice of build system (Maven, Gradle, etc).
Then add a dependency on ``net.corda.core:0.X`` where X is the milestone number you are depending on. The core
module defines the base classes used in this tutorial.
@ -694,7 +694,7 @@ a multi-party transaction, you're on your own. In Corda data is transmitted only
multi-party transactions are a way of life, so we provide lots of support for managing them.
You can learn how transactions are moved between peers and taken through the build-sign-notarise-broadcast
process in a separate tutorial, :doc:`protocol-state-machines`.
process in a separate tutorial, :doc:`flow-state-machines`.
Non-asset-oriented smart contracts
----------------------------------

View File

@ -21,7 +21,7 @@ A scenario contains:
* A set of participating nodes and their roles.
* Some business process you wish to automate (typically simplified from the real thing).
* The smart contracts and protocols that will automate that process.
* The smart contracts and flows that will automate that process.
It may also specify a REST/JSON API, but this is optional.
@ -40,9 +40,9 @@ The process of implementing a scenario looks like this:
in Java and Kotlin. The file should define your state classes and your contract class, which will define the
allowable state transitions. You can learn how these are constructed by reading the ":doc:`tutorial-contract`" tutorial.
3. It isn't enough to just define static data and logic that controls what's allowed. You must also orchestrate the
business process. This is the job of the protocol framework. You can learn how to author these by reading
":doc:`protocol-state-machines`".
4. Once you have created your states, transactions and protocols, you need a way to demonstrate them (outside of the
business process. This is the job of the flow framework. You can learn how to author these by reading
":doc:`flow-state-machines`".
4. Once you have created your states, transactions and flows, you need a way to demonstrate them (outside of the
unit tests, of course). This topic is covered below.
The trader demo
@ -63,7 +63,7 @@ demo drivers and then customise them, as much of the code would end up being sha
Things you will want to adjust:
1. The name of the grouping directory each node role will create its private directory under.
2. The demo protocols that just wrap the real business process in some kind of fake trading logic.
2. The demo flows that just wrap the real business process in some kind of fake trading logic.
The IRS driver program registers REST APIs, but as this is seriously in flux right now and the APIs will change a lot,
we do not recommend you try this as part of your initial explorations unless you are feeling adventurous.