CORDA-941 - Instead of storing the network map contract implementation whitelist along with (#2581)

the constraint, pass it in during verification on LedgerTransaction
This commit is contained in:
Andrius Dagys 2018-02-21 16:45:50 +00:00 committed by Katelyn Baker
parent adf9d50940
commit 7358e902a5
6 changed files with 57 additions and 45 deletions

View File

@ -338,17 +338,6 @@ public final class net.corda.core.contracts.ComponentGroupEnum extends java.lang
public static net.corda.core.contracts.ComponentGroupEnum valueOf(String)
public static net.corda.core.contracts.ComponentGroupEnum[] values()
##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.contracts.ConstraintAttachment extends java.lang.Object implements net.corda.core.contracts.Attachment
public <init>(net.corda.core.contracts.ContractAttachment, String)
public void extractFile(String, java.io.OutputStream)
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.ContractAttachment getContractAttachment()
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
@org.jetbrains.annotations.NotNull public List getSigners()
public int getSize()
@org.jetbrains.annotations.NotNull public final String getStateContract()
@org.jetbrains.annotations.NotNull public java.io.InputStream open()
@org.jetbrains.annotations.NotNull public jar.JarInputStream openAsJAR()
##
@net.corda.core.serialization.CordaSerializable public interface net.corda.core.contracts.Contract
public abstract void verify(net.corda.core.transactions.LedgerTransaction)
##
@ -641,7 +630,6 @@ public static final class net.corda.core.contracts.UniqueIdentifier$Companion ex
@org.jetbrains.annotations.NotNull public abstract net.corda.core.contracts.ContractState upgrade(net.corda.core.contracts.ContractState)
##
@net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public final class net.corda.core.contracts.WhitelistedByZoneAttachmentConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint
public <init>(Map)
public boolean isSatisfiedBy(net.corda.core.contracts.Attachment)
##
@net.corda.core.DoNotImplement public interface net.corda.core.cordapp.Cordapp

View File

@ -1,8 +1,9 @@
package net.corda.core.contracts
import net.corda.core.DoNotImplement
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint.isSatisfiedBy
import net.corda.core.crypto.SecureHash
import net.corda.core.node.services.AttachmentId
import net.corda.core.internal.AttachmentWithContext
import net.corda.core.serialization.CordaSerializable
/** Constrain which contract-code-containing attachment can be used with a [ContractState]. */
@ -27,18 +28,14 @@ data class HashAttachmentConstraint(val attachmentId: SecureHash) : AttachmentCo
* An [AttachmentConstraint] that verifies that the hash of the attachment is in the network parameters whitelist.
* See: [net.corda.core.node.NetworkParameters.whitelistedContractImplementations]
* It allows for centralized control over the cordapps that can be used.
*
* @param whitelistedContractImplementations whitelisted attachment IDs by contract class name.
*/
class WhitelistedByZoneAttachmentConstraint(private val whitelistedContractImplementations: Map<String, List<AttachmentId>>) : AttachmentConstraint {
object WhitelistedByZoneAttachmentConstraint : AttachmentConstraint {
override fun isSatisfiedBy(attachment: Attachment): Boolean {
return whitelistedContractImplementations.let { whitelist ->
when (attachment) {
is ConstraintAttachment -> attachment.id in (whitelist[attachment.stateContract] ?: emptyList())
else -> false
}
}
return if (attachment is AttachmentWithContext) {
val whitelist = attachment.whitelistedContractImplementations
?: throw IllegalStateException("Unable to verify WhitelistedByZoneAttachmentConstraint - whitelist not specified")
attachment.id in (whitelist[attachment.stateContract] ?: emptyList())
} else false
}
}
@ -56,16 +53,4 @@ object AutomaticHashConstraint : AttachmentConstraint {
override fun isSatisfiedBy(attachment: Attachment): Boolean {
throw UnsupportedOperationException("Contracts cannot be satisfied by an AutomaticHashConstraint placeholder")
}
}
/**
* Used only for passing to the Attachment constraint verification.
* Encapsulates a [ContractAttachment] and the state contract
*/
class ConstraintAttachment(val contractAttachment: ContractAttachment, val stateContract: ContractClassName) : Attachment by contractAttachment {
init {
require(stateContract in contractAttachment.allContracts) {
"This ConstraintAttachment was not initialised properly"
}
}
}
}

View File

@ -0,0 +1,22 @@
package net.corda.core.internal
import net.corda.core.contracts.Attachment
import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName
import net.corda.core.node.services.AttachmentId
/**
* Used only for passing to the Attachment constraint verification.
*/
class AttachmentWithContext(
val contractAttachment: ContractAttachment,
val stateContract: ContractClassName,
/** Required for verifying [WhitelistedByZoneAttachmentConstraint] */
val whitelistedContractImplementations: Map<String, List<AttachmentId>>?
) : Attachment by contractAttachment {
init {
require(stateContract in contractAttachment.allContracts) {
"This AttachmentWithContext was not initialised properly"
}
}
}

View File

@ -3,9 +3,11 @@ package net.corda.core.transactions
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.Party
import net.corda.core.internal.AttachmentWithContext
import net.corda.core.internal.UpgradeCommand
import net.corda.core.internal.castIfPossible
import net.corda.core.internal.uncheckedCast
import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.Try
import java.security.PublicKey
@ -27,7 +29,7 @@ import java.util.function.Predicate
// currently sends this across to out-of-process verifiers. We'll need to change that first.
// DOCSTART 1
@CordaSerializable
data class LedgerTransaction(
data class LedgerTransaction @JvmOverloads constructor(
/** The resolved input states which will be consumed/invalidated by the execution of this transaction. */
override val inputs: List<StateAndRef<ContractState>>,
override val outputs: List<TransactionState<ContractState>>,
@ -39,7 +41,8 @@ data class LedgerTransaction(
override val id: SecureHash,
override val notary: Party?,
val timeWindow: TimeWindow?,
val privacySalt: PrivacySalt
val privacySalt: PrivacySalt,
private val networkParameters: NetworkParameters? = null
) : FullTransaction() {
//DOCEND 1
init {
@ -108,7 +111,8 @@ data class LedgerTransaction(
}
val contractAttachment = uniqueAttachmentsForStateContract.first()
if (!state.constraint.isSatisfiedBy(ConstraintAttachment(contractAttachment, state.contract))) {
val constraintAttachment = AttachmentWithContext(contractAttachment, state.contract, networkParameters?.whitelistedContractImplementations)
if (!state.constraint.isSatisfiedBy(constraintAttachment)) {
throw TransactionVerificationException.ContractConstraintRejection(id, state.contract)
}
}
@ -414,5 +418,17 @@ data class LedgerTransaction(
* @throws IllegalArgumentException if no item matches the id.
*/
fun getAttachment(id: SecureHash): Attachment = attachments.first { it.id == id }
fun copy(inputs: List<StateAndRef<ContractState>>,
outputs: List<TransactionState<ContractState>>,
commands: List<CommandWithParties<CommandData>>,
attachments: List<Attachment>,
id: SecureHash,
notary: Party?,
timeWindow: TimeWindow?,
privacySalt: PrivacySalt
) = copy(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, null)
}

View File

@ -103,7 +103,7 @@ open class TransactionBuilder(
val resolvedOutputs = outputs.map { state ->
when {
state.constraint !is AutomaticHashConstraint -> state
useWhitelistedByZoneAttachmentConstraint(state.contract, services.networkParameters) -> state.copy(constraint = WhitelistedByZoneAttachmentConstraint(services.networkParameters.whitelistedContractImplementations))
useWhitelistedByZoneAttachmentConstraint(state.contract, services.networkParameters) -> state.copy(constraint = WhitelistedByZoneAttachmentConstraint)
else -> services.cordappProvider.getContractAttachmentID(state.contract)?.let {
state.copy(constraint = HashAttachmentConstraint(it))
} ?: throw MissingContractAttachments(listOf(state))

View File

@ -5,6 +5,7 @@ import net.corda.core.contracts.ComponentGroupEnum.*
import net.corda.core.crypto.*
import net.corda.core.identity.Party
import net.corda.core.internal.Emoji
import net.corda.core.node.NetworkParameters
import net.corda.core.node.ServicesForResolution
import net.corda.core.node.services.AttachmentId
import net.corda.core.serialization.CordaSerializable
@ -88,7 +89,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
resolveIdentity = { services.identityService.partyFromKey(it) },
resolveAttachment = { services.attachments.openAttachment(it) },
resolveStateRef = { services.loadState(it) },
maxTransactionSize = services.networkParameters.maxTransactionSize
networkParameters = services.networkParameters
)
}
@ -107,14 +108,14 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
resolveStateRef: (StateRef) -> TransactionState<*>?,
resolveContractAttachment: (TransactionState<ContractState>) -> AttachmentId?
): LedgerTransaction {
return toLedgerTransactionInternal(resolveIdentity, resolveAttachment, resolveStateRef, 10485760)
return toLedgerTransactionInternal(resolveIdentity, resolveAttachment, resolveStateRef,null)
}
private fun toLedgerTransactionInternal(
resolveIdentity: (PublicKey) -> Party?,
resolveAttachment: (SecureHash) -> Attachment?,
resolveStateRef: (StateRef) -> TransactionState<*>?,
maxTransactionSize: Int
networkParameters: NetworkParameters?
): LedgerTransaction {
// Look up public keys to authenticated identities.
val authenticatedArgs = commands.map {
@ -125,8 +126,8 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
resolveStateRef(ref)?.let { StateAndRef(it, ref) } ?: throw TransactionResolutionException(ref.txhash)
}
val attachments = attachments.map { resolveAttachment(it) ?: throw AttachmentResolutionException(it) }
val ltx = LedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, notary, timeWindow, privacySalt)
checkTransactionSize(ltx, maxTransactionSize)
val ltx = LedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, notary, timeWindow, privacySalt, networkParameters)
checkTransactionSize(ltx, networkParameters?.maxTransactionSize ?: 10485760)
return ltx
}