CORDA-2570 address code review comments

This commit is contained in:
Tudor Malene 2019-02-13 12:17:48 +00:00 committed by tudor.malene@gmail.com
parent 4a4788ad41
commit 764b33ded5
3 changed files with 25 additions and 11 deletions

View File

@ -107,6 +107,19 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
class ConflictingAttachmentsRejection(txId: SecureHash, val contractClass: String) class ConflictingAttachmentsRejection(txId: SecureHash, val contractClass: String)
: TransactionVerificationException(txId, "Contract constraints failed for: $contractClass, because multiple attachments providing this contract were attached.", null) : TransactionVerificationException(txId, "Contract constraints failed for: $contractClass, because multiple attachments providing this contract were attached.", null)
/**
* Indicates this transaction violates the "no overlap" rule: two attachments are trying to provide the same file
* path. Whereas Java classpaths would normally allow that with the first class taking precedence, this is not
* allowed in transactions for security reasons. This usually indicates that two separate apps share a dependency,
* in which case you could try 'shading the fat jars' to rename classes of dependencies. Or you could manually
* attach dependency JARs when building the transaction.
*
* @property contractClass The fully qualified class name of the failing contract.
*/
@KeepForDJVM
class DuplicateAttachmentsRejection(txId: SecureHash, val attachmentId: Attachment)
: TransactionVerificationException(txId, "The attachment: $attachmentId was added multiple times.", null)
/** /**
* A [Contract] class named by a state could not be constructed. Most likely you do not have a no-argument * A [Contract] class named by a state could not be constructed. Most likely you do not have a no-argument
* constructor, or the class doesn't subclass [Contract]. * constructor, or the class doesn't subclass [Contract].

View File

@ -74,9 +74,6 @@ fun AttachmentConstraint.canBeTransitionedFrom(input: AttachmentConstraint, atta
input is WhitelistedByZoneAttachmentConstraint && output is SignatureAttachmentConstraint -> input is WhitelistedByZoneAttachmentConstraint && output is SignatureAttachmentConstraint ->
attachment.signerKeys.isNotEmpty() && output.key.keys.containsAll(attachment.signerKeys) attachment.signerKeys.isNotEmpty() && output.key.keys.containsAll(attachment.signerKeys)
// TODO Transition from Hash to Signature constraint
input is HashAttachmentConstraint && output is SignatureAttachmentConstraint -> false
else -> false else -> false
} }
} }

View File

@ -63,7 +63,7 @@ class Verifier(val ltx: LedgerTransaction,
// 2. Check that the attachments satisfy the constraints of the states. (The contract verification code is correct.) // 2. Check that the attachments satisfy the constraints of the states. (The contract verification code is correct.)
verifyConstraints(contractAttachmentsByContract) verifyConstraints(contractAttachmentsByContract)
// 3. Check that the actual state constraints are correct. This is necessary because transactions can be build by potentially malicious nodes // 3. Check that the actual state constraints are correct. This is necessary because transactions can be built by potentially malicious nodes
// who can create output states with a weaker constraint which can be exploited in a future transaction. // who can create output states with a weaker constraint which can be exploited in a future transaction.
verifyConstraintsValidity(contractAttachmentsByContract) verifyConstraintsValidity(contractAttachmentsByContract)
@ -82,21 +82,25 @@ class Verifier(val ltx: LedgerTransaction,
private fun getUniqueContractAttachmentsByContract(): Map<ContractClassName, ContractAttachment> { private fun getUniqueContractAttachmentsByContract(): Map<ContractClassName, ContractAttachment> {
val contractClasses = allStates.map { it.contract }.toSet() val contractClasses = allStates.map { it.contract }.toSet()
// Check that there are no duplicate attachments added.
if (ltx.attachments.size != ltx.attachments.toSet().size) throw TransactionVerificationException.DuplicateAttachmentsRejection(ltx.id, ltx.attachments.groupBy { it }.filterValues { it.size > 1 }.keys.first())
// For each attachment this finds all the relevant state contracts that it provides. // For each attachment this finds all the relevant state contracts that it provides.
// And then maps them to the attachment. // And then maps them to the attachment.
val contractAttachmentsPerContract: List<Pair<ContractClassName, ContractAttachment>> = ltx.attachments val contractAttachmentsPerContract: List<Pair<ContractClassName, ContractAttachment>> = ltx.attachments
.mapNotNull { it as? ContractAttachment } .mapNotNull { it as? ContractAttachment } // only contract attachments are relevant.
.distinctBy { it.id }
.flatMap { attachment -> .flatMap { attachment ->
contractClasses.filter { it in attachment.allContracts }.map { it to attachment } // Find which relevant contracts are present in the current attachment and return them as a list
contractClasses
.filter { it in attachment.allContracts }
.map { it to attachment }
} }
// It is forbidden to add multiple attachments for the same contract. // It is forbidden to add multiple attachments for the same contract.
val contractWithMultipleAttachments = contractAttachmentsPerContract val contractWithMultipleAttachments = contractAttachmentsPerContract
.groupBy { it.first } .groupBy { it.first } // Group by contract.
.filter { (_, attachments) -> attachments.size > 1 } .filter { (_, attachments) -> attachments.size > 1 } // And only keep contracts that are in multiple attachments. It's guaranteed that attachments were unique by a previous check.
.keys .keys.firstOrNull() // keep the first one - if any - to throw a meaningful exception.
.firstOrNull()
if (contractWithMultipleAttachments != null) throw TransactionVerificationException.ConflictingAttachmentsRejection(ltx.id, contractWithMultipleAttachments) if (contractWithMultipleAttachments != null) throw TransactionVerificationException.ConflictingAttachmentsRejection(ltx.id, contractWithMultipleAttachments)
val result = contractAttachmentsPerContract.toMap() val result = contractAttachmentsPerContract.toMap()