mirror of
https://github.com/corda/corda.git
synced 2025-06-16 06:08:13 +00:00
Regen docsite
This commit is contained in:
175
docs/build/html/_sources/azure-vm.txt
vendored
Normal file
175
docs/build/html/_sources/azure-vm.txt
vendored
Normal file
@ -0,0 +1,175 @@
|
||||
Working with the Corda Demo on Azure Marketplace
|
||||
================================================
|
||||
|
||||
Corda ships with a VM image which can be used to deploy a pre-configured virtual machine on the `Microsoft Azure Marketplace <https://azure.microsoft.com/en-gb/overview/what-is-azure>`_
|
||||
|
||||
|
||||
This Corda Demo VM is an easy option for running the demos; it is *NOT* a development environment. When you are ready to get developing on Corda and start making contributions to the project please clone the `GitHub Repos <https://github.com/corda/>`_ instead.
|
||||
|
||||
Pre-requisites
|
||||
--------------
|
||||
* Ensure you have a registered Microsoft Azure account and are logged on to the Azure portal.
|
||||
* It is recommended you generate a private-public SSH key pair (see `here <https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys--2/>`_)
|
||||
|
||||
|
||||
Deploying the VM
|
||||
----------------
|
||||
|
||||
Search the Azure Marketplace for Corda.
|
||||
Click the 'Create' button.
|
||||
|
||||
STEP 1: Basics
|
||||
|
||||
* **Name**: Choose an appropriate descriptive name for the VM
|
||||
* **VM Disk Type**: Select 'SSD'
|
||||
* **Username**: Your preferred user name for the administrator account when accessing via SSH
|
||||
* **Authentication type**: Select 'SSH public key', then paste the contents of your SSH public key file (see pre-requisites, above) into the box below. Alternatively select 'Password' to use a password of your choice to administer the VM
|
||||
|
||||
* **Subscription**: Select your subscription name
|
||||
* **Resource group**: Select 'Use existing'. From the drop-down menu, select your account group
|
||||
* **Location**: Select the geographical location physically closest to you
|
||||
|
||||
.. image:: resources/azure_vm_10_00_1.png
|
||||
:width: 300px
|
||||
|
||||
Click 'OK'
|
||||
|
||||
STEP 2: Size
|
||||
|
||||
A range of available hardware configurations will be presented, along with estimated costs. For the purposes of running the demos, a configuration of 2 cores and at least 14GB is recommended
|
||||
|
||||
.. image:: resources/azure_vm_10_05_1.png
|
||||
:width: 300px
|
||||
|
||||
Choose the required configuration and click 'Select'.
|
||||
|
||||
STEP 3: Settings
|
||||
|
||||
Adjust any configuration settings required. For the purposes of running the Corda demos, all settings may be left as default.
|
||||
|
||||
.. image:: resources/azure_vm_10_16_1.png
|
||||
:width: 300px
|
||||
|
||||
STEP 4: Summary
|
||||
|
||||
The banner at the top of the dialog should read 'Validation passed' otherwise go back and adjust settings where needed.
|
||||
|
||||
.. image:: resources/azure_vm_10_19.png
|
||||
:width: 300px
|
||||
|
||||
Click 'OK' to proceed.
|
||||
|
||||
STEP 5: Buy
|
||||
|
||||
Click 'Purchase' to complete the configuration and start the VM deployment.
|
||||
|
||||
The VM will begin the deployment process, which typically takes 4-5 minutes to complete. To see progress, click on the "Deploying" icon displayed.
|
||||
|
||||
.. image:: resources/azure_vm_10_20.png
|
||||
:width: 300px
|
||||
|
||||
Once deployed, click 'Overview' to see the virtual machine details. Note down the **Public IP address**. You will need this to connect to the demo screens via your web browser:
|
||||
|
||||
.. image:: resources/azure_vm_10_26.png
|
||||
:width: 300px
|
||||
|
||||
|
||||
Viewing the SIMM Valuation demo
|
||||
-------------------------------
|
||||
The SIMM Valuation demo creates three nodes, representing three parties in the example workflow (Bank A, Bank B, Bank C). Each node listens on a different port - those used by the demo are:
|
||||
|
||||
**SIMM Valuation Demo ports:** **12005 (node A for Bank A)**, **12007 (node B for Bank B)**, **12009 (node C for Bank C)**
|
||||
|
||||
Open three browser tabs and direct each one to
|
||||
|
||||
.. sourcecode:: shell
|
||||
|
||||
http://(public IP address):(port)/web/simmvaluationdemo
|
||||
|
||||
specifying each of the three ports above in different windows, e.g.
|
||||
|
||||
.. sourcecode:: shell
|
||||
|
||||
http://51.140.41.48/12005/web/simmvaluationdemo
|
||||
|
||||
You will be able to view the basic web interface identifying the different banks.
|
||||
|
||||
Now lets take a look at a transaction between Bank A and B which is not visible to Bank C. This illustrates the restricted data sharing feature of Corda, i.e. data is shared on a need-to-know basis. Nodes provide the dependency graph of a transaction they are sending to another node on demand, but there is no global broadcast of all transactions.
|
||||
|
||||
1. In the browser tab for Bank A (the top right hand corner shows which bank you are administering) click 'Create New Trade' from the top navigation bar
|
||||
2. Select to trade with Bank B
|
||||
3. Select 'EUR Fixed 1y EURIBOR 3m' from the drop down
|
||||
4. Click 'Submit' to create the trade
|
||||
5. In the browser tab for Bank B click 'View Portfolio' from the top navigation bar to see this new trade
|
||||
6. In the browser tab for Bank C click 'View Portfolio' from the top navigation bar and you will not be able to see the trade, as expected
|
||||
|
||||
.. image:: resources/azure_vm_10_51.png
|
||||
:width: 300px
|
||||
|
||||
.. note:: There is a known issue whereby some users may see a 400 error when navigating the SIMM Valuation demo. If you encounter this error, simply navigate back to the root page (http://*(public IP address)*:*(port)*/*web*/*simmvaluationdemo*) in the browser before continuing.
|
||||
|
||||
Viewing the IRS demo
|
||||
--------------------
|
||||
The IRS demo creates three nodes: Bank A, Bank B and a node that runs a notary, a network map and an interest rates oracle together. The two banks agree on an interest rate swap, and then do regular fixings of the deal as the time on a simulated clock passes. Each bank node listens on a different port - those used by the demo are:
|
||||
|
||||
**IRS demo ports:** **11005 (node A for Bank A)**, **11007 (node B for Bank B)**
|
||||
|
||||
Open two browser tabs and direct one to each of the following:
|
||||
|
||||
.. sourcecode:: shell
|
||||
|
||||
http://localhost:11005/web/irsdemo
|
||||
http://localhost:11007/web/irsdemo
|
||||
|
||||
You will be able to see the the nodes' view of the ledger.
|
||||
|
||||
.. image:: resources/azure_vm_10_52.png
|
||||
:width: 300px
|
||||
|
||||
Now lets take a look at how the interest rates oracle provides interest rates for a deal with a semi-annual payment frequency, and how the two counterparties to the trade see the same deal information on their own nodes, i.e. you see what I see.
|
||||
|
||||
1. In the browser tab for Bank A click 'Create Deal' from the top navigation bar
|
||||
2. Modify the terms of the IRS deal, or leave as default
|
||||
3. Click 'Submit' to create the deal
|
||||
4. In the browser tab for Bank A click 'Recent Deals' from the top navigation bar to view the deal
|
||||
5. In the browser tab for Bank B click 'Recent Deals' from the top navigation bar to view the deal. Compare the economic details to those shown in the Bank A tab
|
||||
|
||||
.. image:: resources/azure_vm_10_54.png
|
||||
:width: 300px
|
||||
|
||||
|
||||
Viewing logs (advanced users)
|
||||
-----------------------------
|
||||
Users may wish to view the raw logs generated by each node, which contain more information about the operations performed by each node.
|
||||
|
||||
You can access these using an SSH client of your choice (e.g. Putty) and logging into the virtual machine using the public IP address.
|
||||
Once logged in, navigate to
|
||||
|
||||
.. sourcecode:: shell
|
||||
|
||||
/opt/simm-nodes/
|
||||
|
||||
for the SIMM Valuation demo logs and
|
||||
|
||||
.. sourcecode:: shell
|
||||
|
||||
/opt/irs-nodes/
|
||||
|
||||
for the IRS demo logs.
|
||||
There are separate sub-directories for each of the three nodes (*nodea*, *nodeb*, *nodec*), each containing a */logs* sub-directory.
|
||||
|
||||
The name of the log file will follow the name given to the service it reflects, e.g. *node-clint-vm-test.log*.
|
||||
|
||||
.. image:: resources/azure_vm_10_47.png
|
||||
:width: 300px
|
||||
|
||||
You can open log files with any text editor.
|
||||
|
||||
.. image:: resources/azure_vm_10_49.png
|
||||
:width: 300px
|
||||
|
||||
Next Steps
|
||||
----------
|
||||
Now you have taken a look at two Corda demos do go and visit the `dedicated Corda website <https://www.corda.net>`_
|
||||
|
||||
Or to get straight into the Corda open source codebase, head over to the `Github Corda repo <https://www.github.com/corda>`_
|
@ -20,7 +20,7 @@ script to find it, as in:
|
||||
|
||||
.. sourcecode:: shell
|
||||
|
||||
r3prototyping/lib/dokka.jar
|
||||
corda/lib/dokka.jar
|
||||
|
||||
Note that to install under OS X El Capitan, you will need to tell pip to install under ``/usr/local``, which can be
|
||||
done by specifying the installation target on the command line:
|
||||
|
7
docs/build/html/_sources/data-model.txt
vendored
7
docs/build/html/_sources/data-model.txt
vendored
@ -2,8 +2,7 @@ Data model
|
||||
==========
|
||||
|
||||
This article covers the data model: how *states*, *transactions* and *code contracts* interact with each other and
|
||||
how they are represented in software. It doesn't attempt to give detailed design rationales or information on future
|
||||
design elements: please refer to the R3 wiki for background information.
|
||||
how they are represented in software.
|
||||
|
||||
Overview
|
||||
--------
|
||||
@ -72,9 +71,7 @@ a special type of transaction is provided that moves a state (or set of states)
|
||||
a mismatch. This is a future planned feature.
|
||||
|
||||
As the same terminology often crops up in different distributed ledger designs, let's compare this to other
|
||||
systems you may be familiar with. You can find more detailed design rationales for why the platform
|
||||
differs from existing systems in `the R3 wiki <https://r3-cev.atlassian.net/wiki/display/AWG/Platform+Stream%3A+Corda>`_,
|
||||
but to summarise, the driving factors are:
|
||||
systems you may be familiar with. The key differences are:
|
||||
|
||||
* Improved contract flexibility vs Bitcoin
|
||||
* Improved scalability vs Ethereum, as well as ability to keep parts of the transaction graph private (yet still uniquely addressable)
|
||||
|
@ -188,6 +188,8 @@ and try again.
|
||||
|
||||
.. note:: Java 9 is likely to remove this pre-marking requirement completely.
|
||||
|
||||
.. note:: Accessing the vault from inside an @Suspendable function (e.g. via ``serviceHub.vaultService``) can cause a serialisation error when the fiber suspends. Instead, vault access should be performed from a helper non-suspendable function, which you then call from the @Suspendable function. We are working to fix this.
|
||||
|
||||
Starting your flow
|
||||
------------------
|
||||
|
||||
@ -505,4 +507,4 @@ the features we have planned:
|
||||
* 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 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.
|
||||
reporting logic, or anything else that might be required as part of a communications lifecycle.
|
||||
|
7
docs/build/html/_sources/getting-set-up.txt
vendored
7
docs/build/html/_sources/getting-set-up.txt
vendored
@ -14,6 +14,10 @@ version of Java 8. The JDK can be obtained `from Oracle <http://www.oracle.com/t
|
||||
Other implementations of the JVM are not actively supported, but as mentioned, we are interested in finding out any issues you
|
||||
do have with them.
|
||||
|
||||
.. note:: If you are using a JVM implementation other than Oracle's you may get errors similiar to ``Unresolved reference: javafx``.
|
||||
This means JavaFX is not bundled with the JVM and you will need to install it separately (e.g. OpenJFX is needed
|
||||
with OpenJDK).
|
||||
|
||||
IntelliJ
|
||||
--------
|
||||
We strongly recommend the use of IntelliJ's Development Environment known as IDEA. Download it for free from
|
||||
@ -75,6 +79,5 @@ You can catch up with the latest code by selecting "VCS -> Update Project" in th
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
See :doc:`getting-set-up-fault-finding`.
|
||||
|
||||
See :doc:`getting-set-up-fault-finding`, or get in touch with us either on the `forums <https://discourse.corda.net/>`_ or via `slack <http://slack.corda.net/>`_.
|
||||
|
||||
|
4
docs/build/html/_sources/index.txt
vendored
4
docs/build/html/_sources/index.txt
vendored
@ -1,7 +1,7 @@
|
||||
Welcome to the Corda documentation!
|
||||
===================================
|
||||
|
||||
.. warning:: This build of the docs is from the *master branch*, not a milestone release. It may not reflect the
|
||||
.. warning:: This build of the docs is from the "|version|" branch, not a milestone release. It may not reflect the
|
||||
current state of the code. `Read the docs for M6 <https://docs.corda.net/releases/release-M6.0/>`_.
|
||||
|
||||
This is the developer guide for Corda, a proposed architecture for distributed ledgers. Here are the sources
|
||||
@ -107,6 +107,8 @@ Read on to learn:
|
||||
release-notes
|
||||
codestyle
|
||||
building-the-docs
|
||||
publishing-corda
|
||||
azure-vm
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
86
docs/build/html/_sources/messaging.txt
vendored
86
docs/build/html/_sources/messaging.txt
vendored
@ -17,15 +17,6 @@ messaging subsystem directly. Instead you will build on top of the :doc:`flow fr
|
||||
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
|
||||
---------------
|
||||
|
||||
Every ``Message`` object has an associated *topic* and may have a *session ID*. These are wrapped in a ``TopicSession``.
|
||||
An implementation of ``MessagingService`` can be used to create messages and send them. You can get access to the
|
||||
messaging service via the ``ServiceHub`` object that is provided to your app. Endpoints on the network are
|
||||
identified at the lowest level using ``SingleMessageRecipient`` which may be e.g. an IP address, or in future
|
||||
versions perhaps a routing path through the network.
|
||||
|
||||
.. _network-map-service:
|
||||
|
||||
Network Map Service
|
||||
@ -48,4 +39,79 @@ The network map currently supports:
|
||||
* Looking up node for a party
|
||||
* Suggesting a node providing a specific service, based on suitability for a contract and parties, for example suggesting
|
||||
an appropriate interest rates oracle for a interest rate swap contract. Currently no recommendation logic is in place.
|
||||
The code simply picks the first registered node that supports the required service.
|
||||
|
||||
Message queues
|
||||
--------------
|
||||
|
||||
The node makes use of various queues for its operation. The more important ones are described below. Others are used
|
||||
for maintenance and other minor purposes.
|
||||
|
||||
:``p2p.inbound``:
|
||||
The node listens for messages sent from other peer nodes on this queue. Only clients who are authenticated to be
|
||||
nodes on the same network are given permission to send. Messages which are routed internally are also sent to this
|
||||
queue (e.g. two flows on the same node communicating with each other).
|
||||
|
||||
:``internal.peers.$identity``:
|
||||
These are a set of private queues only available to the node which it uses to route messages destined to other peers.
|
||||
The queue name ends in the base 58 encoding of the peer's identity key. There is at most one queue per peer. The broker
|
||||
creates a bridge from this queue to the peer's ``p2p.inbound`` queue, using the network map service to lookup the
|
||||
peer's network address.
|
||||
|
||||
:``internal.services.$identity``:
|
||||
These are private queues the node may use to route messages to services. The queue name ends in the base 58 encoding
|
||||
of the service's owning identity key. There is at most one queue per service identity (but note that any one service
|
||||
may have several identities). The broker creates bridges to all nodes in the network advertising the service in
|
||||
question. When a session is initiated with a service counterparty the handshake is pushed onto this queue, and a
|
||||
corresponding bridge is used to forward the message to an advertising peer's p2p queue. Once a peer is picked the
|
||||
session continues on as normal.
|
||||
|
||||
:``internal.networkmap``:
|
||||
This is another private queue just for the node which functions in a similar manner to the ``internal.peers.*`` queues
|
||||
except this is used to form a connection to the network map node. The node running the network map service is treated
|
||||
differently as it provides information about the rest of the network.
|
||||
|
||||
:``rpc.requests``:
|
||||
RPC clients send their requests here, and it's only open for sending by clients authenticated as RPC users.
|
||||
|
||||
:``clients.$user.rpc.$random``:
|
||||
RPC clients are given permission to create a temporary queue incorporating their username (``$user``) and sole
|
||||
permission to receive messages from it. RPC requests are required to include a random number (``$random``) from
|
||||
which the node is able to construct the queue the user is listening on and send the response to that. This mechanism
|
||||
prevents other users from being able listen in on the responses.
|
||||
|
||||
Security
|
||||
--------
|
||||
|
||||
Clients attempting to connect to the node's broker fall in one of four groups:
|
||||
|
||||
#. Anyone connecting with the username ``SystemUsers/Node`` is treated as the node hosting the broker, or a logical
|
||||
component of the node. The TLS certificate they provide must match the one broker has for the node. If that's the case
|
||||
they are given full access to all valid queues, otherwise they are rejected.
|
||||
|
||||
#. Anyone connecting with the username ``SystemUsers/Peer`` is treated as a peer on the same Corda network as the node. Their
|
||||
TLS root CA must be the same as the node's root CA - the root CA is the doorman of the network and having the same root CA
|
||||
implies we've been let in by the same doorman. If they are part of the same network then they are only given permission
|
||||
to send to our ``p2p.inbound`` queue, otherwise they are rejected.
|
||||
|
||||
#. Every other username is treated as a RPC user and authenticated against the node's list of valid RPC users. If that
|
||||
is successful then they are only given sufficient permission to perform RPC, otherwise they are rejected.
|
||||
|
||||
#. Clients connecting without a username and password are rejected.
|
||||
|
||||
Artemis provides a feature of annotating each received message with the validated user. This allows the node's messaging
|
||||
service to provide authenticated messages to the rest of the system. For the first two client types described above the
|
||||
validated user is the X.500 subject DN of the client TLS certificate and we assume the common name is the legal name of
|
||||
the peer. This allows the flow framework to authentically determine the ``Party`` initiating a new flow. For RPC clients
|
||||
the validated user is the username itself and the RPC framework uses this to determine what permissions the user has.
|
||||
|
||||
.. note:: ``Party`` lookup is currently done by the legal name which isn't guaranteed to be unique. A future version will
|
||||
use the full X.500 name as it can provide additional structures for uniqueness.
|
||||
|
||||
Messaging types
|
||||
---------------
|
||||
|
||||
Every ``Message`` object has an associated *topic* and may have a *session ID*. These are wrapped in a ``TopicSession``.
|
||||
An implementation of ``MessagingService`` can be used to create messages and send them. You can get access to the
|
||||
messaging service via the ``ServiceHub`` object that is provided to your app. Endpoints on the network are
|
||||
identified at the lowest level using ``SingleMessageRecipient`` which may be e.g. an IP address, or in future
|
||||
versions perhaps a routing path through the network.
|
||||
|
72
docs/build/html/_sources/node-explorer.txt
vendored
72
docs/build/html/_sources/node-explorer.txt
vendored
@ -1,8 +1,8 @@
|
||||
Node Explorer
|
||||
=============
|
||||
|
||||
The node explorer provide views to the node's vault and transaction data using Corda's RPC framework.
|
||||
The user can execute cash transaction commands to issue and move cash to other party on the network or exit cash using the user interface.
|
||||
The node explorer provides views into a node's vault and transaction data using Corda's RPC framework.
|
||||
The user can execute cash transaction commands to issue and move cash to other parties on the network or exit cash (eg. remove from the ledger)
|
||||
|
||||
Running the UI
|
||||
--------------
|
||||
@ -17,6 +17,27 @@ Running the UI
|
||||
|
||||
Running demo nodes
|
||||
------------------
|
||||
|
||||
A demonstration Corda network topology is configured with 5 nodes playing the following roles:
|
||||
|
||||
1. Notary
|
||||
2. Issuer nodes, representing two fictional central banks (UK Bank Plc issuer of GBP and USA Bank Corp issuer of USD)
|
||||
3. Participant nodes, representing two users (Alice and Bob)
|
||||
|
||||
When connected to an *Issuer* node, a user can execute cash transaction commands to issue and move cash to itself or other
|
||||
parties on the network or to exit cash (for itself only).
|
||||
|
||||
When connected to a *Participant* node a user can only execute cash transaction commands to move cash to other parties on the network.
|
||||
|
||||
The Demo Nodes can be started in one of two modes:
|
||||
|
||||
1. Normal
|
||||
|
||||
Fresh clean environment empty of transactions.
|
||||
Firstly, launch an Explorer instance to login to one of the Issuer nodes and issue some cash to the other participants (Bob and Alice).
|
||||
Then launch another Explorer instance to login to a participant node and start making payments (eg. move cash).
|
||||
You will only be able to exit (eg. redeem from the ledger) cash as an issuer node.
|
||||
|
||||
**Windows**::
|
||||
|
||||
gradlew.bat tools:explorer:runDemoNodes
|
||||
@ -25,21 +46,45 @@ Running demo nodes
|
||||
|
||||
./gradlew tools:explorer:runDemoNodes
|
||||
|
||||
.. note:: 3 Corda nodes will be created on the following port on localhost by default.
|
||||
2. Simulation
|
||||
|
||||
In this mode Nodes will automatically commence executing commands as part of a random generation process.
|
||||
Issuer nodes will randomly issue, move and exit cash.
|
||||
Participant nodes will randomly generate spends (eg. move cash to other nodes, including issuers)
|
||||
|
||||
**Windows**::
|
||||
|
||||
gradlew.bat tools:explorer:runSimulationNodes
|
||||
|
||||
**Other**::
|
||||
|
||||
./gradlew tools:explorer:runSimulationNodes
|
||||
|
||||
|
||||
.. note:: 5 Corda nodes will be created on the following port on localhost by default.
|
||||
|
||||
* Notary -> 20002
|
||||
* Alice -> 20004
|
||||
* Bob -> 20006
|
||||
* UK Bank Plc -> 20008 (*Issuer node*)
|
||||
* USA Bank Corp -> 20010 (*Issuer node*)
|
||||
|
||||
Explorer login credentials to the Issuer nodes are defaulted to ``manager`` and ``test``.
|
||||
Explorer login credentials to the Participants nodes are defaulted to ``user1`` and ``test``.
|
||||
Please note you are not allowed to connect to the notary.
|
||||
|
||||
.. note:: Alternatively, you may start the demo nodes from within IntelliJ using either of the run configurations
|
||||
``Explorer - demo nodes`` or ``Explorer - demo nodes (simulation)``
|
||||
|
||||
.. note:: Use the Explorer in conjunction with the Trader Demo and Bank of Corda samples to use other *Issuer* nodes.
|
||||
|
||||
Interface
|
||||
---------
|
||||
Login
|
||||
User can login to any Corda node using the explorer. Alternatively, ``gradlew explorer:runDemoNodes`` can be used to start up demo nodes for testing.
|
||||
Corda node address, username and password are required for login, the address is defaulted to localhost:0 if leave blank.
|
||||
Username and password can be configured via the ``rpcUsers`` field in node's configuration file; for demo nodes, it is defaulted to ``user1`` and ``test``.
|
||||
Username and password can be configured via the ``rpcUsers`` field in node's configuration file.
|
||||
|
||||
.. note:: If you are connecting to the demo nodes, only Alice and Bob (20004, 20006) are accessible using user1 credential, you won't be able to connect to the notary.
|
||||
|
||||
.. image:: resources/explorer/login.png
|
||||
:scale: 50 %
|
||||
:align: center
|
||||
@ -57,12 +102,19 @@ Cash
|
||||
|
||||
.. image:: resources/explorer/vault.png
|
||||
|
||||
New cash transaction
|
||||
This is where you can create new cash transactions.
|
||||
The user can choose from three transaction types (issue, pay and exit) and any party visible on the network.
|
||||
New Transactions
|
||||
This is where you can create new cash transactions.
|
||||
The user can choose from three transaction types (issue, pay and exit) and any party visible on the network.
|
||||
|
||||
General nodes can only execute pay commands to any other party on the network.
|
||||
|
||||
.. image:: resources/explorer/newTransactionCash.png
|
||||
|
||||
Issuer Nodes
|
||||
Issuer nodes can execute issue (to itself or to any other party), pay and exit transactions.
|
||||
The result of the transaction will be visible in the transaction screen when executed.
|
||||
|
||||
.. image:: resources/explorer/newTransaction.png
|
||||
.. image:: resources/explorer/newTransactionIssuer.png
|
||||
|
||||
Transactions
|
||||
The transaction view contains all transactions handled by the node in a table view. It shows basic information on the table e.g. Transaction ID,
|
||||
|
31
docs/build/html/_sources/permissioning.txt
vendored
31
docs/build/html/_sources/permissioning.txt
vendored
@ -2,12 +2,15 @@ Network permissioning
|
||||
=====================
|
||||
|
||||
The keystore located in ``<workspace>/certificates/sslkeystore.jks`` is required to connect to the Corda network securely.
|
||||
In development mode (when ``devMode = true``, see :doc:`corda-configuration-file` for more information) a pre-configured keystore will be used if the keystore does not exist.
|
||||
This is to ensure developers can get the nodes working as quickly as possible.
|
||||
In development mode (when ``devMode = true``, see :doc:`corda-configuration-file` for more information) a pre-configured
|
||||
keystore will be used if the keystore does not exist. This is to ensure developers can get the nodes working as quickly
|
||||
as possible.
|
||||
|
||||
However this is not secure for the real network. This documentation will explain the procedure of obtaining a signed certificate for TestNet.
|
||||
However this is not secure for the real network. This documentation will explain the procedure of obtaining a signed
|
||||
certificate for TestNet.
|
||||
|
||||
.. warning:: The TestNet has not been setup yet as of Milestone 6 release. You will not be able to connect to the certificate signing server.
|
||||
.. warning:: The TestNet has not been setup yet as of Milestone 6 release. You will not be able to connect to the
|
||||
certificate signing server.
|
||||
|
||||
Certificate signing request utility
|
||||
-----------------------------------
|
||||
@ -15,13 +18,19 @@ Certificate signing request utility
|
||||
The utility creates certificate signing request based on node information obtained from the node configuration.
|
||||
The following information from the node configuration file is needed to generate a certificate signing request.
|
||||
|
||||
:myLegalName: Your company's legal name. e.g. "R3 CEV, LLC"
|
||||
:myLegalName: Your company's legal name. e.g. "Mega Corp LLC". This needs to be unique on the network. If another node
|
||||
has already been permissioned with this name then the permissioning server will automatically reject the request. The
|
||||
request will also be rejected if the name contains a ``=`` or ``,``.
|
||||
|
||||
:nearestCity: e.g. "London"
|
||||
.. note:: In a future version the uniquess requirement will be relaxed to a X.500 name. This will allow differentiation
|
||||
between entities with the same name.
|
||||
|
||||
:nearestCity: e.g. "London"
|
||||
|
||||
:emailAddress: e.g. "admin@company.com"
|
||||
|
||||
:certificateSigningService: Certificate signing server URL. A certificate signing server will be hosted by R3 in the near future. e.g."https://testnet.certificate.corda.net"
|
||||
:certificateSigningService: Certificate signing server URL. A certificate signing server will be hosted by R3 in the near
|
||||
future. e.g."https://testnet.certificate.corda.net"
|
||||
|
||||
A new pair of private and public keys will be generated by the utility and will be used to create the request.
|
||||
|
||||
@ -34,18 +43,18 @@ This process only needs to be done once when the node connects to the network fo
|
||||
|
||||
Building the utility
|
||||
--------------------
|
||||
The utility will be created as part of the main build ``buildCordaJAR``.
|
||||
The utility will be created as part of the gradle ``:node`` module ``buildCordaJAR`` task.
|
||||
You can also build the utility JAR by run the following command from the Corda project root directory.
|
||||
|
||||
**Windows**::
|
||||
|
||||
gradlew.bat buildCertSigningRequestUtilityJAR
|
||||
gradlew.bat :node:buildCertSigningRequestUtilityJAR
|
||||
|
||||
**Other**::
|
||||
|
||||
./gradlew buildCertSigningRequestUtilityJAR
|
||||
./gradlew :node:buildCertSigningRequestUtilityJAR
|
||||
|
||||
The utility JAR will be created in ``<Project Root Dir>/build/libs/certSigningRequestUtility.jar``
|
||||
The utility JAR will be created in ``<Project Root Dir>/node/build/libs/certSigningRequestUtility.jar``
|
||||
|
||||
|
||||
Running the utility
|
||||
|
76
docs/build/html/_sources/publishing-corda.txt
vendored
Normal file
76
docs/build/html/_sources/publishing-corda.txt
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
Publishing Corda
|
||||
================
|
||||
|
||||
Before Publishing
|
||||
-----------------
|
||||
|
||||
Before publishing you must make sure the version you plan to publish has a unique version number. Jcenter and Maven
|
||||
Central will not allow overwriting old versions _unless_ the version is a snapshot.
|
||||
|
||||
This guide assumes you are trying to publish to net.corda.*. Any other Maven coordinates require approval from Jcenter
|
||||
and Maven Central.
|
||||
|
||||
Publishing Locally
|
||||
------------------
|
||||
|
||||
To publish the codebase locally to Maven Local you must run:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
gradlew install
|
||||
|
||||
.. note:: This command is an alias for `publishToMavenLocal`.
|
||||
|
||||
Publishing to Jcenter
|
||||
---------------------
|
||||
|
||||
.. note:: The module you wish to publish must be linked to jcenter in bintray. Only the founding account can do this.
|
||||
|
||||
To publish to Jcenter you must first have the following;
|
||||
|
||||
1. An account on bintray in the R3 organisation
|
||||
2. Our GPG key's passphrase for signing the binaries to publish
|
||||
|
||||
Getting Setup
|
||||
`````````````
|
||||
|
||||
You must now set the following environment variables:
|
||||
|
||||
* CORDA_BINTRAY_USER your Bintray username
|
||||
* CORDA_BINTRAY_KEY to your bintray API key (found at: https://bintray.com/profile/edit)
|
||||
* CORDA_BINTRAY_GPG_PASSPHRASE to our GPG passphrase
|
||||
|
||||
Publishing
|
||||
``````````
|
||||
|
||||
Once you are setup you can upload all modules in a project with
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
gradlew bintrayUpload
|
||||
|
||||
Now login to Bintray and navigate to the corda repository, you will see a box stating you have published N files
|
||||
and asking if you wish to publish. You can now publish to Bintray and Jcenter by clicking this button.
|
||||
|
||||
.. warning:: Before publishing you should check that all of the files are uploaded and are signed.
|
||||
|
||||
Within a minute your new version will be available to download and use.
|
||||
|
||||
Publishing to Maven Central
|
||||
---------------------------
|
||||
|
||||
To publish to Maven Central you need the following;
|
||||
|
||||
1. An admin account on our Bintray R3 organisation
|
||||
2. A published version in Bintray
|
||||
3. An account with our Sonatype organisation (Maven Central's host)
|
||||
|
||||
Publishing
|
||||
``````````
|
||||
|
||||
1. Publish to Bintray
|
||||
2. Navigate to the project you wish to publish
|
||||
3. Click "Maven Central"
|
||||
4. Enter your Sonatype credentials to publish a new version
|
||||
|
||||
.. note:: The project you publish must be already published to Bintray and the project must be linked to Jcenter
|
69
docs/build/html/_sources/running-the-demos.txt
vendored
69
docs/build/html/_sources/running-the-demos.txt
vendored
@ -13,6 +13,8 @@ so far. We have:
|
||||
5. The SIMM valuation demo, a large demo which shows two nodes agreeing on a portfolio and valuing the initial margin
|
||||
using the Standard Initial Margin Model.
|
||||
6. The distributed notary demo, which demonstrates a single node getting multiple transactions notarised by a distributed (Raft-based) notary.
|
||||
7. The Bank of Corda demo, which demonstrates a node acting as an issuer of assets (the Bank of Corda) and remote client
|
||||
applications requesting issuance (via RPC, HTTP) of some cash on behalf of a node called Big Corporation.
|
||||
|
||||
.. note:: If any demos don't work please jump on our mailing list and let us know.
|
||||
|
||||
@ -29,15 +31,15 @@ develop the demos themselves. For more details about running via the command lin
|
||||
Trader demo
|
||||
-----------
|
||||
|
||||
This demo brings up three nodes: Bank A, Bank B and a notary/network map node that they both use. Bank A will
|
||||
be the buyer, and self-issues some cash in order to acquire commercial paper from Bank B, the seller.
|
||||
This demo brings up four nodes: Bank A, Bank B, Bank Of Corda and a notary/network map node that they both use. Bank A will
|
||||
be the buyer, and requests some cash from the Bank of Corda in order to acquire commercial paper from Bank B, the seller.
|
||||
|
||||
To run from the command line:
|
||||
|
||||
1. Run ``./gradlew samples:trader-demo:deployNodes`` to create a set of configs and installs under ``samples/trader-demo/build/nodes``
|
||||
2. Run ``./samples/trader-demo/build/nodes/runnodes`` (or ``runnodes.bat`` on Windows) to open up three new terminals with the three nodes.
|
||||
3. Run ``./gradlew samples:trader-demo:runBuyer`` to set up the buyer node with some self-issued cash. This step
|
||||
is not expected to print much.
|
||||
2. Run ``./samples/trader-demo/build/nodes/runnodes`` (or ``runnodes.bat`` on Windows) to open up four new terminals with the four nodes.
|
||||
3. Run ``./gradlew samples:trader-demo:runBuyer`` to instruct the buyer node to request issuance of some cash from the Bank of Corda node.
|
||||
This step will display progress information related to the cash issuance process (in the bank of corda node log output).
|
||||
4. Run ``./gradlew samples:trader-demo:runSeller`` to trigger the transaction. You can see both sides of the
|
||||
trade print their progress and final transaction state in the bank node tabs/windows.
|
||||
|
||||
@ -156,6 +158,63 @@ by using the H2 web console:
|
||||
- The committed states are stored in the ``NOTARY_COMMITTED_STATES`` table. Note that the raw data is not human-readable,
|
||||
but we're only interested in the row count for this demo.
|
||||
|
||||
Bank Of Corda demo
|
||||
------------------
|
||||
|
||||
This demo brings up three nodes: a notary, a node acting as the Bank of Corda that accepts requests for issuance of some asset
|
||||
and a node acting as Big Corporation which requests issuance of an asset (cash in this example).
|
||||
Upon receipt of a request the Bank of Corda node self-issues the asset and then transfers ownership to the requester
|
||||
after successful notarisation and recording of the issue transaction on the ledger.
|
||||
|
||||
.. note:: The Bank of Corda is somewhat like the "Bitcoin faucet", that used to dispense free bitcoins to developers for
|
||||
testing and experimentation purposes.
|
||||
|
||||
To run from the command line (recommended for Mac/UNIX users!):
|
||||
|
||||
1. Run ``./gradlew samples:bank-of-corda-demo:deployNodes`` to create a set of configs and installs under ``samples/bank-of-corda-demo/build/nodes``
|
||||
2. Run ``./samples/bank-of-corda-demo/build/nodes/runnodes`` to open up three new terminal tabs/windows with the three nodes.
|
||||
|
||||
.. note:: to verify the Bank of Corda node is alive and running navigate to the following URL
|
||||
http://localhost:10005/api/bank/date
|
||||
|
||||
.. note:: the Bank of Corda node explicitly advertises with a node service type as follows:
|
||||
``advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer"))))``
|
||||
This allows for 3rd party applications to perform actions based on Node Type.
|
||||
For example, the Explorer tool only allows nodes of this type to issue and exit cash.
|
||||
|
||||
3. Run ``./gradlew samples:bank-of-corda-demo:runRPCCashIssue`` in another terminal window to trigger a cash issuance request
|
||||
4. Run ``./gradlew samples:bank-of-corda-demo:runWebCashIssue`` in another terminal window to trigger another cash issuance request
|
||||
Now look at the other windows to see the output of the demo.
|
||||
|
||||
Or you can run them from inside IntelliJ as follows:
|
||||
|
||||
1. Open the Corda project in IntelliJ and run the "Install" configuration
|
||||
2. Open the Corda samples project in IntelliJ and run the "Bank Of Corda Demo: Run Issuer" configuration
|
||||
3. Run "Bank Of Corda Demo: Run RPC Cash Issue" - requests issuance of some cash on behalf of Big Corporation via RPC
|
||||
4. Run "Bank Of Corda Demo: Run Web Cash Issue" - requests issuance of some cash on behalf of Big Corporation via HTTP
|
||||
|
||||
In the "Bank Of Corda Demo: Run Issuer" window you should see the following information lines displayed:
|
||||
|
||||
- Awaiting issuance request
|
||||
- Self issuing asset
|
||||
- Transferring asset to issuance requester
|
||||
- Confirming asset issuance to requester
|
||||
|
||||
In the the client issue request window you should see the following printed:
|
||||
|
||||
- Successfully processed Cash Issue request
|
||||
|
||||
Launch the Explorer application to visualize the issuance and transfer of cash on each node:
|
||||
|
||||
``./gradlew tools:explorer:run``
|
||||
|
||||
And use the following logon details:
|
||||
|
||||
- for the Bank of Corda node specify localhost, port 10004, username user1, password test
|
||||
- for the Big Corporation node specify localhost, port 10006, username user1, password test
|
||||
|
||||
See https://docs.corda.net/node-explorer.html for further details on usage.
|
||||
|
||||
SIMM and Portfolio Demo - aka the Initial Margin Agreement Demo
|
||||
---------------------------------------------------------------
|
||||
|
||||
|
@ -20,13 +20,8 @@ Setting up your own network
|
||||
Certificates
|
||||
------------
|
||||
|
||||
If two nodes are to communicate successfully then both need to have
|
||||
each other's root certificate in their truststores. The simplest way
|
||||
to achieve this is to have all nodes sign off of a single root.
|
||||
|
||||
Later R3 will provide this root for production use, but for testing you
|
||||
can use ``certSigningRequestUtility.jar`` to generate a node
|
||||
certificate with a fixed test root:
|
||||
All nodes belonging to the same Corda network must have the same root CA. For testing purposes you can
|
||||
use ``certSigningRequestUtility.jar`` to generate a node certificate with a fixed test root:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
|
271
docs/build/html/_sources/tutorial-cordapp.txt
vendored
271
docs/build/html/_sources/tutorial-cordapp.txt
vendored
@ -22,6 +22,25 @@ We recommend you read the non-technical white paper and technical white paper be
|
||||
intended design from beginning to end. It is the kind of document that you should read, or at least, read parts of. Note
|
||||
that because the technical white paper describes the intended end state, it does not always align with the implementation.
|
||||
|
||||
Background
|
||||
----------
|
||||
|
||||
The Example CorDapp implements a basic scenario where a buyer wishes to submit purchase orders to a seller. The scenario
|
||||
defines four nodes:
|
||||
|
||||
* **Controller** which hosts the network map service and validating notary service.
|
||||
* **NodeA** who is the buyer.
|
||||
* **NodeB** who is the seller.
|
||||
* **NodeC** an unrelated third party.
|
||||
|
||||
NodeA can generate purchase orders for lists and quantities of items and associated metadata such as delivery address
|
||||
and delivery date. The flows used to facilitate the agreement process always results in an agreement with the seller as
|
||||
long as the purchase order meets the contract constraints which are defined in ``PurchaseOrderContract.kt``.
|
||||
|
||||
All agreed purchase orders between NodeA and NodeB become "shared facts" between NodeA and NodeB. But note that NodeC
|
||||
won't see any of these transactions or have copies of any of the resulting ``PurchaseOrderState`` objects. This is
|
||||
because data is only propagated on a need-to-know basis.
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
|
||||
@ -201,14 +220,14 @@ Next, IntelliJ will show a bunch of pop-up windows. One of which requires our at
|
||||
.. image:: resources/unlinked-gradle-project.png
|
||||
:width: 400
|
||||
|
||||
Click the 'import gradle project' link. A dialogue will pop-up. Press OK. Gradle will now begin obtianing all the
|
||||
Click the 'import gradle project' link. A dialogue will pop-up. Press OK. Gradle will now obtain all the
|
||||
project dependencies and perform some indexing. It usually takes a minute or so. If you miss the 'import gradle project'
|
||||
dialogue, simply close and re-open IntelliJ again to see it again.
|
||||
|
||||
**Alternative approach**
|
||||
|
||||
Alternatively, one can instruct IntelliJ to create a new project through cloning a repository. From the IntelliJ welcome
|
||||
dialogue (shown above), opt to 'check out from version control', then select git and enter the git url for the CorDpp tempalte
|
||||
dialogue (shown above), opt to 'check out from version control', then select git and enter the git URL for the CorDapp template
|
||||
(https://github.com/corda/cordapp-template). You'll then need to import the Gradle project when prompted, as explained above.
|
||||
|
||||
**If you already have IntelliJ open**
|
||||
@ -245,14 +264,18 @@ Unix/Mac OSX: ``./gradlew deployNodes``
|
||||
|
||||
Windows: ``gradlew.bat deployNodes``
|
||||
|
||||
Building straight away will build the example CorDapp defined the the CorDapp template source. For more information on the example
|
||||
CorDapp see "The Example CorDapp" section below. Gradle will then grab all the dependencies for you and build the
|
||||
example CorDapp.
|
||||
This build process will build the example CorDapp defined in the CorDapp template source. CorDapps can be written in
|
||||
any language targeting the JVM. In our case, we've provided the template source in both Kotlin (``/kotlin/src``) and
|
||||
Java (``/java/src``) Since both sets of source files are functionally identical, we will refer to the Kotlin build
|
||||
throughout the documentation.
|
||||
|
||||
For more information on the example CorDapp see "The Example CorDapp" section below. Gradle will then grab all the
|
||||
dependencies for you and build the example CorDapp.
|
||||
|
||||
The ``deployNodes`` Gradle task allows you easily create a formation of Corda nodes. In the case of the example CorDapp
|
||||
we are creating ``four`` nodes.
|
||||
|
||||
After the building process has finished to see the newly built nodes, you can navigate to the ``/build/nodes`` folder
|
||||
After the building process has finished to see the newly built nodes, you can navigate to the ``kotlin/build/nodes`` folder
|
||||
located in the ``cordapp-template`` root directory. You can ignore the other folders in ``/build`` for now. The ``nodes``
|
||||
folder has the following structure:
|
||||
|
||||
@ -294,13 +317,13 @@ Open the Gradle window by selecting ``View > Tool windows > Gradle`` from the ma
|
||||
open on the right hand side of the IDE. Expand `tasks` and then expand `other`. Double click on `deployNodes`. Gradle will
|
||||
start the build process and output progress to a console window in the IDE.
|
||||
|
||||
Running the Sample CorDapp
|
||||
==========================
|
||||
Running the CorDapp template
|
||||
============================
|
||||
|
||||
Running the Sample CorDapp from the command line
|
||||
------------------------------------------------
|
||||
Running the CorDapp template from the command line
|
||||
--------------------------------------------------
|
||||
|
||||
To run the sample CorDapp navigate to the ``build/nodes`` folder and execute the ``runnodes`` shell script with:
|
||||
To run the sample CorDapp navigate to the ``kotlin/build/nodes`` folder and execute the ``runnodes`` shell script with:
|
||||
|
||||
Unix: ``./runnodes`` or ``sh runnodes``
|
||||
|
||||
@ -319,8 +342,8 @@ message and some pertinent config information, see below:
|
||||
|
||||
--- DEVELOPER SNAPSHOT ------------------------------------------------------------
|
||||
|
||||
Logs can be found in : /Users/rogerwillis/Documents/Corda/cordapp-template/build/nodes/nodea/logs
|
||||
Database connection url is : jdbc:h2:tcp://10.18.0.196:50661/node
|
||||
Logs can be found in : /Users/rogerwillis/Documents/Corda/cordapp-template/kotlin/build/nodes/nodea/logs
|
||||
Database connection URL is : jdbc:h2:tcp://10.18.0.196:50661/node
|
||||
Node listening on address : localhost:10004
|
||||
Loaded plugins : com.example.plugin.ExamplePlugin
|
||||
Embedded web server is listening on : http://10.18.0.196:10005/
|
||||
@ -368,7 +391,7 @@ Running CorDapps on separate machines
|
||||
Corda nodes can be run on separate machines with little additional configuration to the above instructions.
|
||||
|
||||
When you have successfully run the ``deployNodes`` gradle task, choose which nodes you would like to run on separate
|
||||
machines. Copy the folders for those nodes from ``build/nodes`` to the other machines. Make sure that you set the
|
||||
machines. Copy the folders for those nodes from ``kotlin/build/nodes`` to the other machines. Make sure that you set the
|
||||
``networkMapAddress`` property in ``node.conf`` to the correct hostname:port where the network map service node is
|
||||
hosted.
|
||||
|
||||
@ -405,36 +428,17 @@ To stop the nodes, press the red "stop" button at the top right-hand side of the
|
||||
|
||||
The node driver can also be used to as a basis for `debugging your CorDapp`_
|
||||
|
||||
Using the sample CorDapp
|
||||
========================
|
||||
Interacting with the CorDapp template
|
||||
=====================================
|
||||
|
||||
Background
|
||||
----------
|
||||
|
||||
The Example CorDapp implements a basic scenario where a buyer wishes to submit purchase orders to a seller. The scenario
|
||||
defines four nodes:
|
||||
|
||||
* **Controller** which hosts the network map service and validating notary service.
|
||||
* **NodeA** who is the buyer.
|
||||
* **NodeB** who is the seller.
|
||||
* **NodeC** an unrelated third party.
|
||||
|
||||
NodeA can generate purchase orders for lists and quantities of items and associated metadata such as delivery address
|
||||
and delivery date. The flows used to facilitate the agreement process always results in an agreement with the seller as
|
||||
long as the purchase order meets the contract constraints which are defined in ``PurchaseOrderContract.kt``.
|
||||
|
||||
All agreed purchase orders between NodeA and NodeB become "shared facts" between NodeA and NodeB. But note that NodeC
|
||||
won't see any of these transactions or have copies of any of the resulting ``PurchaseOrderState`` objects. This is
|
||||
because data is only propagated on a need-to-know basis.
|
||||
|
||||
Interfaces
|
||||
----------
|
||||
Via HTTP
|
||||
--------
|
||||
|
||||
The CorDapp defines a few HTTP API end-points and also serves some static web content. The end-points allow you to
|
||||
list purchase orders and add purchase orders.
|
||||
|
||||
The nodes can be found using the following port numbers, defined in build.gradle and the respective node.conf file for
|
||||
each node found in `build/nodes/NodeX`` etc:
|
||||
each node found in `kotlin/build/nodes/NodeX`` etc:
|
||||
|
||||
* Controller: ``localhost:10003``
|
||||
* NodeA: ``localhost:10005``
|
||||
@ -465,7 +469,7 @@ To create a purchase order from NodeA to NodeB, use:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
echo '{"orderNumber": "1","deliveryDate": "2018-09-15","deliveryAddress": {"city": "London","country": "UK"},"items" : [{"name": "widget","amount": "3"},{"name": "thing","amount": "4"}]}' | curl -T - -H 'Content-Type: application/json' http://localhost:10005/api/example/NodeB/create-purchase-order
|
||||
echo '{"orderNumber": "1","deliveryDate": "2018-09-15","deliveryAddress": {"city": "London","country": "UK"},"items" : [{"name": "widget","amount": "3"},{"name": "thing","amount": "4"}]}' | cURL -T - -H 'Content-Type: application/json' http://localhost:10005/api/example/NodeB/create-purchase-order
|
||||
|
||||
Note the port number ``10005`` (NodeA) and NodeB referenced in the API end-point path. This command instructs NodeA to
|
||||
create and send a purchase order to NodeB. Upon verification and completion of the process, both nodes (but not NodeC) will
|
||||
@ -550,7 +554,8 @@ navigate to http://localhost:10007/api/example/purchase-orders.
|
||||
Navigate to http://localhost:10005/web/example the refresh button in the top left-hand side of the page. You should
|
||||
see the newly created agreement on the page.
|
||||
|
||||
**Accessing the h2 database via h2 web console:**
|
||||
Using the h2 web console
|
||||
------------------------
|
||||
|
||||
You can connect to the h2 database to see the current state of the ledger, among other data such as the current state of
|
||||
the network map cache. Firstly, navigate to the folder where you downloaded the h2 web console as part of the pre-requisites
|
||||
@ -572,13 +577,14 @@ The h2 web console should start up in a web browser tab. To connect we first nee
|
||||
node outputs its connection string in the terminal window as it starts up. In a terminal window where a node is running,
|
||||
look for the following string:
|
||||
|
||||
``Database connection url is : jdbc:h2:tcp://10.18.0.150:56736/node``
|
||||
``Database connection URL is : jdbc:h2:tcp://10.18.0.150:56736/node``
|
||||
|
||||
you can use the string on the right to connect to the h2 database: just paste it in to the JDBC URL field and click Connect.
|
||||
You will be presented with a web application that enumerates all the available tables and provides an interface for you to
|
||||
query them using SQL.
|
||||
|
||||
**Using the Example RPC client:**
|
||||
Using the Example RPC client
|
||||
----------------------------
|
||||
|
||||
The ``/src/main/kotlin/com/example/client/ExampleClientRPC.kt`` file is a simple utility which uses the client RPC library
|
||||
to connect to a node and log the 'placed' purchase orders. It will log any existing purchase orders and listen for any future
|
||||
@ -606,6 +612,9 @@ application see:
|
||||
* :doc:`Client RPC documentation <clientrpc>`
|
||||
* :doc:`Client RPC tutorial <tutorial-clientrpc-api>`
|
||||
|
||||
Extending the CorDapp template
|
||||
==============================
|
||||
|
||||
CorDapp-template Project Structure
|
||||
----------------------------------
|
||||
|
||||
@ -627,48 +636,90 @@ The CorDapp template has the following directory structure:
|
||||
├── lib
|
||||
│ ├── ...
|
||||
├── settings.gradle
|
||||
└── src
|
||||
├── main
|
||||
│ ├── java
|
||||
│ ├── kotlin
|
||||
│ │ └── com
|
||||
│ │ └── example
|
||||
│ │ ├── Main.kt
|
||||
│ │ ├── api
|
||||
│ │ │ └── ExampleApi.kt
|
||||
│ │ ├── client
|
||||
│ │ │ └── ExampleClientRPC.kt
|
||||
│ │ ├── contract
|
||||
│ │ │ ├── PurchaseOrderContract.kt
|
||||
│ │ │ └── PurchaseOrderState.kt
|
||||
│ │ ├── model
|
||||
│ │ │ └── PurchaseOrder.kt
|
||||
│ │ ├── plugin
|
||||
│ │ │ └── ExamplePlugin.kt
|
||||
│ │ └── flow
|
||||
│ │ └── ExampleFlow.kt
|
||||
│ │ └── service
|
||||
│ │ └── ExampleService.kt
|
||||
│ ├── python
|
||||
│ └── resources
|
||||
│ ├── META-INF
|
||||
│ │ └── services
|
||||
│ │ └── net.corda.core.node.CordaPluginRegistry
|
||||
│ ├── certificates
|
||||
│ │ ├── readme.txt
|
||||
│ │ ├── sslkeystore.jks
|
||||
│ │ └── truststore.jks
|
||||
│ └── exampleWeb
|
||||
│ ├── index.html
|
||||
│ └── js
|
||||
│ └── example.js
|
||||
└── test
|
||||
├── java
|
||||
├── kotlin
|
||||
│ └── com
|
||||
│ └── example
|
||||
│ └── ExampleTest.kt
|
||||
└── resources
|
||||
├── kotlin
|
||||
│ └── src
|
||||
│ ├── main
|
||||
│ │ ├── kotlin
|
||||
│ │ │ └── com
|
||||
│ │ │ └── example
|
||||
│ │ │ ├── Main.kt
|
||||
│ │ │ ├── api
|
||||
│ │ │ │ └── ExampleApi.kt
|
||||
│ │ │ ├── client
|
||||
│ │ │ │ └── ExampleClientRPC.kt
|
||||
│ │ │ ├── contract
|
||||
│ │ │ │ ├── PurchaseOrderContract.kt
|
||||
│ │ │ │ └── PurchaseOrderState.kt
|
||||
│ │ │ ├── model
|
||||
│ │ │ │ └── PurchaseOrder.kt
|
||||
│ │ │ ├── plugin
|
||||
│ │ │ │ └── ExamplePlugin.kt
|
||||
│ │ │ └── flow
|
||||
│ │ │ └── ExampleFlow.kt
|
||||
│ │ │ └── service
|
||||
│ │ │ └── ExampleService.kt
|
||||
│ │ ├── python
|
||||
│ │ └── resources
|
||||
│ │ ├── META-INF
|
||||
│ │ │ └── services
|
||||
│ │ │ └── net.corda.core.node.CordaPluginRegistry
|
||||
│ │ ├── certificates
|
||||
│ │ │ ├── readme.txt
|
||||
│ │ │ ├── sslkeystore.jks
|
||||
│ │ │ └── truststore.jks
|
||||
│ │ └── exampleWeb
|
||||
│ │ ├── index.html
|
||||
│ │ └── js
|
||||
│ │ └── example.js
|
||||
│ └── test
|
||||
│ ├── java
|
||||
│ ├── kotlin
|
||||
│ │ └── com
|
||||
│ │ └── example
|
||||
│ │ └── ExampleTest.kt
|
||||
│ └── resources
|
||||
└── java
|
||||
└── src
|
||||
├── main
|
||||
│ ├── java
|
||||
│ │ └── com
|
||||
│ │ └── example
|
||||
│ │ ├── Main.java
|
||||
│ │ ├── api
|
||||
│ │ │ └── ExampleApi.java
|
||||
│ │ ├── client
|
||||
│ │ │ └── ExampleClientRPC.java
|
||||
│ │ ├── contract
|
||||
│ │ │ ├── PurchaseOrderContract.java
|
||||
│ │ │ └── PurchaseOrderState.java
|
||||
│ │ ├── model
|
||||
│ │ │ └── PurchaseOrder.java
|
||||
│ │ ├── plugin
|
||||
│ │ │ └── ExamplePlugin.java
|
||||
│ │ └── flow
|
||||
│ │ └── ExampleFlow.java
|
||||
│ │ └── service
|
||||
│ │ └── ExampleService.java
|
||||
│ ├── python
|
||||
│ └── resources
|
||||
│ ├── META-INF
|
||||
│ │ └── services
|
||||
│ │ └── net.corda.core.node.CordaPluginRegistry
|
||||
│ ├── certificates
|
||||
│ │ ├── readme.txt
|
||||
│ │ ├── sslkeystore.jks
|
||||
│ │ └── truststore.jks
|
||||
│ └── exampleWeb
|
||||
│ ├── index.html
|
||||
│ └── js
|
||||
│ └── example.js
|
||||
└── test
|
||||
├── java
|
||||
├── kotlin
|
||||
│ └── com
|
||||
│ └── example
|
||||
│ └── ExampleTest.kt
|
||||
└── resources
|
||||
|
||||
In the file structure above, the most important files and directories to note are:
|
||||
|
||||
@ -677,15 +728,18 @@ In the file structure above, the most important files and directories to note ar
|
||||
* **gradle** contains the gradle wrapper, which allows the use of Gradle without installing it yourself and worrying
|
||||
about which version is required.
|
||||
* **lib** contains the Quasar.jar which is required for runtime instrumentation of classes by Quasar.
|
||||
* **src/main/kotlin** contains the source code for the example CorDapp.
|
||||
* **src/main/python** contains a python script which accesses nodes via RPC.
|
||||
* **src/main/resources** contains the certificate store, some static web content to be served by the nodes and the
|
||||
PluginServiceRegistry file.
|
||||
* **src/test/kotlin** contains unit tests for protocols, contracts, etc.
|
||||
* **kotlin** contains the source code for the example CorDapp written in Kotlin.
|
||||
* **kotlin/src/main/kotlin** contains the source code for the example CorDapp.
|
||||
* **kotlin/src/main/python** contains a python script which accesses nodes via RPC.
|
||||
* **kotlin/src/main/resources** contains the certificate store, some static web content to be served by the nodes and the
|
||||
PluginServiceRegistry file.
|
||||
* **kotlin/src/test/kotlin** contains unit tests for protocols, contracts, etc.
|
||||
* **java** contains the same source code, but written in java. This is an aid for users who do not want to develop in
|
||||
Kotlin, and serves as an example of how CorDapps can be developed in any language targeting the JVM.
|
||||
|
||||
Some elements are covered in more detail below.
|
||||
|
||||
The build.gradle File
|
||||
The build.gradle file
|
||||
---------------------
|
||||
|
||||
It is usually necessary to make a couple of changes to the ``build.gradle`` file. Here will cover the most pertinent bits.
|
||||
@ -758,7 +812,7 @@ like to deploy for testing. See further details below:
|
||||
.. sourcecode:: groovy
|
||||
|
||||
task deployNodes(type: com.r3corda.plugins.Cordform, dependsOn: ['build']) {
|
||||
directory "./build/nodes" // The output directory.
|
||||
directory "./kotlin/build/nodes" // The output directory.
|
||||
networkMap "Controller" // The artemis address of the node to be used as the network map.
|
||||
node {
|
||||
name "Controller" // Artemis name of node to be deployed.
|
||||
@ -804,7 +858,7 @@ Re-Deploying Your Nodes Locally
|
||||
If you need to create any additional nodes you can do it via the ``build.gradle`` file as discussed above in
|
||||
``the build.gradle file`` and in more detail in the "cordFormation" section.
|
||||
|
||||
You may also wish to edit the ``/build/nodes/<node name>/node.conf`` files for your nodes. For more information on
|
||||
You may also wish to edit the ``/kotlin/build/nodes/<node name>/node.conf`` files for your nodes. For more information on
|
||||
doing this, see the :doc:`Corda configuration file <corda-configuration-file>` page.
|
||||
|
||||
Once you have made some changes to your CorDapp you can redeploy it with the following command:
|
||||
@ -813,6 +867,39 @@ Unix/Mac OSX: ``./gradlew deployNodes``
|
||||
|
||||
Windows: ``gradlew.bat deployNodes``
|
||||
|
||||
Running Nodes Across Machines
|
||||
-----------------------------
|
||||
|
||||
The nodes can also be set up to communicate between separate machines on the
|
||||
same subnet.
|
||||
|
||||
After deploying the nodes, navigate to the build folder (`kotlin/build/
|
||||
nodes` or `java/build/nodes`) and move some of the individual node folders to
|
||||
separate machines on the same subnet (e.g. using a USB key). It is important
|
||||
that no nodes - including the controller node - end up on more than one
|
||||
machine. Each computer should also have a copy of `runnodes` and
|
||||
`runnodes.bat`.
|
||||
|
||||
For example, you may end up with the following layout:
|
||||
|
||||
* Machine 1: `controller`, `nodea`, `runnodes`, `runnodes.bat`
|
||||
* Machine 2: `nodeb`, `nodec`, `runnodes`, `runnodes.bat`
|
||||
|
||||
You must now edit the configuration file for each node, including the
|
||||
controller. Open each node's config file (`[nodeName]/node.conf`), and make
|
||||
the following changes:
|
||||
|
||||
* Change the artemis address to the machine's ip address (e.g.
|
||||
`artemisAddress="10.18.0.166:10006"`)
|
||||
* Change the network map address to the ip address of the machine where the
|
||||
controller node is running (e.g. `networkMapAddress="10.18.0.166:10002"`)
|
||||
(please note that the controller will not have a network map address)
|
||||
|
||||
Each machine should now run its nodes using `runnodes` or `runnodes.bat`
|
||||
files. Once they are up and running, the nodes should be able to place
|
||||
purchase orders among themselves in the same way as when they were running on
|
||||
the same machine.
|
||||
|
||||
Debugging your CorDapp
|
||||
----------------------
|
||||
|
||||
|
524
docs/build/html/_sources/tutorial-test-dsl.txt
vendored
524
docs/build/html/_sources/tutorial-test-dsl.txt
vendored
@ -1,6 +1,6 @@
|
||||
.. highlight:: kotlin
|
||||
.. role:: kotlin(code)
|
||||
:language: kotlin
|
||||
:language: kotlin
|
||||
.. raw:: html
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
Writing a contract test
|
||||
=======================
|
||||
|
||||
This tutorial will take you through the steps required to write a contract test using Kotlin and/or Java.
|
||||
This tutorial will take you through the steps required to write a contract test using Kotlin and Java.
|
||||
|
||||
The testing DSL allows one to define a piece of the ledger with transactions referring to each other, and ways of
|
||||
verifying their correctness.
|
||||
@ -24,10 +24,13 @@ We start with the empty ledger:
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
@Test
|
||||
fun emptyLedger() {
|
||||
ledger {
|
||||
class CommercialPaperTest{
|
||||
@Test
|
||||
fun emptyLedger() {
|
||||
ledger {
|
||||
}
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
.. sourcecode:: java
|
||||
@ -45,18 +48,43 @@ We start with the empty ledger:
|
||||
The DSL keyword ``ledger`` takes a closure that can build up several transactions and may verify their overall
|
||||
correctness. A ledger is effectively a fresh world with no pre-existing transactions or services within it.
|
||||
|
||||
Let's add a Cash transaction:
|
||||
We will start with defining helper function that returns a ``CommercialPaper`` state:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
fun getPaper(): ICommercialPaperState = CommercialPaper.State(
|
||||
issuance = MEGA_CORP.ref(123),
|
||||
owner = MEGA_CORP_PUBKEY,
|
||||
faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123),
|
||||
maturityDate = TEST_TX_TIME + 7.days
|
||||
)
|
||||
|
||||
.. sourcecode:: java
|
||||
|
||||
private final OpaqueBytes defaultRef = new OpaqueBytes(new byte[]{123});
|
||||
|
||||
private ICommercialPaperState getPaper() {
|
||||
return new JavaCommercialPaper.State(
|
||||
getMEGA_CORP().ref(defaultRef),
|
||||
getMEGA_CORP_PUBKEY(),
|
||||
issuedBy(DOLLARS(1000), getMEGA_CORP().ref(defaultRef)),
|
||||
getTEST_TX_TIME().plus(7, ChronoUnit.DAYS)
|
||||
);
|
||||
}
|
||||
|
||||
It's a ``CommercialPaper`` issued by ``MEGA_CORP`` with face value of $1000 and maturity date in 7 days.
|
||||
|
||||
Let's add a ``CommercialPaper`` transaction:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
@Test
|
||||
fun simpleCashDoesntCompile() {
|
||||
val inState = Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` DUMMY_CASH_ISSUER,
|
||||
owner = DUMMY_PUBKEY_1
|
||||
)
|
||||
fun simpleCPDoesntCompile() {
|
||||
val inState = getPaper()
|
||||
ledger {
|
||||
transaction {
|
||||
input(inState)
|
||||
@ -67,11 +95,8 @@ Let's add a Cash transaction:
|
||||
.. sourcecode:: java
|
||||
|
||||
@Test
|
||||
public void simpleCashDoesntCompile() {
|
||||
Cash.State inState = new Cash.State(
|
||||
issuedBy(DOLLARS(1000), getDUMMY_CASH_ISSUER()),
|
||||
getDUMMY_PUBKEY_1()
|
||||
);
|
||||
public void simpleCPDoesntCompile() {
|
||||
ICommercialPaperState inState = getPaper();
|
||||
ledger(l -> {
|
||||
l.transaction(tx -> {
|
||||
tx.input(inState);
|
||||
@ -83,7 +108,7 @@ Let's add a Cash transaction:
|
||||
We can add a transaction to the ledger using the ``transaction`` primitive. The transaction in turn may be defined by
|
||||
specifying ``input``-s, ``output``-s, ``command``-s and ``attachment``-s.
|
||||
|
||||
The above ``input`` call is a bit special: Transactions don't actually contain input states, just references
|
||||
The above ``input`` call is a bit special; transactions don't actually contain input states, just references
|
||||
to output states of other transactions. Under the hood the above ``input`` call creates a dummy transaction in the
|
||||
ledger (that won't be verified) which outputs the specified state, and references that from this transaction.
|
||||
|
||||
@ -93,11 +118,11 @@ The above code however doesn't compile:
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
Error:(26, 21) Kotlin: Type mismatch: inferred type is Unit but EnforceVerifyOrFail was expected
|
||||
Error:(29, 17) Kotlin: Type mismatch: inferred type is Unit but EnforceVerifyOrFail was expected
|
||||
|
||||
.. sourcecode:: java
|
||||
|
||||
Error:(26, 31) java: incompatible types: bad return type in lambda expression missing return value
|
||||
Error:(35, 27) java: incompatible types: bad return type in lambda expression missing return value
|
||||
|
||||
This is deliberate: The DSL forces us to specify either ``this.verifies()`` or ``this `fails with` "some text"`` on the
|
||||
last line of ``transaction``:
|
||||
@ -107,11 +132,8 @@ last line of ``transaction``:
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
@Test
|
||||
fun simpleCash() {
|
||||
val inState = Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = DUMMY_PUBKEY_1
|
||||
)
|
||||
fun simpleCP() {
|
||||
val inState = getPaper()
|
||||
ledger {
|
||||
transaction {
|
||||
input(inState)
|
||||
@ -123,11 +145,8 @@ last line of ``transaction``:
|
||||
.. sourcecode:: java
|
||||
|
||||
@Test
|
||||
public void simpleCash() {
|
||||
Cash.State inState = new Cash.State(
|
||||
issuedBy(DOLLARS(1000), getMEGA_CORP().ref((byte)1, (byte)1)),
|
||||
getDUMMY_PUBKEY_1()
|
||||
);
|
||||
public void simpleCP() {
|
||||
ICommercialPaperState inState = getPaper();
|
||||
ledger(l -> {
|
||||
l.transaction(tx -> {
|
||||
tx.input(inState);
|
||||
@ -137,30 +156,20 @@ last line of ``transaction``:
|
||||
});
|
||||
}
|
||||
|
||||
The code finally compiles. When run, it produces the following error::
|
||||
|
||||
net.corda.core.contracts.TransactionVerificationException$ContractRejection: java.lang.IllegalArgumentException: Failed requirement: for deposit [01] at issuer Snake Oil Issuer the amounts balance
|
||||
|
||||
.. note:: The reference here to the 'Snake Oil Issuer' is because we are using the pre-canned ``DUMMY_CASH_ISSUER``
|
||||
identity as the issuer of our cash.
|
||||
|
||||
The transaction verification failed, because the sum of inputs does not equal the sum of outputs. We can specify that
|
||||
this is intended behaviour by changing ``this.verifies()`` to ``this `fails with` "the amounts balance"``:
|
||||
Let's take a look at a transaction that fails.
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
@Test
|
||||
fun simpleCashFailsWith() {
|
||||
val inState = Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = DUMMY_PUBKEY_1
|
||||
)
|
||||
fun simpleCPMove() {
|
||||
val inState = getPaper()
|
||||
ledger {
|
||||
transaction {
|
||||
input(inState)
|
||||
this `fails with` "the amounts balance"
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,15 +177,59 @@ this is intended behaviour by changing ``this.verifies()`` to ``this `fails with
|
||||
.. sourcecode:: java
|
||||
|
||||
@Test
|
||||
public void simpleCashFailsWith() {
|
||||
Cash.State inState = new Cash.State(
|
||||
issuedBy(DOLLARS(1000), getMEGA_CORP().ref((byte)1, (byte)1)),
|
||||
getDUMMY_PUBKEY_1()
|
||||
);
|
||||
public void simpleCPMove() {
|
||||
ICommercialPaperState inState = getPaper();
|
||||
ledger(l -> {
|
||||
l.transaction(tx -> {
|
||||
tx.input(inState);
|
||||
return tx.failsWith("the amounts balance");
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move());
|
||||
return tx.verifies();
|
||||
});
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
When run, that code produces the following error:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
net.corda.core.contracts.TransactionVerificationException$ContractRejection: java.lang.IllegalArgumentException: Failed requirement: the state is propagated
|
||||
|
||||
.. sourcecode:: java
|
||||
|
||||
net.corda.core.contracts.TransactionVerificationException$ContractRejection: java.lang.IllegalStateException: the state is propagated
|
||||
|
||||
The transaction verification failed, because we wanted to move paper but didn't specify an output - but the state should be propagated.
|
||||
However we can specify that this is an intended behaviour by changing ``this.verifies()`` to ``this `fails with` "the state is propagated"``:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
@Test
|
||||
fun simpleCPMoveFails() {
|
||||
val inState = getPaper()
|
||||
ledger {
|
||||
transaction {
|
||||
input(inState)
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
this `fails with` "the state is propagated"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.. sourcecode:: java
|
||||
|
||||
@Test
|
||||
public void simpleCPMoveFails() {
|
||||
ICommercialPaperState inState = getPaper();
|
||||
ledger(l -> {
|
||||
l.transaction(tx -> {
|
||||
tx.input(inState);
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move());
|
||||
return tx.failsWith("the state is propagated");
|
||||
});
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
@ -189,17 +242,14 @@ We can continue to build the transaction until it ``verifies``:
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
@Test
|
||||
fun simpleCashSuccess() {
|
||||
val inState = Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = DUMMY_PUBKEY_1
|
||||
)
|
||||
fun simpleCPMoveSuccess() {
|
||||
val inState = getPaper()
|
||||
ledger {
|
||||
transaction {
|
||||
input(inState)
|
||||
this `fails with` "the amounts balance"
|
||||
output(inState.copy(owner = DUMMY_PUBKEY_2))
|
||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
this `fails with` "the state is propagated"
|
||||
output("alice's paper") { inState `owned by` ALICE_PUBKEY }
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -208,55 +258,45 @@ We can continue to build the transaction until it ``verifies``:
|
||||
.. sourcecode:: java
|
||||
|
||||
@Test
|
||||
public void simpleCashSuccess() {
|
||||
Cash.State inState = new Cash.State(
|
||||
issuedBy(DOLLARS(1000), getMEGA_CORP().ref((byte)1, (byte)1)),
|
||||
getDUMMY_PUBKEY_1()
|
||||
);
|
||||
public void simpleCPMoveSuccess() {
|
||||
ICommercialPaperState inState = getPaper();
|
||||
ledger(l -> {
|
||||
l.transaction(tx -> {
|
||||
tx.input(inState);
|
||||
tx.failsWith("the amounts balance");
|
||||
tx.output(inState.copy(inState.getAmount(), getDUMMY_PUBKEY_2()));
|
||||
tx.command(getDUMMY_PUBKEY_1(), new Cash.Commands.Move());
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move());
|
||||
tx.failsWith("the state is propagated");
|
||||
tx.output("alice's paper", inState.withOwner(getALICE_PUBKEY()));
|
||||
return tx.verifies();
|
||||
});
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
``output`` specifies that we want the input state to be transferred to ``DUMMY_PUBKEY_2`` and ``command`` adds the
|
||||
``Move`` command itself, signed by the current owner of the input state, ``DUMMY_PUBKEY_1``.
|
||||
``output`` specifies that we want the input state to be transferred to ``ALICE`` and ``command`` adds the
|
||||
``Move`` command itself, signed by the current owner of the input state, ``MEGA_CORP_PUBKEY``.
|
||||
|
||||
We constructed a complete signed cash transaction from ``DUMMY_PUBKEY_1`` to ``DUMMY_PUBKEY_2`` and verified it. Note
|
||||
how we left in the ``fails with`` line - this is fine, the failure will be tested on the partially constructed
|
||||
transaction.
|
||||
We constructed a complete signed commercial paper transaction and verified it. Note how we left in the ``fails with``
|
||||
line - this is fine, the failure will be tested on the partially constructed transaction.
|
||||
|
||||
What should we do if we wanted to test what happens when the wrong party signs the transaction? If we simply add a
|
||||
``command`` it will ruin the transaction for good... Enter ``tweak``:
|
||||
``command`` it will permanently ruin the transaction... Enter ``tweak``:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
@Test
|
||||
fun simpleCashTweakSuccess() {
|
||||
val inState = Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = DUMMY_PUBKEY_1
|
||||
)
|
||||
fun `simple issuance with tweak`() {
|
||||
ledger {
|
||||
transaction {
|
||||
input(inState)
|
||||
this `fails with` "the amounts balance"
|
||||
output(inState.copy(owner = DUMMY_PUBKEY_2))
|
||||
|
||||
output("paper") { getPaper() } // Some CP is issued onto the ledger by MegaCorp.
|
||||
tweak {
|
||||
command(DUMMY_PUBKEY_2) { Cash.Commands.Move() }
|
||||
this `fails with` "the owning keys are the same as the signing keys"
|
||||
command(DUMMY_PUBKEY_1) { CommercialPaper.Commands.Issue() }
|
||||
timestamp(TEST_TX_TIME)
|
||||
this `fails with` "output states are issued by a command signer"
|
||||
}
|
||||
|
||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||
timestamp(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -265,33 +305,29 @@ What should we do if we wanted to test what happens when the wrong party signs t
|
||||
.. sourcecode:: java
|
||||
|
||||
@Test
|
||||
public void simpleCashTweakSuccess() {
|
||||
Cash.State inState = new Cash.State(
|
||||
issuedBy(DOLLARS(1000), getMEGA_CORP().ref((byte)1, (byte)1)),
|
||||
getDUMMY_PUBKEY_1()
|
||||
);
|
||||
public void simpleIssuanceWithTweak() {
|
||||
ledger(l -> {
|
||||
l.transaction(tx -> {
|
||||
tx.input(inState);
|
||||
tx.failsWith("the amounts balance");
|
||||
tx.output(inState.copy(inState.getAmount(), getDUMMY_PUBKEY_2()));
|
||||
|
||||
tx.output("paper", getPaper()); // Some CP is issued onto the ledger by MegaCorp.
|
||||
tx.tweak(tw -> {
|
||||
tw.command(getDUMMY_PUBKEY_2(), new Cash.Commands.Move());
|
||||
return tw.failsWith("the owning keys are the same as the signing keys");
|
||||
tw.command(getDUMMY_PUBKEY_1(), new JavaCommercialPaper.Commands.Issue());
|
||||
tw.timestamp(getTEST_TX_TIME());
|
||||
return tw.failsWith("output states are issued by a command signer");
|
||||
});
|
||||
tx.command(getDUMMY_PUBKEY_1(), new Cash.Commands.Move());
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Issue());
|
||||
tx.timestamp(getTEST_TX_TIME());
|
||||
return tx.verifies();
|
||||
});
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
``tweak`` creates a local copy of the transaction. This allows the local "ruining" of the transaction allowing testing
|
||||
of different error conditions.
|
||||
|
||||
``tweak`` creates a local copy of the transaction. This makes possible to locally "ruin" the transaction while not
|
||||
modifying the original one, allowing testing of different error conditions.
|
||||
|
||||
We now have a neat little test that tests a single transaction. This is already useful, and in fact testing of a single
|
||||
transaction in this way is very common. There is even a shorthand toplevel ``transaction`` primitive that creates a
|
||||
transaction in this way is very common. There is even a shorthand top-level ``transaction`` primitive that creates a
|
||||
ledger with a single transaction:
|
||||
|
||||
.. container:: codeset
|
||||
@ -299,22 +335,16 @@ ledger with a single transaction:
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
@Test
|
||||
fun simpleCashTweakSuccessTopLevelTransaction() {
|
||||
val inState = Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = DUMMY_PUBKEY_1
|
||||
)
|
||||
fun `simple issuance with tweak and top level transaction`() {
|
||||
transaction {
|
||||
input(inState)
|
||||
this `fails with` "the amounts balance"
|
||||
output(inState.copy(owner = DUMMY_PUBKEY_2))
|
||||
|
||||
output("paper") { getPaper() } // Some CP is issued onto the ledger by MegaCorp.
|
||||
tweak {
|
||||
command(DUMMY_PUBKEY_2) { Cash.Commands.Move() }
|
||||
this `fails with` "the owning keys are the same as the signing keys"
|
||||
command(DUMMY_PUBKEY_1) { CommercialPaper.Commands.Issue() }
|
||||
timestamp(TEST_TX_TIME)
|
||||
this `fails with` "output states are issued by a command signer"
|
||||
}
|
||||
|
||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||
timestamp(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -322,21 +352,16 @@ ledger with a single transaction:
|
||||
.. sourcecode:: java
|
||||
|
||||
@Test
|
||||
public void simpleCashTweakSuccessTopLevelTransaction() {
|
||||
Cash.State inState = new Cash.State(
|
||||
issuedBy(DOLLARS(1000), getMEGA_CORP().ref((byte)1, (byte)1)),
|
||||
getDUMMY_PUBKEY_1()
|
||||
);
|
||||
public void simpleIssuanceWithTweakTopLevelTx() {
|
||||
transaction(tx -> {
|
||||
tx.input(inState);
|
||||
tx.failsWith("the amounts balance");
|
||||
tx.output(inState.copy(inState.getAmount(), getDUMMY_PUBKEY_2()));
|
||||
|
||||
tx.output("paper", getPaper()); // Some CP is issued onto the ledger by MegaCorp.
|
||||
tx.tweak(tw -> {
|
||||
tw.command(getDUMMY_PUBKEY_2(), new Cash.Commands.Move());
|
||||
return tw.failsWith("the owning keys are the same as the signing keys");
|
||||
tw.command(getDUMMY_PUBKEY_1(), new JavaCommercialPaper.Commands.Issue());
|
||||
tw.timestamp(getTEST_TX_TIME());
|
||||
return tw.failsWith("output states are issued by a command signer");
|
||||
});
|
||||
tx.command(getDUMMY_PUBKEY_1(), new Cash.Commands.Move());
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Issue());
|
||||
tx.timestamp(getTEST_TX_TIME());
|
||||
return tx.verifies();
|
||||
});
|
||||
}
|
||||
@ -351,21 +376,30 @@ Now that we know how to define a single transaction, let's look at how to define
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
@Test
|
||||
fun chainCash() {
|
||||
fun `chain commercial paper`() {
|
||||
val issuer = MEGA_CORP.ref(123)
|
||||
|
||||
ledger {
|
||||
unverifiedTransaction {
|
||||
output("MEGA_CORP cash") {
|
||||
Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = MEGA_CORP_PUBKEY
|
||||
)
|
||||
}
|
||||
output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE_PUBKEY)
|
||||
}
|
||||
|
||||
transaction {
|
||||
input("MEGA_CORP cash")
|
||||
output("MEGA_CORP cash".output<Cash.State>().copy(owner = DUMMY_PUBKEY_1))
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
// Some CP is issued onto the ledger by MegaCorp.
|
||||
transaction("Issuance") {
|
||||
output("paper") { getPaper() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||
timestamp(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
|
||||
|
||||
transaction("Trade") {
|
||||
input("paper")
|
||||
input("alice's $900")
|
||||
output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
||||
output("alice's paper") { "paper".output<ICommercialPaperState>() `owned by` ALICE_PUBKEY }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
this.verifies()
|
||||
}
|
||||
}
|
||||
@ -374,141 +408,176 @@ Now that we know how to define a single transaction, let's look at how to define
|
||||
.. sourcecode:: java
|
||||
|
||||
@Test
|
||||
public void chainCash() {
|
||||
public void chainCommercialPaper() {
|
||||
PartyAndReference issuer = getMEGA_CORP().ref(defaultRef);
|
||||
ledger(l -> {
|
||||
l.unverifiedTransaction(tx -> {
|
||||
tx.output("MEGA_CORP cash",
|
||||
new Cash.State(
|
||||
issuedBy(DOLLARS(1000), getMEGA_CORP().ref((byte)1, (byte)1)),
|
||||
getMEGA_CORP_PUBKEY()
|
||||
)
|
||||
);
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
tx.output("alice's $900",
|
||||
new Cash.State(issuedBy(DOLLARS(900), issuer), getALICE_PUBKEY(), null));
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
||||
l.transaction(tx -> {
|
||||
tx.input("MEGA_CORP cash");
|
||||
Cash.State inputCash = l.retrieveOutput(Cash.State.class, "MEGA_CORP cash");
|
||||
tx.output(inputCash.copy(inputCash.getAmount(), getDUMMY_PUBKEY_1()));
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move());
|
||||
// Some CP is issued onto the ledger by MegaCorp.
|
||||
l.transaction("Issuance", tx -> {
|
||||
tx.output("paper", getPaper());
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Issue());
|
||||
tx.timestamp(getTEST_TX_TIME());
|
||||
return tx.verifies();
|
||||
});
|
||||
|
||||
l.transaction("Trade", tx -> {
|
||||
tx.input("paper");
|
||||
tx.input("alice's $900");
|
||||
tx.output("borrowed $900", new Cash.State(issuedBy(DOLLARS(900), issuer), getMEGA_CORP_PUBKEY(), null));
|
||||
JavaCommercialPaper.State inputPaper = l.retrieveOutput(JavaCommercialPaper.State.class, "paper");
|
||||
tx.output("alice's paper", inputPaper.withOwner(getALICE_PUBKEY()));
|
||||
tx.command(getALICE_PUBKEY(), new Cash.Commands.Move());
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move());
|
||||
return tx.verifies();
|
||||
});
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
In this example we declare that ``MEGA_CORP`` has a thousand dollars but we don't care where from, for this we can use
|
||||
|
||||
In this example we declare that ``ALICE`` has $900 but we don't care where from. For this we can use
|
||||
``unverifiedTransaction``. Note how we don't need to specify ``this.verifies()``.
|
||||
|
||||
The ``output`` cash was labelled with ``"MEGA_CORP cash"``, we can subsequently referred to this other transactions, e.g.
|
||||
by ``input("MEGA_CORP cash")`` or ``"MEGA_CORP cash".output<Cash.State>()``.
|
||||
Notice that we labelled output with ``"alice's $900"``, also in transaction named ``"Issuance"``
|
||||
we labelled a commercial paper with ``"paper"``. Now we can subsequently refer to them in other transactions, e.g.
|
||||
by ``input("alice's $900")`` or ``"paper".output<ICommercialPaperState>()``.
|
||||
|
||||
What happens if we reuse the output cash twice?
|
||||
The last transaction named ``"Trade"`` exemplifies simple fact of selling the ``CommercialPaper`` to Alice for her $900,
|
||||
$100 less than the face value at 10% interest after only 7 days.
|
||||
|
||||
We can also test whole ledger calling ``this.verifies()`` and ``this.fails()`` on the ledger level.
|
||||
To do so let's create a simple example that uses the same input twice:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
@Test
|
||||
fun chainCashDoubleSpend() {
|
||||
fun `chain commercial paper double spend`() {
|
||||
val issuer = MEGA_CORP.ref(123)
|
||||
ledger {
|
||||
unverifiedTransaction {
|
||||
output("MEGA_CORP cash") {
|
||||
Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = MEGA_CORP_PUBKEY
|
||||
)
|
||||
}
|
||||
output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE_PUBKEY)
|
||||
}
|
||||
|
||||
transaction {
|
||||
input("MEGA_CORP cash")
|
||||
output("MEGA_CORP cash".output<Cash.State>().copy(owner = DUMMY_PUBKEY_1))
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
// Some CP is issued onto the ledger by MegaCorp.
|
||||
transaction("Issuance") {
|
||||
output("paper") { getPaper() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||
timestamp(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
|
||||
transaction("Trade") {
|
||||
input("paper")
|
||||
input("alice's $900")
|
||||
output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
||||
output("alice's paper") { "paper".output<ICommercialPaperState>() `owned by` ALICE_PUBKEY }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
this.verifies()
|
||||
}
|
||||
|
||||
transaction {
|
||||
input("MEGA_CORP cash")
|
||||
// We send it to another pubkey so that the transaction is not identical to the previous one
|
||||
output("MEGA_CORP cash".output<Cash.State>().copy(owner = DUMMY_PUBKEY_2))
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
input("paper")
|
||||
// We moved a paper to another pubkey.
|
||||
output("bob's paper") { "paper".output<ICommercialPaperState>() `owned by` BOB_PUBKEY }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
this.verifies()
|
||||
}
|
||||
|
||||
this.fails()
|
||||
}
|
||||
}
|
||||
|
||||
.. sourcecode:: java
|
||||
|
||||
@Test
|
||||
public void chainCashDoubleSpend() {
|
||||
public void chainCommercialPaperDoubleSpend() {
|
||||
PartyAndReference issuer = getMEGA_CORP().ref(defaultRef);
|
||||
ledger(l -> {
|
||||
l.unverifiedTransaction(tx -> {
|
||||
tx.output("MEGA_CORP cash",
|
||||
new Cash.State(
|
||||
issuedBy(DOLLARS(1000), getMEGA_CORP().ref((byte)1, (byte)1)),
|
||||
getMEGA_CORP_PUBKEY()
|
||||
)
|
||||
);
|
||||
tx.output("alice's $900",
|
||||
new Cash.State(issuedBy(DOLLARS(900), issuer), getALICE_PUBKEY(), null));
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
||||
l.transaction(tx -> {
|
||||
tx.input("MEGA_CORP cash");
|
||||
Cash.State inputCash = l.retrieveOutput(Cash.State.class, "MEGA_CORP cash");
|
||||
tx.output(inputCash.copy(inputCash.getAmount(), getDUMMY_PUBKEY_1()));
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move());
|
||||
// Some CP is issued onto the ledger by MegaCorp.
|
||||
l.transaction("Issuance", tx -> {
|
||||
tx.output("paper", getPaper());
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Issue());
|
||||
tx.timestamp(getTEST_TX_TIME());
|
||||
return tx.verifies();
|
||||
});
|
||||
|
||||
l.transaction("Trade", tx -> {
|
||||
tx.input("paper");
|
||||
tx.input("alice's $900");
|
||||
tx.output("borrowed $900", new Cash.State(issuedBy(DOLLARS(900), issuer), getMEGA_CORP_PUBKEY(), null));
|
||||
JavaCommercialPaper.State inputPaper = l.retrieveOutput(JavaCommercialPaper.State.class, "paper");
|
||||
tx.output("alice's paper", inputPaper.withOwner(getALICE_PUBKEY()));
|
||||
tx.command(getALICE_PUBKEY(), new Cash.Commands.Move());
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move());
|
||||
return tx.verifies();
|
||||
});
|
||||
|
||||
l.transaction(tx -> {
|
||||
tx.input("MEGA_CORP cash");
|
||||
Cash.State inputCash = l.retrieveOutput(Cash.State.class, "MEGA_CORP cash");
|
||||
// We send it to another pubkey so that the transaction is not identical to the previous one
|
||||
tx.output(inputCash.copy(inputCash.getAmount(), getDUMMY_PUBKEY_2()));
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move());
|
||||
tx.input("paper");
|
||||
JavaCommercialPaper.State inputPaper = l.retrieveOutput(JavaCommercialPaper.State.class, "paper");
|
||||
// We moved a paper to other pubkey.
|
||||
tx.output("bob's paper", inputPaper.withOwner(getBOB_PUBKEY()));
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move());
|
||||
return tx.verifies();
|
||||
});
|
||||
|
||||
l.fails();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
The transactions ``verifies()`` individually, however the state was spent twice!
|
||||
|
||||
We can also verify the complete ledger by calling ``verifies``/``fails`` on the ledger level. We can also use
|
||||
``tweak`` to create a local copy of the whole ledger:
|
||||
The transactions ``verifies()`` individually, however the state was spent twice! That's why we need the global ledger
|
||||
verification (``this.fails()`` at the end). As in previous examples we can use ``tweak`` to create a local copy of the whole ledger:
|
||||
|
||||
.. container:: codeset
|
||||
|
||||
.. sourcecode:: kotlin
|
||||
|
||||
@Test
|
||||
fun chainCashDoubleSpendFailsWith() {
|
||||
fun `chain commercial tweak`() {
|
||||
val issuer = MEGA_CORP.ref(123)
|
||||
ledger {
|
||||
unverifiedTransaction {
|
||||
output("MEGA_CORP cash") {
|
||||
Cash.State(
|
||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||
owner = MEGA_CORP_PUBKEY
|
||||
)
|
||||
}
|
||||
output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE_PUBKEY)
|
||||
}
|
||||
|
||||
transaction {
|
||||
input("MEGA_CORP cash")
|
||||
output("MEGA_CORP cash".output<Cash.State>().copy(owner = DUMMY_PUBKEY_1))
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
// Some CP is issued onto the ledger by MegaCorp.
|
||||
transaction("Issuance") {
|
||||
output("paper") { getPaper() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||
timestamp(TEST_TX_TIME)
|
||||
this.verifies()
|
||||
}
|
||||
|
||||
transaction("Trade") {
|
||||
input("paper")
|
||||
input("alice's $900")
|
||||
output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
||||
output("alice's paper") { "paper".output<ICommercialPaperState>() `owned by` ALICE_PUBKEY }
|
||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
this.verifies()
|
||||
}
|
||||
|
||||
tweak {
|
||||
transaction {
|
||||
input("MEGA_CORP cash")
|
||||
// We send it to another pubkey so that the transaction is not identical to the previous one
|
||||
output("MEGA_CORP cash".output<Cash.State>().copy(owner = DUMMY_PUBKEY_1))
|
||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
input("paper")
|
||||
// We moved a paper to another pubkey.
|
||||
output("bob's paper") { "paper".output<ICommercialPaperState>() `owned by` BOB_PUBKEY }
|
||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Move() }
|
||||
this.verifies()
|
||||
}
|
||||
this.fails()
|
||||
@ -521,39 +590,46 @@ We can also verify the complete ledger by calling ``verifies``/``fails`` on the
|
||||
.. sourcecode:: java
|
||||
|
||||
@Test
|
||||
public void chainCashDoubleSpendFailsWith() {
|
||||
public void chainCommercialPaperTweak() {
|
||||
PartyAndReference issuer = getMEGA_CORP().ref(defaultRef);
|
||||
ledger(l -> {
|
||||
l.unverifiedTransaction(tx -> {
|
||||
tx.output("MEGA_CORP cash",
|
||||
new Cash.State(
|
||||
issuedBy(DOLLARS(1000), getMEGA_CORP().ref((byte)1, (byte)1)),
|
||||
getMEGA_CORP_PUBKEY()
|
||||
)
|
||||
);
|
||||
tx.output("alice's $900",
|
||||
new Cash.State(issuedBy(DOLLARS(900), issuer), getALICE_PUBKEY(), null));
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
||||
l.transaction(tx -> {
|
||||
tx.input("MEGA_CORP cash");
|
||||
Cash.State inputCash = l.retrieveOutput(Cash.State.class, "MEGA_CORP cash");
|
||||
tx.output(inputCash.copy(inputCash.getAmount(), getDUMMY_PUBKEY_1()));
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move());
|
||||
// Some CP is issued onto the ledger by MegaCorp.
|
||||
l.transaction("Issuance", tx -> {
|
||||
tx.output("paper", getPaper());
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Issue());
|
||||
tx.timestamp(getTEST_TX_TIME());
|
||||
return tx.verifies();
|
||||
});
|
||||
|
||||
l.transaction("Trade", tx -> {
|
||||
tx.input("paper");
|
||||
tx.input("alice's $900");
|
||||
tx.output("borrowed $900", new Cash.State(issuedBy(DOLLARS(900), issuer), getMEGA_CORP_PUBKEY(), null));
|
||||
JavaCommercialPaper.State inputPaper = l.retrieveOutput(JavaCommercialPaper.State.class, "paper");
|
||||
tx.output("alice's paper", inputPaper.withOwner(getALICE_PUBKEY()));
|
||||
tx.command(getALICE_PUBKEY(), new Cash.Commands.Move());
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move());
|
||||
return tx.verifies();
|
||||
});
|
||||
|
||||
l.tweak(lw -> {
|
||||
lw.transaction(tx -> {
|
||||
tx.input("MEGA_CORP cash");
|
||||
Cash.State inputCash = l.retrieveOutput(Cash.State.class, "MEGA_CORP cash");
|
||||
// We send it to another pubkey so that the transaction is not identical to the previous one
|
||||
tx.output(inputCash.copy(inputCash.getAmount(), getDUMMY_PUBKEY_2()));
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new Cash.Commands.Move());
|
||||
tx.input("paper");
|
||||
JavaCommercialPaper.State inputPaper = l.retrieveOutput(JavaCommercialPaper.State.class, "paper");
|
||||
// We moved a paper to another pubkey.
|
||||
tx.output("bob's paper", inputPaper.withOwner(getBOB_PUBKEY()));
|
||||
tx.command(getMEGA_CORP_PUBKEY(), new JavaCommercialPaper.Commands.Move());
|
||||
return tx.verifies();
|
||||
});
|
||||
lw.fails();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
||||
l.verifies();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
Reference in New Issue
Block a user