mirror of
https://github.com/corda/corda.git
synced 2025-01-31 08:25:50 +00:00
Require that a cash Issue command is the only command
Require that a cash Issue command is the only cash command in a transaction. Although no problems are anticipated with other commands being present, they could theoretically confuse other verification tools.
This commit is contained in:
parent
20c6be193a
commit
252eb141a7
@ -93,32 +93,35 @@ class Cash : Contract {
|
|||||||
"there are no zero sized outputs" by outputs.none { it.amount.pennies == 0L }
|
"there are no zero sized outputs" by outputs.none { it.amount.pennies == 0L }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verifyIssueCommands(inputs, outputs, tx, currency, issuer)) continue
|
val issueCommand = tx.commands.select<Commands.Issue>().firstOrNull()
|
||||||
|
if (issueCommand != null) {
|
||||||
|
verifyIssueCommand(inputs, outputs, tx, issueCommand, currency, issuer)
|
||||||
|
} else {
|
||||||
|
val inputAmount = inputs.sumCashOrNull() ?: throw IllegalArgumentException("there is at least one cash input for this group")
|
||||||
|
val outputAmount = outputs.sumCashOrZero(currency)
|
||||||
|
|
||||||
val inputAmount = inputs.sumCashOrNull() ?: throw IllegalArgumentException("there is at least one cash input for this group")
|
// If we want to remove cash from the ledger, that must be signed for by the issuer.
|
||||||
val outputAmount = outputs.sumCashOrZero(currency)
|
// A mis-signed or duplicated exit command will just be ignored here and result in the exit amount being zero.
|
||||||
|
val exitCommand = tx.commands.select<Commands.Exit>(party = issuer).singleOrNull()
|
||||||
|
val amountExitingLedger = exitCommand?.value?.amount ?: Amount(0, currency)
|
||||||
|
|
||||||
// If we want to remove cash from the ledger, that must be signed for by the issuer.
|
requireThat {
|
||||||
// A mis-signed or duplicated exit command will just be ignored here and result in the exit amount being zero.
|
"there are no zero sized inputs" by inputs.none { it.amount.pennies == 0L }
|
||||||
val exitCommand = tx.commands.select<Commands.Exit>(party = issuer).singleOrNull()
|
"for deposit ${deposit.reference} at issuer ${deposit.party.name} the amounts balance" by
|
||||||
val amountExitingLedger = exitCommand?.value?.amount ?: Amount(0, currency)
|
(inputAmount == outputAmount + amountExitingLedger)
|
||||||
|
}
|
||||||
|
|
||||||
requireThat {
|
verifyMoveCommands<Commands.Move>(inputs, tx)
|
||||||
"there are no zero sized inputs" by inputs.none { it.amount.pennies == 0L }
|
|
||||||
"for deposit ${deposit.reference} at issuer ${deposit.party.name} the amounts balance" by
|
|
||||||
(inputAmount == outputAmount + amountExitingLedger)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyMoveCommands<Commands.Move>(inputs, tx)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun verifyIssueCommands(inputs: List<State>, outputs: List<State>, tx: TransactionForVerification, currency: Currency, issuer: Party): Boolean {
|
private fun verifyIssueCommand(inputs: List<State>,
|
||||||
val issueCommand = tx.commands.select<Commands.Issue>().singleOrNull()
|
outputs: List<State>,
|
||||||
if (issueCommand == null || outputs.isEmpty()) {
|
tx: TransactionForVerification,
|
||||||
return false
|
issueCommand: AuthenticatedObject<Commands.Issue>,
|
||||||
}
|
currency: Currency,
|
||||||
|
issuer: Party) {
|
||||||
// If we have an issue command, perform special processing: the group is allowed to have no inputs,
|
// If we have an issue command, perform special processing: the group is allowed to have no inputs,
|
||||||
// and the output states must have a deposit reference owned by the signer.
|
// and the output states must have a deposit reference owned by the signer.
|
||||||
//
|
//
|
||||||
@ -132,12 +135,13 @@ class Cash : Contract {
|
|||||||
// The grouping ensures that all outputs have the same deposit reference and currency.
|
// The grouping ensures that all outputs have the same deposit reference and currency.
|
||||||
val inputAmount = inputs.sumCashOrZero(currency)
|
val inputAmount = inputs.sumCashOrZero(currency)
|
||||||
val outputAmount = outputs.sumCash()
|
val outputAmount = outputs.sumCash()
|
||||||
|
val cashCommands = tx.commands.select<Cash.Commands>()
|
||||||
requireThat {
|
requireThat {
|
||||||
"the issue command has a nonce" by (issueCommand.value.nonce != 0L)
|
"the issue command has a nonce" by (issueCommand.value.nonce != 0L)
|
||||||
"output deposits are owned by a command signer" by (issuer in issueCommand.signingParties)
|
"output deposits are owned by a command signer" by (issuer in issueCommand.signingParties)
|
||||||
"output values sum to more than the inputs" by (outputAmount > inputAmount)
|
"output values sum to more than the inputs" by (outputAmount > inputAmount)
|
||||||
|
"there is only a single issue command" by (cashCommands.count() == 1)
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,6 +143,26 @@ class CashTests {
|
|||||||
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
|
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
|
||||||
this `fails requirement` "output values sum to more than the inputs"
|
this `fails requirement` "output values sum to more than the inputs"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Can't have any other commands if we have an issue command (because the issue command overrules them)
|
||||||
|
transaction {
|
||||||
|
input { inState }
|
||||||
|
output { inState.copy(amount = inState.amount * 2) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
|
||||||
|
tweak {
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
|
||||||
|
this `fails requirement` "there is only a single issue command"
|
||||||
|
}
|
||||||
|
tweak {
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||||
|
this `fails requirement` "there is only a single issue command"
|
||||||
|
}
|
||||||
|
tweak {
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Exit(inState.amount / 2) }
|
||||||
|
this `fails requirement` "there is only a single issue command"
|
||||||
|
}
|
||||||
|
this.accepts()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user