mirror of
https://github.com/corda/corda.git
synced 2025-02-21 09:51:57 +00:00
updated following review
This commit is contained in:
parent
ee65d4490b
commit
cdb2c3efa6
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
2
docs/build/html/.buildinfo
vendored
2
docs/build/html/.buildinfo
vendored
@ -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
|
||||
|
4
docs/build/html/_sources/index.txt
vendored
4
docs/build/html/_sources/index.txt
vendored
@ -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
|
||||
|
104
docs/build/html/_sources/protocol-state-machines.txt
vendored
104
docs/build/html/_sources/protocol-state-machines.txt
vendored
@ -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.
|
||||
|
@ -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
|
||||
-------
|
||||
|
||||
|
64
docs/build/html/_sources/tutorial-contract.txt
vendored
64
docs/build/html/_sources/tutorial-contract.txt
vendored
@ -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
|
||||
-------
|
||||
|
||||
|
4
docs/build/html/_static/basic.css
vendored
4
docs/build/html/_static/basic.css
vendored
@ -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%;
|
||||
|
4
docs/build/html/index.html
vendored
4
docs/build/html/index.html
vendored
@ -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>
|
||||
|
91
docs/build/html/protocol-state-machines.html
vendored
91
docs/build/html/protocol-state-machines.html
vendored
@ -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">@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’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("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))
|
||||
}
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<p>We’ll take a look at the <code class="docutils literal"><span class="pre">makeTransactions</span></code> function in a moment. For now, it’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’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’s database to see that the protocol had the intended effect
|
||||
on the node’s persistent state.</p>
|
||||
<p>Here’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"><</span><span class="n">SignedTransaction</span><span class="p">,</span> <span class="n">SignedTransaction</span><span class="p">></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’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’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’t check the transactions are
|
||||
valid).</p>
|
||||
<p>And that’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>
|
||||
|
||||
|
||||
|
2
docs/build/html/searchindex.js
vendored
2
docs/build/html/searchindex.js
vendored
File diff suppressed because one or more lines are too long
33
docs/build/html/tutorial-contract-clauses.html
vendored
33
docs/build/html/tutorial-contract-clauses.html
vendored
@ -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 “<a class="reference internal" href="tutorial-contract.html"><span class="doc">Writing a contract</span></a>”.</p>
|
||||
<p>Clauses are essentially “mini-contracts” 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 “Grouping” 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">"https://en.wikipedia.org/wiki/Commercial_paper"</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">"https://en.wikipedia.org/wiki/Commercial_paper"</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"><</span><span class="n">SingleClause</span><span class="p">></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">"not implemented"</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"><</span><span class="n">AuthenticatedObject</span><span class="p"><</span><span class="n">CommandData</span><span class="p">>></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"><</span><span class="n">AuthenticatedObject</span><span class="p"><</span><span class="n">CommandData</span><span class="p">>></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"><</span><span class="n">Commands</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="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">"https://en.wikipedia.org/wiki/Commercial_paper"</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"><</span><span class="n">SingleClause</span><span class="o">></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">"not implemented"</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"><</span><span class="n">AuthenticatedObject</span><span class="o"><</span><span class="n">CommandData</span><span class="o">>></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"><</span><span class="n">CommandData</span><span class="o">></span> <span class="n">command</span><span class="o">)</span> <span class="o">-></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>
|
||||
|
53
docs/build/html/tutorial-contract.html
vendored
53
docs/build/html/tutorial-contract.html
vendored
@ -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">"the time specified in the time-lock has passed"</span> <span class="k">by</span>
|
||||
<span class="p">(</span><span class="n">time</span> <span class="p">>=</span> <span class="n">tx</span><span class="p">.</span><span class="n">inputs</span><span class="p">.</span><span class="n">filterIsInstance</span><span class="p"><</span><span class="n">TestTimeLock</span><span class="p">.</span><span class="n">State</span><span class="p">>().</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("2015-04-17T16:00:00.00Z"))
|
||||
</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.
|
||||
|
@ -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
|
||||
-------
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user