From d5172ea2d000a06ef0aeb95ba9a2b34ccca6cfc2 Mon Sep 17 00:00:00 2001 From: sofusmortensen Date: Thu, 18 Aug 2016 01:11:05 +0200 Subject: [PATCH] added timestamp check --- .../contracts/universal/UniversalContract.kt | 1 + .../com/r3corda/contracts/universal/FXSwap.kt | 75 ++++++++++++++++++- .../contracts/universal/ZeroCouponBond.kt | 4 + 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/experimental/src/main/kotlin/com/r3corda/contracts/universal/UniversalContract.kt b/experimental/src/main/kotlin/com/r3corda/contracts/universal/UniversalContract.kt index a134d53fdb..5739a88b6e 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/UniversalContract.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/UniversalContract.kt @@ -47,6 +47,7 @@ class UniversalContract : Contract { val actions = actions(inState.details) 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 diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/FXSwap.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/FXSwap.kt index 9ef5779089..fb99bfc4f5 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/FXSwap.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/FXSwap.kt @@ -2,8 +2,10 @@ package com.r3corda.contracts.universal import com.r3corda.core.testing.DUMMY_NOTARY import com.r3corda.core.testing.DUMMY_NOTARY_KEY +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. @@ -11,6 +13,8 @@ import org.junit.Test class FXSwap { + val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z") + val contract = (roadRunner or wileECoyote).may { "execute".givenThat(after("01/09/2017")) { @@ -25,6 +29,13 @@ class FXSwap { val outState1 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transfer1 ) val outState2 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transfer2 ) + val transferBad1 = arrange { wileECoyote.gives(roadRunner, 1200.K*GBP) } // wrong currency + val transferBad2 = arrange { roadRunner.gives(wileECoyote, 900.K*EUR) } // wrong amount + val transferBad3 = arrange { wileECoyote.gives(wileECoyote, 1.M*EUR) } // wrong party + + val outStateBad1 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transferBad1 ) + val outStateBad2 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transferBad2 ) + val outStateBad3 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transferBad3 ) val inState = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contract) @@ -33,6 +44,7 @@ class FXSwap { transaction { output { inState } + timestamp(TEST_TX_TIME_1) this `fails with` "transaction has a single command" @@ -57,6 +69,26 @@ class FXSwap { input { inState } output { outState1 } output { outState2 } + timestamp(TEST_TX_TIME_1) + + tweak { + command(wileECoyote.owningKey) { UniversalContract.Commands.Action("some undefined name") } + this `fails with` "action must be defined" + } + + command(wileECoyote.owningKey) { UniversalContract.Commands.Action("execute") } + + this.verifies() + } + } + + @Test + fun `execute - reversed order`() { + transaction { + input { inState } + output { outState2 } + output { outState1 } + timestamp(TEST_TX_TIME_1) tweak { command(wileECoyote.owningKey) { UniversalContract.Commands.Action("some undefined name") } @@ -75,6 +107,7 @@ class FXSwap { input { inState } output { outState1 } output { outState2 } + timestamp(TEST_TX_TIME_1) command(porkyPig.owningKey) { UniversalContract.Commands.Action("execute") } this `fails with` "action must be authorized" @@ -82,13 +115,53 @@ class FXSwap { } @Test - fun `execute - outState mismatch`() { + fun `execute - outState mismatch 1`() { transaction { input { inState } output { outState1 } + timestamp(TEST_TX_TIME_1) command(roadRunner.owningKey) { UniversalContract.Commands.Action("execute") } this `fails with` "output state must match action result state" } } + + @Test + fun `execute - outState mismatch 2`() { + transaction { + input { inState } + output { outState1 } + output { outStateBad2 } + timestamp(TEST_TX_TIME_1) + + command(roadRunner.owningKey) { UniversalContract.Commands.Action("execute") } + this `fails with` "output states must match action result state" + } + } + + @Test + fun `execute - outState mismatch 3`() { + transaction { + input { inState } + output { outStateBad1 } + output { outState2 } + timestamp(TEST_TX_TIME_1) + + command(roadRunner.owningKey) { UniversalContract.Commands.Action("execute") } + this `fails with` "output states must match action result state" + } + } + + @Test + fun `execute - outState mismatch 4`() { + transaction { + input { inState } + output { outState1 } + output { outStateBad3 } + timestamp(TEST_TX_TIME_1) + + command(roadRunner.owningKey) { UniversalContract.Commands.Action("execute") } + this `fails with` "output states must match action result state" + } + } } \ No newline at end of file diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/ZeroCouponBond.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/ZeroCouponBond.kt index 50dff26651..da05f1c3bb 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/ZeroCouponBond.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/ZeroCouponBond.kt @@ -1,6 +1,7 @@ package com.r3corda.contracts.universal 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 @@ -65,6 +66,7 @@ class ZeroCouponBond { transaction { input { inState } output { outState } + timestamp(TEST_TX_TIME) tweak { command(wileECoyote.owningKey) { UniversalContract.Commands.Action("some undefined name") } @@ -82,6 +84,7 @@ class ZeroCouponBond { transaction { input { inState } output { outState } + timestamp(TEST_TX_TIME) command(porkyPig.owningKey) { UniversalContract.Commands.Action("execute") } this `fails with` "action must be authorized" @@ -93,6 +96,7 @@ class ZeroCouponBond { transaction { input { inState } output { outStateWrong } + timestamp(TEST_TX_TIME) command(roadRunner.owningKey) { UniversalContract.Commands.Action("execute") } this `fails with` "output state must match action result state"