mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
verify of conditions, timestamp working
This commit is contained in:
parent
d5172ea2d0
commit
5daad3580c
@ -32,14 +32,14 @@ class EndDate : Perceivable<Instant>
|
||||
*/
|
||||
data class TimePerceivable(val cmp: Comparison, val instant: Perceivable<Instant>) : Perceivable<Boolean>
|
||||
|
||||
fun parseInstant(str: String) = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US).parse(str).toInstant()
|
||||
fun parseDate(str: String) = Instant.parse(str+"T00:00:00Z")
|
||||
|
||||
fun before(expiry: Perceivable<Instant>) = TimePerceivable(Comparison.LTE, expiry)
|
||||
fun after(expiry: Perceivable<Instant>) = TimePerceivable(Comparison.GTE, expiry)
|
||||
fun before(expiry: Instant) = TimePerceivable(Comparison.LTE, const(expiry))
|
||||
fun after(expiry: Instant) = TimePerceivable(Comparison.GTE, const(expiry))
|
||||
fun before(expiry: String) = TimePerceivable(Comparison.LTE, const(parseInstant(expiry)))
|
||||
fun after(expiry: String) = TimePerceivable(Comparison.GTE, const(parseInstant(expiry)))
|
||||
fun before(expiry: String) = TimePerceivable(Comparison.LTE, const(parseDate(expiry)))
|
||||
fun after(expiry: String) = TimePerceivable(Comparison.GTE, const(parseDate(expiry)))
|
||||
|
||||
data class PerceivableAnd(val left: Perceivable<Boolean>, val right: Perceivable<Boolean>) : Perceivable<Boolean>
|
||||
infix fun Perceivable<Boolean>.and(obs: Perceivable<Boolean>) = PerceivableAnd(this, obs)
|
||||
|
@ -4,6 +4,7 @@ import com.r3corda.core.contracts.*
|
||||
import com.r3corda.core.crypto.Party
|
||||
import com.r3corda.core.crypto.SecureHash
|
||||
import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Created by sofusmortensen on 23/05/16.
|
||||
@ -31,6 +32,24 @@ class UniversalContract : Contract {
|
||||
class Issue : TypeOnlyCommandData(), Commands
|
||||
}
|
||||
|
||||
fun eval(tx: TransactionForContract, condition: Perceivable<Instant>) : Instant = when (condition) {
|
||||
is Const<Instant> -> condition.value
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
|
||||
fun eval(tx: TransactionForContract, condition: Perceivable<Boolean>) : Boolean = when (condition) {
|
||||
is PerceivableAnd -> eval(tx, condition.left) && eval(tx, condition.right)
|
||||
is PerceivableOr -> eval(tx, condition.right) || eval(tx, condition.right)
|
||||
is Const<Boolean> -> condition.value
|
||||
is TimePerceivable -> when (condition.cmp) {
|
||||
Comparison.LTE -> tx.timestamp!!.after!! <= eval(tx, condition.instant)
|
||||
Comparison.GTE -> tx.timestamp!!.before!! >= eval(tx, condition.instant)
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
|
||||
|
||||
override fun verify(tx: TransactionForContract) {
|
||||
|
||||
requireThat {
|
||||
@ -46,18 +65,19 @@ class UniversalContract : Contract {
|
||||
val inState = tx.inputs.single() as State
|
||||
val actions = actions(inState.details)
|
||||
|
||||
val action = actions[value.name] ?: throw IllegalArgumentException("Failed requirement: action must be defined")
|
||||
|
||||
requireThat {
|
||||
"action must be timestamped" by ( tx.timestamp != null )
|
||||
"action must be defined" by ( actions.containsKey(value.name) )
|
||||
"action must be authorized" by ( cmd.signers.any { actions[ value.name ]!!.actors.any { party -> party.owningKey == it } } )
|
||||
"condition must be met" by ( true ) // todo
|
||||
"action must be authorized" by ( cmd.signers.any { action.actors.any { party -> party.owningKey == it } } )
|
||||
"condition must be met" by ( eval(tx, action.condition) )
|
||||
}
|
||||
|
||||
when (tx.outputs.size) {
|
||||
1 -> {
|
||||
val outState = tx.outputs.single() as State
|
||||
requireThat {
|
||||
"output state must match action result state" by (actions[value.name]!!.arrangement.equals(outState.details))
|
||||
"output state must match action result state" by (action.arrangement.equals(outState.details))
|
||||
}
|
||||
}
|
||||
0 -> throw IllegalArgumentException("must have at least one out state")
|
||||
@ -66,7 +86,7 @@ class UniversalContract : Contract {
|
||||
var allContracts = And( tx.outputs.map { (it as State).details }.toSet() )
|
||||
|
||||
requireThat {
|
||||
"output states must match action result state" by (actions[value.name]!!.arrangement.equals(allContracts))
|
||||
"output states must match action result state" by (action.arrangement.equals(allContracts))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,11 +46,11 @@ class ContractDefinition {
|
||||
|
||||
|
||||
val cds_contract = roadRunner.may {
|
||||
"payout".givenThat( acmeCorporationHasDefaulted and before("01/09/2017") ) {
|
||||
"payout".givenThat( acmeCorporationHasDefaulted and before("2017-09-01") ) {
|
||||
wileECoyote.gives(roadRunner, 1.M*USD)
|
||||
}
|
||||
} or wileECoyote.may {
|
||||
"expire".givenThat( after("01/09/2017") ) {}
|
||||
"expire".givenThat( after("2017-09-01") ) {}
|
||||
}
|
||||
|
||||
|
||||
@ -60,21 +60,21 @@ class ContractDefinition {
|
||||
roadRunner.gives(wileECoyote, 1200.K*USD)
|
||||
}
|
||||
} or wileECoyote.may {
|
||||
"expire".givenThat(after("01/09/2017")) {}
|
||||
"expire".givenThat(after("2017-09-01")) {}
|
||||
}
|
||||
|
||||
|
||||
val european_fx_option = roadRunner.may {
|
||||
"exercise".anytime {
|
||||
(roadRunner or wileECoyote).may {
|
||||
"execute".givenThat( after("01/09/2017") ) {
|
||||
"execute".givenThat( after("2017-09-01") ) {
|
||||
wileECoyote.gives( roadRunner, 1.M*EUR )
|
||||
roadRunner.gives( wileECoyote, 1200.K*USD )
|
||||
}
|
||||
}
|
||||
}
|
||||
} or wileECoyote.may {
|
||||
"expire".givenThat( after("01/09/2017")) {}
|
||||
"expire".givenThat( after("2017-09-01")) {}
|
||||
}
|
||||
|
||||
|
||||
|
@ -14,10 +14,11 @@ import java.time.Instant
|
||||
class FXSwap {
|
||||
|
||||
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
||||
val TEST_TX_TIME_TOO_EARLY: Instant get() = Instant.parse("2017-08-31T12:00:00.00Z")
|
||||
|
||||
val contract =
|
||||
(roadRunner or wileECoyote).may {
|
||||
"execute".givenThat(after("01/09/2017")) {
|
||||
"execute".givenThat(after("2017-09-01")) {
|
||||
wileECoyote.gives(roadRunner, 1200.K*USD)
|
||||
roadRunner.gives(wileECoyote, 1.M*EUR)
|
||||
}
|
||||
@ -114,6 +115,19 @@ class FXSwap {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `execute - before maturity`() {
|
||||
transaction {
|
||||
input { inState }
|
||||
output { outState1 }
|
||||
output { outState2 }
|
||||
timestamp(TEST_TX_TIME_TOO_EARLY)
|
||||
|
||||
command(roadRunner.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
this `fails with` "condition must be met"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `execute - outState mismatch 1`() {
|
||||
transaction {
|
||||
|
@ -4,6 +4,7 @@ import com.r3corda.core.testing.DUMMY_NOTARY
|
||||
import com.r3corda.core.testing.TEST_TX_TIME
|
||||
import com.r3corda.core.testing.transaction
|
||||
import org.junit.Test
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Created by sofusmortensen on 01/06/16.
|
||||
@ -13,7 +14,7 @@ class ZeroCouponBond {
|
||||
|
||||
val contract =
|
||||
(roadRunner or wileECoyote).may {
|
||||
"execute".givenThat(after("01/09/2017")) {
|
||||
"execute".givenThat(after("2017-09-01")) {
|
||||
wileECoyote.gives(roadRunner, 100.K*GBP)
|
||||
}
|
||||
}
|
||||
@ -21,11 +22,13 @@ class ZeroCouponBond {
|
||||
|
||||
val contractMove =
|
||||
(porkyPig or wileECoyote).may {
|
||||
"execute".givenThat(after("01/09/2017")) {
|
||||
"execute".givenThat(after("2017-09-01")) {
|
||||
wileECoyote.gives(porkyPig, 100.K*GBP)
|
||||
}
|
||||
}
|
||||
|
||||
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
||||
|
||||
val transfer = arrange { wileECoyote.gives(roadRunner, 100.K*GBP) }
|
||||
val transferWrong = arrange { wileECoyote.gives(roadRunner, 80.K*GBP) }
|
||||
|
||||
@ -66,7 +69,7 @@ class ZeroCouponBond {
|
||||
transaction {
|
||||
input { inState }
|
||||
output { outState }
|
||||
timestamp(TEST_TX_TIME)
|
||||
timestamp(TEST_TX_TIME_1)
|
||||
|
||||
tweak {
|
||||
command(wileECoyote.owningKey) { UniversalContract.Commands.Action("some undefined name") }
|
||||
@ -84,7 +87,7 @@ class ZeroCouponBond {
|
||||
transaction {
|
||||
input { inState }
|
||||
output { outState }
|
||||
timestamp(TEST_TX_TIME)
|
||||
timestamp(TEST_TX_TIME_1)
|
||||
|
||||
command(porkyPig.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
this `fails with` "action must be authorized"
|
||||
@ -96,7 +99,7 @@ class ZeroCouponBond {
|
||||
transaction {
|
||||
input { inState }
|
||||
output { outStateWrong }
|
||||
timestamp(TEST_TX_TIME)
|
||||
timestamp(TEST_TX_TIME_1)
|
||||
|
||||
command(roadRunner.owningKey) { UniversalContract.Commands.Action("execute") }
|
||||
this `fails with` "output state must match action result state"
|
||||
|
Loading…
Reference in New Issue
Block a user