Merge open master

This commit is contained in:
Andras Slemmer
2017-08-29 15:08:04 +01:00
526 changed files with 7849 additions and 7317 deletions

View File

@ -22,9 +22,7 @@ The ``Contract`` interface is defined as follows:
Where:
* ``verify(tx: LedgerTransaction)`` determines whether transactions involving states which reference this
contract type are valid
* ``legalContractReference`` is the hash of the legal prose contract that ``verify`` seeks to express in code
* ``verify(tx: LedgerTransaction)`` determines whether transactions involving states which reference this contract type are valid
verify()
--------
@ -187,8 +185,6 @@ execution of ``verify()``:
}
}
}
override val legalContractReference: SecureHash = SecureHash.sha256("X contract hash")
}
.. sourcecode:: java
@ -209,9 +205,6 @@ execution of ``verify()``:
// Transfer verification logic.
}
}
private final SecureHash legalContractReference = SecureHash.sha256("X contract hash");
@Override public final SecureHash getLegalContractReference() { return legalContractReference; }
}
Grouping states
@ -297,13 +290,5 @@ We can now verify these groups individually:
Legal prose
-----------
Current, ``legalContractReference`` is simply the SHA-256 hash of a contract:
.. container:: codeset
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/contracts/asset/Cash.kt
:language: kotlin
:start-after: DOCSTART 2
:end-before: DOCEND 2
In the future, a contract's legal prose will be included as an attachment instead.
Currently, a ``Contract`` subtype may refer to the legal prose it implements via a ``LegalProseReference`` annotation.
In the future, a contract's legal prose will be included as an attachment.

View File

@ -90,7 +90,7 @@ in order to initialise the ORM layer.
Several examples of entities and mappings are provided in the codebase, including ``Cash.State`` and
``CommercialPaper.State``. For example, here's the first version of the cash schema.
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/schemas/CashSchemaV1.kt
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/schemas/CashSchemaV1.kt
:language: kotlin
Identity mapping

View File

@ -129,7 +129,7 @@ For example, here is a relatively complex state definition, for a state represen
.. container:: codeset
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/contracts/asset/Cash.kt
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt
:language: kotlin
:start-after: DOCSTART 1
:end-before: DOCEND 1

View File

@ -11,12 +11,11 @@ API: Transactions
Transaction workflow
--------------------
There are four states the transaction can occupy:
At any time, a transaction can occupy one of three states:
* ``TransactionBuilder``, a builder for a transaction in construction
* ``WireTransaction``, an immutable transaction
* ``TransactionBuilder``, a builder for an in-construction transaction
* ``SignedTransaction``, an immutable transaction with 1+ associated signatures
* ``LedgerTransaction``, a transaction that can be checked for validity
* ``LedgerTransaction``, an immutable transaction that can be checked for validity
Here are the possible transitions between transaction states:
@ -26,8 +25,8 @@ TransactionBuilder
------------------
Creating a builder
^^^^^^^^^^^^^^^^^^
The first step when creating a transaction is to instantiate a ``TransactionBuilder``. We can create a builder for each
transaction type as follows:
The first step when creating a new transaction is to instantiate a ``TransactionBuilder``. We create a builder for a
transaction as follows:
.. container:: codeset
@ -342,7 +341,7 @@ SignedTransaction
-----------------
A ``SignedTransaction`` is a combination of:
* An immutable ``WireTransaction``
* An immutable transaction
* A list of signatures over that transaction
.. container:: codeset
@ -357,45 +356,9 @@ transaction's signatures.
Verifying the transaction's contents
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To verify a transaction, we need to retrieve any states in the transaction chain that our node doesn't
currently have in its local storage from the proposer(s) of the transaction. This process is handled by a built-in flow
called ``ReceiveTransactionFlow``. See :doc:`api-flows` for more details.
When verifying a ``SignedTransaction``, we don't verify the ``SignedTransaction`` *per se*, but rather the
``WireTransaction`` it contains. We extract this ``WireTransaction`` as follows:
.. container:: codeset
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
:language: kotlin
:start-after: DOCSTART 31
:end-before: DOCEND 31
:dedent: 12
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
:language: java
:start-after: DOCSTART 31
:end-before: DOCEND 31
:dedent: 12
However, this still isn't enough. The ``WireTransaction`` holds its inputs as ``StateRef`` instances, and its
attachments as hashes. These do not provide enough information to properly validate the transaction's contents. To
resolve these into actual ``ContractState`` and ``Attachment`` instances, we need to use the ``ServiceHub`` to convert
the ``WireTransaction`` into a ``LedgerTransaction``:
.. container:: codeset
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
:language: kotlin
:start-after: DOCSTART 32
:end-before: DOCEND 32
:dedent: 12
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
:language: java
:start-after: DOCSTART 32
:end-before: DOCEND 32
:dedent: 12
To verify a transaction, we need to retrieve any states in the transaction chain that our node doesn't currently have
in its local storage from the proposer(s) of the transaction. This process is handled by a built-in flow called
``ReceiveTransactionFlow``. See :doc:`api-flows` for more details.
We can now *verify* the transaction to ensure that it satisfies the contracts of all the transaction's input and output
states:
@ -414,8 +377,27 @@ states:
:end-before: DOCEND 33
:dedent: 12
We will generally also want to conduct some additional validation of the transaction, beyond what is provided for in
the contract. Here's an example of how we might do this:
We can also conduct additional validation of the transaction, beyond what is performed by its contracts. However, the
``SignedTransaction`` holds its inputs as ``StateRef`` instances, and its attachments as hashes. These do not provide
enough information to properly validate the transaction's contents. To resolve these into actual ``ContractState`` and
``Attachment`` instances, we need to use the ``ServiceHub`` to convert the ``SignedTransaction`` into a
``LedgerTransaction``:
.. container:: codeset
.. literalinclude:: ../../docs/source/example-code/src/main/kotlin/net/corda/docs/FlowCookbook.kt
:language: kotlin
:start-after: DOCSTART 32
:end-before: DOCEND 32
:dedent: 12
.. literalinclude:: ../../docs/source/example-code/src/main/java/net/corda/docs/FlowCookbookJava.java
:language: java
:start-after: DOCSTART 32
:end-before: DOCEND 32
:dedent: 12
We can now perform additional verification. Here's a simple example:
.. container:: codeset
@ -558,8 +540,8 @@ Or using another one of our public keys, as follows:
Notarising and recording
^^^^^^^^^^^^^^^^^^^^^^^^
Notarising and recording a transaction is handled by a built-in flow called ``FinalityFlow``. See
:doc:`api-flows` for more details.
Notarising and recording a transaction is handled by a built-in flow called ``FinalityFlow``. See :doc:`api-flows` for
more details.
Notary-change transactions
^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -70,6 +70,15 @@ There are four implementations of this interface which can be chained together t
.. note:: It is a requirement to register any custom contract schemas to be used in Vault Custom queries in the associated `CordaPluginRegistry` configuration for the respective CorDapp using the ``requiredSchemas`` configuration field (which specifies a set of `MappedSchema`)
All ``QueryCriteria`` implementations are composable using ``and`` and ``or`` operators, as also illustrated above.
All ``QueryCriteria`` implementations provide an explicitly specifiable set of common attributes:
1. State status attribute (``Vault.StateStatus``), which defaults to filtering on UNCONSUMED states.
When chaining several criterias using AND / OR, the last value of this attribute will override any previous.
2. Contract state types (``<Set<Class<out ContractState>>``), which will contain at minimum one type (by default this will be ``ContractState`` which resolves to all types).
When chaining several criteria using AND / OR, all specified contract state types are combined into a single set.
An example of a custom query is illustrated here:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt
@ -77,10 +86,6 @@ An example of a custom query is illustrated here:
:start-after: DOCSTART VaultQueryExample20
:end-before: DOCEND VaultQueryExample20
All ``QueryCriteria`` implementations are composable using ``and`` and ``or`` operators, as also illustrated above.
All ``QueryCriteria`` implementations provide an explicitly specifiable ``StateStatus`` attribute which defaults to filtering on UNCONSUMED states.
.. note:: Custom contract states that implement the ``Queryable`` interface may now extend common schemas types ``FungiblePersistentState`` or, ``LinearPersistentState``. Previously, all custom contracts extended the root ``PersistentState`` class and defined repeated mappings of ``FungibleAsset`` and ``LinearState`` attributes. See ``SampleCashSchemaV2`` and ``DummyLinearStateSchemaV2`` as examples.
Examples of these ``QueryCriteria`` objects are presented below for Kotlin and Java.
@ -317,22 +322,22 @@ Query for consumed deal states or linear ids, specify a paging specification and
Aggregations on cash using various functions:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryJavaTests.kt
:language: kotlin
.. literalinclude:: ../../node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java
:language: java
:start-after: DOCSTART VaultJavaQueryExample21
:end-before: DOCEND VaultJavaQueryExample21
Aggregations on cash grouped by currency for various functions:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryJavaTests.kt
:language: kotlin
.. literalinclude:: ../../node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java
:language: java
:start-after: DOCSTART VaultJavaQueryExample22
:end-before: DOCEND VaultJavaQueryExample22
Sum aggregation on cash grouped by issuer party and currency and sorted by sum:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryJavaTests.kt
:language: kotlin
.. literalinclude:: ../../node/src/test/java/net/corda/node/services/vault/VaultQueryJavaTests.java
:language: java
:start-after: DOCSTART VaultJavaQueryExample23
:end-before: DOCEND VaultJavaQueryExample23

