updated following review

This commit is contained in:
jamescarlyle 2016-09-06 17:56:01 +01:00
parent ee65d4490b
commit cdb2c3efa6
16 changed files with 489 additions and 123 deletions

View File

@ -122,8 +122,11 @@ interface ContractState {
*
* The encumbered state refers to another by index, and the referred encumbrance state
* is an output state in a particular position on the same transaction that created the encumbered state. An alternative
* would be encumber by reference to a StateRef., which would allow the specification of encumbrance by a state created
* in a prior transaction.
* implementation would be encumber by reference to a StateRef., which would allow the specification of encumbrance
* by a state created in a prior transaction.
*
* Note that an encumbered state that is being consumed must have its encumbrance consumed in the same transaction,
* otherwise the transaction is not valid.
*/
val encumbrance: Int? get() = null
}

View File

@ -83,17 +83,21 @@ sealed class TransactionType {
encumberedInput ->
if (tx.inputs.none { it.ref.txhash == encumberedInput.ref.txhash &&
it.ref.index == encumberedInput.state.data.encumbrance }) {
throw TransactionVerificationException.TransactionMissingEncumbranceException(tx)
throw TransactionVerificationException.TransactionMissingEncumbranceException(
tx, encumberedInput.state.data.encumbrance!!,
TransactionVerificationException.Direction.INPUT
)
}
}
// Check that an encumbered state does not refer to itself as the encumbrance, and that the number of outputs
// can contain the encumbrance.
tx.outputs.forEachIndexed { i, it ->
if (it.data.encumbrance != null) {
if (it.data.encumbrance == i || it.data.encumbrance!! >= tx.outputs.size) {
throw TransactionVerificationException.TransactionMissingEncumbranceException(tx)
}
// Check that, in the outputs, an encumbered state does not refer to itself as the encumbrance,
// and that the number of outputs can contain the encumbrance.
for ((i, output) in tx.outputs.withIndex() ) {
val encumbranceIndex = output.data.encumbrance ?: continue
if (encumbranceIndex == i || encumbranceIndex >= tx.outputs.size) {
throw TransactionVerificationException.TransactionMissingEncumbranceException(
tx, encumbranceIndex,
TransactionVerificationException.Direction.OUTPUT)
}
}
}

View File

@ -100,5 +100,12 @@ sealed class TransactionVerificationException(val tx: LedgerTransaction, cause:
class NotaryChangeInWrongTransactionType(tx: LedgerTransaction, val outputNotary: Party) : TransactionVerificationException(tx, null) {
override fun toString(): String = "Found unexpected notary change in transaction. Tx notary: ${tx.notary}, found: ${outputNotary}"
}
class TransactionMissingEncumbranceException(tx: LedgerTransaction) : TransactionVerificationException(tx, null)
class TransactionMissingEncumbranceException(tx: LedgerTransaction, val missing: Int, val inOut: Direction) : TransactionVerificationException(tx, null) {
override val message: String?
get() = "Missing required encumbrance ${missing} in ${inOut}"
}
enum class Direction {
INPUT,
OUTPUT
}
}

View File

@ -13,8 +13,7 @@ import kotlin.test.assertFailsWith
val TEST_TIMELOCK_ID = TransactionEncumbranceTests.TestTimeLock()
class TransactionEncumbranceTests {
val defaultRef = OpaqueBytes(ByteArray(1, {1}))
val defaultIssuer = MEGA_CORP.ref(defaultRef)
val defaultIssuer = MEGA_CORP.ref(1)
val encumberedState = Cash.State(
amount = 1000.DOLLARS `issued by` defaultIssuer,
owner = DUMMY_PUBKEY_1,
@ -34,13 +33,10 @@ class TransactionEncumbranceTests {
override val legalContractReference = SecureHash.sha256("TestTimeLock")
override fun verify(tx: TransactionForContract) {
val timestamp: Timestamp? = tx.timestamp
val timeLockCommand = tx.commands.select<TestTimeLock.Commands>().first()
if (timeLockCommand.value is TestTimeLock.Commands.Exit) {
val time = timestamp?.before ?: throw IllegalArgumentException("Transactions containing time-locks must be timestamped")
requireThat {
"the time specified in the time-lock has passed" by
(time >= tx.inputs.filterIsInstance<TestTimeLock.State>().first().validFrom)
}
val time = timestamp?.before ?: throw IllegalArgumentException("Transactions containing time-locks must be timestamped")
requireThat {
"the time specified in the time-lock has passed" by
(time >= tx.inputs.filterIsInstance<TestTimeLock.State>().single().validFrom)
}
}
data class State(
@ -50,10 +46,6 @@ class TransactionEncumbranceTests {
get() = throw UnsupportedOperationException()
override val contract: Contract = TEST_TIMELOCK_ID
}
interface Commands : CommandData {
class Issue : TypeOnlyCommandData(), Commands
class Exit : TypeOnlyCommandData(), Commands
}
}
@Test
@ -68,26 +60,22 @@ class TransactionEncumbranceTests {
}
}
// An encumbered state must not be encumbered by itself.
assertFailsWith(TransactionVerificationException.TransactionMissingEncumbranceException::class) {
transaction {
input { unencumberedState }
input { unencumberedState }
output { unencumberedState }
// The encumbered state refers to an encumbrance in position 1, so what follows is wrong.
output { encumberedState }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
this.verifies()
}
transaction {
input { unencumberedState }
input { unencumberedState }
output { unencumberedState }
// The encumbered state refers to an encumbrance in position 1, so what follows is wrong.
output { encumberedState }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
this `fails with` "Missing required encumbrance 1 in OUTPUT"
}
// An encumbered state must not reference an index greater than the size of the output states.
assertFailsWith(TransactionVerificationException.TransactionMissingEncumbranceException::class) {
transaction {
input { unencumberedState }
// The encumbered state refers to an encumbrance in position 1, so there should be at least 2 outputs.
output { encumberedState }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
this.verifies()
}
transaction {
input { unencumberedState }
// The encumbered state refers to an encumbrance in position 1, so there should be at least 2 outputs.
output { encumberedState }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
this `fails with` "Missing required encumbrance 1 in OUTPUT"
}
}
@ -95,43 +83,37 @@ class TransactionEncumbranceTests {
@Test
fun testEncumbranceEffects() {
// A transaction containing an input state that is encumbered must fail if the encumbrance is not in the correct position.
assertFailsWith(TransactionVerificationException.TransactionMissingEncumbranceException::class) {
ledger {
unverifiedTransaction {
output("state encumbered by 5pm time-lock") { encumberedState }
output { unencumberedState }
output("5pm time-lock") { FIVE_PM_TIMELOCK }
}
transaction {
input("state encumbered by 5pm time-lock")
input("5pm time-lock")
output { stateWithNewOwner }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
command(DUMMY_PUBKEY_1) { TestTimeLock.Commands.Exit() }
timestamp(FIVE_PM)
this.verifies()
}
ledger {
unverifiedTransaction {
output("state encumbered by 5pm time-lock") { encumberedState }
output { unencumberedState }
output("5pm time-lock") { FIVE_PM_TIMELOCK }
}
transaction {
input("state encumbered by 5pm time-lock")
input("5pm time-lock")
output { stateWithNewOwner }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
timestamp(FIVE_PM)
this `fails with` "Missing required encumbrance 1 in INPUT"
}
}
// A transaction containing an input state that is encumbered must fail if the encumbrance is not in the correct transaction.
assertFailsWith(TransactionVerificationException.TransactionMissingEncumbranceException::class) {
ledger {
unverifiedTransaction {
output("state encumbered by 5pm time-lock") { encumberedState }
output { unencumberedState }
}
unverifiedTransaction {
output("5pm time-lock") { FIVE_PM_TIMELOCK }
}
transaction {
input("state encumbered by 5pm time-lock")
input("5pm time-lock")
output { stateWithNewOwner }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
command(DUMMY_PUBKEY_1) { TestTimeLock.Commands.Exit() }
timestamp(FIVE_PM)
this.verifies()
}
ledger {
unverifiedTransaction {
output("state encumbered by 5pm time-lock") { encumberedState }
output { unencumberedState }
}
unverifiedTransaction {
output("5pm time-lock") { FIVE_PM_TIMELOCK }
}
transaction {
input("state encumbered by 5pm time-lock")
input("5pm time-lock")
output { stateWithNewOwner }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
timestamp(FIVE_PM)
this `fails with` "Missing required encumbrance 1 in INPUT"
}
}
// A transaction with an input state that is encumbered must succeed if the rules of the encumbrance are met.
@ -144,9 +126,8 @@ class TransactionEncumbranceTests {
transaction {
input("state encumbered by 5pm time-lock")
input("5pm time-lock")
output { stateWithNewOwner }
output { unencumberedState }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
command(DUMMY_PUBKEY_1) { TestTimeLock.Commands.Exit() }
timestamp(FIVE_PM)
this.verifies()
}
@ -161,9 +142,8 @@ class TransactionEncumbranceTests {
transaction {
input("state encumbered by 5pm time-lock")
input("5pm time-lock")
output { stateWithNewOwner }
output { unencumberedState }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
command(DUMMY_PUBKEY_1) { TestTimeLock.Commands.Exit() }
timestamp(FOUR_PM)
this `fails with` "the time specified in the time-lock has passed"
}

View File

@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 02da61908148262295ef57918c434b8d
config: 0e8996317ea97eb6f5837df02a05a760
tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@ -1,7 +1,9 @@
Welcome to the Corda repository!
================================
This documentation describes the prototype of a proposed architecture for distributed ledgers.
This documentation describes Corda, a proposed architecture for distributed ledgers, the vision for which is outlined in the `Corda Introductory Whitepaper`_.
.. _`Corda Introductory Whitepaper`: _static/corda-introductory-whitepaper.pdf
The goal of this prototype is to explore fundamentally better designs for distributed ledgers than what presently exists
on the market, tailor made for the needs of the financial industry. We are attempting to prove or disprove the

View File

@ -559,4 +559,106 @@ the sub-protocol to have the tracker it will use be passed in as a parameter. Th
and linked ahead of time.
In future, the progress tracking framework will become a vital part of how exceptions, errors, and other faults are
surfaced to human operators for investigation and resolution.
surfaced to human operators for investigation and resolution.
Unit testing
------------
A protocol can be a fairly complex thing that interacts with many services and other parties over the network. That
means unit testing one requires some infrastructure to provide lightweight mock implementations. The MockNetwork
provides this testing infrastructure layer; you can find this class in the node module
A good example to examine for learning how to unit test protocols is the ``ResolveTransactionsProtocol`` tests. This
protocol takes care of downloading and verifying transaction graphs, with all the needed dependencies. We start
with this basic skeleton:
.. container:: codeset
.. sourcecode:: kotlin
class ResolveTransactionsProtocolTest {
lateinit var net: MockNetwork
lateinit var a: MockNetwork.MockNode
lateinit var b: MockNetwork.MockNode
lateinit var notary: Party
@Before
fun setup() {
net = MockNetwork()
val nodes = net.createSomeNodes()
a = nodes.partyNodes[0]
b = nodes.partyNodes[1]
notary = nodes.notaryNode.info.identity
net.runNetwork()
}
@After
fun tearDown() {
net.stopNodes()
}
}
We create a mock network in our ``@Before`` setup method and create a couple of nodes. We also record the identity
of the notary in our test network, which will come in handy later. We also tidy up when we're done.
Next, we write a test case:
.. container:: codeset
.. sourcecode:: kotlin
@Test
fun resolveFromTwoHashes() {
val (stx1, stx2) = makeTransactions()
val p = ResolveTransactionsProtocol(setOf(stx2.id), a.info.identity)
val future = b.services.startProtocol("resolve", p)
net.runNetwork()
val results = future.get()
assertEquals(listOf(stx1.id, stx2.id), results.map { it.id })
assertEquals(stx1, b.storage.validatedTransactions.getTransaction(stx1.id))
assertEquals(stx2, b.storage.validatedTransactions.getTransaction(stx2.id))
}
We'll take a look at the ``makeTransactions`` function in a moment. For now, it's enough to know that it returns two
``SignedTransaction`` objects, the second of which spends the first. Both transactions are known by node A
but not node B.
The test logic is simple enough: we create the protocol, giving it node A's identity as the target to talk to.
Then we start it on node B and use the ``net.runNetwork()`` method to bounce messages around until things have
settled (i.e. there are no more messages waiting to be delivered). All this is done using an in memory message
routing implementation that is fast to initialise and use. Finally, we obtain the result of the protocol and do
some tests on it. We also check the contents of node B's database to see that the protocol had the intended effect
on the node's persistent state.
Here's what ``makeTransactions`` looks like:
.. container:: codeset
.. sourcecode:: kotlin
private fun makeTransactions(): Pair<SignedTransaction, SignedTransaction> {
// Make a chain of custody of dummy states and insert into node A.
val dummy1: SignedTransaction = DummyContract.generateInitial(MEGA_CORP.ref(1), 0, notary).let {
it.signWith(MEGA_CORP_KEY)
it.signWith(DUMMY_NOTARY_KEY)
it.toSignedTransaction(false)
}
val dummy2: SignedTransaction = DummyContract.move(dummy1.tx.outRef(0), MINI_CORP_PUBKEY).let {
it.signWith(MEGA_CORP_KEY)
it.signWith(DUMMY_NOTARY_KEY)
it.toSignedTransaction()
}
a.services.recordTransactions(dummy1, dummy2)
return Pair(dummy1, dummy2)
}
We're using the ``DummyContract``, a simple test smart contract which stores a single number in its states, along
with ownership and issuer information. You can issue such states, exit them and re-assign ownership (move them).
It doesn't do anything else. This code simply creates a transaction that issues a dummy state (the issuer is
``MEGA_CORP``, a pre-defined unit test identity), signs it with the test notary and MegaCorp keys and then
converts the builder to the final ``SignedTransaction``. It then does so again, but this time instead of issuing
it re-assigns ownership instead. The chain of two transactions is finally committed to node A by sending them
directly to the ``a.services.recordTransaction`` method (note that this method doesn't check the transactions are
valid).
And that's it: you can explore the documentation for the `MockNode API <api/com.r3corda.node.internal.testing/-mock-network/index.html>`_ here.

View File

@ -10,7 +10,7 @@ Writing a contract using clauses
This tutorial will take you through restructuring the commercial paper contract to use clauses. You should have
already completed ":doc:`tutorial-contract`".
Clauses are essentially "mini-contracts" which contain verification logic, and are composed together to form
Clauses are essentially micro-contracts which contain independent verification logic, and are composed together to form
a contract. With appropriate design, they can be made to be reusable, for example issuing contract state objects is
generally the same for all fungible contracts, so a single issuance clause can be shared. This cuts down on scope for
error, and improves consistency of behaviour.
@ -27,25 +27,24 @@ In the case of commercial paper, we have a "Grouping" outermost clause, which wi
Commercial paper class
----------------------
First we need to change the class from implementing ``Contract``, to extend ``ClauseVerifier``. This is an abstract
class which provides a verify() function for us, and requires we provide a property (``clauses``) for the clauses to test,
and a function (``extractCommands``) to extract the applicable commands from the transaction. This is important because
``ClauseVerifier`` checks that no commands applicable to the contract are left unprocessed at the end. The following
examples are trimmed to the modified class definition and added elements, for brevity:
To use the clause verification logic, the contract needs to call the ``verifyClauses()`` function, passing in the transaction,
a list of clauses to verify, and a collection of commands the clauses are expected to handle all of. This list of
commands is important because ``verifyClauses()`` checks that none of the commands are left unprocessed at the end, and
raises an error if they are. The following examples are trimmed to the modified class definition and added elements, for
brevity:
.. container:: codeset
.. sourcecode:: kotlin
class CommercialPaper : ClauseVerifier {
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
class CommercialPaper : Contract {
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper")
override val clauses: List<SingleClause>
get() = throw UnsupportedOperationException("not implemented")
override fun extractCommands(tx: TransactionForContract): List<AuthenticatedObject<CommandData>>
private fun extractCommands(tx: TransactionForContract): List<AuthenticatedObject<CommandData>>
= tx.commands.select<Commands>()
override fun verify(tx: TransactionForContract) = verifyClauses(tx, listOf(Clauses.Group()), extractCommands(tx))
.. sourcecode:: java
public class CommercialPaper implements Contract {
@ -54,11 +53,6 @@ examples are trimmed to the modified class definition and added elements, for br
return SecureHash.Companion.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
}
@Override
public List<SingleClause> getClauses() {
throw UnsupportedOperationException("not implemented");
}
@Override
public Collection<AuthenticatedObject<CommandData>> extractCommands(@NotNull TransactionForContract tx) {
return tx.getCommands()
@ -67,6 +61,11 @@ examples are trimmed to the modified class definition and added elements, for br
.collect(Collectors.toList());
}
@Override
public void verify(@NotNull TransactionForContract tx) throws IllegalArgumentException {
ClauseVerifier.verifyClauses(tx, Collections.singletonList(new Clause.Group()), extractCommands(tx));
}
Clauses
-------

View File

@ -845,6 +845,70 @@ be implemented once in a separate contract, with the controlling data being held
Future versions of the prototype will explore these concepts in more depth.
Encumbrances
------------
All contract states may be *encumbered* by up to one other state, which we call an **encumbrance**.
The encumbrance state, if present, forces additional controls over the encumbered state, since the encumbrance state contract
will also be verified during the execution of the transaction. For example, a contract state could be encumbered
with a time-lock contract state; the state is then only processable in a transaction that verifies that the time
specified in the encumbrance time-lock has passed.
The encumbered state refers to its encumbrance by index, and the referred encumbrance state
is an output state in a particular position on the same transaction that created the encumbered state. Note that an
encumbered state that is being consumed must have its encumbrance consumed in the same transaction, otherwise the
transaction is not valid.
The encumbrance reference is optional in the ``ContractState`` interface:
.. container:: codeset
.. sourcecode:: kotlin
val encumbrance: Int? get() = null
.. sourcecode:: java
@Nullable
@Override
public Integer getEncumbrance() {
return null;
}
The time-lock contract mentioned above can be implemented very simply:
.. container:: codeset
.. sourcecode:: kotlin
class TestTimeLock : Contract {
...
override fun verify(tx: TransactionForContract) {
val timestamp: Timestamp? = tx.timestamp
...
requireThat {
"the time specified in the time-lock has passed" by
(time >= tx.inputs.filterIsInstance<TestTimeLock.State>().single().validFrom)
}
}
...
}
We can then set up an encumbered state:
.. container:: codeset
.. sourcecode:: kotlin
val encumberedState = Cash.State(amount = 1000.DOLLARS `issued by` defaultIssuer, owner = DUMMY_PUBKEY_1, encumbrance = 1)
val fourPmTimelock = TestTimeLock.State(Instant.parse("2015-04-17T16:00:00.00Z"))
When we construct a transaction that generates the encumbered state, we must place the encumbrance in the corresponding output
position of that transaction. And when we subsequently consume that encumbered state, the same encumbrance state must be
available somewhere within the input set of states.
Clauses
-------

View File

@ -85,10 +85,6 @@ div.sphinxsidebar #searchbox input[type="text"] {
width: 170px;
}
div.sphinxsidebar #searchbox input[type="submit"] {
width: 30px;
}
img {
border: 0;
max-width: 100%;

View File

@ -164,7 +164,7 @@
<div class="section" id="welcome-to-the-corda-repository">
<h1>Welcome to the Corda repository!<a class="headerlink" href="#welcome-to-the-corda-repository" title="Permalink to this headline"></a></h1>
<p>This documentation describes the prototype of a proposed architecture for distributed ledgers.</p>
<p>This documentation describes Corda, a proposed architecture for distributed ledgers, the vision for which is outlined in the <a class="reference external" href="_static/corda-introductory-whitepaper.pdf">Corda Introductory Whitepaper</a>.</p>
<p>The goal of this prototype is to explore fundamentally better designs for distributed ledgers than what presently exists
on the market, tailor made for the needs of the financial industry. We are attempting to prove or disprove the
following hypothesis:</p>
@ -275,6 +275,7 @@ following hypothesis:</p>
<li class="toctree-l2"><a class="reference internal" href="tutorial-contract.html#how-to-test-your-contract">How to test your contract</a></li>
<li class="toctree-l2"><a class="reference internal" href="tutorial-contract.html#adding-a-generation-api-to-your-contract">Adding a generation API to your contract</a></li>
<li class="toctree-l2"><a class="reference internal" href="tutorial-contract.html#non-asset-oriented-based-smart-contracts">Non-asset-oriented based smart contracts</a></li>
<li class="toctree-l2"><a class="reference internal" href="tutorial-contract.html#encumbrances">Encumbrances</a></li>
<li class="toctree-l2"><a class="reference internal" href="tutorial-contract.html#clauses">Clauses</a></li>
</ul>
</li>
@ -299,6 +300,7 @@ following hypothesis:</p>
<li class="toctree-l2"><a class="reference internal" href="protocol-state-machines.html#implementing-the-seller">Implementing the seller</a></li>
<li class="toctree-l2"><a class="reference internal" href="protocol-state-machines.html#implementing-the-buyer">Implementing the buyer</a></li>
<li class="toctree-l2"><a class="reference internal" href="protocol-state-machines.html#progress-tracking">Progress tracking</a></li>
<li class="toctree-l2"><a class="reference internal" href="protocol-state-machines.html#unit-testing">Unit testing</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="oracles.html">Writing oracle services</a><ul>

View File

@ -116,6 +116,7 @@
<li class="toctree-l2"><a class="reference internal" href="#implementing-the-seller">Implementing the seller</a></li>
<li class="toctree-l2"><a class="reference internal" href="#implementing-the-buyer">Implementing the buyer</a></li>
<li class="toctree-l2"><a class="reference internal" href="#progress-tracking">Progress tracking</a></li>
<li class="toctree-l2"><a class="reference internal" href="#unit-testing">Unit testing</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="oracles.html">Writing oracle services</a></li>
@ -696,6 +697,96 @@ and linked ahead of time.</p>
<p>In future, the progress tracking framework will become a vital part of how exceptions, errors, and other faults are
surfaced to human operators for investigation and resolution.</p>
</div>
<div class="section" id="unit-testing">
<h2>Unit testing<a class="headerlink" href="#unit-testing" title="Permalink to this headline"></a></h2>
<p>A protocol can be a fairly complex thing that interacts with many services and other parties over the network. That
means unit testing one requires some infrastructure to provide lightweight mock implementations. The MockNetwork
provides this testing infrastructure layer; you can find this class in the node module</p>
<p>A good example to examine for learning how to unit test protocols is the <code class="docutils literal"><span class="pre">ResolveTransactionsProtocol</span></code> tests. This
protocol takes care of downloading and verifying transaction graphs, with all the needed dependencies. We start
with this basic skeleton:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ResolveTransactionsProtocolTest</span> <span class="p">{</span>
<span class="k">lateinit</span> <span class="k">var</span> <span class="py">net</span><span class="p">:</span> <span class="n">MockNetwork</span>
<span class="k">lateinit</span> <span class="k">var</span> <span class="py">a</span><span class="p">:</span> <span class="n">MockNetwork</span><span class="p">.</span><span class="n">MockNode</span>
<span class="k">lateinit</span> <span class="k">var</span> <span class="py">b</span><span class="p">:</span> <span class="n">MockNetwork</span><span class="p">.</span><span class="n">MockNode</span>
<span class="k">lateinit</span> <span class="k">var</span> <span class="py">notary</span><span class="p">:</span> <span class="n">Party</span>
<span class="n">@Before</span>
<span class="k">fun</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
<span class="n">net</span> <span class="p">=</span> <span class="n">MockNetwork</span><span class="p">()</span>
<span class="k">val</span> <span class="py">nodes</span> <span class="p">=</span> <span class="n">net</span><span class="p">.</span><span class="n">createSomeNodes</span><span class="p">()</span>
<span class="n">a</span> <span class="p">=</span> <span class="n">nodes</span><span class="p">.</span><span class="n">partyNodes</span><span class="p">[</span><span class="m">0</span><span class="p">]</span>
<span class="n">b</span> <span class="p">=</span> <span class="n">nodes</span><span class="p">.</span><span class="n">partyNodes</span><span class="p">[</span><span class="m">1</span><span class="p">]</span>
<span class="n">notary</span> <span class="p">=</span> <span class="n">nodes</span><span class="p">.</span><span class="n">notaryNode</span><span class="p">.</span><span class="n">info</span><span class="p">.</span><span class="n">identity</span>
<span class="n">net</span><span class="p">.</span><span class="n">runNetwork</span><span class="p">()</span>
<span class="p">}</span>
<span class="n">@After</span>
<span class="k">fun</span> <span class="nf">tearDown</span><span class="p">()</span> <span class="p">{</span>
<span class="n">net</span><span class="p">.</span><span class="n">stopNodes</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</div>
</div>
<p>We create a mock network in our <code class="docutils literal"><span class="pre">&#64;Before</span></code> setup method and create a couple of nodes. We also record the identity
of the notary in our test network, which will come in handy later. We also tidy up when we&#8217;re done.</p>
<p>Next, we write a test case:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre><span></span>@Test
fun resolveFromTwoHashes() {
val (stx1, stx2) = makeTransactions()
val p = ResolveTransactionsProtocol(setOf(stx2.id), a.info.identity)
val future = b.services.startProtocol(&quot;resolve&quot;, p)
net.runNetwork()
val results = future.get()
assertEquals(listOf(stx1.id, stx2.id), results.map { it.id })
assertEquals(stx1, b.storage.validatedTransactions.getTransaction(stx1.id))
assertEquals(stx2, b.storage.validatedTransactions.getTransaction(stx2.id))
}
</pre></div>
</div>
</div>
<p>We&#8217;ll take a look at the <code class="docutils literal"><span class="pre">makeTransactions</span></code> function in a moment. For now, it&#8217;s enough to know that it returns two
<code class="docutils literal"><span class="pre">SignedTransaction</span></code> objects, the second of which spends the first. Both transactions are known by node A
but not node B.</p>
<p>The test logic is simple enough: we create the protocol, giving it node A&#8217;s identity as the target to talk to.
Then we start it on node B and use the <code class="docutils literal"><span class="pre">net.runNetwork()</span></code> method to bounce messages around until things have
settled (i.e. there are no more messages waiting to be delivered). All this is done using an in memory message
routing implementation that is fast to initialise and use. Finally, we obtain the result of the protocol and do
some tests on it. We also check the contents of node B&#8217;s database to see that the protocol had the intended effect
on the node&#8217;s persistent state.</p>
<p>Here&#8217;s what <code class="docutils literal"><span class="pre">makeTransactions</span></code> looks like:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">private</span> <span class="k">fun</span> <span class="nf">makeTransactions</span><span class="p">():</span> <span class="n">Pair</span><span class="p">&lt;</span><span class="n">SignedTransaction</span><span class="p">,</span> <span class="n">SignedTransaction</span><span class="p">&gt;</span> <span class="p">{</span>
<span class="c1">// Make a chain of custody of dummy states and insert into node A.</span>
<span class="k">val</span> <span class="py">dummy1</span><span class="p">:</span> <span class="n">SignedTransaction</span> <span class="p">=</span> <span class="n">DummyContract</span><span class="p">.</span><span class="n">generateInitial</span><span class="p">(</span><span class="n">MEGA_CORP</span><span class="p">.</span><span class="n">ref</span><span class="p">(</span><span class="m">1</span><span class="p">),</span> <span class="m">0</span><span class="p">,</span> <span class="n">notary</span><span class="p">).</span><span class="n">let</span> <span class="p">{</span>
<span class="n">it</span><span class="p">.</span><span class="n">signWith</span><span class="p">(</span><span class="n">MEGA_CORP_KEY</span><span class="p">)</span>
<span class="n">it</span><span class="p">.</span><span class="n">signWith</span><span class="p">(</span><span class="n">DUMMY_NOTARY_KEY</span><span class="p">)</span>
<span class="n">it</span><span class="p">.</span><span class="n">toSignedTransaction</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">val</span> <span class="py">dummy2</span><span class="p">:</span> <span class="n">SignedTransaction</span> <span class="p">=</span> <span class="n">DummyContract</span><span class="p">.</span><span class="n">move</span><span class="p">(</span><span class="n">dummy1</span><span class="p">.</span><span class="n">tx</span><span class="p">.</span><span class="n">outRef</span><span class="p">(</span><span class="m">0</span><span class="p">),</span> <span class="n">MINI_CORP_PUBKEY</span><span class="p">).</span><span class="n">let</span> <span class="p">{</span>
<span class="n">it</span><span class="p">.</span><span class="n">signWith</span><span class="p">(</span><span class="n">MEGA_CORP_KEY</span><span class="p">)</span>
<span class="n">it</span><span class="p">.</span><span class="n">signWith</span><span class="p">(</span><span class="n">DUMMY_NOTARY_KEY</span><span class="p">)</span>
<span class="n">it</span><span class="p">.</span><span class="n">toSignedTransaction</span><span class="p">()</span>
<span class="p">}</span>
<span class="n">a</span><span class="p">.</span><span class="n">services</span><span class="p">.</span><span class="n">recordTransactions</span><span class="p">(</span><span class="n">dummy1</span><span class="p">,</span> <span class="n">dummy2</span><span class="p">)</span>
<span class="k">return</span> <span class="n">Pair</span><span class="p">(</span><span class="n">dummy1</span><span class="p">,</span> <span class="n">dummy2</span><span class="p">)</span>
<span class="p">}</span>
</pre></div>
</div>
</div>
<p>We&#8217;re using the <code class="docutils literal"><span class="pre">DummyContract</span></code>, a simple test smart contract which stores a single number in its states, along
with ownership and issuer information. You can issue such states, exit them and re-assign ownership (move them).
It doesn&#8217;t do anything else. This code simply creates a transaction that issues a dummy state (the issuer is
<code class="docutils literal"><span class="pre">MEGA_CORP</span></code>, a pre-defined unit test identity), signs it with the test notary and MegaCorp keys and then
converts the builder to the final <code class="docutils literal"><span class="pre">SignedTransaction</span></code>. It then does so again, but this time instead of issuing
it re-assigns ownership instead. The chain of two transactions is finally committed to node A by sending them
directly to the <code class="docutils literal"><span class="pre">a.services.recordTransaction</span></code> method (note that this method doesn&#8217;t check the transactions are
valid).</p>
<p>And that&#8217;s it: you can explore the documentation for the <a class="reference external" href="api/com.r3corda.node.internal.testing/-mock-network/index.html">MockNode API</a> here.</p>
</div>
</div>

File diff suppressed because one or more lines are too long

View File

@ -174,7 +174,7 @@
<h1>Writing a contract using clauses<a class="headerlink" href="#writing-a-contract-using-clauses" title="Permalink to this headline"></a></h1>
<p>This tutorial will take you through restructuring the commercial paper contract to use clauses. You should have
already completed &#8220;<a class="reference internal" href="tutorial-contract.html"><span class="doc">Writing a contract</span></a>&#8221;.</p>
<p>Clauses are essentially &#8220;mini-contracts&#8221; which contain verification logic, and are composed together to form
<p>Clauses are essentially micro-contracts which contain independent verification logic, and are composed together to form
a contract. With appropriate design, they can be made to be reusable, for example issuing contract state objects is
generally the same for all fungible contracts, so a single issuance clause can be shared. This cuts down on scope for
error, and improves consistency of behaviour.</p>
@ -197,20 +197,19 @@ In the case of commercial paper, we have a &#8220;Grouping&#8221; outermost clau
</div></blockquote>
<div class="section" id="commercial-paper-class">
<h2>Commercial paper class<a class="headerlink" href="#commercial-paper-class" title="Permalink to this headline"></a></h2>
<p>First we need to change the class from implementing <code class="docutils literal"><span class="pre">Contract</span></code>, to extend <code class="docutils literal"><span class="pre">ClauseVerifier</span></code>. This is an abstract
class which provides a verify() function for us, and requires we provide a property (<code class="docutils literal"><span class="pre">clauses</span></code>) for the clauses to test,
and a function (<code class="docutils literal"><span class="pre">extractCommands</span></code>) to extract the applicable commands from the transaction. This is important because
<code class="docutils literal"><span class="pre">ClauseVerifier</span></code> checks that no commands applicable to the contract are left unprocessed at the end. The following
examples are trimmed to the modified class definition and added elements, for brevity:</p>
<p>To use the clause verification logic, the contract needs to call the <code class="docutils literal"><span class="pre">verifyClauses()</span></code> function, passing in the transaction,
a list of clauses to verify, and a collection of commands the clauses are expected to handle all of. This list of
commands is important because <code class="docutils literal"><span class="pre">verifyClauses()</span></code> checks that none of the commands are left unprocessed at the end, and
raises an error if they are. The following examples are trimmed to the modified class definition and added elements, for
brevity:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CommercialPaper</span> <span class="p">:</span> <span class="n">ClauseVerifier</span> <span class="p">{</span>
<span class="k">override</span> <span class="k">val</span> <span class="py">legalContractReference</span><span class="p">:</span> <span class="n">SecureHash</span> <span class="p">=</span> <span class="n">SecureHash</span><span class="p">.</span><span class="n">sha256</span><span class="p">(</span><span class="s">&quot;https://en.wikipedia.org/wiki/Commercial_paper&quot;</span><span class="p">);</span>
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CommercialPaper</span> <span class="p">:</span> <span class="n">Contract</span> <span class="p">{</span>
<span class="k">override</span> <span class="k">val</span> <span class="py">legalContractReference</span><span class="p">:</span> <span class="n">SecureHash</span> <span class="p">=</span> <span class="n">SecureHash</span><span class="p">.</span><span class="n">sha256</span><span class="p">(</span><span class="s">&quot;https://en.wikipedia.org/wiki/Commercial_paper&quot;</span><span class="p">)</span>
<span class="k">override</span> <span class="k">val</span> <span class="py">clauses</span><span class="p">:</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">SingleClause</span><span class="p">&gt;</span>
<span class="k">get</span><span class="p">()</span> <span class="p">=</span> <span class="k">throw</span> <span class="n">UnsupportedOperationException</span><span class="p">(</span><span class="s">&quot;not implemented&quot;</span><span class="p">)</span>
<span class="k">override</span> <span class="k">fun</span> <span class="nf">extractCommands</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionForContract</span><span class="p">):</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">AuthenticatedObject</span><span class="p">&lt;</span><span class="n">CommandData</span><span class="p">&gt;&gt;</span>
<span class="k">private</span> <span class="k">fun</span> <span class="nf">extractCommands</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionForContract</span><span class="p">):</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">AuthenticatedObject</span><span class="p">&lt;</span><span class="n">CommandData</span><span class="p">&gt;&gt;</span>
<span class="p">=</span> <span class="n">tx</span><span class="p">.</span><span class="n">commands</span><span class="p">.</span><span class="n">select</span><span class="p">&lt;</span><span class="n">Commands</span><span class="p">&gt;()</span>
<span class="k">override</span> <span class="k">fun</span> <span class="nf">verify</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionForContract</span><span class="p">)</span> <span class="p">=</span> <span class="n">verifyClauses</span><span class="p">(</span><span class="n">tx</span><span class="p">,</span> <span class="n">listOf</span><span class="p">(</span><span class="n">Clauses</span><span class="p">.</span><span class="n">Group</span><span class="p">()),</span> <span class="n">extractCommands</span><span class="p">(</span><span class="n">tx</span><span class="p">))</span>
</pre></div>
</div>
<div class="highlight-java"><div class="highlight"><pre><span></span><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CommercialPaper</span> <span class="kd">implements</span> <span class="n">Contract</span> <span class="o">{</span>
@ -219,11 +218,6 @@ examples are trimmed to the modified class definition and added elements, for br
<span class="k">return</span> <span class="n">SecureHash</span><span class="o">.</span><span class="na">Companion</span><span class="o">.</span><span class="na">sha256</span><span class="o">(</span><span class="s">&quot;https://en.wikipedia.org/wiki/Commercial_paper&quot;</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">SingleClause</span><span class="o">&gt;</span> <span class="nf">getClauses</span><span class="o">()</span> <span class="o">{</span>
<span class="k">throw</span> <span class="n">UnsupportedOperationException</span><span class="o">(</span><span class="s">&quot;not implemented&quot;</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">Collection</span><span class="o">&lt;</span><span class="n">AuthenticatedObject</span><span class="o">&lt;</span><span class="n">CommandData</span><span class="o">&gt;&gt;</span> <span class="nf">extractCommands</span><span class="o">(</span><span class="nd">@NotNull</span> <span class="n">TransactionForContract</span> <span class="n">tx</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">tx</span><span class="o">.</span><span class="na">getCommands</span><span class="o">()</span>
@ -231,6 +225,11 @@ examples are trimmed to the modified class definition and added elements, for br
<span class="o">.</span><span class="na">filter</span><span class="o">((</span><span class="n">AuthenticatedObject</span><span class="o">&lt;</span><span class="n">CommandData</span><span class="o">&gt;</span> <span class="n">command</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="k">return</span> <span class="n">command</span><span class="o">.</span><span class="na">getValue</span><span class="o">()</span> <span class="k">instanceof</span> <span class="n">Commands</span><span class="o">;</span> <span class="o">})</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="n">Collectors</span><span class="o">.</span><span class="na">toList</span><span class="o">());</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">verify</span><span class="o">(</span><span class="nd">@NotNull</span> <span class="n">TransactionForContract</span> <span class="n">tx</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">IllegalArgumentException</span> <span class="o">{</span>
<span class="n">ClauseVerifier</span><span class="o">.</span><span class="na">verifyClauses</span><span class="o">(</span><span class="n">tx</span><span class="o">,</span> <span class="n">Collections</span><span class="o">.</span><span class="na">singletonList</span><span class="o">(</span><span class="k">new</span> <span class="n">Clause</span><span class="o">.</span><span class="na">Group</span><span class="o">()),</span> <span class="n">extractCommands</span><span class="o">(</span><span class="n">tx</span><span class="o">));</span>
<span class="o">}</span>
</pre></div>
</div>
</div>

View File

@ -114,6 +114,7 @@
<li class="toctree-l2"><a class="reference internal" href="#how-to-test-your-contract">How to test your contract</a></li>
<li class="toctree-l2"><a class="reference internal" href="#adding-a-generation-api-to-your-contract">Adding a generation API to your contract</a></li>
<li class="toctree-l2"><a class="reference internal" href="#non-asset-oriented-based-smart-contracts">Non-asset-oriented based smart contracts</a></li>
<li class="toctree-l2"><a class="reference internal" href="#encumbrances">Encumbrances</a></li>
<li class="toctree-l2"><a class="reference internal" href="#clauses">Clauses</a></li>
</ul>
</li>
@ -924,6 +925,58 @@ The logic that implements measurement of the threshold, different signing combin
be implemented once in a separate contract, with the controlling data being held in the named state.</p>
<p>Future versions of the prototype will explore these concepts in more depth.</p>
</div>
<div class="section" id="encumbrances">
<h2>Encumbrances<a class="headerlink" href="#encumbrances" title="Permalink to this headline"></a></h2>
<p>All contract states may be <em>encumbered</em> by up to one other state, which we call an <strong>encumbrance</strong>.</p>
<p>The encumbrance state, if present, forces additional controls over the encumbered state, since the encumbrance state contract
will also be verified during the execution of the transaction. For example, a contract state could be encumbered
with a time-lock contract state; the state is then only processable in a transaction that verifies that the time
specified in the encumbrance time-lock has passed.</p>
<p>The encumbered state refers to its encumbrance by index, and the referred encumbrance state
is an output state in a particular position on the same transaction that created the encumbered state. Note that an
encumbered state that is being consumed must have its encumbrance consumed in the same transaction, otherwise the
transaction is not valid.</p>
<p>The encumbrance reference is optional in the <code class="docutils literal"><span class="pre">ContractState</span></code> interface:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">val</span> <span class="py">encumbrance</span><span class="p">:</span> <span class="n">Int</span><span class="p">?</span> <span class="k">get</span><span class="p">()</span> <span class="p">=</span> <span class="k">null</span>
</pre></div>
</div>
<div class="highlight-java"><div class="highlight"><pre><span></span><span class="nd">@Nullable</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="n">Integer</span> <span class="nf">getEncumbrance</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
</pre></div>
</div>
</div>
<p>The time-lock contract mentioned above can be implemented very simply:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">TestTimeLock</span> <span class="p">:</span> <span class="n">Contract</span> <span class="p">{</span>
<span class="p">...</span>
<span class="k">override</span> <span class="k">fun</span> <span class="nf">verify</span><span class="p">(</span><span class="n">tx</span><span class="p">:</span> <span class="n">TransactionForContract</span><span class="p">)</span> <span class="p">{</span>
<span class="k">val</span> <span class="py">timestamp</span><span class="p">:</span> <span class="n">Timestamp</span><span class="p">?</span> <span class="p">=</span> <span class="n">tx</span><span class="p">.</span><span class="n">timestamp</span>
<span class="p">...</span>
<span class="n">requireThat</span> <span class="p">{</span>
<span class="s">&quot;the time specified in the time-lock has passed&quot;</span> <span class="k">by</span>
<span class="p">(</span><span class="n">time</span> <span class="p">&gt;=</span> <span class="n">tx</span><span class="p">.</span><span class="n">inputs</span><span class="p">.</span><span class="n">filterIsInstance</span><span class="p">&lt;</span><span class="n">TestTimeLock</span><span class="p">.</span><span class="n">State</span><span class="p">&gt;().</span><span class="n">single</span><span class="p">().</span><span class="n">validFrom</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">...</span>
<span class="p">}</span>
</pre></div>
</div>
</div>
<p>We can then set up an encumbered state:</p>
<div class="codeset container">
<div class="highlight-kotlin"><div class="highlight"><pre><span></span>val encumberedState = Cash.State(amount = 1000.DOLLARS `issued by` defaultIssuer, owner = DUMMY_PUBKEY_1, encumbrance = 1)
val fourPmTimelock = TestTimeLock.State(Instant.parse(&quot;2015-04-17T16:00:00.00Z&quot;))
</pre></div>
</div>
</div>
<p>When we construct a transaction that generates the encumbered state, we must place the encumbrance in the corresponding output
position of that transaction. And when we subsequently consume that encumbered state, the same encumbrance state must be
available somewhere within the input set of states.</p>
</div>
<div class="section" id="clauses">
<h2>Clauses<a class="headerlink" href="#clauses" title="Permalink to this headline"></a></h2>
<p>Instead of structuring contracts as a single entity, they can be broken down into reusable chunks known as clauses.

View File

@ -845,6 +845,70 @@ be implemented once in a separate contract, with the controlling data being held
Future versions of the prototype will explore these concepts in more depth.
Encumbrances
------------
All contract states may be *encumbered* by up to one other state, which we call an **encumbrance**.
The encumbrance state, if present, forces additional controls over the encumbered state, since the encumbrance state contract
will also be verified during the execution of the transaction. For example, a contract state could be encumbered
with a time-lock contract state; the state is then only processable in a transaction that verifies that the time
specified in the encumbrance time-lock has passed.
The encumbered state refers to its encumbrance by index, and the referred encumbrance state
is an output state in a particular position on the same transaction that created the encumbered state. Note that an
encumbered state that is being consumed must have its encumbrance consumed in the same transaction, otherwise the
transaction is not valid.
The encumbrance reference is optional in the ``ContractState`` interface:
.. container:: codeset
.. sourcecode:: kotlin
val encumbrance: Int? get() = null
.. sourcecode:: java
@Nullable
@Override
public Integer getEncumbrance() {
return null;
}
The time-lock contract mentioned above can be implemented very simply:
.. container:: codeset
.. sourcecode:: kotlin
class TestTimeLock : Contract {
...
override fun verify(tx: TransactionForContract) {
val timestamp: Timestamp? = tx.timestamp
...
requireThat {
"the time specified in the time-lock has passed" by
(time >= tx.inputs.filterIsInstance<TestTimeLock.State>().single().validFrom)
}
}
...
}
We can then set up an encumbered state:
.. container:: codeset
.. sourcecode:: kotlin
val encumberedState = Cash.State(amount = 1000.DOLLARS `issued by` defaultIssuer, owner = DUMMY_PUBKEY_1, encumbrance = 1)
val fourPmTimelock = TestTimeLock.State(Instant.parse("2015-04-17T16:00:00.00Z"))
When we construct a transaction that generates the encumbered state, we must place the encumbrance in the corresponding output
position of that transaction. And when we subsequently consume that encumbered state, the same encumbrance state must be
available somewhere within the input set of states.
Clauses
-------