From cef2c906ab1184f988407ef3a42932bcbb23d1f0 Mon Sep 17 00:00:00 2001 From: Andrius Dagys Date: Thu, 1 Dec 2016 12:05:32 +0000 Subject: [PATCH] Minor transaction verification logic refactoring --- .../corda/core/contracts/TransactionTypes.kt | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionTypes.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionTypes.kt index 09d8478185..39d8d1830a 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionTypes.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionTypes.kt @@ -49,15 +49,20 @@ sealed class TransactionType { /** Just uses the default [TransactionBuilder] with no special logic */ class Builder(notary: Party?) : TransactionBuilder(General(), notary) {} - /** - * Check the transaction is contract-valid by running the verify() for each input and output state contract. - * If any contract fails to verify, the whole transaction is considered to be invalid. - */ override fun verifyTransaction(tx: LedgerTransaction) { - // Make sure the notary has stayed the same. As we can't tell how inputs and outputs connect, if there - // are any inputs, all outputs must have the same notary. - // TODO: Is that the correct set of restrictions? May need to come back to this, see if we can be more - // flexible on output notaries. + verifyNoNotaryChange(tx) + verifyEncumbrances(tx) + verifyContracts(tx) + } + + /** + * Make sure the notary has stayed the same. As we can't tell how inputs and outputs connect, if there + * are any inputs, all outputs must have the same notary. + * + * TODO: Is that the correct set of restrictions? May need to come back to this, see if we can be more + * flexible on output notaries. + */ + private fun verifyNoNotaryChange(tx: LedgerTransaction) { if (tx.notary != null && tx.inputs.isNotEmpty()) { tx.outputs.forEach { if (it.notary != tx.notary) { @@ -65,25 +70,16 @@ sealed class TransactionType { } } } + } - val ctx = tx.toTransactionForContract() - - // TODO: This will all be replaced in future once the sandbox and contract constraints work is done. - val contracts = (ctx.inputs.map { it.contract } + ctx.outputs.map { it.contract }).toSet() - for (contract in contracts) { - try { - contract.verify(ctx) - } catch(e: Throwable) { - throw TransactionVerificationException.ContractRejection(tx, contract, e) - } - } - + private fun verifyEncumbrances(tx: LedgerTransaction) { // Validate that all encumbrances exist within the set of input states. - tx.inputs.filter { it.state.data.encumbrance != null }.forEach { encumberedInput -> - val isMissing = tx.inputs.none { + val encumberedInputs = tx.inputs.filter { it.state.data.encumbrance != null } + encumberedInputs.forEach { encumberedInput -> + val encumbranceStateExists = tx.inputs.any { it.ref.txhash == encumberedInput.ref.txhash && it.ref.index == encumberedInput.state.data.encumbrance } - if (isMissing) { + if (!encumbranceStateExists) { throw TransactionVerificationException.TransactionMissingEncumbranceException( tx, encumberedInput.state.data.encumbrance!!, TransactionVerificationException.Direction.INPUT @@ -103,6 +99,23 @@ sealed class TransactionType { } } + /** + * Check the transaction is contract-valid by running the verify() for each input and output state contract. + * If any contract fails to verify, the whole transaction is considered to be invalid. + */ + private fun verifyContracts(tx: LedgerTransaction) { + val ctx = tx.toTransactionForContract() + // TODO: This will all be replaced in future once the sandbox and contract constraints work is done. + val contracts = (ctx.inputs.map { it.contract } + ctx.outputs.map { it.contract }).toSet() + for (contract in contracts) { + try { + contract.verify(ctx) + } catch(e: Throwable) { + throw TransactionVerificationException.ContractRejection(tx, contract, e) + } + } + } + override fun getRequiredSigners(tx: LedgerTransaction) = tx.commands.flatMap { it.signers }.toSet() }