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
22 changed files with 130 additions and 54 deletions

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