Merge branch 'master' into feature/CORDA-2570/revert-to-one-attachment-per-contract

# Conflicts:
#	core/src/main/kotlin/net/corda/core/internal/TransactionVerifierServiceInternal.kt
#	core/src/test/kotlin/net/corda/core/contracts/ConstraintsPropagationTests.kt
This commit is contained in:
tudor.malene@gmail.com
2019-02-13 13:10:28 +00:00
9 changed files with 160 additions and 93 deletions

View File

@ -275,15 +275,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,

View File

@ -375,6 +375,24 @@ class ConstraintsPropagationTests {
SignableData(wireTransaction.id, SignatureMetadata(4, Crypto.findSignatureScheme(nodeKey).schemeNumberID)), nodeKey))
recordTransactions(SignedTransaction(wireTransaction, sigs))
}
@Test
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"))
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
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())
verifies()
}
}
}
@Test
fun `Input state contract version is compatible with the same version`() {
@ -414,6 +432,50 @@ class ConstraintsPropagationTests {
}
}
@Test
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"))
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
transaction {
input("c1")
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())
verifies()
}
}
}
@Test
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"))
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), emptyMap())
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())
verifies()
}
}
}
@Test
fun `Input state without contract version is compatible with any version`() {
ledgerServices.ledger(DUMMY_NOTARY) {