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:
szymonsztuka 2018-12-06 11:28:53 +00:00 committed by GitHub
parent 2833013119
commit d2d13c1dfc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 130 additions and 54 deletions

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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()

View File

@ -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)
}

View File

@ -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(

View File

@ -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)
}
}

View File

@ -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")

View File

@ -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>

View File

@ -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();

View File

@ -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
}

View File

@ -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)
}

View File

@ -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 {
/**

View File

@ -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)

View File

@ -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)
}

View File

@ -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)

View File

@ -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)
}