mirror of
https://github.com/corda/corda.git
synced 2025-06-21 16:49:45 +00:00
Making sure non-serialisable objects in FlowException do not interfere with the flow session (#651)
Also TransactionVerificationException no longer has reference to non-serialisable LedgerTransaction
This commit is contained in:
@ -20,16 +20,16 @@ sealed class TransactionType {
|
||||
fun verify(tx: LedgerTransaction) {
|
||||
require(tx.notary != null || tx.timestamp == null) { "Transactions with timestamps must be notarised." }
|
||||
val duplicates = detectDuplicateInputs(tx)
|
||||
if (duplicates.isNotEmpty()) throw TransactionVerificationException.DuplicateInputStates(tx, duplicates)
|
||||
if (duplicates.isNotEmpty()) throw TransactionVerificationException.DuplicateInputStates(tx.id, duplicates)
|
||||
val missing = verifySigners(tx)
|
||||
if (missing.isNotEmpty()) throw TransactionVerificationException.SignersMissing(tx, missing.toList())
|
||||
if (missing.isNotEmpty()) throw TransactionVerificationException.SignersMissing(tx.id, missing.toList())
|
||||
verifyTransaction(tx)
|
||||
}
|
||||
|
||||
/** Check that the list of signers includes all the necessary keys */
|
||||
fun verifySigners(tx: LedgerTransaction): Set<PublicKey> {
|
||||
val notaryKey = tx.inputs.map { it.state.notary.owningKey }.toSet()
|
||||
if (notaryKey.size > 1) throw TransactionVerificationException.MoreThanOneNotary(tx)
|
||||
if (notaryKey.size > 1) throw TransactionVerificationException.MoreThanOneNotary(tx.id)
|
||||
|
||||
val requiredKeys = getRequiredSigners(tx) + notaryKey
|
||||
val missing = requiredKeys - tx.mustSign
|
||||
@ -81,7 +81,7 @@ sealed class TransactionType {
|
||||
if (tx.notary != null && tx.inputs.isNotEmpty()) {
|
||||
tx.outputs.forEach {
|
||||
if (it.notary != tx.notary) {
|
||||
throw TransactionVerificationException.NotaryChangeInWrongTransactionType(tx, it.notary)
|
||||
throw TransactionVerificationException.NotaryChangeInWrongTransactionType(tx.id, tx.notary, it.notary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,13 +90,14 @@ sealed class TransactionType {
|
||||
private fun verifyEncumbrances(tx: LedgerTransaction) {
|
||||
// Validate that all encumbrances exist within the set of input states.
|
||||
val encumberedInputs = tx.inputs.filter { it.state.encumbrance != null }
|
||||
encumberedInputs.forEach { encumberedInput ->
|
||||
encumberedInputs.forEach { (state, ref) ->
|
||||
val encumbranceStateExists = tx.inputs.any {
|
||||
it.ref.txhash == encumberedInput.ref.txhash && it.ref.index == encumberedInput.state.encumbrance
|
||||
it.ref.txhash == ref.txhash && it.ref.index == state.encumbrance
|
||||
}
|
||||
if (!encumbranceStateExists) {
|
||||
throw TransactionVerificationException.TransactionMissingEncumbranceException(
|
||||
tx, encumberedInput.state.encumbrance!!,
|
||||
tx.id,
|
||||
state.encumbrance!!,
|
||||
TransactionVerificationException.Direction.INPUT
|
||||
)
|
||||
}
|
||||
@ -108,7 +109,8 @@ sealed class TransactionType {
|
||||
val encumbranceIndex = output.encumbrance ?: continue
|
||||
if (encumbranceIndex == i || encumbranceIndex >= tx.outputs.size) {
|
||||
throw TransactionVerificationException.TransactionMissingEncumbranceException(
|
||||
tx, encumbranceIndex,
|
||||
tx.id,
|
||||
encumbranceIndex,
|
||||
TransactionVerificationException.Direction.OUTPUT)
|
||||
}
|
||||
}
|
||||
@ -126,7 +128,7 @@ sealed class TransactionType {
|
||||
try {
|
||||
contract.verify(ctx)
|
||||
} catch(e: Throwable) {
|
||||
throw TransactionVerificationException.ContractRejection(tx, contract, e)
|
||||
throw TransactionVerificationException.ContractRejection(tx.id, contract, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -164,7 +166,7 @@ sealed class TransactionType {
|
||||
}
|
||||
check(tx.commands.isEmpty())
|
||||
} catch (e: IllegalStateException) {
|
||||
throw TransactionVerificationException.InvalidNotaryChange(tx)
|
||||
throw TransactionVerificationException.InvalidNotaryChange(tx.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import net.corda.core.crypto.Party
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import java.security.PublicKey
|
||||
import java.util.*
|
||||
|
||||
@ -95,25 +94,25 @@ class AttachmentResolutionException(val hash: SecureHash) : FlowException() {
|
||||
override fun toString(): String = "Attachment resolution failure for $hash"
|
||||
}
|
||||
|
||||
sealed class TransactionVerificationException(val tx: LedgerTransaction, cause: Throwable?) : FlowException(cause) {
|
||||
class ContractRejection(tx: LedgerTransaction, val contract: Contract, cause: Throwable?) : TransactionVerificationException(tx, cause)
|
||||
class MoreThanOneNotary(tx: LedgerTransaction) : TransactionVerificationException(tx, null)
|
||||
class SignersMissing(tx: LedgerTransaction, val missing: List<PublicKey>) : TransactionVerificationException(tx, null) {
|
||||
sealed class TransactionVerificationException(val txId: SecureHash, cause: Throwable?) : FlowException(cause) {
|
||||
class ContractRejection(txId: SecureHash, val contract: Contract, cause: Throwable?) : TransactionVerificationException(txId, cause)
|
||||
class MoreThanOneNotary(txId: SecureHash) : TransactionVerificationException(txId, null)
|
||||
class SignersMissing(txId: SecureHash, val missing: List<PublicKey>) : TransactionVerificationException(txId, null) {
|
||||
override fun toString(): String = "Signers missing: ${missing.joinToString()}"
|
||||
}
|
||||
|
||||
class DuplicateInputStates(tx: LedgerTransaction, val duplicates: Set<StateRef>) : TransactionVerificationException(tx, null) {
|
||||
class DuplicateInputStates(txId: SecureHash, val duplicates: Set<StateRef>) : TransactionVerificationException(txId, null) {
|
||||
override fun toString(): String = "Duplicate inputs: ${duplicates.joinToString()}"
|
||||
}
|
||||
|
||||
class InvalidNotaryChange(tx: LedgerTransaction) : TransactionVerificationException(tx, null)
|
||||
class NotaryChangeInWrongTransactionType(tx: LedgerTransaction, val outputNotary: Party) : TransactionVerificationException(tx, null) {
|
||||
class InvalidNotaryChange(txId: SecureHash) : TransactionVerificationException(txId, null)
|
||||
class NotaryChangeInWrongTransactionType(txId: SecureHash, val txNotary: Party, val outputNotary: Party) : TransactionVerificationException(txId, null) {
|
||||
override fun toString(): String {
|
||||
return "Found unexpected notary change in transaction. Tx notary: ${tx.notary}, found: $outputNotary"
|
||||
return "Found unexpected notary change in transaction. Tx notary: $txNotary, found: $outputNotary"
|
||||
}
|
||||
}
|
||||
|
||||
class TransactionMissingEncumbranceException(tx: LedgerTransaction, val missing: Int, val inOut: Direction) : TransactionVerificationException(tx, null) {
|
||||
class TransactionMissingEncumbranceException(txId: SecureHash, val missing: Int, val inOut: Direction) : TransactionVerificationException(txId, null) {
|
||||
override val message: String get() = "Missing required encumbrance $missing in $inOut"
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,8 @@ import java.security.PublicKey
|
||||
*
|
||||
* All the above refer to inputs using a (txhash, output index) pair.
|
||||
*/
|
||||
// TODO LedgerTransaction is not supposed to be serialisable as it references attachments, etc. The verification logic
|
||||
// currently sends this across to out-of-process verifiers. We'll need to change that first.
|
||||
@CordaSerializable
|
||||
class LedgerTransaction(
|
||||
/** The resolved input states which will be consumed/invalidated by the execution of this transaction. */
|
||||
|
Reference in New Issue
Block a user