mirror of
https://github.com/corda/corda.git
synced 2025-06-15 21:58:17 +00:00
updated following review
This commit is contained in:
@ -122,8 +122,11 @@ interface ContractState {
|
|||||||
*
|
*
|
||||||
* The encumbered state refers to another by index, and the referred encumbrance state
|
* 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
|
* 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
|
* implementation would be encumber by reference to a StateRef., which would allow the specification of encumbrance
|
||||||
* in a prior transaction.
|
* 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
|
val encumbrance: Int? get() = null
|
||||||
}
|
}
|
||||||
|
@ -83,17 +83,21 @@ sealed class TransactionType {
|
|||||||
encumberedInput ->
|
encumberedInput ->
|
||||||
if (tx.inputs.none { it.ref.txhash == encumberedInput.ref.txhash &&
|
if (tx.inputs.none { it.ref.txhash == encumberedInput.ref.txhash &&
|
||||||
it.ref.index == encumberedInput.state.data.encumbrance }) {
|
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
|
// Check that, in the outputs, an encumbered state does not refer to itself as the encumbrance,
|
||||||
// can contain the encumbrance.
|
// and that the number of outputs can contain the encumbrance.
|
||||||
tx.outputs.forEachIndexed { i, it ->
|
for ((i, output) in tx.outputs.withIndex() ) {
|
||||||
if (it.data.encumbrance != null) {
|
val encumbranceIndex = output.data.encumbrance ?: continue
|
||||||
if (it.data.encumbrance == i || it.data.encumbrance!! >= tx.outputs.size) {
|
if (encumbranceIndex == i || encumbranceIndex >= tx.outputs.size) {
|
||||||
throw TransactionVerificationException.TransactionMissingEncumbranceException(tx)
|
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) {
|
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}"
|
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()
|
val TEST_TIMELOCK_ID = TransactionEncumbranceTests.TestTimeLock()
|
||||||
|
|
||||||
class TransactionEncumbranceTests {
|
class TransactionEncumbranceTests {
|
||||||
val defaultRef = OpaqueBytes(ByteArray(1, {1}))
|
val defaultIssuer = MEGA_CORP.ref(1)
|
||||||
val defaultIssuer = MEGA_CORP.ref(defaultRef)
|
|
||||||
val encumberedState = Cash.State(
|
val encumberedState = Cash.State(
|
||||||
amount = 1000.DOLLARS `issued by` defaultIssuer,
|
amount = 1000.DOLLARS `issued by` defaultIssuer,
|
||||||
owner = DUMMY_PUBKEY_1,
|
owner = DUMMY_PUBKEY_1,
|
||||||
@ -34,13 +33,10 @@ class TransactionEncumbranceTests {
|
|||||||
override val legalContractReference = SecureHash.sha256("TestTimeLock")
|
override val legalContractReference = SecureHash.sha256("TestTimeLock")
|
||||||
override fun verify(tx: TransactionForContract) {
|
override fun verify(tx: TransactionForContract) {
|
||||||
val timestamp: Timestamp? = tx.timestamp
|
val timestamp: Timestamp? = tx.timestamp
|
||||||
val timeLockCommand = tx.commands.select<TestTimeLock.Commands>().first()
|
val time = timestamp?.before ?: throw IllegalArgumentException("Transactions containing time-locks must be timestamped")
|
||||||
if (timeLockCommand.value is TestTimeLock.Commands.Exit) {
|
requireThat {
|
||||||
val time = timestamp?.before ?: throw IllegalArgumentException("Transactions containing time-locks must be timestamped")
|
"the time specified in the time-lock has passed" by
|
||||||
requireThat {
|
(time >= tx.inputs.filterIsInstance<TestTimeLock.State>().single().validFrom)
|
||||||
"the time specified in the time-lock has passed" by
|
|
||||||
(time >= tx.inputs.filterIsInstance<TestTimeLock.State>().first().validFrom)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data class State(
|
data class State(
|
||||||
@ -50,10 +46,6 @@ class TransactionEncumbranceTests {
|
|||||||
get() = throw UnsupportedOperationException()
|
get() = throw UnsupportedOperationException()
|
||||||
override val contract: Contract = TEST_TIMELOCK_ID
|
override val contract: Contract = TEST_TIMELOCK_ID
|
||||||
}
|
}
|
||||||
interface Commands : CommandData {
|
|
||||||
class Issue : TypeOnlyCommandData(), Commands
|
|
||||||
class Exit : TypeOnlyCommandData(), Commands
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -68,26 +60,22 @@ class TransactionEncumbranceTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// An encumbered state must not be encumbered by itself.
|
// An encumbered state must not be encumbered by itself.
|
||||||
assertFailsWith(TransactionVerificationException.TransactionMissingEncumbranceException::class) {
|
transaction {
|
||||||
transaction {
|
input { unencumberedState }
|
||||||
input { unencumberedState }
|
input { unencumberedState }
|
||||||
input { unencumberedState }
|
output { unencumberedState }
|
||||||
output { unencumberedState }
|
// The encumbered state refers to an encumbrance in position 1, so what follows is wrong.
|
||||||
// The encumbered state refers to an encumbrance in position 1, so what follows is wrong.
|
output { encumberedState }
|
||||||
output { encumberedState }
|
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
this `fails with` "Missing required encumbrance 1 in OUTPUT"
|
||||||
this.verifies()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// An encumbered state must not reference an index greater than the size of the output states.
|
// An encumbered state must not reference an index greater than the size of the output states.
|
||||||
assertFailsWith(TransactionVerificationException.TransactionMissingEncumbranceException::class) {
|
transaction {
|
||||||
transaction {
|
input { unencumberedState }
|
||||||
input { unencumberedState }
|
// The encumbered state refers to an encumbrance in position 1, so there should be at least 2 outputs.
|
||||||
// The encumbered state refers to an encumbrance in position 1, so there should be at least 2 outputs.
|
output { encumberedState }
|
||||||
output { encumberedState }
|
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
this `fails with` "Missing required encumbrance 1 in OUTPUT"
|
||||||
this.verifies()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -95,43 +83,37 @@ class TransactionEncumbranceTests {
|
|||||||
@Test
|
@Test
|
||||||
fun testEncumbranceEffects() {
|
fun testEncumbranceEffects() {
|
||||||
// A transaction containing an input state that is encumbered must fail if the encumbrance is not in the correct position.
|
// 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 {
|
||||||
ledger {
|
unverifiedTransaction {
|
||||||
unverifiedTransaction {
|
output("state encumbered by 5pm time-lock") { encumberedState }
|
||||||
output("state encumbered by 5pm time-lock") { encumberedState }
|
output { unencumberedState }
|
||||||
output { unencumberedState }
|
output("5pm time-lock") { FIVE_PM_TIMELOCK }
|
||||||
output("5pm time-lock") { FIVE_PM_TIMELOCK }
|
}
|
||||||
}
|
transaction {
|
||||||
transaction {
|
input("state encumbered by 5pm time-lock")
|
||||||
input("state encumbered by 5pm time-lock")
|
input("5pm time-lock")
|
||||||
input("5pm time-lock")
|
output { stateWithNewOwner }
|
||||||
output { stateWithNewOwner }
|
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
timestamp(FIVE_PM)
|
||||||
command(DUMMY_PUBKEY_1) { TestTimeLock.Commands.Exit() }
|
this `fails with` "Missing required encumbrance 1 in INPUT"
|
||||||
timestamp(FIVE_PM)
|
|
||||||
this.verifies()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// A transaction containing an input state that is encumbered must fail if the encumbrance is not in the correct transaction.
|
// 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 {
|
||||||
ledger {
|
unverifiedTransaction {
|
||||||
unverifiedTransaction {
|
output("state encumbered by 5pm time-lock") { encumberedState }
|
||||||
output("state encumbered by 5pm time-lock") { encumberedState }
|
output { unencumberedState }
|
||||||
output { unencumberedState }
|
}
|
||||||
}
|
unverifiedTransaction {
|
||||||
unverifiedTransaction {
|
output("5pm time-lock") { FIVE_PM_TIMELOCK }
|
||||||
output("5pm time-lock") { FIVE_PM_TIMELOCK }
|
}
|
||||||
}
|
transaction {
|
||||||
transaction {
|
input("state encumbered by 5pm time-lock")
|
||||||
input("state encumbered by 5pm time-lock")
|
input("5pm time-lock")
|
||||||
input("5pm time-lock")
|
output { stateWithNewOwner }
|
||||||
output { stateWithNewOwner }
|
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
timestamp(FIVE_PM)
|
||||||
command(DUMMY_PUBKEY_1) { TestTimeLock.Commands.Exit() }
|
this `fails with` "Missing required encumbrance 1 in INPUT"
|
||||||
timestamp(FIVE_PM)
|
|
||||||
this.verifies()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// A transaction with an input state that is encumbered must succeed if the rules of the encumbrance are met.
|
// 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 {
|
transaction {
|
||||||
input("state encumbered by 5pm time-lock")
|
input("state encumbered by 5pm time-lock")
|
||||||
input("5pm time-lock")
|
input("5pm time-lock")
|
||||||
output { stateWithNewOwner }
|
output { unencumberedState }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
||||||
command(DUMMY_PUBKEY_1) { TestTimeLock.Commands.Exit() }
|
|
||||||
timestamp(FIVE_PM)
|
timestamp(FIVE_PM)
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
@ -161,9 +142,8 @@ class TransactionEncumbranceTests {
|
|||||||
transaction {
|
transaction {
|
||||||
input("state encumbered by 5pm time-lock")
|
input("state encumbered by 5pm time-lock")
|
||||||
input("5pm time-lock")
|
input("5pm time-lock")
|
||||||
output { stateWithNewOwner }
|
output { unencumberedState }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
||||||
command(DUMMY_PUBKEY_1) { TestTimeLock.Commands.Exit() }
|
|
||||||
timestamp(FOUR_PM)
|
timestamp(FOUR_PM)
|
||||||
this `fails with` "the time specified in the time-lock has passed"
|
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
|
# 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.
|
# 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
|
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!
|
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
|
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
|
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.
|
and linked ahead of time.
|
||||||
|
|
||||||
In future, the progress tracking framework will become a vital part of how exceptions, errors, and other faults are
|
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
|
This tutorial will take you through restructuring the commercial paper contract to use clauses. You should have
|
||||||
already completed ":doc:`tutorial-contract`".
|
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
|
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
|
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.
|
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
|
Commercial paper class
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
First we need to change the class from implementing ``Contract``, to extend ``ClauseVerifier``. This is an abstract
|
To use the clause verification logic, the contract needs to call the ``verifyClauses()`` function, passing in the transaction,
|
||||||
class which provides a verify() function for us, and requires we provide a property (``clauses``) for the clauses to test,
|
a list of clauses to verify, and a collection of commands the clauses are expected to handle all of. This list of
|
||||||
and a function (``extractCommands``) to extract the applicable commands from the transaction. This is important because
|
commands is important because ``verifyClauses()`` checks that none of the commands are left unprocessed at the end, and
|
||||||
``ClauseVerifier`` checks that no commands applicable to the contract are left unprocessed at the end. The following
|
raises an error if they are. The following examples are trimmed to the modified class definition and added elements, for
|
||||||
examples are trimmed to the modified class definition and added elements, for brevity:
|
brevity:
|
||||||
|
|
||||||
.. container:: codeset
|
.. container:: codeset
|
||||||
|
|
||||||
.. sourcecode:: kotlin
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
class CommercialPaper : ClauseVerifier {
|
class CommercialPaper : Contract {
|
||||||
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
|
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper")
|
||||||
|
|
||||||
override val clauses: List<SingleClause>
|
private fun extractCommands(tx: TransactionForContract): List<AuthenticatedObject<CommandData>>
|
||||||
get() = throw UnsupportedOperationException("not implemented")
|
|
||||||
|
|
||||||
override fun extractCommands(tx: TransactionForContract): List<AuthenticatedObject<CommandData>>
|
|
||||||
= tx.commands.select<Commands>()
|
= tx.commands.select<Commands>()
|
||||||
|
|
||||||
|
override fun verify(tx: TransactionForContract) = verifyClauses(tx, listOf(Clauses.Group()), extractCommands(tx))
|
||||||
|
|
||||||
.. sourcecode:: java
|
.. sourcecode:: java
|
||||||
|
|
||||||
public class CommercialPaper implements Contract {
|
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");
|
return SecureHash.Companion.sha256("https://en.wikipedia.org/wiki/Commercial_paper");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<SingleClause> getClauses() {
|
|
||||||
throw UnsupportedOperationException("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<AuthenticatedObject<CommandData>> extractCommands(@NotNull TransactionForContract tx) {
|
public Collection<AuthenticatedObject<CommandData>> extractCommands(@NotNull TransactionForContract tx) {
|
||||||
return tx.getCommands()
|
return tx.getCommands()
|
||||||
@ -67,6 +61,11 @@ examples are trimmed to the modified class definition and added elements, for br
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void verify(@NotNull TransactionForContract tx) throws IllegalArgumentException {
|
||||||
|
ClauseVerifier.verifyClauses(tx, Collections.singletonList(new Clause.Group()), extractCommands(tx));
|
||||||
|
}
|
||||||
|
|
||||||
Clauses
|
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.
|
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
|
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;
|
width: 170px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.sphinxsidebar #searchbox input[type="submit"] {
|
|
||||||
width: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
border: 0;
|
border: 0;
|
||||||
max-width: 100%;
|
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">
|
<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>
|
<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
|
<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
|
on the market, tailor made for the needs of the financial industry. We are attempting to prove or disprove the
|
||||||
following hypothesis:</p>
|
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#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#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#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>
|
<li class="toctree-l2"><a class="reference internal" href="tutorial-contract.html#clauses">Clauses</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</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-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#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#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>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="oracles.html">Writing oracle services</a><ul>
|
<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-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="#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="#progress-tracking">Progress tracking</a></li>
|
||||||
|
<li class="toctree-l2"><a class="reference internal" href="#unit-testing">Unit testing</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="oracles.html">Writing oracle services</a></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
|
<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>
|
surfaced to human operators for investigation and resolution.</p>
|
||||||
</div>
|
</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>
|
</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>
|
<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
|
<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>
|
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
|
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
|
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>
|
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></blockquote>
|
||||||
<div class="section" id="commercial-paper-class">
|
<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>
|
<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
|
<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,
|
||||||
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,
|
a list of clauses to verify, and a collection of commands the clauses are expected to handle all of. This list of
|
||||||
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
|
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
|
||||||
<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
|
raises an error if they are. The following examples are trimmed to the modified class definition and added elements, for
|
||||||
examples are trimmed to the modified class definition and added elements, for brevity:</p>
|
brevity:</p>
|
||||||
<div class="codeset container">
|
<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>
|
<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">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">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="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="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="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>
|
</pre></div>
|
||||||
</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>
|
<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="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="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="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="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>
|
<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">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="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="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>
|
</pre></div>
|
||||||
</div>
|
</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="#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="#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="#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>
|
<li class="toctree-l2"><a class="reference internal" href="#clauses">Clauses</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</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>
|
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>
|
<p>Future versions of the prototype will explore these concepts in more depth.</p>
|
||||||
</div>
|
</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">
|
<div class="section" id="clauses">
|
||||||
<h2>Clauses<a class="headerlink" href="#clauses" title="Permalink to this headline">¶</a></h2>
|
<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.
|
<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.
|
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
|
Clauses
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user