Replace 'by' with 'using' to avoid overloading Kotlin keyword in contracts DSL. Deprecates 'by' with offered replacement.

This commit is contained in:
gary-rowe 2017-04-04 21:41:50 +01:00 committed by Mike Hearn
parent 36d5d0d7b2
commit 108f171e03
16 changed files with 149 additions and 141 deletions

View File

@ -57,9 +57,16 @@ infix fun Amount<Currency>.issuedBy(deposit: PartyAndReference) = Amount(quantit
object Requirements { object Requirements {
@Suppress("NOTHING_TO_INLINE") // Inlining this takes it out of our committed ABI. @Suppress("NOTHING_TO_INLINE") // Inlining this takes it out of our committed ABI.
infix inline fun String.by(expr: Boolean) { infix inline fun String.using(expr: Boolean) {
if (!expr) throw IllegalArgumentException("Failed requirement: $this") if (!expr) throw IllegalArgumentException("Failed requirement: $this")
} }
// Avoid overloading Kotlin keywords
@Deprecated("This function is deprecated, use 'using' instead",
ReplaceWith("using (expr)", "net.corda.core.contracts.Requirements.using"))
@Suppress("NOTHING_TO_INLINE") // Inlining this takes it out of our committed ABI.
infix inline fun String.by(expr: Boolean) {
using(expr)
}
} }
inline fun <R> requireThat(body: Requirements.() -> R) = Requirements.body() inline fun <R> requireThat(body: Requirements.() -> R) = Requirements.body()
@ -124,7 +131,7 @@ inline fun <reified T : MoveCommand> verifyMoveCommand(inputs: List<OwnableState
val command = commands.requireSingleCommand<T>() val command = commands.requireSingleCommand<T>()
val keysThatSigned = command.signers.toSet() val keysThatSigned = command.signers.toSet()
requireThat { requireThat {
"the owning keys are a subset of the signing keys" by keysThatSigned.containsAll(owningPubKeys) "the owning keys are a subset of the signing keys" using keysThatSigned.containsAll(owningPubKeys)
} }
return command.value return command.value
} }

View File

@ -252,8 +252,8 @@ interface LinearState : ContractState {
val inputIds = inputs.map { it.linearId }.distinct() val inputIds = inputs.map { it.linearId }.distinct()
val outputIds = outputs.map { it.linearId }.distinct() val outputIds = outputs.map { it.linearId }.distinct()
requireThat { requireThat {
"LinearStates are not merged" by (inputIds.count() == inputs.count()) "LinearStates are not merged" using (inputIds.count() == inputs.count())
"LinearStates are not split" by (outputIds.count() == outputs.count()) "LinearStates are not split" using (outputIds.count() == outputs.count())
} }
return emptySet() return emptySet()
} }

View File

@ -33,10 +33,10 @@ object ContractUpgradeFlow {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract<ContractState, *> val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract<ContractState, *>
requireThat { requireThat {
"The signing keys include all participant keys" by keysThatSigned.containsAll(participants) "The signing keys include all participant keys" using keysThatSigned.containsAll(participants)
"Inputs state reference the legacy contract" by (input.contract.javaClass == upgradedContract.legacyContract) "Inputs state reference the legacy contract" using (input.contract.javaClass == upgradedContract.legacyContract)
"Outputs state reference the upgraded contract" by (output.contract.javaClass == command.upgradedContractClass) "Outputs state reference the upgraded contract" using (output.contract.javaClass == command.upgradedContractClass)
"Output state must be an upgraded version of the input state" by (output == upgradedContract.upgrade(input)) "Output state must be an upgraded version of the input state" using (output == upgradedContract.upgrade(input))
} }
} }
@ -74,9 +74,9 @@ object ContractUpgradeFlow {
val proposedTx = proposal.stx.tx val proposedTx = proposal.stx.tx
val expectedTx = assembleBareTx(oldStateAndRef, proposal.modification).toWireTransaction() val expectedTx = assembleBareTx(oldStateAndRef, proposal.modification).toWireTransaction()
requireThat { requireThat {
"The instigator is one of the participants" by oldStateAndRef.state.data.participants.contains(otherSide.owningKey) "The instigator is one of the participants" using oldStateAndRef.state.data.participants.contains(otherSide.owningKey)
"The proposed upgrade ${proposal.modification.javaClass} is a trusted upgrade path" by (proposal.modification == authorisedUpgrade) "The proposed upgrade ${proposal.modification.javaClass} is a trusted upgrade path" using (proposal.modification == authorisedUpgrade)
"The proposed tx matches the expected tx for this upgrade" by (proposedTx == expectedTx) "The proposed tx matches the expected tx for this upgrade" using (proposedTx == expectedTx)
} }
ContractUpgradeFlow.verify(oldStateAndRef.state.data, expectedTx.outRef<ContractState>(0).state.data, expectedTx.commands.single()) ContractUpgradeFlow.verify(oldStateAndRef.state.data, expectedTx.outRef<ContractState>(0).state.data, expectedTx.commands.single())
} }

View File

@ -33,7 +33,7 @@ class TransactionEncumbranceTests {
val timeLockInput = tx.inputs.filterIsInstance<State>().singleOrNull() ?: return val timeLockInput = tx.inputs.filterIsInstance<State>().singleOrNull() ?: return
val time = tx.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 { requireThat {
"the time specified in the time-lock has passed" by (time >= timeLockInput.validFrom) "the time specified in the time-lock has passed" using (time >= timeLockInput.validFrom)
} }
} }

View File

