diff --git a/contracts/src/main/kotlin/com/r3corda/contracts/generic/GenericContract.kt b/contracts/src/main/kotlin/com/r3corda/contracts/generic/GenericContract.kt index 17cdd0c160..7043d316dc 100644 --- a/contracts/src/main/kotlin/com/r3corda/contracts/generic/GenericContract.kt +++ b/contracts/src/main/kotlin/com/r3corda/contracts/generic/GenericContract.kt @@ -38,22 +38,40 @@ class GenericContract : Contract { val cmd = tx.commands.requireSingleCommand() - val outState = tx.outStates.single() as State - val value = cmd.value when (value) { is Commands.Action -> { val inState = tx.inStates.single() as State val actions = actions(inState.details) + requireThat { "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 } } ) - "output state must match action result state" by ( actions[ value.name ]!!.kontract.equals(outState.details)) "condition must be met" by ( true ) // todo } + + when (tx.outStates.size) { + 1 -> { + val outState = tx.outStates.single() as State + requireThat { + "output state must match action result state" by (actions[value.name]!!.kontract.equals(outState.details)) + } + } + 0 -> throw IllegalArgumentException("must have at least one out state") + else -> { + + var allContracts = And( tx.outStates.map { (it as State).details }.toSet() ) + + requireThat { + "output states must match action result state" by (actions[value.name]!!.kontract.equals(allContracts)) + } + + } + } } is Commands.Issue -> { + val outState = tx.outStates.single() as State requireThat { "the transaction is signed by all involved parties" by ( liableParties(outState.details).all { it in cmd.signers } ) "the transaction has no input states" by tx.inStates.isEmpty() diff --git a/contracts/src/main/kotlin/com/r3corda/contracts/generic/Kontract.kt b/contracts/src/main/kotlin/com/r3corda/contracts/generic/Kontract.kt index e6e8de789c..b119bf7251 100644 --- a/contracts/src/main/kotlin/com/r3corda/contracts/generic/Kontract.kt +++ b/contracts/src/main/kotlin/com/r3corda/contracts/generic/Kontract.kt @@ -22,7 +22,7 @@ data class Transfer(val amount: Observable, val currency: Currency, val fr constructor(amount: Amount, from: Party, to: Party ) : this(const(amount.pennies), amount.token, from, to) } -data class And(val kontracts: Array) : Kontract +data class And(val kontracts: Set) : Kontract // diff --git a/contracts/src/main/kotlin/com/r3corda/contracts/generic/Util.kt b/contracts/src/main/kotlin/com/r3corda/contracts/generic/Util.kt index ae5448ef96..a493c7deba 100644 --- a/contracts/src/main/kotlin/com/r3corda/contracts/generic/Util.kt +++ b/contracts/src/main/kotlin/com/r3corda/contracts/generic/Util.kt @@ -13,7 +13,7 @@ val Int.K: Long get() = this.toLong() * 1000 val zero = Zero() -infix fun Kontract.and(kontract: Kontract) = And( arrayOf(this, kontract) ) +infix fun Kontract.and(kontract: Kontract) = And( setOf(this, kontract) ) infix fun Action.or(kontract: Action) = Or( arrayOf(this, kontract) ) infix fun Or.or(kontract: Action) = Or( this.contracts.plusElement( kontract ) ) infix fun Or.or(ors: Or) = Or( this.contracts.plus(ors.contracts) ) diff --git a/contracts/src/main/kotlin/com/r3corda/contracts/generic/literal.kt b/contracts/src/main/kotlin/com/r3corda/contracts/generic/literal.kt index 033a5d0b6d..fbe44728ea 100644 --- a/contracts/src/main/kotlin/com/r3corda/contracts/generic/literal.kt +++ b/contracts/src/main/kotlin/com/r3corda/contracts/generic/literal.kt @@ -19,7 +19,7 @@ class ContractBuilder { when (contracts.size) { 0 -> zero 1 -> contracts[0] - else -> And(contracts.toTypedArray()) + else -> And(contracts.toSet()) } } diff --git a/contracts/src/test/kotlin/com/r3corda/contracts/generic/FXSwap.kt b/contracts/src/test/kotlin/com/r3corda/contracts/generic/FXSwap.kt index 506afb3797..ce5d3d38c2 100644 --- a/contracts/src/test/kotlin/com/r3corda/contracts/generic/FXSwap.kt +++ b/contracts/src/test/kotlin/com/r3corda/contracts/generic/FXSwap.kt @@ -18,6 +18,13 @@ class FXSwap { } } + val transfer1 = kontract { wileECoyote.gives(roadRunner, 1200.K*USD) } + val transfer2 = kontract { roadRunner.gives(wileECoyote, 1.M*EUR) } + + val outState1 = GenericContract.State( DUMMY_NOTARY, transfer1 ) + val outState2 = GenericContract.State( DUMMY_NOTARY, transfer2 ) + + val inState = GenericContract.State( DUMMY_NOTARY, contract) @Test @@ -43,4 +50,21 @@ class FXSwap { } } + @Test + fun `execute`() { + transaction { + input { inState } + output { outState1 } + output { outState2 } + + tweak { + arg(wileECoyote.owningKey) { GenericContract.Commands.Action("some undefined name") } + this `fails requirement` "action must be defined" + } + + arg(wileECoyote.owningKey) { GenericContract.Commands.Action("execute") } + + this.accepts() + } + } } \ No newline at end of file