View File

@ -6,6 +6,15 @@ from the previous milestone release.
UNRELEASED
----------
* Vault query common attributes (state status and contract state types) are now handled correctly when using composite
criteria specifications. State status is overridable. Contract states types are aggregatable.
* Cash selection algorithm is now pluggable (with H2 being the default implementation)
* Removed usage of Requery ORM library (repalced with JPA/Hibernate)
* Vault Query performance improvement (replaced expensive per query SQL statement to obtain concrete state types
with single query on start-up followed by dynamic updates using vault state observable))
* Vault Query fix: filter by multiple issuer names in ``FungibleAssetQueryCriteria``
@ -40,6 +49,25 @@ UNRELEASED
If you specifically need well known identities, use the network map, which is the authoritative source of current well
known identities.
* Currency-related API in ``net.corda.core.contracts.ContractsDSL`` has moved to ```net.corda.finance.CurrencyUtils`.
* Remove `IssuerFlow` as it allowed nodes to request arbitrary amounts of cash to be issued from any remote node. Use
`CashIssueFlow` instead.
* Some utility/extension functions (``sumOrThrow``, ``sumOrNull``, ``sumOrZero`` on ``Amount`` and ``Commodity``)
have moved to be static methods on the classes themselves. This improves the API for Java users who no longer
have to see or known about file-level FooKt style classes generated by the Kotlin compile, but means that IntelliJ
no longer auto-suggests these extension functions in completion unless you add import lines for them yourself
(this is Kotlin IDE bug KT-15286).
* ``:finance`` module now acting as a CorDapp with regard to flow registration, schemas and serializable types.
* ``WebServerPluginRegistry`` now has a ``customizeJSONSerialization`` which can be overridden to extend the REST JSON
serializers. In particular the IRS demos must now register the ``BusinessCalendar`` serializers.
* Moved ``:finance`` gradle project files into a ``net.corda.finance`` package namespace.
This may require adjusting imports of Cash flow references and also of ``StartFlow`` permission in ``gradle.build`` files.
Milestone 14
------------

View File

@ -60,7 +60,7 @@ Currently, users need special permissions to start flows via RPC. These permissi
]
.. note:: Currently, the node's web server has super-user access, meaning that it can run any RPC operation without
logging in. This will be changed in a future release.
logging in. This will be changed in a future release.
Observables
-----------

View File

@ -23,6 +23,11 @@ Cash shares a common superclass, ``OnLedgerAsset``, with the Commodity contract.
assets which can be issued, moved and exited on chain, with the subclasses handling asset-specific data types and
behaviour.
.. note:: Corda supports a pluggable cash selection algorithm by implementing the ``CashSelection`` interface.
The default implementation uses an H2 specific query that can be overridden for different database providers.
Please see ``CashSelectionH2Impl`` and its associated declaration in
``META-INF\services\net.corda.finance.contracts.asset.CashSelection``
Commodity
---------

View File

@ -20,7 +20,6 @@ The Corda repository comprises the following folders:
* **lib** contains some dependencies
* **node** contains the core code of the Corda node (eg: node driver, node services, messaging, persistence)
* **node-api** contains data structures shared between the node and the client module, e.g. types sent via RPC
* **node-schemas** contains entity classes used to represent relational database tables
* **samples** contains all our Corda demos and code samples
* **test-utils** contains some utilities for unit testing contracts ( the contracts testing DSL) and protocols (the
mock network) implementation

View File

@ -64,7 +64,7 @@ applicationDistribution.into("bin") {
}
task integrationTest(type: Test) {
testClassesDir = sourceSets.integrationTest.output.classesDir
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
@ -89,7 +89,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
rpcUsers = [
['username' : "user",
'password' : "password",
'permissions' : ["StartFlow.net.corda.flows.CashFlow"]]
'permissions' : ["StartFlow.net.corda.finance.flows.CashFlow"]]
]
}
}

View File