@ -79,13 +79,13 @@ data class TradeApprovalContract(override val legalContractReference: SecureHash
when (command.value) { when (command.value) {
is Commands.Issue -> { is Commands.Issue -> {
requireThat { requireThat {
"Issue of new WorkflowContract must not include any inputs" by (tx.inputs.isEmpty()) "Issue of new WorkflowContract must not include any inputs" using (tx.inputs.isEmpty())
"Issue of new WorkflowContract must be in a unique transaction" by (tx.outputs.size == 1) "Issue of new WorkflowContract must be in a unique transaction" using (tx.outputs.size == 1)
} }
val issued = tx.outputs.get(0) as TradeApprovalContract.State val issued = tx.outputs.get(0) as TradeApprovalContract.State
requireThat { requireThat {
"Issue requires the source Party as signer" by (command.signers.contains(issued.source.owningKey)) "Issue requires the source Party as signer" using (command.signers.contains(issued.source.owningKey))
"Initial Issue state must be NEW" by (issued.state == WorkflowState.NEW) "Initial Issue state must be NEW" using (issued.state == WorkflowState.NEW)
} }
} }
is Commands.Completed -> { is Commands.Completed -> {
@ -95,11 +95,11 @@ data class TradeApprovalContract(override val legalContractReference: SecureHash
val before = group.inputs.single() val before = group.inputs.single()
val after = group.outputs.single() val after = group.outputs.single()
requireThat { requireThat {
"Only a non-final trade can be modified" by (before.state == WorkflowState.NEW) "Only a non-final trade can be modified" using (before.state == WorkflowState.NEW)
"Output must be a final state" by (after.state in setOf(WorkflowState.APPROVED, WorkflowState.REJECTED)) "Output must be a final state" using (after.state in setOf(WorkflowState.APPROVED, WorkflowState.REJECTED))
"Completed command can only change state" by (before == after.copy(state = before.state)) "Completed command can only change state" using (before == after.copy(state = before.state))
"Completed command requires the source Party as signer" by (command.signers.contains(before.source.owningKey)) "Completed command requires the source Party as signer" using (command.signers.contains(before.source.owningKey))
"Completed command requires the counterparty as signer" by (command.signers.contains(before.counterparty.owningKey)) "Completed command requires the counterparty as signer" using (command.signers.contains(before.counterparty.owningKey))
} }
} }
} }

View File

@ -78,7 +78,7 @@ class UniversalContract : Contract {
} }
} }
is Fixing -> { is Fixing -> {
requireThat { "Fixing must be included" by false } requireThat { "Fixing must be included" using false }
0.0.bd 0.0.bd
} }
is Interest -> { is Interest -> {
@ -95,7 +95,7 @@ class UniversalContract : Contract {
fun validateImmediateTransfers(tx: TransactionForContract, arrangement: Arrangement): Arrangement = when (arrangement) { fun validateImmediateTransfers(tx: TransactionForContract, arrangement: Arrangement): Arrangement = when (arrangement) {
is Obligation -> { is Obligation -> {
val amount = eval(tx, arrangement.amount) val amount = eval(tx, arrangement.amount)
requireThat { "transferred quantity is non-negative" by (amount >= BigDecimal.ZERO) } requireThat { "transferred quantity is non-negative" using (amount >= BigDecimal.ZERO) }
Obligation(const(amount), arrangement.currency, arrangement.from, arrangement.to) Obligation(const(amount), arrangement.currency, arrangement.from, arrangement.to)
} }
is And -> And(arrangement.arrangements.map { validateImmediateTransfers(tx, it) }.toSet()) is And -> And(arrangement.arrangements.map { validateImmediateTransfers(tx, it) }.toSet())
@ -180,7 +180,7 @@ class UniversalContract : Contract {
override fun verify(tx: TransactionForContract) { override fun verify(tx: TransactionForContract) {
requireThat { requireThat {
"transaction has a single command".by(tx.commands.size == 1) "transaction has a single command".using(tx.commands.size == 1)
} }
val cmd = tx.commands.requireSingleCommand<UniversalContract.Commands>() val cmd = tx.commands.requireSingleCommand<UniversalContract.Commands>()
@ -207,10 +207,10 @@ class UniversalContract : Contract {
assert(rest is Zero) assert(rest is Zero)
requireThat { requireThat {
"action must be timestamped" by (tx.timestamp != null) "action must be timestamped" using (tx.timestamp != null)
// "action must be authorized" by (cmd.signers.any { action.actors.any { party -> party.owningKey == it } }) // "action must be authorized" by (cmd.signers.any { action.actors.any { party -> party.owningKey == it } })
// todo perhaps merge these two requirements? // todo perhaps merge these two requirements?
"condition must be met" by (eval(tx, action.condition)) "condition must be met" using (eval(tx, action.condition))
} }
// verify that any resulting transfers can be resolved // verify that any resulting transfers can be resolved
@ -221,8 +221,8 @@ class UniversalContract : Contract {
1 -> { 1 -> {
val outState = tx.outputs.single() as State val outState = tx.outputs.single() as State
requireThat { requireThat {
"output state must match action result state" by (arrangement.equals(outState.details)) "output state must match action result state" using (arrangement.equals(outState.details))
"output state must match action result state" by (rest == zero) "output state must match action result state" using (rest == zero)
} }
} }
0 -> throw IllegalArgumentException("must have at least one out state") 0 -> throw IllegalArgumentException("must have at least one out state")
@ -230,7 +230,7 @@ class UniversalContract : Contract {
val allContracts = And(tx.outputs.map { (it as State).details }.toSet()) val allContracts = And(tx.outputs.map { (it as State).details }.toSet())
requireThat { requireThat {
"output states must match action result state" by (arrangement.equals(allContracts)) "output states must match action result state" using (arrangement.equals(allContracts))
} }
} }
@ -239,17 +239,17 @@ class UniversalContract : Contract {
is Commands.Issue -> { is Commands.Issue -> {
val outState = tx.outputs.single() as State val outState = tx.outputs.single() as State
requireThat { requireThat {
"the transaction is signed by all liable parties" by (liableParties(outState.details).all { it in cmd.signers }) "the transaction is signed by all liable parties" using (liableParties(outState.details).all { it in cmd.signers })
"the transaction has no input states" by tx.inputs.isEmpty() "the transaction has no input states" using tx.inputs.isEmpty()
} }
} }
is Commands.Move -> { is Commands.Move -> {
val inState = tx.inputs.single() as State val inState = tx.inputs.single() as State
val outState = tx.outputs.single() as State val outState = tx.outputs.single() as State
requireThat { requireThat {
"the transaction is signed by all liable parties" by "the transaction is signed by all liable parties" using
(liableParties(outState.details).all { it in cmd.signers }) (liableParties(outState.details).all { it in cmd.signers })
"output state does not reflect move command" by "output state does not reflect move command" using
(replaceParty(inState.details, value.from, value.to).equals(outState.details)) (replaceParty(inState.details, value.from, value.to).equals(outState.details))
} }
} }
@ -267,8 +267,8 @@ class UniversalContract : Contract {
value.fixes.associateBy({ it.of }, { it.value }), unusedFixes) value.fixes.associateBy({ it.of }, { it.value }), unusedFixes)
requireThat { requireThat {
"relevant fixing must be included" by unusedFixes.isEmpty() "relevant fixing must be included" using unusedFixes.isEmpty()
"output state does not reflect fix command" by "output state does not reflect fix command" using
(expectedArr.equals(outState.details)) (expectedArr.equals(outState.details))
} }
} }

View File

@ -208,12 +208,12 @@ public class JavaCommercialPaper implements Contract {
Amount<Issued<Currency>> received = CashKt.sumCashBy(tx.getOutputs(), input.getOwner()); Amount<Issued<Currency>> received = CashKt.sumCashBy(tx.getOutputs(), input.getOwner());
requireThat(require -> { requireThat(require -> {
require.by("must be timestamped", timestamp != null); require.using("must be timestamped", timestamp != null);
require.by("received amount equals the face value: " require.using("received amount equals the face value: "
+ received + " vs " + input.getFaceValue(), received.equals(input.getFaceValue())); + received + " vs " + input.getFaceValue(), received.equals(input.getFaceValue()));
require.by("the paper must have matured", time != null && !time.isBefore(input.getMaturityDate())); require.using("the paper must have matured", time != null && !time.isBefore(input.getMaturityDate()));
require.by("the received amount equals the face value", input.getFaceValue().equals(received)); require.using("the received amount equals the face value", input.getFaceValue().equals(received));
require.by("the paper must be destroyed", outputs.isEmpty()); require.using("the paper must be destroyed", outputs.isEmpty());
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
@ -244,11 +244,11 @@ public class JavaCommercialPaper implements Contract {
: timestampCommand.getBefore(); : timestampCommand.getBefore();
requireThat(require -> { requireThat(require -> {
require.by("output values sum to more than the inputs", inputs.isEmpty()); require.using("output values sum to more than the inputs", inputs.isEmpty());
require.by("output values sum to more than the inputs", output.faceValue.getQuantity() > 0); require.using("output values sum to more than the inputs", output.faceValue.getQuantity() > 0);
require.by("must be timestamped", timestampCommand != null); require.using("must be timestamped", timestampCommand != null);
require.by("the maturity date is not in the past", time != null && time.isBefore(output.getMaturityDate())); require.using("the maturity date is not in the past", time != null && time.isBefore(output.getMaturityDate()));
require.by("output states are issued by a command signer", cmd.getSigners().contains(output.issuance.getParty().getOwningKey())); require.using("output states are issued by a command signer", cmd.getSigners().contains(output.issuance.getParty().getOwningKey()));
return Unit.INSTANCE; return Unit.INSTANCE;
}); });

View File

@ -143,8 +143,8 @@ class CommercialPaper : Contract {
val command = commands.requireSingleCommand<Commands.Move>() val command = commands.requireSingleCommand<Commands.Move>()
val input = inputs.single() val input = inputs.single()
requireThat { requireThat {
"the transaction is signed by the owner of the CP" by (input.owner in command.signers) "the transaction is signed by the owner of the CP" using (input.owner in command.signers)
"the state is propagated" by (outputs.size == 1) "the state is propagated" using (outputs.size == 1)
// Don't need to check anything else, as if outputs.size == 1 then the output is equal to // Don't need to check anything else, as if outputs.size == 1 then the output is equal to
// the input ignoring the owner field due to the grouping. // the input ignoring the owner field due to the grouping.
} }
@ -169,10 +169,10 @@ class CommercialPaper : Contract {
val received = tx.outputs.sumCashBy(input.owner) val received = tx.outputs.sumCashBy(input.owner)
val time = timestamp?.after ?: throw IllegalArgumentException("Redemptions must be timestamped") val time = timestamp?.after ?: throw IllegalArgumentException("Redemptions must be timestamped")
requireThat { requireThat {
"the paper must have matured" by (time >= input.maturityDate) "the paper must have matured" using (time >= input.maturityDate)
"the received amount equals the face value" by (received == input.faceValue) "the received amount equals the face value" using (received == input.faceValue)
"the paper must be destroyed" by outputs.isEmpty() "the paper must be destroyed" using outputs.isEmpty()
"the transaction is signed by the owner of the CP" by (input.owner in command.signers) "the transaction is signed by the owner of the CP" using (input.owner in command.signers)
} }
return setOf(command.value) return setOf(command.value)

View File

@ -69,8 +69,8 @@ class CommercialPaperLegacy : Contract {
is Commands.Move -> { is Commands.Move -> {
val input = inputs.single() val input = inputs.single()
requireThat { requireThat {
"the transaction is signed by the owner of the CP" by (input.owner in command.signers) "the transaction is signed by the owner of the CP" using (input.owner in command.signers)
"the state is propagated" by (outputs.size == 1) "the state is propagated" using (outputs.size == 1)
// Don't need to check anything else, as if outputs.size == 1 then the output is equal to // Don't need to check anything else, as if outputs.size == 1 then the output is equal to
// the input ignoring the owner field due to the grouping. // the input ignoring the owner field due to the grouping.
} }
@ -82,10 +82,10 @@ class CommercialPaperLegacy : Contract {
val received = tx.outputs.sumCashBy(input.owner) val received = tx.outputs.sumCashBy(input.owner)
val time = timestamp?.after ?: throw IllegalArgumentException("Redemptions must be timestamped") val time = timestamp?.after ?: throw IllegalArgumentException("Redemptions must be timestamped")
requireThat { requireThat {
"the paper must have matured" by (time >= input.maturityDate) "the paper must have matured" using (time >= input.maturityDate)
"the received amount equals the face value" by (received == input.faceValue) "the received amount equals the face value" using (received == input.faceValue)
"the paper must be destroyed" by outputs.isEmpty() "the paper must be destroyed" using outputs.isEmpty()
"the transaction is signed by the owner of the CP" by (input.owner in command.signers) "the transaction is signed by the owner of the CP" using (input.owner in command.signers)
} }
} }
@ -94,14 +94,14 @@ class CommercialPaperLegacy : Contract {
val time = timestamp?.before ?: throw IllegalArgumentException("Issuances must be timestamped") val time = timestamp?.before ?: throw IllegalArgumentException("Issuances must be timestamped")
requireThat { requireThat {
// Don't allow people to issue commercial paper under other entities identities. // Don't allow people to issue commercial paper under other entities identities.
"output states are issued by a command signer" by "output states are issued by a command signer" using
(output.issuance.party.owningKey in command.signers) (output.issuance.party.owningKey in command.signers)
"output values sum to more than the inputs" by (output.faceValue.quantity > 0) "output values sum to more than the inputs" using (output.faceValue.quantity > 0)
"the maturity date is not in the past" by (time < output.maturityDate) "the maturity date is not in the past" using (time < output.maturityDate)
// Don't allow an existing CP state to be replaced by this issuance. // Don't allow an existing CP state to be replaced by this issuance.
// TODO: this has a weird/incorrect assertion string because it doesn't quite match the logic in the clause version. // TODO: this has a weird/incorrect assertion string because it doesn't quite match the logic in the clause version.
// TODO: Consider how to handle the case of mistaken issuances, or other need to patch. // TODO: Consider how to handle the case of mistaken issuances, or other need to patch.
"output values sum to more than the inputs" by inputs.isEmpty() "output values sum to more than the inputs" using inputs.isEmpty()
} }
} }

View File

@ -160,8 +160,8 @@ class Obligation<P : Any> : Contract {
.filter { it.amount.token in template.acceptableIssuedProducts } .filter { it.amount.token in template.acceptableIssuedProducts }
// Catch that there's nothing useful here, so we can dump out a useful error // Catch that there's nothing useful here, so we can dump out a useful error
requireThat { requireThat {
"there are fungible asset state outputs" by (assetStates.size > 0) "there are fungible asset state outputs" using (assetStates.size > 0)
"there are defined acceptable fungible asset states" by (acceptableAssetStates.size > 0) "there are defined acceptable fungible asset states" using (acceptableAssetStates.size > 0)
} }
val amountReceivedByOwner = acceptableAssetStates.groupBy { it.owner } val amountReceivedByOwner = acceptableAssetStates.groupBy { it.owner }
@ -184,15 +184,15 @@ class Obligation<P : Any> : Contract {
requireThat { requireThat {
// Insist that we can be the only contract consuming inputs, to ensure no other contract can think it's being // Insist that we can be the only contract consuming inputs, to ensure no other contract can think it's being
// settled as well // settled as well
"all move commands relate to this contract" by (moveCommands.map { it.value.contractHash } "all move commands relate to this contract" using (moveCommands.map { it.value.contractHash }
.all { it == null || it == Obligation<P>().legalContractReference }) .all { it == null || it == Obligation<P>().legalContractReference })
// Settle commands exclude all other commands, so we don't need to check for contracts moving at the same // Settle commands exclude all other commands, so we don't need to check for contracts moving at the same
// time. // time.
"amounts paid must match recipients to settle" by inputs.map { it.owner }.containsAll(amountReceivedByOwner.keys) "amounts paid must match recipients to settle" using inputs.map { it.owner }.containsAll(amountReceivedByOwner.keys)
"amount in settle command ${command.value.amount} matches settled total $totalAmountSettled" by (command.value.amount == totalAmountSettled) "amount in settle command ${command.value.amount} matches settled total $totalAmountSettled" using (command.value.amount == totalAmountSettled)
"signatures are present from all obligors" by command.signers.containsAll(requiredSigners) "signatures are present from all obligors" using command.signers.containsAll(requiredSigners)
"there are no zero sized inputs" by inputs.none { it.amount.quantity == 0L } "there are no zero sized inputs" using inputs.none { it.amount.quantity == 0L }
"at obligor ${obligor} the obligations after settlement balance" by "at obligor ${obligor} the obligations after settlement balance" using
(inputAmount == outputAmount + Amount(totalPenniesSettled, groupingKey)) (inputAmount == outputAmount + Amount(totalPenniesSettled, groupingKey))
} }
return setOf(command.value) return setOf(command.value)
@ -216,8 +216,8 @@ class Obligation<P : Any> : Contract {
private fun verify(inputs: List<State<P>>, private fun verify(inputs: List<State<P>>,
outputs: List<State<P>>): Set<C> { outputs: List<State<P>>): Set<C> {
requireThat { requireThat {
"all inputs are in the normal state " by inputs.all { it.lifecycle == Lifecycle.NORMAL } "all inputs are in the normal state " using inputs.all { it.lifecycle == Lifecycle.NORMAL }
"all outputs are in the normal state " by outputs.all { it.lifecycle == Lifecycle.NORMAL } "all outputs are in the normal state " using outputs.all { it.lifecycle == Lifecycle.NORMAL }
} }
return emptySet() return emptySet()
} }
@ -407,17 +407,17 @@ class Obligation<P : Any> : Contract {
val expectedOutput = input.copy(lifecycle = expectedOutputLifecycle) val expectedOutput = input.copy(lifecycle = expectedOutputLifecycle)
requireThat { requireThat {
"there is a timestamp from the authority" by (timestamp != null) "there is a timestamp from the authority" using (timestamp != null)
"the due date has passed" by (timestamp!!.after?.isAfter(deadline) ?: false) "the due date has passed" using (timestamp!!.after?.isAfter(deadline) ?: false)
"input state lifecycle is correct" by (input.lifecycle == expectedInputLifecycle) "input state lifecycle is correct" using (input.lifecycle == expectedInputLifecycle)
"output state corresponds exactly to input state, with lifecycle changed" by (expectedOutput == actualOutput) "output state corresponds exactly to input state, with lifecycle changed" using (expectedOutput == actualOutput)
} }
} }
} }
val owningPubKeys = inputs.filter { it is State<P> }.map { (it as State<P>).beneficiary }.toSet() val owningPubKeys = inputs.filter { it is State<P> }.map { (it as State<P>).beneficiary }.toSet()
val keysThatSigned = setLifecycleCommand.signers.toSet() val keysThatSigned = setLifecycleCommand.signers.toSet()
requireThat { requireThat {
"the owning keys are a subset of the signing keys" by keysThatSigned.containsAll(owningPubKeys) "the owning keys are a subset of the signing keys" using keysThatSigned.containsAll(owningPubKeys)
} }
} }
@ -434,10 +434,10 @@ class Obligation<P : Any> : Contract {
val netState = states.firstOrNull()?.bilateralNetState val netState = states.firstOrNull()?.bilateralNetState
requireThat { requireThat {
"at least two states are provided" by (states.size >= 2) "at least two states are provided" using (states.size >= 2)
"all states are in the normal lifecycle state " by (states.all { it.lifecycle == Lifecycle.NORMAL }) "all states are in the normal lifecycle state " using (states.all { it.lifecycle == Lifecycle.NORMAL })
"all states must be bilateral nettable" by (states.all { it.bilateralNetState == netState }) "all states must be bilateral nettable" using (states.all { it.bilateralNetState == netState })
"signer is in the state parties" by (signer in netState!!.partyKeys) "signer is in the state parties" using (signer in netState!!.partyKeys)
} }
val out = states.reduce(State<P>::net) val out = states.reduce(State<P>::net)
@ -484,7 +484,7 @@ class Obligation<P : Any> : Contract {
notary: Party, notary: Party,
vararg states: State<P>) { vararg states: State<P>) {
requireThat { requireThat {
"all states are in the normal lifecycle state " by (states.all { it.lifecycle == Lifecycle.NORMAL }) "all states are in the normal lifecycle state " using (states.all { it.lifecycle == Lifecycle.NORMAL })
} }
val groups = states.groupBy { it.multilateralNetState } val groups = states.groupBy { it.multilateralNetState }
val partyLookup = HashMap<PublicKey, AnonymousParty>() val partyLookup = HashMap<PublicKey, AnonymousParty>()
@ -563,11 +563,11 @@ class Obligation<P : Any> : Contract {
val obligationOwner = states.first().data.beneficiary val obligationOwner = states.first().data.beneficiary
requireThat { requireThat {
"all fungible asset states use the same notary" by (assetStatesAndRefs.all { it.state.notary == notary }) "all fungible asset states use the same notary" using (assetStatesAndRefs.all { it.state.notary == notary })
"all obligation states are in the normal state" by (statesAndRefs.all { it.state.data.lifecycle == Lifecycle.NORMAL }) "all obligation states are in the normal state" using (statesAndRefs.all { it.state.data.lifecycle == Lifecycle.NORMAL })
"all obligation states use the same notary" by (statesAndRefs.all { it.state.notary == notary }) "all obligation states use the same notary" using (statesAndRefs.all { it.state.notary == notary })
"all obligation states have the same obligor" by (statesAndRefs.all { it.state.data.obligor == obligationIssuer }) "all obligation states have the same obligor" using (statesAndRefs.all { it.state.data.obligor == obligationIssuer })
"all obligation states have the same beneficiary" by (statesAndRefs.all { it.state.data.beneficiary == obligationOwner }) "all obligation states have the same beneficiary" using (statesAndRefs.all { it.state.data.beneficiary == obligationOwner })
} }
// TODO: A much better (but more complex) solution would be to have two iterators, one for obligations, // TODO: A much better (but more complex) solution would be to have two iterators, one for obligations,

View File

@ -109,8 +109,8 @@ abstract class AbstractConserveAmount<S : FungibleAsset<T>, C : CommandData, T :
val amountExitingLedger: Amount<Issued<T>> = exitCommand?.value?.amount ?: Amount(0, groupingKey) val amountExitingLedger: Amount<Issued<T>> = exitCommand?.value?.amount ?: Amount(0, groupingKey)
requireThat { requireThat {
"there are no zero sized inputs" by inputs.none { it.amount.quantity == 0L } "there are no zero sized inputs" using inputs.none { it.amount.quantity == 0L }
"for reference ${deposit.reference} at issuer ${deposit.party} the amounts balance: ${inputAmount.quantity} - ${amountExitingLedger.quantity} != ${outputAmount.quantity}" by "for reference ${deposit.reference} at issuer ${deposit.party} the amounts balance: ${inputAmount.quantity} - ${amountExitingLedger.quantity} != ${outputAmount.quantity}" using
(inputAmount == outputAmount + amountExitingLedger) (inputAmount == outputAmount + amountExitingLedger)
} }

View File

@ -41,11 +41,11 @@ abstract class AbstractIssue<in S : ContractState, C : CommandData, T : Any>(
val inputAmount = inputs.sumOrZero(groupingKey) val inputAmount = inputs.sumOrZero(groupingKey)
val outputAmount = outputs.sum() val outputAmount = outputs.sum()
requireThat { requireThat {
"the issue command has a nonce" by (issueCommand.value.nonce != 0L) "the issue command has a nonce" using (issueCommand.value.nonce != 0L)
// TODO: This doesn't work with the trader demo, so use the underlying key instead // TODO: This doesn't work with the trader demo, so use the underlying key instead
// "output states are issued by a command signer" by (issuer in issueCommand.signingParties) // "output states are issued by a command signer" by (issuer in issueCommand.signingParties)
"output states are issued by a command signer" by (issuer.owningKey in issueCommand.signers) "output states are issued by a command signer" using (issuer.owningKey in issueCommand.signers)
"output values sum to more than the inputs" by (outputAmount > inputAmount) "output values sum to more than the inputs" using (outputAmount > inputAmount)
} }
// This is safe because we've taken the command from a collection of C objects at the start // This is safe because we've taken the command from a collection of C objects at the start

View File

@ -79,9 +79,10 @@ open class NetClause<C : CommandData, P : Any> : Clause<ContractState, C, Unit>(
// Sum the columns of the matrices. This will yield the net amount payable to/from each party to/from all other participants. // Sum the columns of the matrices. This will yield the net amount payable to/from each party to/from all other participants.
// The two summaries must match, reflecting that the amounts owed match on both input and output. // The two summaries must match, reflecting that the amounts owed match on both input and output.
requireThat { requireThat {
"all input states use the same template" by (inputs.all { it.template == template }) "all input states use the same template" using (inputs.all { it.template == template })
"all output states use the same template" by (outputs.all { it.template == template }) "all output states use the same template" using (outputs.all { it.template == template })
"amounts owed on input and output must match" by (sumAmountsDue(inputBalances) == sumAmountsDue(outputBalances)) "amounts owed on input and output must match" using (sumAmountsDue(inputBalances) == sumAmountsDue
(outputBalances))
} }
// TODO: Handle proxies nominated by parties, i.e. a central clearing service // TODO: Handle proxies nominated by parties, i.e. a central clearing service

View File

@ -14,7 +14,7 @@ open class NoZeroSizedOutputs<in S : FungibleAsset<T>, C : CommandData, T : Any>
commands: List<AuthenticatedObject<C>>, commands: List<AuthenticatedObject<C>>,
groupingKey: Issued<T>?): Set<C> { groupingKey: Issued<T>?): Set<C> {
requireThat { requireThat {
"there are no zero sized outputs" by outputs.none { it.amount.quantity == 0L } "there are no zero sized outputs" using outputs.none { it.amount.quantity == 0L }
} }
return emptySet() return emptySet()
} }

View File

@ -479,22 +479,22 @@ class InterestRateSwap : Contract {
// These functions may make more sense to use for basket types, but for now let's leave them here // These functions may make more sense to use for basket types, but for now let's leave them here
fun checkLegDates(legs: List<CommonLeg>) { fun checkLegDates(legs: List<CommonLeg>) {
requireThat { requireThat {
"Effective date is before termination date" by legs.all { it.effectiveDate < it.terminationDate } "Effective date is before termination date" using legs.all { it.effectiveDate < it.terminationDate }
"Effective dates are in alignment" by legs.all { it.effectiveDate == legs[0].effectiveDate } "Effective dates are in alignment" using legs.all { it.effectiveDate == legs[0].effectiveDate }
"Termination dates are in alignment" by legs.all { it.terminationDate == legs[0].terminationDate } "Termination dates are in alignment" using legs.all { it.terminationDate == legs[0].terminationDate }
} }
} }
fun checkLegAmounts(legs: List<CommonLeg>) { fun checkLegAmounts(legs: List<CommonLeg>) {
requireThat { requireThat {
"The notional is non zero" by legs.any { it.notional.quantity > (0).toLong() } "The notional is non zero" using legs.any { it.notional.quantity > (0).toLong() }
"The notional for all legs must be the same" by legs.all { it.notional == legs[0].notional } "The notional for all legs must be the same" using legs.all { it.notional == legs[0].notional }
} }
for (leg: CommonLeg in legs) { for (leg: CommonLeg in legs) {
if (leg is FixedLeg<*>) { if (leg is FixedLeg<*>) {
requireThat { requireThat {
// TODO: Confirm: would someone really enter a swap with a negative fixed rate? // TODO: Confirm: would someone really enter a swap with a negative fixed rate?
"Fixed leg rate must be positive" by leg.fixedRate.isPositive() "Fixed leg rate must be positive" using leg.fixedRate.isPositive()
} }
} }
} }
@ -547,21 +547,21 @@ class InterestRateSwap : Contract {
val command = tx.commands.requireSingleCommand<Commands.Agree>() val command = tx.commands.requireSingleCommand<Commands.Agree>()
val irs = outputs.filterIsInstance<State<*>>().single() val irs = outputs.filterIsInstance<State<*>>().single()
requireThat { requireThat {
"There are no in states for an agreement" by inputs.isEmpty() "There are no in states for an agreement" using inputs.isEmpty()
"There are events in the fix schedule" by (irs.calculation.fixedLegPaymentSchedule.isNotEmpty()) "There are events in the fix schedule" using (irs.calculation.fixedLegPaymentSchedule.isNotEmpty())
"There are events in the float schedule" by (irs.calculation.floatingLegPaymentSchedule.isNotEmpty()) "There are events in the float schedule" using (irs.calculation.floatingLegPaymentSchedule.isNotEmpty())
"All notionals must be non zero" by (irs.fixedLeg.notional.quantity > 0 && irs.floatingLeg.notional.quantity > 0) "All notionals must be non zero" using (irs.fixedLeg.notional.quantity > 0 && irs.floatingLeg.notional.quantity > 0)
"The fixed leg rate must be positive" by (irs.fixedLeg.fixedRate.isPositive()) "The fixed leg rate must be positive" using (irs.fixedLeg.fixedRate.isPositive())
"The currency of the notionals must be the same" by (irs.fixedLeg.notional.token == irs.floatingLeg.notional.token) "The currency of the notionals must be the same" using (irs.fixedLeg.notional.token == irs.floatingLeg.notional.token)
"All leg notionals must be the same" by (irs.fixedLeg.notional == irs.floatingLeg.notional) "All leg notionals must be the same" using (irs.fixedLeg.notional == irs.floatingLeg.notional)
"The effective date is before the termination date for the fixed leg" by (irs.fixedLeg.effectiveDate < irs.fixedLeg.terminationDate) "The effective date is before the termination date for the fixed leg" using (irs.fixedLeg.effectiveDate < irs.fixedLeg.terminationDate)
"The effective date is before the termination date for the floating leg" by (irs.floatingLeg.effectiveDate < irs.floatingLeg.terminationDate) "The effective date is before the termination date for the floating leg" using (irs.floatingLeg.effectiveDate < irs.floatingLeg.terminationDate)
"The effective dates are aligned" by (irs.floatingLeg.effectiveDate == irs.fixedLeg.effectiveDate) "The effective dates are aligned" using (irs.floatingLeg.effectiveDate == irs.fixedLeg.effectiveDate)
"The termination dates are aligned" by (irs.floatingLeg.terminationDate == irs.fixedLeg.terminationDate) "The termination dates are aligned" using (irs.floatingLeg.terminationDate == irs.fixedLeg.terminationDate)
"The rates are valid" by checkRates(listOf(irs.fixedLeg, irs.floatingLeg)) "The rates are valid" using checkRates(listOf(irs.fixedLeg, irs.floatingLeg))
"The schedules are valid" by checkSchedules(listOf(irs.fixedLeg, irs.floatingLeg)) "The schedules are valid" using checkSchedules(listOf(irs.fixedLeg, irs.floatingLeg))
"The fixing period date offset cannot be negative" by (irs.floatingLeg.fixingPeriodOffset >= 0) "The fixing period date offset cannot be negative" using (irs.floatingLeg.fixingPeriodOffset >= 0)
// TODO: further tests // TODO: further tests
} }
@ -588,8 +588,8 @@ class InterestRateSwap : Contract {
// Having both of these tests are "redundant" as far as verify() goes, however, by performing both // Having both of these tests are "redundant" as far as verify() goes, however, by performing both
// we can relay more information back to the user in the case of failure. // we can relay more information back to the user in the case of failure.
requireThat { requireThat {
"There is at least one difference in the IRS floating leg payment schedules" by !paymentDifferences.isEmpty() "There is at least one difference in the IRS floating leg payment schedules" using !paymentDifferences.isEmpty()
"There is only one change in the IRS floating leg payment schedule" by (paymentDifferences.size == 1) "There is only one change in the IRS floating leg payment schedule" using (paymentDifferences.size == 1)
} }
val changedRates = paymentDifferences.single().second // Ignore the date of the changed rate (we checked that earlier). val changedRates = paymentDifferences.single().second // Ignore the date of the changed rate (we checked that earlier).
@ -597,19 +597,19 @@ class InterestRateSwap : Contract {
val fixValue = command.value.fix val fixValue = command.value.fix
// Need to check that everything is the same apart from the new fixed rate entry. // Need to check that everything is the same apart from the new fixed rate entry.
requireThat { requireThat {
"The fixed leg parties are constant" by (irs.fixedLeg.fixedRatePayer == prevIrs.fixedLeg.fixedRatePayer) // Although superseded by the below test, this is included for a regression issue "The fixed leg parties are constant" using (irs.fixedLeg.fixedRatePayer == prevIrs.fixedLeg.fixedRatePayer) // Although superseded by the below test, this is included for a regression issue
"The fixed leg is constant" by (irs.fixedLeg == prevIrs.fixedLeg) "The fixed leg is constant" using (irs.fixedLeg == prevIrs.fixedLeg)
"The floating leg is constant" by (irs.floatingLeg == prevIrs.floatingLeg) "The floating leg is constant" using (irs.floatingLeg == prevIrs.floatingLeg)
"The common values are constant" by (irs.common == prevIrs.common) "The common values are constant" using (irs.common == prevIrs.common)
"The fixed leg payment schedule is constant" by (irs.calculation.fixedLegPaymentSchedule == prevIrs.calculation.fixedLegPaymentSchedule) "The fixed leg payment schedule is constant" using (irs.calculation.fixedLegPaymentSchedule == prevIrs.calculation.fixedLegPaymentSchedule)
"The expression is unchanged" by (irs.calculation.expression == prevIrs.calculation.expression) "The expression is unchanged" using (irs.calculation.expression == prevIrs.calculation.expression)
"There is only one changed payment in the floating leg" by (paymentDifferences.size == 1) "There is only one changed payment in the floating leg" using (paymentDifferences.size == 1)
"There changed payment is a floating payment" by (oldFloatingRatePaymentEvent.rate is ReferenceRate) "There changed payment is a floating payment" using (oldFloatingRatePaymentEvent.rate is ReferenceRate)
"The new payment is a fixed payment" by (newFixedRatePaymentEvent.rate is FixedRate) "The new payment is a fixed payment" using (newFixedRatePaymentEvent.rate is FixedRate)
"The changed payments dates are aligned" by (oldFloatingRatePaymentEvent.date == newFixedRatePaymentEvent.date) "The changed payments dates are aligned" using (oldFloatingRatePaymentEvent.date == newFixedRatePaymentEvent.date)
"The new payment has the correct rate" by (newFixedRatePaymentEvent.rate.ratioUnit!!.value == fixValue.value) "The new payment has the correct rate" using (newFixedRatePaymentEvent.rate.ratioUnit!!.value == fixValue.value)
"The fixing is for the next required date" by (prevIrs.calculation.nextFixingDate() == fixValue.of.forDay) "The fixing is for the next required date" using (prevIrs.calculation.nextFixingDate() == fixValue.of.forDay)
"The fix payment has the same currency as the notional" by (newFixedRatePaymentEvent.flow.token == irs.floatingLeg.notional.token) "The fix payment has the same currency as the notional" using (newFixedRatePaymentEvent.flow.token == irs.floatingLeg.notional.token)
// "The fixing is not in the future " by (fixCommand) // The oracle should not have signed this . // "The fixing is not in the future " by (fixCommand) // The oracle should not have signed this .
} }
@ -627,7 +627,7 @@ class InterestRateSwap : Contract {
groupingKey: UniqueIdentifier?): Set<Commands> { groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Pay>() val command = tx.commands.requireSingleCommand<Commands.Pay>()
requireThat { requireThat {
"Payments not supported / verifiable yet" by false "Payments not supported / verifiable yet" using false
} }
return setOf(command.value) return setOf(command.value)
} }
@ -644,8 +644,8 @@ class InterestRateSwap : Contract {
val command = tx.commands.requireSingleCommand<Commands.Mature>() val command = tx.commands.requireSingleCommand<Commands.Mature>()
val irs = inputs.filterIsInstance<State<*>>().single() val irs = inputs.filterIsInstance<State<*>>().single()
requireThat { requireThat {
"No more fixings to be applied" by (irs.calculation.nextFixingDate() == null) "No more fixings to be applied" using (irs.calculation.nextFixingDate() == null)
"The irs is fully consumed and there is no id matched output state" by outputs.isEmpty() "The irs is fully consumed and there is no id matched output state" using outputs.isEmpty()
} }
return setOf(command.value) return setOf(command.value)

View File

@ -47,10 +47,10 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
val command = tx.commands.requireSingleCommand<Commands.Update>() val command = tx.commands.requireSingleCommand<Commands.Update>()
requireThat { requireThat {
"there is only one input" by (inputs.size == 1) "there is only one input" using (inputs.size == 1)
"there is only one output" by (outputs.size == 1) "there is only one output" using (outputs.size == 1)
"the valuer hasn't changed" by (inputs[0].valuer == outputs[0].valuer) "the valuer hasn't changed" using (inputs[0].valuer == outputs[0].valuer)
"the linear id hasn't changed" by (inputs[0].linearId == outputs[0].linearId) "the linear id hasn't changed" using (inputs[0].linearId == outputs[0].linearId)
} }
return setOf(command.value) return setOf(command.value)
@ -68,10 +68,10 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
val command = tx.commands.requireSingleCommand<Commands.Agree>() val command = tx.commands.requireSingleCommand<Commands.Agree>()
requireThat { requireThat {
"there are no inputs" by (inputs.size == 0) "there are no inputs" using (inputs.size == 0)
"there is one output" by (outputs.size == 1) "there is one output" using (outputs.size == 1)
"valuer must be a party" by (outputs[0].parties.contains(outputs[0].valuer)) "valuer must be a party" using (outputs[0].parties.contains(outputs[0].valuer))
"all participants must be parties" by (outputs[0].parties.map { it.owningKey }.containsAll(outputs[0].participants)) "all participants must be parties" using (outputs[0].parties.map { it.owningKey }.containsAll(outputs[0].participants))
} }
return setOf(command.value) return setOf(command.value)