Add missing checks on settled amount

The obligation `Settle` command takes in an amount to be settled, but only uses the underlying token from it.
This enforces that the settled amount matches the value seen moving.
This commit is contained in:
Ross Nicoll 2016-07-29 15:57:20 +01:00
parent 44dee97899
commit 406196fb69
2 changed files with 19 additions and 4 deletions

View File

@ -182,14 +182,16 @@ class Obligation<P> : 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<P>().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

View File

@ -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<Currency>(Amount(oneMillionDollars.quantity, inState.issuanceDef)) }
command(ALICE_PUBKEY) { Obligation.Commands.Settle<Currency>(Amount(oneMillionDollars.quantity / 2, inState.issuanceDef)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().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<Currency>(Amount(oneMillionDollars.quantity / 2, inState.issuanceDef)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
this `fails with` "amount in settle command"
}
}
}
@Test