mirror of
https://github.com/corda/corda.git
synced 2025-04-09 04:15:35 +00:00
Further tweaks based on feedback - simplified the embedded timelock contract
This commit is contained in:
parent
cdb2c3efa6
commit
e187c4d91d
@ -80,7 +80,6 @@ class Cash : OnLedgerAsset<Currency, Cash.State>() {
|
||||
|
||||
/** There must be a MoveCommand signed by this key to claim the amount. */
|
||||
override val owner: PublicKey,
|
||||
/** Cash may be encumbered by an additional state. */
|
||||
override val encumbrance: Int? = null
|
||||
) : FungibleAsset<Currency> {
|
||||
constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: PublicKey)
|
||||
|
@ -115,10 +115,11 @@ interface ContractState {
|
||||
/**
|
||||
* All contract states may be _encumbered_ by up to one other state.
|
||||
*
|
||||
* The encumbrance state, if present, forces additional controls over this 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 encumbrance state, if present, forces additional controls over the encumbered state, since the platform checks
|
||||
* that the encumbrance state is present as an input in the same transaction that consumes the encumbered state, and
|
||||
* the contract code and rules of the encumbrance state will also be verified during the execution of the transaction.
|
||||
* For example, a cash contract state could be encumbered with a time-lock contract state; the cash 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 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
|
||||
|
@ -2,13 +2,11 @@ package com.r3corda.core.contracts
|
||||
|
||||
import com.r3corda.contracts.asset.Cash
|
||||
import com.r3corda.core.crypto.SecureHash
|
||||
import com.r3corda.core.serialization.OpaqueBytes
|
||||
import com.r3corda.core.testing.*
|
||||
import org.junit.Test
|
||||
import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
val TEST_TIMELOCK_ID = TransactionEncumbranceTests.TestTimeLock()
|
||||
|
||||
@ -32,32 +30,30 @@ class TransactionEncumbranceTests {
|
||||
class TestTimeLock : Contract {
|
||||
override val legalContractReference = SecureHash.sha256("TestTimeLock")
|
||||
override fun verify(tx: TransactionForContract) {
|
||||
val timestamp: Timestamp? = tx.timestamp
|
||||
val time = timestamp?.before ?: throw IllegalArgumentException("Transactions containing time-locks must be timestamped")
|
||||
val time = tx.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(
|
||||
val validFrom: Instant
|
||||
) : ContractState {
|
||||
override val participants: List<PublicKey>
|
||||
get() = throw UnsupportedOperationException()
|
||||
override val participants: List<PublicKey> = emptyList()
|
||||
override val contract: Contract = TEST_TIMELOCK_ID
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun trivial() {
|
||||
// A transaction containing an input state that is encumbered must fail if the encumbrance is missing on the inputs.
|
||||
assertFailsWith(TransactionVerificationException.TransactionMissingEncumbranceException::class) {
|
||||
transaction {
|
||||
input { encumberedState }
|
||||
output { unencumberedState }
|
||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
||||
this.verifies()
|
||||
}
|
||||
// A transaction containing an input state that is encumbered must fail if the encumbrance has not been presented
|
||||
// on the input states.
|
||||
transaction {
|
||||
input { encumberedState }
|
||||
output { unencumberedState }
|
||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
||||
this `fails with` "Missing required encumbrance 1 in INPUT"
|
||||
}
|
||||
// An encumbered state must not be encumbered by itself.
|
||||
transaction {
|
||||
@ -70,6 +66,7 @@ class TransactionEncumbranceTests {
|
||||
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.
|
||||
// In this test, the output encumbered state refers to an encumbrance in position 1, but there is only one output.
|
||||
transaction {
|
||||
input { unencumberedState }
|
||||
// The encumbered state refers to an encumbrance in position 1, so there should be at least 2 outputs.
|
||||
@ -82,8 +79,10 @@ 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.
|
||||
ledger {
|
||||
// This test fails because the encumbered state is pointing to the ordinary cash state as the encumbrance,
|
||||
// instead of the timelock by mistake, so when we try and use it the transaction fails as we didn't include the
|
||||
// encumbrance cash state.
|
||||
ledger {
|
||||
unverifiedTransaction {
|
||||
output("state encumbered by 5pm time-lock") { encumberedState }
|
||||
output { unencumberedState }
|
||||
@ -98,7 +97,10 @@ class TransactionEncumbranceTests {
|
||||
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.
|
||||
// A transaction containing an input state that is encumbered must fail if the encumbrance is not in the correct
|
||||
// transaction. In this test, the intended encumbrance is presented alongside the encumbered state for consumption,
|
||||
// although the encumbered state always refers to the encumbrance produced in the same transaction, and the in this case
|
||||
// the encumbrance was created in a separate transaction.
|
||||
ledger {
|
||||
unverifiedTransaction {
|
||||
output("state encumbered by 5pm time-lock") { encumberedState }
|
||||
@ -133,6 +135,7 @@ class TransactionEncumbranceTests {
|
||||
}
|
||||
}
|
||||
// A transaction with an input state that is encumbered must fail if the rules of the encumbrance are not met.
|
||||
// In this test, the time-lock encumbrance is being processed in a transaction before the time allowed.
|
||||
ledger {
|
||||
unverifiedTransaction {
|
||||
output("state encumbered by 5pm time-lock") { encumberedState }
|
||||
|
@ -909,6 +909,16 @@ When we construct a transaction that generates the encumbered state, we must pla
|
||||
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.
|
||||
|
||||
In future, we will consider the concept of a *covenant*. This is where the encumbrance travels alongside each iteration of
|
||||
the encumbered state. For example, a cash state may be encumbered with a *domicile* encumbrance, which checks the domicile of
|
||||
the identity of the owner that the cash state is being moved to, in order to uphold sanction screening regulations, and prevent
|
||||
cash being paid to parties domiciled in e.g. North Korea. In this case, the encumbrance should be permanently attached to
|
||||
the all future cash states stemmimg from this one.
|
||||
|
||||
We will also consider marking states that are capable of being encumbrances as such. This will prevent states being used
|
||||
as encumbrances inadvertently. For example, the time-lock above would be usable as an encumbrance, but it makes no sense to
|
||||
be able to encumber a cash state with another one.
|
||||
|
||||
Clauses
|
||||
-------
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user