diff --git a/contracts/src/main/kotlin/com/r3corda/contracts/asset/Obligation.kt b/contracts/src/main/kotlin/com/r3corda/contracts/asset/Obligation.kt index b6e0fca243..3756c27a20 100644 --- a/contracts/src/main/kotlin/com/r3corda/contracts/asset/Obligation.kt +++ b/contracts/src/main/kotlin/com/r3corda/contracts/asset/Obligation.kt @@ -182,14 +182,16 @@ class Obligation

: ClauseVerifier() { } } - // Insist that we can be the only contract consuming inputs, to ensure no other contract can think it's being - // settled as well + val totalAmountSettled = Amount(totalPenniesSettled, command.value.amount.token) requireThat { + // Insist that we can be the only contract consuming inputs, to ensure no other contract can think it's being + // settled as well "all move commands relate to this contract" by (moveCommands.map { it.value.contractHash } .all { it == null || it == Obligation

().legalContractReference }) // Settle commands exclude all other commands, so we don't need to check for contracts moving at the same // time. "amounts paid must match recipients to settle" by inputs.map { it.owner }.containsAll(amountReceivedByOwner.keys) + "amount in settle command ${command.value.amount} matches settled total ${totalAmountSettled}" by (command.value.amount == totalAmountSettled) "signatures are present from all obligors" by command.signers.containsAll(requiredSigners) "there are no zero sized inputs" by inputs.none { it.amount.quantity == 0L } "at obligor ${obligor.name} the obligations after settlement balance" by diff --git a/contracts/src/test/kotlin/com/r3corda/contracts/asset/ObligationTests.kt b/contracts/src/test/kotlin/com/r3corda/contracts/asset/ObligationTests.kt index 33db57d779..2b66556ea6 100644 --- a/contracts/src/test/kotlin/com/r3corda/contracts/asset/ObligationTests.kt +++ b/contracts/src/test/kotlin/com/r3corda/contracts/asset/ObligationTests.kt @@ -462,9 +462,9 @@ class ObligationTests { transaction("Settlement") { input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)) input(500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE_PUBKEY) - output("Alice's $5,000,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) } + output("Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) } output("Bob's $500,000") { 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY } - command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.issuanceDef)) } + command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.issuanceDef)) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation().legalContractReference) } this.verifies() } @@ -482,6 +482,19 @@ class ObligationTests { this `fails with` "all inputs are in the normal state" } } + + // Make sure settlement amount must match the amount leaving the ledger + ledger { + obligationTestRoots(this) + transaction("Settlement") { + input("Alice's $1,000,000 obligation to Bob") + input("Alice's $1,000,000") + output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY } + command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.issuanceDef)) } + command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation().legalContractReference) } + this `fails with` "amount in settle command" + } + } } @Test