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