mirror of
https://github.com/corda/corda.git
synced 2025-04-07 19:34:41 +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[] 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
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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.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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user