mirror of
https://github.com/corda/corda.git
synced 2025-06-01 23:20:54 +00:00
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:
parent
adf9d50940
commit
7358e902a5
@ -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 valueOf(String)
|
||||||
public static net.corda.core.contracts.ComponentGroupEnum[] values()
|
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
|
@net.corda.core.serialization.CordaSerializable public interface net.corda.core.contracts.Contract
|
||||||
public abstract void verify(net.corda.core.transactions.LedgerTransaction)
|
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)
|
@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
|
@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)
|
public boolean isSatisfiedBy(net.corda.core.contracts.Attachment)
|
||||||
##
|
##
|
||||||
@net.corda.core.DoNotImplement public interface net.corda.core.cordapp.Cordapp
|
@net.corda.core.DoNotImplement public interface net.corda.core.cordapp.Cordapp
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.core.DoNotImplement
|
import net.corda.core.DoNotImplement
|
||||||
|
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint.isSatisfiedBy
|
||||||
import net.corda.core.crypto.SecureHash
|
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
|
import net.corda.core.serialization.CordaSerializable
|
||||||
|
|
||||||
/** Constrain which contract-code-containing attachment can be used with a [ContractState]. */
|
/** 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.
|
* An [AttachmentConstraint] that verifies that the hash of the attachment is in the network parameters whitelist.
|
||||||
* See: [net.corda.core.node.NetworkParameters.whitelistedContractImplementations]
|
* See: [net.corda.core.node.NetworkParameters.whitelistedContractImplementations]
|
||||||
* It allows for centralized control over the cordapps that can be used.
|
* 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 {
|
override fun isSatisfiedBy(attachment: Attachment): Boolean {
|
||||||
return whitelistedContractImplementations.let { whitelist ->
|
return if (attachment is AttachmentWithContext) {
|
||||||
when (attachment) {
|
val whitelist = attachment.whitelistedContractImplementations
|
||||||
is ConstraintAttachment -> attachment.id in (whitelist[attachment.stateContract] ?: emptyList())
|
?: throw IllegalStateException("Unable to verify WhitelistedByZoneAttachmentConstraint - whitelist not specified")
|
||||||
else -> false
|
attachment.id in (whitelist[attachment.stateContract] ?: emptyList())
|
||||||
}
|
} else false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,16 +53,4 @@ object AutomaticHashConstraint : AttachmentConstraint {
|
|||||||
override fun isSatisfiedBy(attachment: Attachment): Boolean {
|
override fun isSatisfiedBy(attachment: Attachment): Boolean {
|
||||||
throw UnsupportedOperationException("Contracts cannot be satisfied by an AutomaticHashConstraint placeholder")
|
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,9 +3,11 @@ package net.corda.core.transactions
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.AttachmentWithContext
|
||||||
import net.corda.core.internal.UpgradeCommand
|
import net.corda.core.internal.UpgradeCommand
|
||||||
import net.corda.core.internal.castIfPossible
|
import net.corda.core.internal.castIfPossible
|
||||||
import net.corda.core.internal.uncheckedCast
|
import net.corda.core.internal.uncheckedCast
|
||||||
|
import net.corda.core.node.NetworkParameters
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.utilities.Try
|
import net.corda.core.utilities.Try
|
||||||
import java.security.PublicKey
|
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.
|
// currently sends this across to out-of-process verifiers. We'll need to change that first.
|
||||||
// DOCSTART 1
|
// DOCSTART 1
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
data class LedgerTransaction(
|
data class LedgerTransaction @JvmOverloads constructor(
|
||||||
/** The resolved input states which will be consumed/invalidated by the execution of this transaction. */
|
/** The resolved input states which will be consumed/invalidated by the execution of this transaction. */
|
||||||
override val inputs: List<StateAndRef<ContractState>>,
|
override val inputs: List<StateAndRef<ContractState>>,
|
||||||
override val outputs: List<TransactionState<ContractState>>,
|
override val outputs: List<TransactionState<ContractState>>,
|
||||||
@ -39,7 +41,8 @@ data class LedgerTransaction(
|
|||||||
override val id: SecureHash,
|
override val id: SecureHash,
|
||||||
override val notary: Party?,
|
override val notary: Party?,
|
||||||
val timeWindow: TimeWindow?,
|
val timeWindow: TimeWindow?,
|
||||||
val privacySalt: PrivacySalt
|
val privacySalt: PrivacySalt,
|
||||||
|
private val networkParameters: NetworkParameters? = null
|
||||||
) : FullTransaction() {
|
) : FullTransaction() {
|
||||||
//DOCEND 1
|
//DOCEND 1
|
||||||
init {
|
init {
|
||||||
@ -108,7 +111,8 @@ data class LedgerTransaction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val contractAttachment = uniqueAttachmentsForStateContract.first()
|
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)
|
throw TransactionVerificationException.ContractConstraintRejection(id, state.contract)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -414,5 +418,17 @@ data class LedgerTransaction(
|
|||||||
* @throws IllegalArgumentException if no item matches the id.
|
* @throws IllegalArgumentException if no item matches the id.
|
||||||
*/
|
*/
|
||||||
fun getAttachment(id: SecureHash): Attachment = attachments.first { it.id == 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ open class TransactionBuilder(
|
|||||||
val resolvedOutputs = outputs.map { state ->
|
val resolvedOutputs = outputs.map { state ->
|
||||||
when {
|
when {
|
||||||
state.constraint !is AutomaticHashConstraint -> state
|
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 {
|
else -> services.cordappProvider.getContractAttachmentID(state.contract)?.let {
|
||||||
state.copy(constraint = HashAttachmentConstraint(it))
|
state.copy(constraint = HashAttachmentConstraint(it))
|
||||||
} ?: throw MissingContractAttachments(listOf(state))
|
} ?: throw MissingContractAttachments(listOf(state))
|
||||||
|
@ -5,6 +5,7 @@ import net.corda.core.contracts.ComponentGroupEnum.*
|
|||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.Emoji
|
import net.corda.core.internal.Emoji
|
||||||
|
import net.corda.core.node.NetworkParameters
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
import net.corda.core.node.services.AttachmentId
|
import net.corda.core.node.services.AttachmentId
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
@ -88,7 +89,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
|||||||
resolveIdentity = { services.identityService.partyFromKey(it) },
|
resolveIdentity = { services.identityService.partyFromKey(it) },
|
||||||
resolveAttachment = { services.attachments.openAttachment(it) },
|
resolveAttachment = { services.attachments.openAttachment(it) },
|
||||||
resolveStateRef = { services.loadState(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<*>?,
|
resolveStateRef: (StateRef) -> TransactionState<*>?,
|
||||||
resolveContractAttachment: (TransactionState<ContractState>) -> AttachmentId?
|
resolveContractAttachment: (TransactionState<ContractState>) -> AttachmentId?
|
||||||
): LedgerTransaction {
|
): LedgerTransaction {
|
||||||
return toLedgerTransactionInternal(resolveIdentity, resolveAttachment, resolveStateRef, 10485760)
|
return toLedgerTransactionInternal(resolveIdentity, resolveAttachment, resolveStateRef,null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toLedgerTransactionInternal(
|
private fun toLedgerTransactionInternal(
|
||||||
resolveIdentity: (PublicKey) -> Party?,
|
resolveIdentity: (PublicKey) -> Party?,
|
||||||
resolveAttachment: (SecureHash) -> Attachment?,
|
resolveAttachment: (SecureHash) -> Attachment?,
|
||||||
resolveStateRef: (StateRef) -> TransactionState<*>?,
|
resolveStateRef: (StateRef) -> TransactionState<*>?,
|
||||||
maxTransactionSize: Int
|
networkParameters: NetworkParameters?
|
||||||
): LedgerTransaction {
|
): LedgerTransaction {
|
||||||
// Look up public keys to authenticated identities.
|
// Look up public keys to authenticated identities.
|
||||||
val authenticatedArgs = commands.map {
|
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)
|
resolveStateRef(ref)?.let { StateAndRef(it, ref) } ?: throw TransactionResolutionException(ref.txhash)
|
||||||
}
|
}
|
||||||
val attachments = attachments.map { resolveAttachment(it) ?: throw AttachmentResolutionException(it) }
|
val attachments = attachments.map { resolveAttachment(it) ?: throw AttachmentResolutionException(it) }
|
||||||
val ltx = LedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, notary, timeWindow, privacySalt)
|
val ltx = LedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, notary, timeWindow, privacySalt, networkParameters)
|
||||||
checkTransactionSize(ltx, maxTransactionSize)
|
checkTransactionSize(ltx, networkParameters?.maxTransactionSize ?: 10485760)
|
||||||
return ltx
|
return ltx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user