mirror of
https://github.com/corda/corda.git
synced 2025-03-21 19:45:21 +00:00
CORDA-2577 Disable or delete the no-downgrade rule (#4741)
With (Contract JARs) rolling upgrades the downgrade rule cannot be effectively check as the platform can't tell the difference between a transaction that's downgrading because of an attack, vs a transaction that's downgrading because Alice has upgraded but Bob hasn't yet. During a rolling upgrade we would expect state versions to fluctuate up and down as data gets read/written by a mix of nodes. With the feature as implemented Alice will upgrade and start trading with Bob. Bob will be able to read and process the states Alice sent him, but the moment he tries to consume such a state he will fail. This will result in cascading flow deaths and a hung business network the moment an upgrade starts.
This commit is contained in:
parent
873e9a5442
commit
10e7c07c11
@ -1132,10 +1132,6 @@ public static final class net.corda.core.contracts.TransactionVerificationExcept
|
||||
public <init>(net.corda.core.crypto.SecureHash, net.corda.core.contracts.TransactionState<? extends net.corda.core.contracts.ContractState>)
|
||||
##
|
||||
@CordaSerializable
|
||||
public static final class net.corda.core.contracts.TransactionVerificationException$TransactionVerificationVersionException extends net.corda.core.contracts.TransactionVerificationException
|
||||
public <init>(net.corda.core.crypto.SecureHash, String, String, String)
|
||||
##
|
||||
@CordaSerializable
|
||||
public abstract class net.corda.core.contracts.TypeOnlyCommandData extends java.lang.Object implements net.corda.core.contracts.CommandData
|
||||
public <init>()
|
||||
public boolean equals(Object)
|
||||
|
@ -262,15 +262,6 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S
|
||||
@KeepForDJVM
|
||||
class OverlappingAttachmentsException(txId: SecureHash, path: String) : TransactionVerificationException(txId, "Multiple attachments define a file at $path.", null)
|
||||
|
||||
/**
|
||||
* Thrown when a transaction appears to be trying to downgrade a state to an earlier version of the app that defines it.
|
||||
* This could be an attempt to exploit a bug in the app, so we prevent it.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class TransactionVerificationVersionException(txId: SecureHash, contractClassName: ContractClassName, inputVersion: String, outputVersion: String)
|
||||
: TransactionVerificationException(txId, "No-Downgrade Rule has been breached for contract class $contractClassName. " +
|
||||
"The output state contract version '$outputVersion' is lower than the version of the input state '$inputVersion'.", null)
|
||||
|
||||
/**
|
||||
* Thrown to indicate that a contract attachment is not signed by the network-wide package owner. Please note that
|
||||
* the [txId] will always be [SecureHash.zeroHash] because package ownership is an error with a particular attachment,
|
||||
|
@ -44,7 +44,6 @@ class Verifier(val ltx: LedgerTransaction, private val transactionClassLoader: C
|
||||
// list, the contents of which need to be deserialized under the correct classloader.
|
||||
checkNoNotaryChange()
|
||||
checkEncumbrancesValid()
|
||||
validateContractVersions()
|
||||
validateStatesAgainstContract()
|
||||
val hashToSignatureConstrainedContracts = verifyConstraintsValidity()
|
||||
verifyConstraints(hashToSignatureConstrainedContracts)
|
||||
@ -207,20 +206,6 @@ class Verifier(val ltx: LedgerTransaction, private val transactionClassLoader: C
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that contract class versions of output states are greater than or equal to the versions of the input states.
|
||||
*/
|
||||
private fun validateContractVersions() {
|
||||
contractAttachmentsByContract.forEach { contractClassName, attachments ->
|
||||
val outputVersion = attachments.signed?.version ?: attachments.unsigned?.version ?: CordappImpl.DEFAULT_CORDAPP_VERSION
|
||||
inputVersions[contractClassName]?.let {
|
||||
if (it > outputVersion) {
|
||||
throw TransactionVerificationException.TransactionVerificationVersionException(ltx.id, contractClassName, "$it", "$outputVersion")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For all input and output [TransactionState]s, validates that the wrapped [ContractState] matches up with the
|
||||
* wrapped [Contract], as declared by the [BelongsToContract] annotation on the [ContractState]'s class.
|
||||
|
@ -391,7 +391,7 @@ class ConstraintsPropagationTests {
|
||||
recordTransactions(SignedTransaction(wireTransaction, sigs))
|
||||
}
|
||||
@Test
|
||||
fun `Input state contract version is not compatible with lower version`() {
|
||||
fun `Input state contract version may be incompatible with lower version`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||
@ -404,7 +404,7 @@ class ConstraintsPropagationTests {
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
failsWith("No-Downgrade Rule has been breached for contract class net.corda.finance.contracts.asset.Cash. The output state contract version '1' is lower than the version of the input state '2'.")
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -448,7 +448,7 @@ class ConstraintsPropagationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `All input states contract version must be lower that current contract version`() {
|
||||
fun `Input states contract version may be lower that current contract version`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
|
||||
@ -467,13 +467,13 @@ class ConstraintsPropagationTests {
|
||||
input("c2")
|
||||
output(Cash.PROGRAM_ID, "c3", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(2000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
failsWith("No-Downgrade Rule has been breached for contract class net.corda.finance.contracts.asset.Cash. The output state contract version '1' is lower than the version of the input state '2'.")
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Input state with contract version can not be downgraded to no version`() {
|
||||
fun `Input state with contract version can be downgraded to no version`() {
|
||||
ledgerServices.ledger(DUMMY_NOTARY) {
|
||||
ledgerServices.recordTransaction(transaction {
|
||||
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
|
||||
@ -486,7 +486,7 @@ class ConstraintsPropagationTests {
|
||||
input("c1")
|
||||
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
|
||||
command(ALICE_PUBKEY, Cash.Commands.Move())
|
||||
failsWith("No-Downgrade Rule has been breached for contract class net.corda.finance.contracts.asset.Cash. The output state contract version '1' is lower than the version of the input state '2'.")
|
||||
verifies()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ CorDapp is 4 or greater, then transaction verification will fail with a ``Transa
|
||||
the owning ``Contract`` *can* be identified, but the ``ContractState`` has been bundled with a different contract, then
|
||||
transaction verification will fail with a ``TransactionContractConflictException``.
|
||||
|
||||
.. _contract_non-downgrade_rule_ref:
|
||||
.. _contract_downgrade_rule_ref:
|
||||
|
||||
App versioning with signature constraints
|
||||
-----------------------------------------
|
||||
|
@ -100,10 +100,7 @@ properly for future releases.
|
||||
future not hold true. You should know the platform version of the node releases you want to target.
|
||||
|
||||
The new ``versionId`` number is a version code for **your** app, and is unrelated to Corda's own versions.
|
||||
It is used to block state downgrades: when a state constraint can be satisfied
|
||||
by multiple attachments, the version is tracked in the ledger and cannot decrement. This ensures security
|
||||
fixes in CorDapps stick and can't be reversed by downgrading states to an earlier version. See
|
||||
":ref:`contract_non-downgrade_rule_ref`" for more information.
|
||||
It is used to informative purposes only. See ":ref:`contract_downgrade_rule_ref`" for more information.
|
||||
|
||||
**Split your app into contract and workflow JARs.** The duplication between ``contract`` and ``workflow`` blocks exists because you should split your app into
|
||||
two separate JARs/modules, one that contains on-ledger validation code like states and contracts, and one
|
||||
|
@ -50,10 +50,6 @@ Version 4.0
|
||||
|
||||
* ``JacksonSupport.createInMemoryMapper`` was incorrectly marked as deprecated and is no longer so.
|
||||
|
||||
* Transaction building and verification enforces new contract attachment version non-downgrade rule.
|
||||
For a given contract class, the contract attachment of the output states must be of the same or newer version than the contract attachment of the input states.
|
||||
See :ref:`Contract attachment non-downgrade rule <contract_non-downgrade_rule_ref>` for further information.
|
||||
|
||||
* Standardised CorDapp version identifiers in jar manifests (aligned with associated cordapp Gradle plugin changes).
|
||||
Updated all samples to reflect new conventions.
|
||||
|
||||
|
@ -122,9 +122,6 @@ allows tool developers to assume that if a class name appears to be owned by an
|
||||
semantics of that class actually *were* defined by that organisation, thus eliminating edge cases that
|
||||
might otherwise cause confusion.
|
||||
|
||||
**No downgrades.** Transaction building and verification enforces new contract attachment version non-downgrade rule.
|
||||
For a given contract class, the contract attachment of the output states must be of the same or newer version than
|
||||
the contract attachment of the input states. See :ref:`Contract attachment non-downgrade rule <contract_non-downgrade_rule_ref>` for further information.
|
||||
|
||||
Network parameters in transactions
|
||||
++++++++++++++++++++++++++++++++++
|
||||
|
@ -96,9 +96,7 @@ It's entirely expected and reasonable to have an open source contracts module an
|
||||
sophisticated or proprietary business logic, machine learning models, even user interface code. There's nothing that restricts it to just
|
||||
being Corda flows or services.
|
||||
|
||||
.. important:: The ``versionId`` specified for the JAR manifest is checked by the platform. Downgrades are not allowed: you cannot take a state
|
||||
that was created with version 5 of your app, and then create a state with version 4. This is to prevent attacks in which bugs
|
||||
are fixed, but an adversary uses an old version of the app to continue exploiting them. Version tracking in states is handled for you
|
||||
automatically as long as the information is provided in your Gradle file. See ":ref:`contract_non-downgrade_rule_ref`" for more information.
|
||||
.. important:: The ``versionId`` specified for the JAR manifest is checked by the platform and is used for informative purposes only.
|
||||
See ":ref:`contract_downgrade_rule_ref`" for more information.
|
||||
|
||||
.. note:: You can read the original design doc here: :doc:`design/targetversion/design`.
|
Loading…
x
Reference in New Issue
Block a user