From 74c80aafd657e2718787f97932b89a069af7949f Mon Sep 17 00:00:00 2001 From: josecoll Date: Fri, 9 Nov 2018 12:27:28 +0000 Subject: [PATCH] CORDA-2178 Signature constraints minimum platform version checking (#4161) * Minimum platform version checking for new signature constraints feature. * Fix broken JUnit * NP safety checking on network parameters. * Warning and auto-downgrade of signed states that do not meet the minimum network platform version. --- .../net/corda/core/internal/CordaUtils.kt | 9 ++++++--- .../core/transactions/LedgerTransaction.kt | 3 +++ .../core/transactions/TransactionBuilder.kt | 20 ++++++++++++++++++- .../transactions/TransactionBuilderTest.kt | 3 ++- docs/source/api-contract-constraints.rst | 4 ++++ 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt b/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt index 8c4be6530f..6595851d22 100644 --- a/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt @@ -20,12 +20,15 @@ import org.slf4j.MDC const val PLATFORM_VERSION = 4 fun ServicesForResolution.ensureMinimumPlatformVersion(requiredMinPlatformVersion: Int, feature: String) { - val currentMinPlatformVersion = networkParameters.minimumPlatformVersion - if (currentMinPlatformVersion < requiredMinPlatformVersion) { + checkMinimumPlatformVersion(networkParameters.minimumPlatformVersion, requiredMinPlatformVersion, feature) +} + +fun checkMinimumPlatformVersion(minimumPlatformVersion: Int, requiredMinPlatformVersion: Int, feature: String) { + if (minimumPlatformVersion < requiredMinPlatformVersion) { throw ZoneVersionTooLowException( "$feature requires all nodes on the Corda compatibility zone to be running at least platform version " + "$requiredMinPlatformVersion. The current zone is only enforcing a minimum platform version of " + - "$currentMinPlatformVersion. Please contact your zone operator." + "$minimumPlatformVersion. Please contact your zone operator." ) } } diff --git a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt index ebce06de33..64ccb45cbf 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt @@ -7,6 +7,7 @@ import net.corda.core.crypto.isFulfilledBy import net.corda.core.identity.Party import net.corda.core.internal.AttachmentWithContext import net.corda.core.internal.castIfPossible +import net.corda.core.internal.checkMinimumPlatformVersion import net.corda.core.internal.uncheckedCast import net.corda.core.node.NetworkParameters import net.corda.core.serialization.CordaSerializable @@ -148,6 +149,8 @@ data class LedgerTransaction @JvmOverloads constructor( val contractAttachment = uniqueAttachmentsForStateContract.first() val constraintAttachment = AttachmentWithContext(contractAttachment, state.contract, networkParameters?.whitelistedContractImplementations) + if (state.constraint is SignatureAttachmentConstraint) + checkMinimumPlatformVersion(networkParameters?.minimumPlatformVersion ?: 1, 4, "Signature constraints") if (!state.constraint.isSatisfiedBy(constraintAttachment)) { throw TransactionVerificationException.ContractConstraintRejection(id, state.contract) } diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt index e246128e0f..a3156d9bcf 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt @@ -56,6 +56,10 @@ open class TransactionBuilder @JvmOverloads constructor( private val inputsWithTransactionState = arrayListOf>() private val referencesWithTransactionState = arrayListOf>() + companion object { + private val log = contextLogger() + } + /** * Creates a copy of the builder. */ @@ -165,7 +169,21 @@ open class TransactionBuilder @JvmOverloads constructor( return when { attachmentSigners.isEmpty() -> HashAttachmentConstraint(attachmentId) - else -> makeSignatureAttachmentConstraint(attachmentSigners) + else -> { + // Auto downgrade: signature constraints only available with a corda network minimum platform version of >= 4 + if (services.networkParameters.minimumPlatformVersion < 4) { + log.warn("Signature constraints not available on network requiring a minimum platform version of ${services.networkParameters.minimumPlatformVersion}") + if (useWhitelistedByZoneAttachmentConstraint(state.contract, services.networkParameters)) { + log.warn("Reverting back to using whitelisted zone constraints") + WhitelistedByZoneAttachmentConstraint + } + else { + log.warn("Reverting back to using hash constraints") + HashAttachmentConstraint(attachmentId) + } + } + else makeSignatureAttachmentConstraint(attachmentSigners) + } } } diff --git a/core/src/test/kotlin/net/corda/core/transactions/TransactionBuilderTest.kt b/core/src/test/kotlin/net/corda/core/transactions/TransactionBuilderTest.kt index cd9456cdc3..ecfdabdf20 100644 --- a/core/src/test/kotlin/net/corda/core/transactions/TransactionBuilderTest.kt +++ b/core/src/test/kotlin/net/corda/core/transactions/TransactionBuilderTest.kt @@ -8,6 +8,7 @@ import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.SecureHash import net.corda.core.identity.Party import net.corda.core.internal.AbstractAttachment +import net.corda.core.internal.PLATFORM_VERSION import net.corda.core.node.ServicesForResolution import net.corda.core.node.ZoneVersionTooLowException import net.corda.core.node.services.AttachmentStorage @@ -40,7 +41,7 @@ class TransactionBuilderTest { val cordappProvider = rigorousMock() doReturn(cordappProvider).whenever(services).cordappProvider doReturn(contractAttachmentId).whenever(cordappProvider).getContractAttachmentID(DummyContract.PROGRAM_ID) - doReturn(testNetworkParameters()).whenever(services).networkParameters + doReturn(testNetworkParameters(minimumPlatformVersion = PLATFORM_VERSION)).whenever(services).networkParameters val attachmentStorage = rigorousMock() doReturn(attachmentStorage).whenever(services).attachments diff --git a/docs/source/api-contract-constraints.rst b/docs/source/api-contract-constraints.rst index caf9a58168..3611118906 100644 --- a/docs/source/api-contract-constraints.rst +++ b/docs/source/api-contract-constraints.rst @@ -88,6 +88,10 @@ and the smoothest to deploy: no restarts or contract upgrade transactions are ne When CorDapp is build using :ref:`corda-gradle-plugin ` the JAR is signed by Corda development key by default, an external keystore can be configured or signing can be disabled. +.. warning:: CorDapps can only use signature constraints when participating in a Corda network using a minimum platform version of 4. + An auto downgrade rule applies to signed CorDapps built and tested with Corda 4 but running on a Corda network of a lower version: + if the associated contract class is whitelisted in the network parameters then zone constraints are applied, otherwise hash constraints are used. + **Defaults.** The default constraint type is either a zone constraint, if the network parameters in effect when the transaction is built contain an entry for that contract class, or a hash constraint if not.