mirror of
https://github.com/corda/corda.git
synced 2025-06-18 15:18:16 +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:
@ -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()
|
||||
|
Reference in New Issue
Block a user