mirror of
https://github.com/corda/corda.git
synced 2024-12-20 05:28:21 +00:00
Attachment query with contract version, related to CORDA-2150, CORDA-2157 (#4357)
TransactionBuilder loads attachment using attachment storage instead of CordappLoader, contract class version is now Integer (format and stored in db as Integer).
This commit is contained in:
parent
2833013119
commit
d2d13c1dfc
@ -3466,7 +3466,7 @@ public static final class net.corda.core.node.services.vault.AttachmentQueryCrit
|
||||
public <init>(net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<java.time.Instant>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<String>>)
|
||||
public <init>(net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<java.time.Instant>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<String>>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<java.security.PublicKey>>)
|
||||
public <init>(net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<java.time.Instant>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<String>>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<java.security.PublicKey>>, net.corda.core.node.services.vault.ColumnPredicate<Boolean>)
|
||||
public <init>(net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<java.time.Instant>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<String>>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<java.security.PublicKey>>, net.corda.core.node.services.vault.ColumnPredicate<Boolean>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<String>>)
|
||||
public <init>(net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<java.time.Instant>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<String>>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<java.security.PublicKey>>, net.corda.core.node.services.vault.ColumnPredicate<Boolean>, net.corda.core.node.services.vault.ColumnPredicate<Integer>)
|
||||
@Nullable
|
||||
public final net.corda.core.node.services.vault.ColumnPredicate<String> component1()
|
||||
@Nullable
|
||||
@ -3480,11 +3480,11 @@ public static final class net.corda.core.node.services.vault.AttachmentQueryCrit
|
||||
@Nullable
|
||||
public final net.corda.core.node.services.vault.ColumnPredicate<Boolean> component6()
|
||||
@Nullable
|
||||
public final net.corda.core.node.services.vault.ColumnPredicate<java.util.List<String>> component7()
|
||||
public final net.corda.core.node.services.vault.ColumnPredicate<Integer> component7()
|
||||
@NotNull
|
||||
public final net.corda.core.node.services.vault.AttachmentQueryCriteria$AttachmentsQueryCriteria copy(net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<java.time.Instant>)
|
||||
@NotNull
|
||||
public final net.corda.core.node.services.vault.AttachmentQueryCriteria$AttachmentsQueryCriteria copy(net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<java.time.Instant>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<String>>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<java.security.PublicKey>>, net.corda.core.node.services.vault.ColumnPredicate<Boolean>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<String>>)
|
||||
public final net.corda.core.node.services.vault.AttachmentQueryCriteria$AttachmentsQueryCriteria copy(net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<String>, net.corda.core.node.services.vault.ColumnPredicate<java.time.Instant>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<String>>, net.corda.core.node.services.vault.ColumnPredicate<java.util.List<java.security.PublicKey>>, net.corda.core.node.services.vault.ColumnPredicate<Boolean>, net.corda.core.node.services.vault.ColumnPredicate<Integer>)
|
||||
public boolean equals(Object)
|
||||
@Nullable
|
||||
public final net.corda.core.node.services.vault.ColumnPredicate<java.util.List<String>> getContractClassNamesCondition()
|
||||
@ -3497,7 +3497,7 @@ public static final class net.corda.core.node.services.vault.AttachmentQueryCrit
|
||||
@Nullable
|
||||
public final net.corda.core.node.services.vault.ColumnPredicate<String> getUploaderCondition()
|
||||
@Nullable
|
||||
public final net.corda.core.node.services.vault.ColumnPredicate<java.util.List<String>> getVersionCondition()
|
||||
public final net.corda.core.node.services.vault.ColumnPredicate<Integer> getVersionCondition()
|
||||
public int hashCode()
|
||||
@Nullable
|
||||
public final net.corda.core.node.services.vault.ColumnPredicate<Boolean> isSignedCondition()
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.corda.core.contracts
|
||||
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.internal.UNKNOWN_VERSION
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
|
||||
import java.security.PublicKey
|
||||
|
||||
/**
|
||||
@ -20,7 +20,7 @@ class ContractAttachment @JvmOverloads constructor(
|
||||
val additionalContracts: Set<ContractClassName> = emptySet(),
|
||||
val uploader: String? = null,
|
||||
override val signerKeys: List<PublicKey> = emptyList(),
|
||||
val version: String = UNKNOWN_VERSION) : Attachment by attachment {
|
||||
val version: Int = DEFAULT_CORDAPP_VERSION) : Attachment by attachment {
|
||||
|
||||
val allContracts: Set<ContractClassName> get() = additionalContracts + contract
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
package net.corda.core.cordapp
|
||||
|
||||
const val CORDAPP_CONTRACT_VERSION = "Implementation-Version" //TODO will be changed to "Corda-Contract-Version"
|
||||
|
||||
const val DEFAULT_CORDAPP_VERSION: Int = 1
|
@ -20,7 +20,6 @@ const val DEPLOYED_CORDAPP_UPLOADER = "app"
|
||||
const val RPC_UPLOADER = "rpc"
|
||||
const val P2P_UPLOADER = "p2p"
|
||||
const val UNKNOWN_UPLOADER = "unknown"
|
||||
const val UNKNOWN_VERSION = "unknown"
|
||||
|
||||
private val TRUSTED_UPLOADERS = listOf(DEPLOYED_CORDAPP_UPLOADER, RPC_UPLOADER)
|
||||
|
||||
|
@ -283,7 +283,7 @@ sealed class AttachmentQueryCriteria : GenericQueryCriteria<AttachmentQueryCrite
|
||||
val contractClassNamesCondition: ColumnPredicate<List<ContractClassName>>? = null,
|
||||
val signersCondition: ColumnPredicate<List<PublicKey>>? = null,
|
||||
val isSignedCondition: ColumnPredicate<Boolean>? = null,
|
||||
val versionCondition: ColumnPredicate<List<String>>? = null) : AttachmentQueryCriteria() {
|
||||
val versionCondition: ColumnPredicate<Int>? = null) : AttachmentQueryCriteria() {
|
||||
override fun visit(parser: AttachmentsQueryCriteriaParser): Collection<Predicate> {
|
||||
return parser.parseCriteria(this)
|
||||
}
|
||||
@ -302,7 +302,7 @@ sealed class AttachmentQueryCriteria : GenericQueryCriteria<AttachmentQueryCrite
|
||||
fun withContractClassNames(contractClassNamesPredicate: ColumnPredicate<List<ContractClassName>>) = copy(contractClassNamesCondition = contractClassNamesPredicate)
|
||||
fun withSigners(signersPredicate: ColumnPredicate<List<PublicKey>>) = copy(signersCondition = signersPredicate)
|
||||
fun isSigned(isSignedPredicate: ColumnPredicate<Boolean>) = copy(isSignedCondition = isSignedPredicate)
|
||||
fun withVersions(versionsPredicate: ColumnPredicate<List<String>>) = copy(versionCondition = versionsPredicate)
|
||||
fun withVersion(versionPredicate: ColumnPredicate<Int>) = copy(versionCondition = versionPredicate)
|
||||
}
|
||||
|
||||
class AndComposition(override val a: AttachmentQueryCriteria, override val b: AttachmentQueryCriteria): AttachmentQueryCriteria(), GenericQueryCriteria.ChainableQueryCriteria.AndVisitor<AttachmentQueryCriteria, AttachmentsQueryCriteriaParser, AttachmentSort>
|
||||
|
@ -213,7 +213,8 @@ data class AttachmentSort(val columns: Collection<AttachmentSortColumn>) : BaseS
|
||||
enum class AttachmentSortAttribute(val columnName: String) {
|
||||
INSERTION_DATE("insertion_date"),
|
||||
UPLOADER("uploader"),
|
||||
FILENAME("filename")
|
||||
FILENAME("filename"),
|
||||
VERSION ("version")
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
|
@ -4,6 +4,7 @@ import co.paralleluniverse.strands.Strand
|
||||
import net.corda.core.CordaInternal
|
||||
import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.*
|
||||
@ -14,7 +15,9 @@ import net.corda.core.node.ZoneVersionTooLowException
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.node.services.KeyManagementService
|
||||
import net.corda.core.node.services.vault.AttachmentQueryCriteria
|
||||
import net.corda.core.node.services.vault.AttachmentSort
|
||||
import net.corda.core.node.services.vault.Builder
|
||||
import net.corda.core.node.services.vault.Sort
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializationFactory
|
||||
import net.corda.core.utilities.contextLogger
|
||||
@ -398,14 +401,21 @@ open class TransactionBuilder @JvmOverloads constructor(
|
||||
* This method should only be called for upgradeable contracts.
|
||||
*
|
||||
* For now we use the currently installed CorDapp version.
|
||||
* TODO - When the SignatureConstraint and contract version logic is in, this will need to query the attachments table and find the latest one that satisfies all constraints.
|
||||
* TODO - select a version of the contract that is no older than the one from the previous transactions.
|
||||
*/
|
||||
private fun selectAttachmentThatSatisfiesConstraints(isReference: Boolean, contractClassName: String, states: List<TransactionState<ContractState>>, services: ServicesForResolution): AttachmentId {
|
||||
val constraints = states.map { it.constraint }
|
||||
require(constraints.none { it in automaticConstraints })
|
||||
require(isReference || constraints.none { it is HashAttachmentConstraint })
|
||||
return services.cordappProvider.getContractAttachmentID(contractClassName) ?: throw MissingContractAttachments(states)
|
||||
|
||||
//TODO will be set by the code pending in the other PR
|
||||
val minimumRequiredContractClassVersion = DEFAULT_CORDAPP_VERSION
|
||||
|
||||
//TODO consider move it to attachment service method e.g. getContractAttachmentWithHighestVersion(contractClassName, minContractVersion)
|
||||
val attachmentQueryCriteria = AttachmentQueryCriteria.AttachmentsQueryCriteria(contractClassNamesCondition = Builder.equal(listOf(contractClassName)),
|
||||
versionCondition = Builder.greaterThanOrEqual(minimumRequiredContractClassVersion))
|
||||
val attachmentSort = AttachmentSort(listOf(AttachmentSort.AttachmentSortColumn(AttachmentSort.AttachmentSortAttribute.VERSION, Sort.Direction.DESC)))
|
||||
|
||||
return services.attachments.queryAttachments(attachmentQueryCriteria, attachmentSort).firstOrNull() ?: throw MissingContractAttachments(states)
|
||||
}
|
||||
|
||||
private fun useWhitelistedByZoneAttachmentConstraint(contractClassName: ContractClassName, networkParameters: NetworkParameters) = contractClassName in networkParameters.whitelistedContractImplementations.keys
|
||||
|
@ -4,6 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
|
||||
import com.natpryce.hamkrest.*
|
||||
import com.natpryce.hamkrest.assertion.assert
|
||||
import net.corda.core.contracts.Attachment
|
||||
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.testing.internal.matchers.flow.willReturn
|
||||
import net.corda.testing.internal.matchers.flow.willThrow
|
||||
@ -89,7 +90,7 @@ class AttachmentTests : WithMockNet {
|
||||
val corruptBytes = "arggghhhh".toByteArray()
|
||||
System.arraycopy(corruptBytes, 0, attachment, 0, corruptBytes.size)
|
||||
|
||||
val corruptAttachment = NodeAttachmentService.DBAttachment(attId = id.toString(), content = attachment, version = "1.0")
|
||||
val corruptAttachment = NodeAttachmentService.DBAttachment(attId = id.toString(), content = attachment, version = DEFAULT_CORDAPP_VERSION)
|
||||
badAliceNode.updateAttachment(corruptAttachment)
|
||||
|
||||
// Get n1 to fetch the attachment. Should receive corrupted bytes.
|
||||
|
@ -4,6 +4,7 @@ import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.cordapp.CordappProvider
|
||||
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.Party
|
||||
@ -13,6 +14,10 @@ import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.node.ZoneVersionTooLowException
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.node.services.NetworkParametersStorage
|
||||
import net.corda.core.node.services.vault.AttachmentQueryCriteria
|
||||
import net.corda.core.node.services.vault.AttachmentSort
|
||||
import net.corda.core.node.services.vault.Builder
|
||||
import net.corda.core.node.services.vault.Sort
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
@ -38,6 +43,10 @@ class TransactionBuilderTest {
|
||||
private val contractAttachmentId = SecureHash.randomSHA256()
|
||||
private val attachments = rigorousMock<AttachmentStorage>()
|
||||
private val networkParametersStorage = rigorousMock<NetworkParametersStorage>()
|
||||
private val attachmentQueryCriteria = AttachmentQueryCriteria.AttachmentsQueryCriteria(
|
||||
contractClassNamesCondition = Builder.equal(listOf("net.corda.testing.contracts.DummyContract")),
|
||||
versionCondition = Builder.greaterThanOrEqual(DEFAULT_CORDAPP_VERSION))
|
||||
private val attachmentSort = AttachmentSort(listOf(AttachmentSort.AttachmentSortColumn(AttachmentSort.AttachmentSortAttribute.VERSION, Sort.Direction.DESC)))
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
@ -57,6 +66,7 @@ class TransactionBuilderTest {
|
||||
doReturn(setOf(DummyContract.PROGRAM_ID)).whenever(attachment).allContracts
|
||||
doReturn("app").whenever(attachment).uploader
|
||||
doReturn(emptyList<Party>()).whenever(attachment).signerKeys
|
||||
doReturn(listOf(contractAttachmentId)).whenever(attachmentStorage).queryAttachments(attachmentQueryCriteria, attachmentSort)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -122,6 +132,7 @@ class TransactionBuilderTest {
|
||||
|
||||
doReturn(attachments).whenever(services).attachments
|
||||
doReturn(signedAttachment).whenever(attachments).openAttachment(contractAttachmentId)
|
||||
doReturn(listOf(contractAttachmentId)).whenever(attachments).queryAttachments(attachmentQueryCriteria, attachmentSort)
|
||||
|
||||
val outputState = TransactionState(data = DummyState(), contract = DummyContract.PROGRAM_ID, notary = notary)
|
||||
val builder = TransactionBuilder()
|
||||
|
@ -4,12 +4,18 @@ import com.nhaarman.mockito_kotlin.any
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.node.services.NetworkParametersStorage
|
||||
import net.corda.core.node.services.vault.AttachmentQueryCriteria
|
||||
import net.corda.core.node.services.vault.AttachmentSort
|
||||
import net.corda.core.node.services.vault.Builder
|
||||
import net.corda.core.node.services.vault.Sort
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
@ -17,6 +23,7 @@ import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.nodeapi.internal.AttachmentsClassLoaderStaticContractTests.AttachmentDummyContract.Companion.ATTACHMENT_PROGRAM_ID
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
@ -86,6 +93,11 @@ class AttachmentsClassLoaderStaticContractTests {
|
||||
doReturn(setOf(AttachmentDummyContract.ATTACHMENT_PROGRAM_ID)).whenever(attachment).allContracts
|
||||
doReturn("app").whenever(attachment).uploader
|
||||
doReturn(emptyList<Party>()).whenever(attachment).signerKeys
|
||||
val contractAttachmentId = SecureHash.randomSHA256()
|
||||
val attachmentQueryCriteria = AttachmentQueryCriteria.AttachmentsQueryCriteria(contractClassNamesCondition = Builder.equal(listOf(ATTACHMENT_PROGRAM_ID)),
|
||||
versionCondition = Builder.greaterThanOrEqual(DEFAULT_CORDAPP_VERSION))
|
||||
val attachmentSort = AttachmentSort(listOf(AttachmentSort.AttachmentSortColumn(AttachmentSort.AttachmentSortAttribute.VERSION, Sort.Direction.DESC)))
|
||||
doReturn(listOf(contractAttachmentId)).whenever(attachmentStorage).queryAttachments(attachmentQueryCriteria, attachmentSort)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -101,7 +113,7 @@ class AttachmentsClassLoaderStaticContractTests {
|
||||
|
||||
@Test
|
||||
fun `verify that contract DummyContract is in classPath`() {
|
||||
val contractClass = Class.forName("net.corda.nodeapi.internal.AttachmentsClassLoaderStaticContractTests\$AttachmentDummyContract")
|
||||
val contractClass = Class.forName(ATTACHMENT_PROGRAM_ID)
|
||||
assertThat(contractClass.newInstance()).isInstanceOf(Contract::class.java)
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.corda.node.internal.cordapp
|
||||
|
||||
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
|
||||
import net.corda.core.internal.cordapp.CordappImpl
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Info.Companion.UNKNOWN_VALUE
|
||||
import java.util.jar.Attributes
|
||||
@ -34,7 +35,7 @@ operator fun Manifest.get(key: String): String? = mainAttributes.getValue(key)
|
||||
fun Manifest.toCordappInfo(defaultShortName: String): CordappImpl.Info {
|
||||
val shortName = this["Name"] ?: defaultShortName
|
||||
val vendor = this["Implementation-Vendor"] ?: UNKNOWN_VALUE
|
||||
val version = this["Implementation-Version"] ?: UNKNOWN_VALUE
|
||||
val version = this["Implementation-Version"] ?: DEFAULT_CORDAPP_VERSION.toString()
|
||||
val minPlatformVersion = this["Min-Platform-Version"]?.toIntOrNull() ?: 1
|
||||
val targetPlatformVersion = this["Target-Platform-Version"]?.toIntOrNull() ?: minPlatformVersion
|
||||
return CordappImpl.Info(
|
||||
|
@ -212,7 +212,7 @@ object DefaultKryoCustomizer {
|
||||
kryo.writeClassAndObject(output, obj.additionalContracts)
|
||||
output.writeString(obj.uploader)
|
||||
kryo.writeClassAndObject(output, obj.signerKeys)
|
||||
output.writeString(obj.version)
|
||||
output.writeInt(obj.version)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@ -223,7 +223,7 @@ object DefaultKryoCustomizer {
|
||||
val additionalContracts = kryo.readClassAndObject(input) as Set<ContractClassName>
|
||||
val uploader = input.readString()
|
||||
val signers = kryo.readClassAndObject(input) as List<PublicKey>
|
||||
val version = input.readString()
|
||||
val version = input.readInt()
|
||||
val context = kryo.serializationContext()!!
|
||||
val attachmentStorage = context.serviceHub.attachments
|
||||
|
||||
@ -242,7 +242,7 @@ object DefaultKryoCustomizer {
|
||||
val additionalContracts = kryo.readClassAndObject(input) as Set<ContractClassName>
|
||||
val uploader = input.readString()
|
||||
val signers = kryo.readClassAndObject(input) as List<PublicKey>
|
||||
val version = input.readString()
|
||||
val version = input.readInt()
|
||||
return ContractAttachment(attachment, contract, additionalContracts, uploader, signers, version)
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,8 @@ import com.google.common.hash.Hashing
|
||||
import com.google.common.hash.HashingInputStream
|
||||
import com.google.common.io.CountingInputStream
|
||||
import net.corda.core.CordaRuntimeException
|
||||
import net.corda.core.contracts.Attachment
|
||||
import net.corda.core.contracts.ContractAttachment
|
||||
import net.corda.core.contracts.ContractClassName
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.cordapp.CORDAPP_CONTRACT_VERSION
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.internal.*
|
||||
@ -19,6 +18,7 @@ import net.corda.core.node.services.vault.AttachmentQueryCriteria
|
||||
import net.corda.core.node.services.vault.AttachmentSort
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
|
||||
import net.corda.node.services.vault.HibernateAttachmentQueryCriteriaParser
|
||||
import net.corda.node.utilities.NonInvalidatingCache
|
||||
import net.corda.node.utilities.NonInvalidatingWeightBasedCache
|
||||
@ -34,7 +34,6 @@ import java.nio.file.Paths
|
||||
import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.jar.Attributes.Name.IMPLEMENTATION_VERSION
|
||||
import java.util.jar.JarInputStream
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import javax.persistence.*
|
||||
@ -109,9 +108,9 @@ class NodeAttachmentService(
|
||||
foreignKey = ForeignKey(name = "FK__signers__attachments"))
|
||||
var signers: List<PublicKey>? = null,
|
||||
|
||||
// Assumption: only Contract Attachments are versioned.
|
||||
@Column(name = "version", nullable = true)
|
||||
var version: String? = null
|
||||
// Assumption: only Contract Attachments are versioned, version unknown or value for other attachments other than Contract Attachment defaults to 1
|
||||
@Column(name = "version", nullable = false)
|
||||
var version: Int = DEFAULT_CORDAPP_VERSION
|
||||
)
|
||||
|
||||
@VisibleForTesting
|
||||
@ -235,7 +234,7 @@ class NodeAttachmentService(
|
||||
val contracts = attachment.contractClassNames
|
||||
if (contracts != null && contracts.isNotEmpty()) {
|
||||
ContractAttachment(it, contracts.first(), contracts.drop(1).toSet(), attachment.uploader, attachment.signers?.toList()
|
||||
?: emptyList(), attachment.version ?: UNKNOWN_VERSION)
|
||||
?: emptyList(), attachment.version)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
@ -356,7 +355,11 @@ class NodeAttachmentService(
|
||||
|
||||
private fun getVersion(attachmentBytes: ByteArray) =
|
||||
JarInputStream(attachmentBytes.inputStream()).use {
|
||||
it.manifest?.mainAttributes?.getValue(IMPLEMENTATION_VERSION) ?: "1.0"
|
||||
try {
|
||||
it.manifest?.mainAttributes?.getValue(CORDAPP_CONTRACT_VERSION)?.toInt() ?: DEFAULT_CORDAPP_VERSION
|
||||
} catch (e: NumberFormatException) {
|
||||
DEFAULT_CORDAPP_VERSION
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("OverridingDeprecatedMember")
|
||||
|
@ -1,14 +1,14 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||
logicalFilePath="migration/node-services.changelog-init.xml">
|
||||
|
||||
<changeSet author="R3.Corda" id="add_version_column">
|
||||
<addColumn tableName="node_attachments">
|
||||
<column name="version" type="NVARCHAR(64)"/>
|
||||
<column name="version" type="INT"/>
|
||||
</addColumn>
|
||||
<sql>update node_attachments set version = 1</sql>
|
||||
<addNotNullConstraint tableName="node_attachments" columnName="version" columnDataType="INT"/>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
@ -581,8 +581,8 @@ public class VaultQueryJavaTests {
|
||||
Pair<Path, PublicKey> anotherSignedContractJarAndKey = INSTANCE.makeTestSignedContractJar(path, "com.example.AnotherContract");
|
||||
Path anotherSignedContractJar = anotherSignedContractJarAndKey.component1();
|
||||
|
||||
Path contractJarV2 = INSTANCE.makeTestContractJar(path, "com.example.MyContract", false, "2.0");
|
||||
Pair<Path, PublicKey> signedContractJarAndKeyV2 = INSTANCE.makeTestSignedContractJar(path, "com.example.MyContract", "2.0");
|
||||
Path contractJarV2 = INSTANCE.makeTestContractJar(path, "com.example.MyContract", false, 2);
|
||||
Pair<Path, PublicKey> signedContractJarAndKeyV2 = INSTANCE.makeTestSignedContractJar(path, "com.example.MyContract", 2);
|
||||
Path signedContractJarV2 = signedContractJarAndKeyV2.component1();
|
||||
|
||||
storage.importAttachment(Files.newInputStream(sampleJar),"uploaderA", "sample.jar");
|
||||
@ -616,15 +616,15 @@ public class VaultQueryJavaTests {
|
||||
|
||||
// version
|
||||
FieldInfo version = getField("version", NodeAttachmentService.DBAttachment.class);
|
||||
ColumnPredicate<List<String>> version2Predicate = equal(version, asList("2.0")).component2();
|
||||
ColumnPredicate<Integer> version2Predicate = equal(version, 2).component2();
|
||||
|
||||
AttachmentsQueryCriteria criteria4 = new AttachmentsQueryCriteria().withContractClassNames(contractClassNamesPredicate).isSigned(isSignedPredicate).withVersions(version2Predicate);
|
||||
AttachmentsQueryCriteria criteria4 = new AttachmentsQueryCriteria().withContractClassNames(contractClassNamesPredicate).isSigned(isSignedPredicate).withVersion(version2Predicate);
|
||||
assertThat(storage.queryAttachments(criteria4).size()).isEqualTo(1);
|
||||
|
||||
ColumnPredicate<List<String>> version1Predicate = equal(version, asList("1.0")).component2();
|
||||
ColumnPredicate<Integer> version1Predicate = equal(version, 1).component2();
|
||||
ColumnPredicate<List<String>> manyContractClassNamesPredicate = equal(contractClassNames, asList("com.example.MyContract", "com.example.AnotherContract")).component2();
|
||||
|
||||
AttachmentsQueryCriteria criteria5 = new AttachmentsQueryCriteria().withContractClassNames(manyContractClassNamesPredicate).isSigned(isSignedPredicate).withVersions(version1Predicate);
|
||||
AttachmentsQueryCriteria criteria5 = new AttachmentsQueryCriteria().withContractClassNames(manyContractClassNamesPredicate).isSigned(isSignedPredicate).withVersion(version1Predicate);
|
||||
assertThat(storage.queryAttachments(criteria5).size()).isEqualTo(2);
|
||||
|
||||
selfCleaningDir.close();
|
||||
|
@ -9,6 +9,7 @@ import net.corda.testing.core.internal.ContractJarTestUtils.makeTestJar
|
||||
import net.corda.testing.core.internal.ContractJarTestUtils.makeTestSignedContractJar
|
||||
import net.corda.testing.core.internal.SelfCleaningDir
|
||||
import net.corda.core.contracts.ContractAttachment
|
||||
import net.corda.core.cordapp.DEFAULT_CORDAPP_VERSION
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.flows.FlowLogic
|
||||
@ -209,8 +210,8 @@ class NodeAttachmentServiceTest {
|
||||
val contractJar = makeTestContractJar(file.path, "com.example.MyContract")
|
||||
val (signedContractJar, publicKey) = makeTestSignedContractJar(file.path, "com.example.MyContract")
|
||||
val (anotherSignedContractJar, _) = makeTestSignedContractJar(file.path,"com.example.AnotherContract")
|
||||
val contractJarV2 = makeTestContractJar(file.path,"com.example.MyContract", version = "2.0")
|
||||
val (signedContractJarV2, publicKeyV2) = makeTestSignedContractJar(file.path,"com.example.MyContract", version = "2.0")
|
||||
val contractJarV2 = makeTestContractJar(file.path,"com.example.MyContract", version = 2)
|
||||
val (signedContractJarV2, _) = makeTestSignedContractJar(file.path,"com.example.MyContract", version = 2)
|
||||
|
||||
sampleJar.read { storage.importAttachment(it, "uploaderA", "sample.jar") }
|
||||
contractJar.read { storage.importAttachment(it, "uploaderB", "contract.jar") }
|
||||
@ -238,7 +239,7 @@ class NodeAttachmentServiceTest {
|
||||
1,
|
||||
storage.queryAttachments(AttachmentsQueryCriteria(
|
||||
contractClassNamesCondition = Builder.equal(listOf("com.example.MyContract")),
|
||||
versionCondition = Builder.equal(listOf("2.0")),
|
||||
versionCondition = Builder.equal(2),
|
||||
isSignedCondition = Builder.equal(true))).size
|
||||
)
|
||||
|
||||
@ -246,9 +247,33 @@ class NodeAttachmentServiceTest {
|
||||
2,
|
||||
storage.queryAttachments(AttachmentsQueryCriteria(
|
||||
contractClassNamesCondition = Builder.equal(listOf("com.example.MyContract", "com.example.AnotherContract")),
|
||||
versionCondition = Builder.equal(listOf("1.0")),
|
||||
versionCondition = Builder.equal(1),
|
||||
isSignedCondition = Builder.equal(true))).size
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
2,storage.queryAttachments(AttachmentsQueryCriteria(
|
||||
contractClassNamesCondition = Builder.equal(listOf("com.example.MyContract")),
|
||||
versionCondition = Builder.greaterThanOrEqual(1),
|
||||
isSignedCondition = Builder.equal(true)),
|
||||
AttachmentSort(listOf(AttachmentSort.AttachmentSortColumn(AttachmentSort.AttachmentSortAttribute.VERSION)))).size
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
1,storage.queryAttachments(AttachmentsQueryCriteria(
|
||||
contractClassNamesCondition = Builder.equal(listOf("com.example.MyContract")),
|
||||
versionCondition = Builder.greaterThanOrEqual(2),
|
||||
isSignedCondition = Builder.equal(true)),
|
||||
AttachmentSort(listOf(AttachmentSort.AttachmentSortColumn(AttachmentSort.AttachmentSortAttribute.VERSION)))).size
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
0,storage.queryAttachments(AttachmentsQueryCriteria(
|
||||
contractClassNamesCondition = Builder.equal(listOf("com.example.MyContract")),
|
||||
versionCondition = Builder.greaterThanOrEqual(10),
|
||||
isSignedCondition = Builder.equal(true)),
|
||||
AttachmentSort(listOf(AttachmentSort.AttachmentSortColumn(AttachmentSort.AttachmentSortAttribute.VERSION)))).size
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,7 +347,7 @@ class NodeAttachmentServiceTest {
|
||||
val bytes = testJar.readAll()
|
||||
val corruptBytes = "arggghhhh".toByteArray()
|
||||
System.arraycopy(corruptBytes, 0, bytes, 0, corruptBytes.size)
|
||||
val corruptAttachment = NodeAttachmentService.DBAttachment(attId = id.toString(), content = bytes, version = "1.0")
|
||||
val corruptAttachment = NodeAttachmentService.DBAttachment(attId = id.toString(), content = bytes, version = DEFAULT_CORDAPP_VERSION)
|
||||
session.merge(corruptAttachment)
|
||||
id
|
||||
}
|
||||
|
@ -32,5 +32,5 @@ class ContractAttachmentSerializer(factory: SerializerFactory) : CustomSerialize
|
||||
}
|
||||
|
||||
@KeepForDJVM
|
||||
data class ContractAttachmentProxy(val attachment: Attachment, val contract: ContractClassName, val contracts: Set<ContractClassName>, val uploader: String?, val signers: List<PublicKey>, val version: String)
|
||||
data class ContractAttachmentProxy(val attachment: Attachment, val contract: ContractClassName, val contracts: Set<ContractClassName>, val uploader: String?, val signers: List<PublicKey>, val version: Int)
|
||||
}
|
@ -35,6 +35,9 @@ interface TestCordapp {
|
||||
/** Returns whether the CorDapp should be jar signed. */
|
||||
val signJar: Boolean
|
||||
|
||||
/** Returns the contract version, default to 1 if not specified. */
|
||||
val cordaContractVersion: Int
|
||||
|
||||
/** Return a copy of this [TestCordapp] but with the specified name. */
|
||||
fun withName(name: String): TestCordapp
|
||||
|
||||
@ -57,6 +60,8 @@ interface TestCordapp {
|
||||
* Optionally can pass in the location of an existing java key store to use */
|
||||
fun signJar(keyStorePath: Path? = null): TestCordappImpl
|
||||
|
||||
fun withCordaContractVersion(version: Int): TestCordappImpl
|
||||
|
||||
class Factory {
|
||||
companion object {
|
||||
/**
|
||||
|
@ -10,6 +10,7 @@ data class TestCordappImpl(override val name: String,
|
||||
override val targetVersion: Int,
|
||||
override val config: Map<String, Any>,
|
||||
override val packages: Set<String>,
|
||||
override val cordaContractVersion: Int = 1,
|
||||
override val signJar: Boolean = false,
|
||||
val keyStorePath: Path? = null,
|
||||
val classes: Set<Class<*>>
|
||||
@ -25,6 +26,8 @@ data class TestCordappImpl(override val name: String,
|
||||
|
||||
override fun withTargetVersion(targetVersion: Int): TestCordappImpl = copy(targetVersion = targetVersion)
|
||||
|
||||
override fun withCordaContractVersion(version: Int): TestCordappImpl = copy(cordaContractVersion = version)
|
||||
|
||||
override fun withConfig(config: Map<String, Any>): TestCordappImpl = copy(config = config)
|
||||
|
||||
override fun signJar(keyStorePath: Path?): TestCordappImpl = copy(signJar = true, keyStorePath = keyStorePath)
|
||||
|
@ -7,6 +7,7 @@ import net.corda.testing.core.internal.JarSignatureTestUtils.signJar
|
||||
import net.corda.core.internal.delete
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.toPath
|
||||
import net.corda.core.cordapp.CORDAPP_CONTRACT_VERSION
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import java.io.OutputStream
|
||||
import java.net.URI
|
||||
@ -41,7 +42,7 @@ object ContractJarTestUtils {
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun makeTestSignedContractJar(workingDir: Path, contractName: String, version: String = "1.0"): Pair<Path, PublicKey> {
|
||||
fun makeTestSignedContractJar(workingDir: Path, contractName: String, version: Int = 1): Pair<Path, PublicKey> {
|
||||
val alias = "testAlias"
|
||||
val pwd = "testPassword"
|
||||
workingDir.generateKey(alias, pwd, ALICE_NAME.toString())
|
||||
@ -53,13 +54,13 @@ object ContractJarTestUtils {
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun makeTestContractJar(workingDir: Path, contractName: String, signed: Boolean = false, version: String = "1.0"): Path {
|
||||
fun makeTestContractJar(workingDir: Path, contractName: String, signed: Boolean = false, version: Int = 1): Path {
|
||||
val packages = contractName.split(".")
|
||||
val jarName = "attachment-${packages.last()}-$version-${(if (signed) "signed" else "")}.jar"
|
||||
val className = packages.last()
|
||||
createTestClass(workingDir, className, packages.subList(0, packages.size - 1))
|
||||
workingDir.createJar(jarName, "${contractName.replace(".", "/")}.class")
|
||||
workingDir.addManifest(jarName, Pair(Attributes.Name.IMPLEMENTATION_VERSION, version))
|
||||
workingDir.addManifest(jarName, Pair(Attributes.Name(CORDAPP_CONTRACT_VERSION), version.toString()))
|
||||
return workingDir.resolve(jarName)
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,6 @@ object JarSignatureTestUtils {
|
||||
JarInputStream(FileInputStream((this / fileName).toFile())).use { input ->
|
||||
val manifest = input.manifest ?: Manifest()
|
||||
entry.forEach { (attributeName, value) ->
|
||||
// eg. Attributes.Name.IMPLEMENTATION_VERSION, version
|
||||
manifest.mainAttributes[attributeName] = value
|
||||
}
|
||||
val output = JarOutputStream(FileOutputStream((this / fileName).toFile()), manifest)
|
||||
|
@ -26,7 +26,7 @@ import java.util.jar.JarInputStream
|
||||
*/
|
||||
class MockAttachmentStorage : AttachmentStorage, SingletonSerializeAsToken() {
|
||||
|
||||
private data class ContractAttachmentMetadata(val name: ContractClassName, val version: String, val isSigned: Boolean)
|
||||
private data class ContractAttachmentMetadata(val name: ContractClassName, val version: Int, val isSigned: Boolean)
|
||||
|
||||
private val _files = HashMap<SecureHash, Pair<Attachment, ByteArray>>()
|
||||
private val _contractClasses = HashMap<ContractAttachmentMetadata, SecureHash>()
|
||||
@ -54,13 +54,13 @@ class MockAttachmentStorage : AttachmentStorage, SingletonSerializeAsToken() {
|
||||
if (criteria.isSignedCondition != null) {
|
||||
val isSigned = criteria.isSignedCondition == Builder.equal(true)
|
||||
contractClassNames.map {contractClassName ->
|
||||
ContractAttachmentMetadata(contractClassName, "1.0", isSigned)
|
||||
ContractAttachmentMetadata(contractClassName, 1, isSigned)
|
||||
}
|
||||
}
|
||||
else {
|
||||
contractClassNames.flatMap { contractClassName ->
|
||||
listOf(ContractAttachmentMetadata(contractClassName, "1.0", false),
|
||||
ContractAttachmentMetadata(contractClassName, "1.0", true))
|
||||
listOf(ContractAttachmentMetadata(contractClassName, 1, false),
|
||||
ContractAttachmentMetadata(contractClassName, 1, true))
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,10 +102,10 @@ class MockAttachmentStorage : AttachmentStorage, SingletonSerializeAsToken() {
|
||||
if (contractClassNames == null || contractClassNames.isEmpty()) baseAttachment
|
||||
else {
|
||||
contractClassNames.map {contractClassName ->
|
||||
val contractClassMetadata = ContractAttachmentMetadata(contractClassName, "1.0", signers.isNotEmpty())
|
||||
val contractClassMetadata = ContractAttachmentMetadata(contractClassName, 1, signers.isNotEmpty())
|
||||
_contractClasses[contractClassMetadata] = sha256
|
||||
}
|
||||
ContractAttachment(baseAttachment, contractClassNames.first(), contractClassNames.toSet(), uploader, signers, "1.0")
|
||||
ContractAttachment(baseAttachment, contractClassNames.first(), contractClassNames.toSet(), uploader, signers, 1)
|
||||
}
|
||||
_files[sha256] = Pair(attachment, bytes)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user