@ -1,30 +1,22 @@
package net.corda.docs
import net.corda.contracts.asset.Cash
import net.corda.core.concurrent.CordaFuture
import net.corda.core.contracts.DOLLARS
import net.corda.core.internal.concurrent.transpose
import net.corda.core.messaging.startFlow
import net.corda.core.messaging.vaultTrackBy
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.Vault
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.node.services.startFlowPermission
import net.corda.finance.DOLLARS
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.nodeapi.User
import net.corda.testing.*
import net.corda.testing.driver.driver
import net.corda.testing.expect
import net.corda.testing.expectEvents
import net.corda.testing.parallel
import net.corda.testing.sequence
import org.junit.Test
import java.util.*
import kotlin.concurrent.thread
import kotlin.test.assertEquals
class IntegrationTestingTutorial {
@ -33,7 +25,8 @@ class IntegrationTestingTutorial {
// START 1
driver {
val aliceUser = User("aliceUser", "testPassword1", permissions = setOf(
startFlowPermission<CashIssueFlow>()
startFlowPermission<CashIssueFlow>(),
startFlowPermission<CashPaymentFlow>()
))
val bobUser = User("bobUser", "testPassword2", permissions = setOf(
startFlowPermission<CashPaymentFlow>()
@ -63,18 +56,21 @@ class IntegrationTestingTutorial {
// START 4
val issueRef = OpaqueBytes.of(0)
val futures = Stack<CordaFuture<*>>()
(1..10).map { i ->
thread {
futures.push(aliceProxy.startFlow(::CashIssueFlow,
i.DOLLARS,
issueRef,
bob.nodeInfo.legalIdentity,
notary.nodeInfo.notaryIdentity
).returnValue)
}
}.forEach(Thread::join) // Ensure the stack of futures is populated.
futures.forEach { it.getOrThrow() }
aliceProxy.startFlow(::CashIssueFlow,
i.DOLLARS,
issueRef,
notary.nodeInfo.notaryIdentity
).returnValue
}.transpose().getOrThrow()
// We wait for all of the issuances to run before we start making payments
(1..10).map { i ->
aliceProxy.startFlow(::CashPaymentFlow,
i.DOLLARS,
bob.nodeInfo.legalIdentity,
true
).returnValue
}.transpose().getOrThrow()
bobVaultUpdates.expectEvents {
parallel(

View File

@ -3,7 +3,6 @@ package net.corda.docs;
import co.paralleluniverse.fibers.Suspendable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import net.corda.contracts.asset.Cash;
import net.corda.core.contracts.*;
import net.corda.core.crypto.SecureHash;
import net.corda.core.crypto.TransactionSignature;
@ -21,6 +20,7 @@ import net.corda.core.transactions.WireTransaction;
import net.corda.core.utilities.ProgressTracker;
import net.corda.core.utilities.ProgressTracker.Step;
import net.corda.core.utilities.UntrustworthyData;
import net.corda.finance.contracts.asset.Cash;
import net.corda.testing.contracts.DummyContract;
import net.corda.testing.contracts.DummyState;
import org.bouncycastle.asn1.x500.X500Name;
@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
@ -280,7 +281,7 @@ public class FlowCookbookJava {
TypeOnlyCommandData typeOnlyCommandData = new DummyContract.Commands.Create();
// 2. Include additional data which can be used by the contract
// during verification, alongside fulfilling the roles above
CommandData commandDataWithData = new Cash.Commands.Issue(12345678);
CommandData commandDataWithData = new Cash.Commands.Issue();
// Attachments are identified by their hash.
// The attachment with the corresponding hash must have been
@ -394,15 +395,18 @@ public class FlowCookbookJava {
----------------------------*/
progressTracker.setCurrentStep(TX_VERIFICATION);
// Verifying a transaction will also verify every transaction in the transaction's dependency chain, which will require
// transaction data access on counterparty's node. The ``SendTransactionFlow`` can be used to automate the sending
// and data vending process. The ``SendTransactionFlow`` will listen for data request until the transaction
// is resolved and verified on the other side:
// Verifying a transaction will also verify every transaction in
// the transaction's dependency chain, which will require
// transaction data access on counterparty's node. The
// ``SendTransactionFlow`` can be used to automate the sending and
// data vending process. The ``SendTransactionFlow`` will listen
// for data request until the transaction is resolved and verified
// on the other side:
// DOCSTART 12
subFlow(new SendTransactionFlow(counterparty, twiceSignedTx));
// Optional request verification to further restrict data access.
subFlow(new SendTransactionFlow(counterparty, twiceSignedTx){
subFlow(new SendTransactionFlow(counterparty, twiceSignedTx) {
@Override
protected void verifyDataRequest(@NotNull FetchDataFlow.Request.Data dataRequest) {
// Extra request verification.
@ -425,41 +429,43 @@ public class FlowCookbookJava {
List<StateAndRef<DummyState>> resolvedStateAndRef = subFlow(new ReceiveStateAndRefFlow<DummyState>(counterparty));
// DOCEND 14
// A ``SignedTransaction`` is a pairing of a ``WireTransaction``
// with signatures over this ``WireTransaction``. We don't verify
// a signed transaction per se, but rather the ``WireTransaction``
// it contains.
// DOCSTART 31
WireTransaction wireTx = twiceSignedTx.getTx();
// DOCEND 31
// Before we can verify the transaction, we need the
// ``ServiceHub`` to use our node's local storage to resolve the
// transaction's inputs and attachments into actual objects,
// rather than just references. We do this by converting the
// ``WireTransaction`` into a ``LedgerTransaction``.
// DOCSTART 32
LedgerTransaction ledgerTx = wireTx.toLedgerTransaction(getServiceHub());
// DOCEND 32
// We can now verify the transaction.
// DOCSTART 33
ledgerTx.verify();
// DOCEND 33
try {
// We'll often want to perform our own additional verification
// too. Just because a transaction is valid based on the contract
// rules and requires our signature doesn't mean we have to
// sign it! We need to make sure the transaction represents an
// agreement we actually want to enter into.
// DOCSTART 34
DummyState outputState = (DummyState) wireTx.getOutputs().get(0).getData();
if (outputState.getMagicNumber() != 777) {
// ``FlowException`` is a special exception type. It will be
// propagated back to any counterparty flows waiting for a
// message from this flow, notifying them that the flow has
// failed.
throw new FlowException("We expected a magic number of 777.");
// We can now verify the transaction to ensure that it satisfies
// the contracts of all the transaction's input and output states.
// DOCSTART 33
twiceSignedTx.verify(getServiceHub());
// DOCEND 33
// We'll often want to perform our own additional verification
// too. Just because a transaction is valid based on the contract
// rules and requires our signature doesn't mean we have to
// sign it! We need to make sure the transaction represents an
// agreement we actually want to enter into.
// To do this, we need to convert our ``SignedTransaction``
// into a ``LedgerTransaction``. This will use our ServiceHub
// to resolve the transaction's inputs and attachments into
// actual objects, rather than just references.
// DOCSTART 32
LedgerTransaction ledgerTx = twiceSignedTx.toLedgerTransaction(getServiceHub());
// DOCEND 32
// We can now perform our additional verification.
// DOCSTART 34
DummyState outputState = ledgerTx.outputsOfType(DummyState.class).get(0);
if (outputState.getMagicNumber() != 777) {
// ``FlowException`` is a special exception type. It will be
// propagated back to any counterparty flows waiting for a
// message from this flow, notifying them that the flow has
// failed.
throw new FlowException("We expected a magic number of 777.");
}
// DOCEND 34
} catch (GeneralSecurityException e) {
// Handle this as required.
}
// DOCEND 34
// Of course, if you are not a required signer on the transaction,
// you have no power to decide whether it is valid or not. If it

View File

@ -1,9 +1,6 @@
package net.corda.docs
import net.corda.client.rpc.notUsed
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.Amount
import net.corda.core.contracts.USD
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow
import net.corda.core.messaging.vaultQueryBy
@ -13,10 +10,12 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializationCustomization
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.OpaqueBytes
import net.corda.flows.CashExitFlow
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.node.services.startFlowPermission
import net.corda.finance.USD
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashExitFlow
import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
import net.corda.node.services.FlowPermissions.Companion.startFlowPermission
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.nodeapi.User
import net.corda.testing.ALICE
@ -128,7 +127,7 @@ fun generateTransactions(proxy: CordaRPCOps) {
proxy.startFlow(::CashPaymentFlow, Amount(quantity, USD), me)
} else {
val quantity = Math.abs(random.nextLong() % 1000)
proxy.startFlow(::CashIssueFlow, Amount(quantity, USD), issueRef, me, notary)
proxy.startFlow(::CashIssueFlow, Amount(quantity, USD), issueRef, notary)
ownedQuantity += quantity
}
}

View File

@ -16,7 +16,10 @@ import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.unwrap
import net.corda.flows.*
import net.corda.finance.flows.AbstractCashFlow
import net.corda.finance.flows.CashException
import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
import java.util.*
// DOCSTART CustomVaultQuery
@ -132,8 +135,7 @@ object TopupIssuerFlow {
val notaryParty = serviceHub.networkMapCache.notaryNodes[0].notaryIdentity
// invoke Cash subflow to issue Asset
progressTracker.currentStep = ISSUING
val issueRecipient = serviceHub.myInfo.legalIdentity
val issueCashFlow = CashIssueFlow(amount, issuerPartyRef, issueRecipient, notaryParty, anonymous = false)
val issueCashFlow = CashIssueFlow(amount, issuerPartyRef, notaryParty)
val issueTx = subFlow(issueCashFlow)
// NOTE: issueCashFlow performs a Broadcast (which stores a local copy of the txn to the ledger)
// short-circuit when issuing to self

View File

@ -3,7 +3,6 @@
package net.corda.docs
import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
@ -17,12 +16,12 @@ import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.ProgressTracker.Step
import net.corda.core.utilities.UntrustworthyData
import net.corda.core.utilities.seconds
import net.corda.core.utilities.unwrap
import net.corda.finance.contracts.asset.Cash
import net.corda.testing.ALICE_PUBKEY
import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState
@ -262,8 +261,8 @@ object FlowCookbook {
// fork the contract's verification logic.
val typeOnlyCommandData: TypeOnlyCommandData = DummyContract.Commands.Create()
// 2. Include additional data which can be used by the contract
// during verification, alongside fulfilling the roles above
val commandDataWithData: CommandData = Cash.Commands.Issue(nonce = 12345678)
// during verification, alongside fulfilling the roles above.
val commandDataWithData: CommandData = Cash.Commands.Issue()
// Attachments are identified by their hash.
// The attachment with the corresponding hash must have been
@ -379,10 +378,13 @@ object FlowCookbook {
---------------------------**/
progressTracker.currentStep = TX_VERIFICATION
// Verifying a transaction will also verify every transaction in the transaction's dependency chain, which will require
// transaction data access on counterparty's node. The ``SendTransactionFlow`` can be used to automate the sending
// and data vending process. The ``SendTransactionFlow`` will listen for data request until the transaction
// is resolved and verified on the other side:
// Verifying a transaction will also verify every transaction in
// the transaction's dependency chain, which will require
// transaction data access on counterparty's node. The
// ``SendTransactionFlow`` can be used to automate the sending and
// data vending process. The ``SendTransactionFlow`` will listen
// for data request until the transaction is resolved and verified
// on the other side:
// DOCSTART 12
subFlow(SendTransactionFlow(counterparty, twiceSignedTx))
@ -401,7 +403,8 @@ object FlowCookbook {
val verifiedTransaction = subFlow(ReceiveTransactionFlow(counterparty))
// DOCEND 13
// We can also send and receive a `StateAndRef` dependency chain and automatically resolve its dependencies.
// We can also send and receive a `StateAndRef` dependency chain
// and automatically resolve its dependencies.
// DOCSTART 14
subFlow(SendStateAndRefFlow(counterparty, dummyStates))
@ -409,24 +412,10 @@ object FlowCookbook {
val resolvedStateAndRef = subFlow(ReceiveStateAndRefFlow<DummyState>(counterparty))
// DOCEND 14
// A ``SignedTransaction`` is a pairing of a ``WireTransaction``
// with signatures over this ``WireTransaction``. We don't verify
// a signed transaction per se, but rather the ``WireTransaction``
// it contains.
// DOCSTART 31
val wireTx: WireTransaction = twiceSignedTx.tx
// DOCEND 31
// Before we can verify the transaction, we need the
// ``ServiceHub`` to use our node's local storage to resolve the
// transaction's inputs and attachments into actual objects,
// rather than just references. We do this by converting the
// ``WireTransaction`` into a ``LedgerTransaction``.
// DOCSTART 32
val ledgerTx: LedgerTransaction = wireTx.toLedgerTransaction(serviceHub)
// DOCEND 32
// We can now verify the transaction.
// We can now verify the transaction to ensure that it satisfies
// the contracts of all the transaction's input and output states.
// DOCSTART 33
ledgerTx.verify()
twiceSignedTx.verify(serviceHub)
// DOCEND 33
// We'll often want to perform our own additional verification
@ -434,8 +423,18 @@ object FlowCookbook {
// rules and requires our signature doesn't mean we have to
// sign it! We need to make sure the transaction represents an
// agreement we actually want to enter into.
// To do this, we need to convert our ``SignedTransaction``
// into a ``LedgerTransaction``. This will use our ServiceHub
// to resolve the transaction's inputs and attachments into
// actual objects, rather than just references.
// DOCSTART 32
val ledgerTx: LedgerTransaction = twiceSignedTx.toLedgerTransaction(serviceHub)
// DOCEND 32
// We can now perform our additional verification.
// DOCSTART 34
val outputState: DummyState = wireTx.outputsOfType<DummyState>().single()
val outputState: DummyState = ledgerTx.outputsOfType<DummyState>().single()
if (outputState.magicNumber == 777) {
// ``FlowException`` is a special exception type. It will be
// propagated back to any counterparty flows waiting for a

View File

@ -1,17 +1,14 @@
package net.corda.docs
import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.Amount
import net.corda.core.contracts.Issued
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.withoutIssuer
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.*
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.vault.QueryCriteria
@ -20,7 +17,8 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.unwrap
import net.corda.schemas.CashSchemaV1
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.schemas.CashSchemaV1
import java.util.*
@CordaSerializable
@ -42,8 +40,8 @@ private fun gatherOurInputs(serviceHub: ServiceHub,
val ourParties = ourKeys.map { serviceHub.identityService.partyFromKey(it) ?: throw IllegalStateException("Unable to resolve party from key") }
val fungibleCriteria = QueryCriteria.FungibleAssetQueryCriteria(owner = ourParties)
val notaryName = if (notary != null) notary.name else serviceHub.networkMapCache.getAnyNotary()!!.name
val vaultCriteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(notaryName = listOf(notaryName))
val notaries = notary ?: serviceHub.networkMapCache.getAnyNotary()
val vaultCriteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(notary = listOf(notaries as AbstractParty))
val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.equal(amountRequired.token.product.currencyCode) }
val cashCriteria = QueryCriteria.VaultCustomQueryCriteria(logicalExpression)

View File

@ -2,7 +2,6 @@ package net.corda.docs
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.containsAny
import net.corda.core.flows.FinalityFlow
@ -11,8 +10,6 @@ import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.Vault
import net.corda.core.node.services.queryBy
import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria
import net.corda.core.serialization.CordaSerializable
@ -35,7 +32,7 @@ enum class WorkflowState {
* Minimal contract to encode a simple workflow with one initial state and two possible eventual states.
* It is assumed one party unilaterally submits and the other manually retrieves the deal and completes it.
*/
data class TradeApprovalContract(override val legalContractReference: SecureHash = SecureHash.sha256("Example of workflow type transaction")) : Contract {
data class TradeApprovalContract(private val blank: Void? = null) : Contract {
interface Commands : CommandData {
class Issue : TypeOnlyCommandData(), Commands // Record receipt of deal details

View File

@ -1,11 +1,12 @@
package net.corda.docs
import net.corda.contracts.getCashBalances
import net.corda.core.contracts.*
import net.corda.core.contracts.Amount
import net.corda.core.node.services.ServiceInfo
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.flows.CashIssueFlow
import net.corda.finance.*
import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashIssueFlow
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.testing.DUMMY_NOTARY
@ -65,9 +66,7 @@ class CustomVaultQueryTest {
// Use NodeA as issuer and create some dollars
val flowHandle1 = nodeA.services.startFlow(CashIssueFlow(amountToIssue,
OpaqueBytes.of(0x01),
nodeA.info.legalIdentity,
notaryNode.info.notaryIdentity,
false))
notaryNode.info.notaryIdentity))
// Wait for the flow to stop and print
flowHandle1.resultFuture.getOrThrow()
}

View File

@ -1,12 +1,12 @@
package net.corda.docs
import net.corda.contracts.getCashBalances
import net.corda.core.contracts.*
import net.corda.core.node.services.ServiceInfo
import net.corda.core.toFuture
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.flows.CashIssueFlow
import net.corda.finance.*
import net.corda.finance.contracts.getCashBalances
import net.corda.finance.flows.CashIssueFlow
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.testing.DUMMY_NOTARY
@ -46,9 +46,7 @@ class FxTransactionBuildTutorialTest {
// Use NodeA as issuer and create some dollars
val flowHandle1 = nodeA.services.startFlow(CashIssueFlow(DOLLARS(1000),
OpaqueBytes.of(0x01),
nodeA.info.legalIdentity,
notaryNode.info.notaryIdentity,
false))
notaryNode.info.notaryIdentity))
// Wait for the flow to stop and print
flowHandle1.resultFuture.getOrThrow()
printBalances()
@ -56,9 +54,7 @@ class FxTransactionBuildTutorialTest {
// Using NodeB as Issuer create some pounds.
val flowHandle2 = nodeB.services.startFlow(CashIssueFlow(POUNDS(1000),
OpaqueBytes.of(0x01),
nodeB.info.legalIdentity,
notaryNode.info.notaryIdentity,
false))
notaryNode.info.notaryIdentity))
// Wait for flow to come to an end and print
flowHandle2.resultFuture.getOrThrow()
printBalances()

View File

@ -94,7 +94,7 @@ Our flow has two parties (B and S for buyer and seller) and will proceed as foll
recording the transaction in B's local vault, and then sending it on to S who also checks it and commits
the transaction to S's local vault.
You can find the implementation of this flow in the file ``finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt``.
You can find the implementation of this flow in the file ``finance/src/main/kotlin/net/corda/finance/TwoPartyTradeFlow.kt``.
Assuming no malicious termination, they both end the flow being in possession of a valid, signed transaction that
represents an atomic asset swap.
@ -235,7 +235,7 @@ Let's implement the ``Seller.call`` method. This will be run when the flow is in
.. container:: codeset
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt
:language: kotlin
:start-after: DOCSTART 4
:end-before: DOCEND 4
@ -269,7 +269,7 @@ OK, let's do the same for the buyer side:
.. container:: codeset
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt
:language: kotlin
:start-after: DOCSTART 1
:end-before: DOCEND 1
@ -428,7 +428,7 @@ override ``checkTransaction()`` to add our own custom validation logic:
.. container:: codeset
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt
:language: kotlin
:start-after: DOCSTART 5
:end-before: DOCEND 5
@ -504,7 +504,7 @@ A flow might declare some steps with code inside the flow class like this:
.. container:: codeset
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt
:language: kotlin
:start-after: DOCSTART 2
:end-before: DOCEND 2
@ -545,7 +545,7 @@ steps by overriding the ``Step`` class like this:
.. container:: codeset
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/flows/TwoPartyTradeFlow.kt
.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt
:language: kotlin
:start-after: DOCSTART 3
:end-before: DOCEND 3

View File

@ -37,17 +37,11 @@ Just as every Corda state must implement the ``ContractState`` interface, every
// Implements the contract constraints in code.
@Throws(IllegalArgumentException::class)
fun verify(tx: LedgerTransaction)
// Expresses the contract constraints as legal prose.
val legalContractReference: SecureHash
}
You can read about function declarations in Kotlin `here <https://kotlinlang.org/docs/reference/functions.html>`_.
We can see that ``Contract`` expresses its constraints in two ways:
* In legal prose, through a hash referencing a legal contract that expresses the contract's constraints in legal prose
* In code, through a ``verify`` function that takes a transaction as input, and:
We can see that ``Contract`` expresses its constraints through a ``verify`` function that takes a transaction as input, and:
* Throws an ``IllegalArgumentException`` if it rejects the transaction proposal
* Returns silently if it accepts the transaction proposal
@ -67,7 +61,7 @@ transfer them or redeem them for cash. One way to enforce this behaviour would b
* Its value must be non-negative
* The lender and the borrower cannot be the same entity
* The IOU's borrower must sign the transaction
* The IOU's lender must sign the transaction
We can picture this transaction as follows:
@ -79,18 +73,19 @@ Defining IOUContract
--------------------
Let's write a contract that enforces these constraints. We'll do this by modifying either ``TemplateContract.java`` or
``TemplateContract.kt`` and updating ``TemplateContract`` to define an ``IOUContract``:
``App.kt`` and updating ``TemplateContract`` to define an ``IOUContract``:
.. container:: codeset
.. code-block:: kotlin
package com.iou
...
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
open class IOUContract : Contract {
...
class IOUContract : Contract {
// Our Create command.
class Create : CommandData
@ -109,19 +104,16 @@ Let's write a contract that enforces these constraints. We'll do this by modifyi
// Constraints on the signers.
"There must only be one signer." using (command.signers.toSet().size == 1)
"The signer must be the borrower." using (command.signers.contains(out.borrower.owningKey))
"The signer must be the lender." using (command.signers.contains(out.lender.owningKey))
}
}
// The legal contract reference - we'll leave this as a dummy hash for now.
override val legalContractReference = SecureHash.zeroHash
}
.. code-block:: java
package com.iou;
package com.template.contract;
import com.google.common.collect.ImmutableSet;
import com.template.state.IOUState;
import net.corda.core.contracts.AuthenticatedObject;
import net.corda.core.contracts.CommandData;
import net.corda.core.contracts.Contract;
@ -146,25 +138,23 @@ Let's write a contract that enforces these constraints. We'll do this by modifyi
check.using("There should be one output state of type IOUState.", tx.getOutputs().size() == 1);
// IOU-specific constraints.
final IOUState out = (IOUState) tx.getOutputs().getData().get(0);
final IOUState out = (IOUState) tx.getOutputs().get(0).getData();
final Party lender = out.getLender();
final Party borrower = out.getBorrower();
check.using("The IOU's value must be non-negative.",out.getValue() > 0);
check.using("The lender and the borrower cannot be the same entity.", lender != borrower);
// Constraints on the signers.
check.using("There must only be one signer.", ImmutableSet.of(command.getSigners()).size() == 1);
check.using("The signer must be the borrower.", command.getSigners().contains(borrower.getOwningKey()));
check.using("There must only be one signer.", command.getSigners().size() == 1);
check.using("The signer must be the lender.", command.getSigners().contains(lender.getOwningKey()));
return null;
});
}
// The legal contract reference - we'll leave this as a dummy hash for now.
private final SecureHash legalContractReference = SecureHash.Companion.getZeroHash();
@Override public final SecureHash getLegalContractReference() { return legalContractReference; }
}
If you're following along in Java, you'll also need to rename ``TemplateContract.java`` to ``IOUContract.java``.
Let's walk through this code step by step.
The Create command
@ -175,7 +165,7 @@ The first thing we add to our contract is a *command*. Commands serve two functi
example, a transaction proposing the creation of an IOU could have to satisfy different constraints to one redeeming
an IOU
* They allow us to define the required signers for the transaction. For example, IOU creation might require signatures
from the borrower alone, whereas the transfer of an IOU might require signatures from both the IOU's borrower and lender
from the lender only, whereas the transfer of an IOU might require signatures from both the IOU's borrower and lender
Our contract has one command, a ``Create`` command. All commands must implement the ``CommandData`` interface.
@ -215,7 +205,7 @@ following are true:
* The transaction has inputs
* The transaction doesn't have exactly one output
* The IOU itself is invalid
* The transaction doesn't require the borrower's signature
* The transaction doesn't require the lender's signature
Command constraints
~~~~~~~~~~~~~~~~~~~
@ -254,7 +244,7 @@ other statements - in this case, we're extracting the transaction's single ``IOU
Signer constraints
~~~~~~~~~~~~~~~~~~
Finally, we require the borrower's signature on the transaction. A transaction's required signers is equal to the union
Finally, we require the lender's signature on the transaction. A transaction's required signers is equal to the union
of all the signers listed on the commands. We therefore extract the signers from the ``Create`` command we
retrieved earlier.
@ -266,7 +256,7 @@ We've now written an ``IOUContract`` constraining the evolution of each ``IOUSta
* Creating an ``IOUState`` requires an issuance transaction with no inputs, a single ``IOUState`` output, and a
``Create`` command
* The ``IOUState`` created by the issuance transaction must have a non-negative value, and the lender and borrower
must be different entities.
must be different entities
Before we move on, make sure you go back and modify ``IOUState`` to point to the new ``IOUContract`` class.

View File

@ -33,24 +33,20 @@ FlowLogic
Flows are implemented as ``FlowLogic`` subclasses. You define the steps taken by the flow by overriding
``FlowLogic.call``.
We'll write our flow in either ``TemplateFlow.java`` or ``TemplateFlow.kt``. Overwrite the existing template code with
the following:
We'll write our flow in either ``TemplateFlow.java`` or ``App.kt``. Overwrite both the existing flows in the template
with the following:
.. container:: codeset
.. code-block:: kotlin
package com.iou
...
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.Command
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
import net.corda.core.flows.FinalityFlow
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.flows.*
...
@InitiatingFlow
@StartableByRPC
@ -79,7 +75,7 @@ the following:
txBuilder.verify(serviceHub)
// Signing the transaction.
val signedTx = serviceHub.toSignedTransaction(txBuilder)
val signedTx = serviceHub.signInitialTransaction(txBuilder)
// Finalising the transaction.
subFlow(FinalityFlow(signedTx))
@ -88,19 +84,17 @@ the following:
.. code-block:: java
package com.iou;
package com.template.flow;
import co.paralleluniverse.fibers.Suspendable;
import com.template.contract.IOUContract;
import com.template.state.IOUState;
import net.corda.core.contracts.Command;
import net.corda.core.flows.FlowException;
import net.corda.core.flows.FlowLogic;
import net.corda.core.flows.InitiatingFlow;
import net.corda.core.flows.StartableByRPC;
import net.corda.core.flows.*;
import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;
import net.corda.flows.FinalityFlow;
@InitiatingFlow
@StartableByRPC
@ -118,6 +112,11 @@ the following:
this.otherParty = otherParty;
}
@Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
/**
* The flow logic is encapsulated within the call() method.
*/
@ -128,7 +127,7 @@ the following:
final Party me = getServiceHub().getMyInfo().getLegalIdentity();
final Party notary = getServiceHub().getNetworkMapCache().getAnyNotary(null);
// We create a transaction builder
// We create a transaction builder.
final TransactionBuilder txBuilder = new TransactionBuilder();
txBuilder.setNotary(notary);
@ -141,7 +140,7 @@ the following:
txBuilder.verify(getServiceHub());
// Signing the transaction.
final SignedTransaction signedTx = getServiceHub().toSignedTransaction(txBuilder);
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Finalising the transaction.
subFlow(new FinalityFlow(signedTx));
@ -150,6 +149,8 @@ the following:
}
}
If you're following along in Java, you'll also need to rename ``TemplateFlow.java`` to ``IOUFlow.java``.
We now have our own ``FlowLogic`` subclass that overrides ``FlowLogic.call``. There's a few things to note:
* ``FlowLogic.call`` has a return type that matches the type parameter passed to ``FlowLogic`` - this is type returned
@ -163,6 +164,9 @@ We now have our own ``FlowLogic`` subclass that overrides ``FlowLogic.call``. Th
* ``@InitiatingFlow`` means that this flow can be started directly by the node
* ``StartableByRPC`` allows the node owner to start this flow via an RPC call
* We override the progress tracker, even though we are not providing any progress tracker steps yet. The progress
tracker is required for the node shell to establish when the flow has ended
Let's walk through the steps of ``FlowLogic.call`` one-by-one:
Retrieving participant information

View File

@ -9,29 +9,32 @@ Running our CorDapp
Now that we've written a CorDapp, it's time to test it by running it on some real Corda nodes.
Clean up
--------
Before running our node, delete the ``client/TemplateClient.java`` (for Java) or ``client/TemplateClient.kt`` (for
Kotlin) file. We won't be using it, and it will cause build errors unless we remove it.
Deploying our CorDapp
---------------------
Let's take a look at the nodes we're going to deploy. Open the project's build file under ``java-source/build.gradle``
or ``kotlin-source/build.gradle`` and scroll down to the ``task deployNodes`` section. This section defines four
nodes - the Controller, and NodeA, NodeB and NodeC:
Let's take a look at the nodes we're going to deploy. Open the project's ``build.gradle`` file and scroll down to the
``task deployNodes`` section. This section defines three nodes - the Controller, NodeA, and NodeB:
.. container:: codeset
.. code-block:: kotlin
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
directory "./build/nodes"
networkMap "CN=Controller,O=R3,OU=corda,L=London,C=GB"
networkMap "CN=Controller,O=R3,OU=corda,L=London,C=UK"
node {
name "CN=Controller,O=R3,OU=corda,L=London,C=GB"
name "CN=Controller,O=R3,OU=corda,L=London,C=UK"
advertisedServices = ["corda.notary.validating"]
p2pPort 10002
rpcPort 10003
webPort 10004
cordapps = []
}
node {
name "CN=NodeA,O=NodeA,L=London,C=GB"
name "CN=NodeA,O=NodeA,L=London,C=UK"
advertisedServices = []
p2pPort 10005
rpcPort 10006
@ -48,15 +51,6 @@ nodes - the Controller, and NodeA, NodeB and NodeC:
cordapps = []
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
}
node {
name "CN=NodeC,O=NodeC,L=Paris,C=FR"
advertisedServices = []
p2pPort 10011
rpcPort 10012
webPort 10013
cordapps = []
rpcUsers = [[ user: "user1", "password": "test", "permissions": []]]
}
}
We have three standard nodes, plus a special Controller node that is running the network map service, and is also
@ -80,8 +74,8 @@ We can do that now by running the following commands from the root of the projec
Running the nodes
-----------------
Running ``deployNodes`` will build the nodes under both ``java-source/build/nodes`` and ``kotlin-source/build/nodes``.
If we navigate to one of these folders, we'll see four node folder. Each node folder has the following structure:
Running ``deployNodes`` will build the nodes under ``build/nodes``. If we navigate to one of these folders, we'll see
the three node folders. Each node folder has the following structure:
.. code:: python
@ -97,17 +91,11 @@ Let's start the nodes by running the following commands from the root of the pro
.. code:: python
// On Windows for a Java CorDapp
java-source/build/nodes/runnodes.bat
// On Windows
build/nodes/runnodes.bat
// On Windows for a Kotlin CorDapp
kotlin-source/build/nodes/runnodes.bat
// On Mac for a Java CorDapp
java-source/build/nodes/runnodes
// On Mac for a Kotlin CorDapp
kotlin-source/build/nodes/runnodes
// On Mac
build/nodes/runnodes
This will start a terminal window for each node, and an additional terminal window for each node's webserver - eight
terminal windows in all. Give each node a moment to start - you'll know it's ready when its terminal windows displays
@ -138,10 +126,8 @@ We want to create an IOU of 100 with Node B. We start the ``IOUFlow`` by typing:
start IOUFlow iouValue: 99, otherParty: "NodeB"
Node A and Node B will automatically agree an IOU.
If the flow worked, it should have led to the recording of a new IOU in the vaults of both Node A and Node B. Equally
importantly, Node C - although it sits on the same network - should not be aware of this transaction.
Node A and Node B will automatically agree an IOU. If the flow worked, it should have led to the recording of a new IOU
in the vaults of both Node A and Node B.
We can check the flow has worked by using an RPC operation to check the contents of each node's vault. Typing ``run``
will display a list of the available commands. We can examine the contents of a node's vault by running:
@ -166,8 +152,7 @@ The vaults of Node A and Node B should both display the following output:
value: 99
lender: "CN=NodeA,O=NodeA,L=London,C=GB"
borrower: "CN=NodeB,O=NodeB,L=New York,C=US"
contract:
legalContractReference: "559322B95BCF7913E3113962DC3F3CBD71C818C66977721580C045DC41C813A5"
contract: {}
participants:
- "CN=NodeA,O=NodeA,L=London,C=GB"
- "CN=NodeB,O=NodeB,L=New York,C=US"
@ -178,13 +163,6 @@ The vaults of Node A and Node B should both display the following output:
index: 0
second: "(observable)"
But the vault of Node C should output nothing!
.. code:: python
first: []
second: "(observable)"
Conclusion
----------
We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Like all CorDapps, our

View File

@ -64,31 +64,26 @@ you wish to add them later, its as simple as adding an additional property to yo
Defining IOUState
-----------------
Let's open ``TemplateState.java`` (for Java) or ``TemplateState.kt`` (for Kotlin) and update ``TemplateState`` to
Let's open ``TemplateState.java`` (for Java) or ``App.kt`` (for Kotlin) and update ``TemplateState`` to
define an ``IOUState``:
.. container:: codeset
.. code-block:: kotlin
package com.iou
import net.corda.core.contracts.ContractState
import net.corda.core.identity.Party
class IOUState(val value: Int,
val lender: Party,
val borrower: Party,
override val contract: TemplateContract) : ContractState {
val borrower: Party) : ContractState {
override val contract = TemplateContract()
override val participants get() = listOf(lender, borrower)
}
.. code-block:: java
package com.iou;
package com.template.state;
import com.google.common.collect.ImmutableList;
import com.template.contract.TemplateContract;
import net.corda.core.contracts.ContractState;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.Party;
@ -99,7 +94,7 @@ define an ``IOUState``:
private final int value;
private final Party lender;
private final Party borrower;
private final IOUContract contract = new IOUContract();
private final TemplateContract contract = new TemplateContract();
public IOUState(int value, Party lender, Party borrower) {
this.value = value;
@ -121,7 +116,7 @@ define an ``IOUState``:
@Override
// TODO: Once we've defined IOUContract, come back and update this.
public IOUContract getContract() {
public TemplateContract getContract() {
return contract;
}
@ -131,6 +126,8 @@ define an ``IOUState``:
}
}
If you're following along in Java, you'll also need to rename ``TemplateState.java`` to ``IOUState.java``.
We've made the following changes:
* We've renamed ``TemplateState`` to ``IOUState``

View File

@ -25,11 +25,11 @@ Open a terminal window in the directory where you want to download the CorDapp t
.. code-block:: text
# Clone the template from GitHub:
git clone https://github.com/corda/cordapp-template-java.git ; cd cordapp-template
git clone https://github.com/corda/cordapp-template-java.git ; cd cordapp-template-java
*or*
git clone https://github.com/corda/cordapp-template-kotlin.git ; cd cordapp-template
git clone https://github.com/corda/cordapp-template-kotlin.git ; cd cordapp-template-kotlin
# Retrieve a list of the stable Milestone branches using:
git branch -a --list *release-M*
@ -39,35 +39,26 @@ Open a terminal window in the directory where you want to download the CorDapp t
Template structure
------------------
We can write our CorDapp in either Java or Kotlin, and will be providing the code in both languages throughout. If
you want to write the CorDapp in Java, you'll be modifying the files under ``java-source``. If you prefer to use
Kotlin, you'll be modifying the files under ``kotlin-source``.
To implement our IOU CorDapp, we'll only need to modify three files:
We can write our CorDapp in either Java or Kotlin, and will be providing the code in both languages throughout. To
implement our IOU CorDapp in Java, we'll need to modify three files. For Kotlin, we'll simply be modifying the
``App.kt`` file:
.. container:: codeset
.. code-block:: java
// 1. The state
java-source/src/main/java/com/template/state/TemplateState.java
src/main/java/com/template/state/TemplateState.java
// 2. The contract
java-source/src/main/java/com/template/contract/TemplateContract.java
src/main/java/com/template/contract/TemplateContract.java
// 3. The flow
java-source/src/main/java/com/template/flow/TemplateFlow.java
src/main/java/com/template/flow/TemplateFlow.java
.. code-block:: kotlin
// 1. The state
kotlin-source/src/main/kotlin/com/template/state/TemplateState.kt
// 2. The contract
kotlin-source/src/main/kotlin/com/template/contract/TemplateContract.kt
// 3. The flow
kotlin-source/src/main/kotlin/com/template/flow/TemplateFlow.kt
src/main/kotlin/com/template/App.kt
Progress so far
---------------

View File

@ -8,6 +8,8 @@ Unreleased
* Merged handling of well known and confidential identities in the identity service.
* Remove `IssuerFlow` as it allowed nodes to request arbitrary amounts of cash to be issued from any remote node.
Milestone 14
------------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

After

Width:  |  Height:  |  Size: 199 KiB

View File

@ -41,7 +41,7 @@ To run from the command line in Windows:
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`` 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
3. Run ``gradlew samples:trader-demo:runBank`` to instruct the buyer node to request issuance of some cash from the Bank of Corda node
4. Run ``gradlew samples:trader-demo:runSeller`` to trigger the transaction. If you entered ``flow watch``
you can see flows running on both sides of transaction. Additionally you should see final trade information displayed
to your terminal.

View File

@ -11,23 +11,33 @@ Remember that each state references a contract. The contract imposes constraints
If the transaction does not obey the constraints of all the contracts of all its states, it cannot become a valid
ledger update.
We need to modify our contract so that the lender's signature is required in any IOU creation transaction. This will
only require changing a single line of code. In ``IOUContract.java``/``IOUContract.kt``, update the final line of the
``requireThat`` block as follows:
We need to modify our contract so that the borrower's signature is required in any IOU creation transaction. This will
only require changing a single line of code. In ``IOUContract.java``/``IOUContract.kt``, update the final two lines of
the ``requireThat`` block as follows:
.. container:: codeset
.. code-block:: kotlin
// Constraints on the signers.
"There must be two signers." using (command.signers.toSet().size == 2)
"The borrower and lender must be signers." using (command.signers.containsAll(listOf(
out.borrower.owningKey, out.lender.owningKey)))
.. code-block:: java
...
import com.google.common.collect.ImmutableList;
...
// Constraints on the signers.
check.using("There must be two signers.", command.getSigners().size() == 2);
check.using("The borrower and lender must be signers.", command.getSigners().containsAll(
ImmutableList.of(borrower.getOwningKey(), lender.getOwningKey())));
Progress so far
---------------
Our contract now imposes an additional constraint - the lender must also sign an IOU creation transaction. Next, we
need to update ``IOUFlow`` so that it actually gathers the counterparty's signature as part of the flow.
Our contract now imposes an additional constraint - the borrower must also sign an IOU creation transaction. Next, we
need to update ``IOUFlow`` so that it actually gathers the borrower's signature as part of the flow.

View File

@ -9,17 +9,17 @@ Updating the flow
To update the flow, we'll need to do two things:
* Update the borrower's side of the flow to request the lender's signature
* Create a flow for the lender to run in response to a signature request from the borrower
* Update the lender's side of the flow to request the borrower's signature
* Create a flow for the borrower to run in response to a signature request from the lender
Updating the borrower's flow
----------------------------
Updating the lender's flow
--------------------------
In the original CorDapp, we automated the process of notarising a transaction and recording it in every party's vault
by invoking a built-in flow called ``FinalityFlow`` as a subflow. We're going to use another pre-defined flow, called
``CollectSignaturesFlow``, to gather the lender's signature.
``CollectSignaturesFlow``, to gather the borrower's signature.
We also need to add the lender's public key to the transaction's command, making the lender one of the required signers
on the transaction.
We also need to add the borrower's public key to the transaction's command, making the borrower one of the required
signers on the transaction.
In ``IOUFlow.java``/``IOUFlow.kt``, update ``IOUFlow.call`` as follows:
@ -27,6 +27,8 @@ In ``IOUFlow.java``/``IOUFlow.kt``, update ``IOUFlow.call`` as follows:
.. code-block:: kotlin
...
// We add the items to the builder.
val state = IOUState(iouValue, me, otherParty)
val cmd = Command(IOUContract.Create(), listOf(me.owningKey, otherParty.owningKey))
@ -36,16 +38,24 @@ In ``IOUFlow.java``/``IOUFlow.kt``, update ``IOUFlow.call`` as follows:
txBuilder.verify(serviceHub)
// Signing the transaction.
val signedTx = serviceHub.toSignedTransaction(txBuilder)
val signedTx = serviceHub.signInitialTransaction(txBuilder)
// Obtaining the counterparty's signature
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx))
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, CollectSignaturesFlow.tracker()))
// Finalising the transaction.
subFlow(FinalityFlow(fullySignedTx))
.. code-block:: java
...
import com.google.common.collect.ImmutableList;
import java.security.PublicKey;
import java.util.List;
...
// We add the items to the builder.
IOUState state = new IOUState(iouValue, me, otherParty);
List<PublicKey> requiredSigners = ImmutableList.of(me.getOwningKey(), otherParty.getOwningKey());
@ -56,65 +66,42 @@ In ``IOUFlow.java``/``IOUFlow.kt``, update ``IOUFlow.call`` as follows:
txBuilder.verify(getServiceHub());
// Signing the transaction.
final SignedTransaction signedTx = getServiceHub().toSignedTransaction(txBuilder);
final SignedTransaction signedTx = getServiceHub().signInitialTransaction(txBuilder);
// Obtaining the counterparty's signature
final SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(signedTx, null));
final SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(signedTx, CollectSignaturesFlow.Companion.tracker()));
// Finalising the transaction.
subFlow(new FinalityFlow(fullySignedTx));
To make the lender a required signer, we simply add the lender's public key to the list of signers on the command.
return null;
To make the borrower a required signer, we simply add the borrower's public key to the list of signers on the command.
``CollectSignaturesFlow``, meanwhile, takes a transaction signed by the flow initiator, and returns a transaction
signed by all the transaction's other required signers. We then pass this fully-signed transaction into
``FinalityFlow``.
The lender's flow
-----------------
Reorganising our class
^^^^^^^^^^^^^^^^^^^^^^
Before we define the lender's flow, let's reorganise ``IOUFlow.java``/``IOUFlow.kt`` a little bit:
* Rename ``IOUFlow`` to ``Initiator``
* In Java, make the ``Initiator`` class static, rename its constructor to match the new name, and move the definition
inside an enclosing ``IOUFlow`` class
* In Kotlin, move the definition of ``Initiator`` class inside an enclosing ``IOUFlow`` singleton object
We will end up with the following structure:
.. container:: codeset
.. code-block:: kotlin
object IOUFlow {
@InitiatingFlow
@StartableByRPC
class Initiator(val iouValue: Int,
val otherParty: Party) : FlowLogic<Unit>() {
.. code-block:: java
public class IOUFlow {
@InitiatingFlow
@StartableByRPC
public static class Initiator extends FlowLogic<Void> {
Writing the lender's flow
^^^^^^^^^^^^^^^^^^^^^^^^^
Creating the borrower's flow
----------------------------
We're now ready to write the lender's flow, which will respond to the borrower's attempt to gather our signature.
Inside the ``IOUFlow`` class/singleton object, add the following class:
In a new ``IOUFlowResponder.java`` file in Java, or within the ``App.kt`` file in Kotlin, add the following class:
.. container:: codeset
.. code-block:: kotlin
@InitiatedBy(Initiator::class)
class Acceptor(val otherParty: Party) : FlowLogic<Unit>() {
...
import net.corda.core.transactions.SignedTransaction
...
@InitiatedBy(IOUFlow::class)
class IOUFlowResponder(val otherParty: Party) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val signTransactionFlow = object : SignTransactionFlow(otherParty) {
val signTransactionFlow = object : SignTransactionFlow(otherParty, SignTransactionFlow.tracker()) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
val output = stx.tx.outputs.single().data
"This must be an IOU transaction." using (output is IOUState)
@ -129,12 +116,26 @@ Inside the ``IOUFlow`` class/singleton object, add the following class:
.. code-block:: java
@InitiatedBy(Initiator.class)
public static class Acceptor extends FlowLogic<Void> {
package com.template.flow;
import co.paralleluniverse.fibers.Suspendable;
import com.template.state.IOUState;
import net.corda.core.contracts.ContractState;
import net.corda.core.flows.FlowException;
import net.corda.core.flows.FlowLogic;
import net.corda.core.flows.InitiatedBy;
import net.corda.core.flows.SignTransactionFlow;
import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.utilities.ProgressTracker;
import static net.corda.core.contracts.ContractsDSL.requireThat;
@InitiatedBy(IOUFlow.class)
public class IOUFlowResponder extends FlowLogic<Void> {
private final Party otherParty;
public Acceptor(Party otherParty) {
public IOUFlowResponder(Party otherParty) {
this.otherParty = otherParty;
}
@ -142,8 +143,8 @@ Inside the ``IOUFlow`` class/singleton object, add the following class:
@Override
public Void call() throws FlowException {
class signTxFlow extends SignTransactionFlow {
private signTxFlow(Party otherParty) {
super(otherParty, null);
private signTxFlow(Party otherParty, ProgressTracker progressTracker) {
super(otherParty, progressTracker);
}
@Override
@ -158,18 +159,19 @@ Inside the ``IOUFlow`` class/singleton object, add the following class:
}
}
subFlow(new signTxFlow(otherParty));
subFlow(new signTxFlow(otherParty, SignTransactionFlow.Companion.tracker()));
return null;
}
}
As with the ``Initiator``, our ``Acceptor`` flow is a ``FlowLogic`` subclass where we've overridden ``FlowLogic.call``.
As with the ``IOUFlow``, our ``IOUFlowResponder`` flow is a ``FlowLogic`` subclass where we've overridden
``FlowLogic.call``.
The flow is annotated with ``InitiatedBy(Initiator.class)``, which means that your node will invoke ``Acceptor.call``
when it receives a message from a instance of ``Initiator`` running on another node. What will this message from the
``Initiator`` be? If we look at the definition of ``CollectSignaturesFlow``, we can see that we'll be sent a
``SignedTransaction``, and are expected to send back our signature over that transaction.
The flow is annotated with ``InitiatedBy(IOUFlow.class)``, which means that your node will invoke
``IOUFlowResponder.call`` when it receives a message from a instance of ``Initiator`` running on another node. What
will this message from the ``IOUFlow`` be? If we look at the definition of ``CollectSignaturesFlow``, we can see that
we'll be sent a ``SignedTransaction``, and are expected to send back our signature over that transaction.
We could handle this manually. However, there is also a pre-defined flow called ``SignTransactionFlow`` that can handle
this process for us automatically. ``SignTransactionFlow`` is an abstract class, and we must subclass it and override
@ -179,7 +181,7 @@ Once we've defined the subclass, we invoke it using ``FlowLogic.subFlow``, and t
and the lender's flow is conducted automatically.
CheckTransactions
~~~~~~~~~~~~~~~~~
^^^^^^^^^^^^^^^^^
``SignTransactionFlow`` will automatically verify the transaction and its signatures before signing it. However, just
because a transaction is valid doesn't mean we necessarily want to sign. What if we don't want to deal with the
counterparty in question, or the value is too high, or we're not happy with the transaction's structure?
@ -200,4 +202,4 @@ We can now run our updated CorDapp, using the instructions :doc:`here <hello-wor
Our CorDapp now requires agreement from both the lender and the borrower before an IOU can be created on the ledger.
This prevents either the lender or the borrower from unilaterally updating the ledger in a way that only benefits
themselves.
themselves.

View File

@ -6,5 +6,4 @@ Two-party flows
tut-two-party-introduction
tut-two-party-contract
tut-two-party-flow
tut-two-party-running
tut-two-party-flow

View File

@ -12,14 +12,14 @@ elements:
* An ``IOUContract``, controlling the evolution of IOUs over time
* An ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger
However, in our original CorDapp, only the IOU's borrower was required to sign transactions issuing IOUs. The lender
However, in our original CorDapp, only the IOU's lender was required to sign transactions issuing IOUs. The borrower
had no say in whether the issuance of the IOU was a valid ledger update or not.
In this tutorial, we'll update our code so that the borrower requires the lender's agreement before they can issue an
In this tutorial, we'll update our code so that the lender requires the borrower's agreement before they can issue an
IOU onto the ledger. We'll need to make two changes:
* The ``IOUContract`` will need to be updated so that transactions involving an ``IOUState`` will require the lender's
signature (as well as the borrower's) to become valid ledger updates
* The ``IOUFlow`` will need to be updated to allow for the gathering of the lender's signature
* The ``IOUContract`` will need to be updated so that transactions involving an ``IOUState`` will require the borrower's
signature (as well as the lender's) to become valid ledger updates
* The ``IOUFlow`` will need to be updated to allow for the gathering of the borrower's signature
We'll start by updating the contract.

View File

@ -1,28 +0,0 @@
Running our CorDapp
===================
Conclusion
----------
We have written a simple CorDapp that allows IOUs to be issued onto the ledger. Like all CorDapps, our
CorDapp is made up of three key parts:
* The ``IOUState``, representing IOUs on the ledger
* The ``IOUContract``, controlling the evolution of IOUs over time
* The ``IOUFlow``, orchestrating the process of agreeing the creation of an IOU on-ledger.
Together, these three parts completely determine how IOUs are created and evolved on the ledger.
Next steps
----------
You should now be ready to develop your own CorDapps. There's
`a more fleshed-out version of the IOU CorDapp <https://github.com/corda/cordapp-tutorial>`_
with an API and web front-end, and a set of example CorDapps in
`the main Corda repo <https://github.com/corda/corda>`_, under ``samples``. An explanation of how to run these
samples :doc:`here <running-the-demos>`.
As you write CorDapps, you can learn more about the API available :doc:`here <api>`.
If you get stuck at any point, please reach out on `Slack <https://slack.corda.net/>`_,
`Discourse <https://discourse.corda.net/>`_, or `Stack Overflow <https://stackoverflow.com/questions/tagged/corda>`_.

View File

@ -132,7 +132,7 @@ When starting a standalone node using a configuration file we must supply the RP
.. code-block:: text
rpcUsers : [
{ username=user, password=password, permissions=[ StartFlow.net.corda.flows.CashFlow ] }
{ username=user, password=password, permissions=[ StartFlow.net.corda.finance.flows.CashFlow ] }
]
When using the gradle Cordformation plugin to configure and deploy a node you must supply the RPC credentials in a similar
@ -143,7 +143,7 @@ manner:
rpcUsers = [
['username' : "user",
'password' : "password",
'permissions' : ["StartFlow.net.corda.flows.CashFlow"]]
'permissions' : ["StartFlow.net.corda.finance.flows.CashFlow"]]
]
You can then deploy and launch the nodes (Notary and Alice) as follows:

View File

@ -66,8 +66,6 @@ We start by defining the ``CommercialPaper`` class. As in the previous tutorial,
.. sourcecode:: kotlin
class CommercialPaper : Contract {
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper")
override fun verify(tx: LedgerTransaction) = verifyClause(tx, Clauses.Group(), tx.commands.select<Commands>())
interface Commands : CommandData {
@ -79,11 +77,6 @@ We start by defining the ``CommercialPaper`` class. As in the previous tutorial,
.. sourcecode:: java
public class CommercialPaper implements Contract {
@Override
public SecureHash getLegalContractReference() {
return SecureHash.Companion.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
}
@Override
public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
ClauseVerifier.verifyClause(tx, new Clauses.Group(), extractCommands(tx));

View File

@ -59,8 +59,6 @@ Kotlin syntax works.
.. sourcecode:: kotlin
class CommercialPaper : Contract {
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
override fun verify(tx: LedgerTransaction) {
TODO()
}
@ -69,22 +67,16 @@ Kotlin syntax works.
.. sourcecode:: java
public class CommercialPaper implements Contract {
@Override
public SecureHash getLegalContractReference() {
return SecureHash.Companion.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
}
@Override
public void verify(LedgerTransaction tx) {
throw new UnsupportedOperationException();
}
}
Every contract must have at least a ``getLegalContractReference()`` and a ``verify()`` method. In Kotlin we express
a getter without a setter as an immutable property (val). The *legal contract reference* is supposed to be a hash
of a document that describes the legal contract and may take precedence over the code, in case of a dispute.
Every contract must have at least a ``verify()`` method.
.. note:: The way legal contract prose is bound to a smart contract implementation will change in future.
.. note:: In the future there will be a way to bind legal contract prose to a smart contract implementation,
that may take precedence over the code in case of a dispute.
The verify method returns nothing. This is intentional: the function either completes correctly, or throws an exception,
in which case the transaction is rejected.

View File

@ -407,10 +407,10 @@ list:
.. sourcecode:: none
com.example.flow.ExampleFlow$Initiator
net.corda.flows.CashExitFlow
net.corda.flows.CashIssueFlow
net.corda.flows.CashPaymentFlow
net.corda.flows.ContractUpgradeFlow
net.corda.finance.flows.CashExitFlow
net.corda.finance.flows.CashIssueFlow
net.corda.finance.flows.CashPaymentFlow
net.corda.finance.flows.ContractUpgradeFlow
We can create a new IOU using the ``ExampleFlow$Initiator`` flow. For example, from the interactive shell of NodeA, you
can agree an IOU of 50 with NodeB by running ``flow start Initiator iouValue: 50, otherParty: NodeB``.
@ -450,8 +450,7 @@ We can see a list of the states in our node's vault using ``run vaultAndUpdates`
linearId:
externalId: null
id: "84628565-2688-45ef-bb06-aae70fcf3be7"
contract:
legalContractReference: "4DDE2A47C361106CBAEC06CC40FE418A994822A3C8054851FEECD51207BFAF82"
contract: {}
participants:
- "CN=NodeB,O=NodeB,L=New York,C=US"
- "CN=NodeA,O=NodeA,L=London,C=UK"
@ -485,8 +484,7 @@ abbreviated the output below):
linearId:
externalId: null
id: "84628565-2688-45ef-bb06-aae70fcf3be7"
contract:
legalContractReference: "4DDE2A47C361106CBAEC06CC40FE418A994822A3C8054851FEECD51207BFAF82"
contract: {}
participants:
- "CN=NodeB,O=NodeB,L=New York,C=US"
- "CN=NodeA,O=NodeA,L=London,C=UK"

View File

@ -87,9 +87,9 @@ we just use a helper that handles it for us. We also assume that we already have
.. sourcecode:: kotlin
val inputState = StateAndRef(sate, stateRef)
val moveTransactionBuilder = DummyContract.move(inputState, newOwner = aliceParty.owningKey)
val moveTransactionBuilder = DummyContract.withNewOwnerAndAmount(inputState, newOwner = aliceParty.owningKey)
The ``DummyContract.move()`` method will a new transaction builder with our old state as the input, a new state
The ``DummyContract.withNewOwnerAndAmount()`` method will a new transaction builder with our old state as the input, a new state
with Alice as the owner, and a relevant contract command for "move".
Again we sign the transaction, and build it:

View File

@ -92,6 +92,8 @@ The ``WebServerPluginRegistry`` class defines the following:
be distributed within the CorDapp jars. This static content will not be available if the bundled web server is not
started
* ``customizeJSONSerialization``, which can be overridden to register custom JSON serializers if required by the REST api.
* The static web content itself should be placed inside the ``src/main/resources`` directory
To learn about how to use gradle to build your cordapp against Corda and generate an artifact please read