mirror of
https://github.com/corda/corda.git
synced 2024-12-19 21:17:58 +00:00
Cross provider Issuer Reference database storage (#2032)
* consistent storage of Issuer Reference using `ByteArray` Kotlin type in Schema definition and a custom Hibernate Type to map this to a VARBINARY database type. Creation of a new Issued type now also validates maximum size permissible (512).
This commit is contained in:
parent
19aba62fc6
commit
f5c9fd8f44
@ -117,6 +117,9 @@ dependencies {
|
||||
|
||||
// JPA 2.1 annotations.
|
||||
compile "org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final"
|
||||
|
||||
// required to use @Type annotation
|
||||
compile "org.hibernate:hibernate-core:$hibernate_version"
|
||||
}
|
||||
|
||||
// TODO Consider moving it to quasar-utils in the future (introduced with PR-1388)
|
||||
|
@ -45,9 +45,17 @@ interface NamedByHash {
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class Issued<out P : Any>(val issuer: PartyAndReference, val product: P) {
|
||||
init {
|
||||
require(issuer.reference.bytes.size <= MAX_ISSUER_REF_SIZE) { "Maximum issuer reference size is $MAX_ISSUER_REF_SIZE." }
|
||||
}
|
||||
override fun toString() = "$product issued by $issuer"
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum permissible size of an issuer reference.
|
||||
*/
|
||||
const val MAX_ISSUER_REF_SIZE = 512
|
||||
|
||||
/**
|
||||
* Strips the issuer and returns an [Amount] of the raw token directly. This is useful when you are mixing code that
|
||||
* cares about specific issuers with code that will accept any, or which is imposing issuer constraints via some
|
||||
|
@ -103,3 +103,5 @@ fun ByteArray.sha256(): SecureHash.SHA256 = SecureHash.sha256(this)
|
||||
* Compute the SHA-256 hash for the contents of the [OpaqueBytes].
|
||||
*/
|
||||
fun OpaqueBytes.sha256(): SecureHash.SHA256 = SecureHash.sha256(this.bytes)
|
||||
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
package net.corda.core.schemas
|
||||
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.FungibleAsset
|
||||
import net.corda.core.contracts.OwnableState
|
||||
import net.corda.core.contracts.UniqueIdentifier
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import org.hibernate.annotations.Type
|
||||
import java.util.*
|
||||
import javax.persistence.*
|
||||
import javax.persistence.Column
|
||||
import javax.persistence.ElementCollection
|
||||
import javax.persistence.MappedSuperclass
|
||||
|
||||
/**
|
||||
* JPA representation of the common schema entities
|
||||
@ -74,7 +74,8 @@ object CommonSchemaV1 : MappedSchema(schemaFamily = CommonSchema.javaClass, vers
|
||||
@Column(name = "issuer_name")
|
||||
var issuer: AbstractParty,
|
||||
|
||||
@Column(name = "issuer_reference")
|
||||
@Column(name = "issuer_ref", length = MAX_ISSUER_REF_SIZE)
|
||||
@Type(type = "corda-wrapper-binary")
|
||||
var issuerRef: ByteArray
|
||||
) : PersistentState()
|
||||
}
|
@ -4,7 +4,9 @@ import net.corda.core.contracts.Amount
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.utilities.*
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import java.sql.Connection
|
||||
import java.sql.DatabaseMetaData
|
||||
import java.sql.ResultSet
|
||||
@ -60,7 +62,7 @@ class CashSelectionH2Impl : AbstractCashSelection() {
|
||||
if (onlyFromIssuerParties.isNotEmpty())
|
||||
psSelectJoin.setObject(++pIndex, onlyFromIssuerParties.map { it.owningKey.toStringShort() as Any}.toTypedArray() )
|
||||
if (withIssuerRefs.isNotEmpty())
|
||||
psSelectJoin.setObject(++pIndex, withIssuerRefs.map { it.bytes.toHexString() as Any }.toTypedArray())
|
||||
psSelectJoin.setObject(++pIndex, withIssuerRefs.map { it.bytes as Any }.toTypedArray())
|
||||
log.debug { psSelectJoin.toString() }
|
||||
|
||||
return psSelectJoin.executeQuery()
|
||||
|
@ -63,13 +63,13 @@ class CashSelectionPostgreSQLImpl : AbstractCashSelection() {
|
||||
paramOffset += 1
|
||||
}
|
||||
if (onlyFromIssuerParties.isNotEmpty()) {
|
||||
val issuerKeys = connection.createArrayOf("VARCHAR", onlyFromIssuerParties.map
|
||||
val issuerKeys = connection.createArrayOf("BYTEA", onlyFromIssuerParties.map
|
||||
{ it.owningKey.toBase58String() }.toTypedArray())
|
||||
statement.setArray(3 + paramOffset, issuerKeys)
|
||||
paramOffset += 1
|
||||
}
|
||||
if (withIssuerRefs.isNotEmpty()) {
|
||||
val issuerRefs = connection.createArrayOf("BYTEA", withIssuerRefs.map
|
||||
val issuerRefs = connection.createArrayOf("VARCHAR", withIssuerRefs.map
|
||||
{ it.bytes }.toTypedArray())
|
||||
statement.setArray(3 + paramOffset, issuerRefs)
|
||||
paramOffset += 1
|
||||
|
@ -5,6 +5,8 @@ import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.schemas.PersistentState
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
|
||||
import net.corda.core.contracts.MAX_ISSUER_REF_SIZE
|
||||
import org.hibernate.annotations.Type
|
||||
import javax.persistence.*
|
||||
|
||||
/**
|
||||
@ -36,7 +38,8 @@ object CashSchemaV1 : MappedSchema(schemaFamily = CashSchema.javaClass, version
|
||||
@Column(name = "issuer_key_hash", length = MAX_HASH_HEX_SIZE)
|
||||
var issuerPartyHash: String,
|
||||
|
||||
@Column(name = "issuer_ref")
|
||||
@Column(name = "issuer_ref", length = MAX_ISSUER_REF_SIZE)
|
||||
@Type(type = "corda-wrapper-binary")
|
||||
var issuerRef: ByteArray
|
||||
) : PersistentState()
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package net.corda.finance.schemas
|
||||
|
||||
import net.corda.core.contracts.MAX_ISSUER_REF_SIZE
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.schemas.PersistentState
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
|
||||
import org.hibernate.annotations.Type
|
||||
import java.time.Instant
|
||||
import javax.persistence.Column
|
||||
import javax.persistence.Entity
|
||||
@ -48,7 +50,8 @@ object CommercialPaperSchemaV1 : MappedSchema(schemaFamily = CommercialPaperSche
|
||||
@Column(name = "face_value_issuer_key_hash", length = MAX_HASH_HEX_SIZE)
|
||||
var faceValueIssuerPartyHash: String,
|
||||
|
||||
@Column(name = "face_value_issuer_ref")
|
||||
@Column(name = "face_value_issuer_ref", length = MAX_ISSUER_REF_SIZE)
|
||||
@Type(type = "corda-wrapper-binary")
|
||||
var faceValueIssuerRef: ByteArray
|
||||
) : PersistentState()
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class DummyFungibleContract : OnLedgerAsset<Currency, DummyFungibleContract.Comm
|
||||
_quantity = this.amount.quantity,
|
||||
currency = this.amount.token.product.currencyCode,
|
||||
_issuerParty = this.amount.token.issuer.party,
|
||||
_issuerRef = this.amount.token.issuer.reference.bytes
|
||||
_issuerRef = this.amount.token.issuer.reference
|
||||
)
|
||||
is SampleCashSchemaV3 -> SampleCashSchemaV3.PersistentCashState(
|
||||
participants = this.participants.toMutableSet(),
|
||||
|
@ -1,8 +1,10 @@
|
||||
package net.corda.finance.schemas
|
||||
|
||||
import net.corda.core.contracts.MAX_ISSUER_REF_SIZE
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.schemas.PersistentState
|
||||
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
|
||||
import org.hibernate.annotations.Type
|
||||
import javax.persistence.Column
|
||||
import javax.persistence.Entity
|
||||
import javax.persistence.Index
|
||||
@ -35,7 +37,8 @@ object SampleCashSchemaV1 : MappedSchema(schemaFamily = CashSchema.javaClass, ve
|
||||
@Column(name = "issuer_key_hash", length = MAX_HASH_HEX_SIZE)
|
||||
var issuerPartyHash: String,
|
||||
|
||||
@Column(name = "issuer_ref")
|
||||
@Column(name = "issuer_ref", length = MAX_ISSUER_REF_SIZE)
|
||||
@Type(type = "corda-wrapper-binary")
|
||||
var issuerRef: ByteArray
|
||||
) : PersistentState()
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package net.corda.finance.schemas
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.schemas.CommonSchemaV1
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import javax.persistence.Column
|
||||
import javax.persistence.Entity
|
||||
import javax.persistence.Index
|
||||
@ -32,6 +33,6 @@ object SampleCashSchemaV2 : MappedSchema(schemaFamily = CashSchema.javaClass, ve
|
||||
@Transient
|
||||
val _issuerParty: AbstractParty,
|
||||
@Transient
|
||||
val _issuerRef: ByteArray
|
||||
) : CommonSchemaV1.FungibleState(_participants.toMutableSet(), _owner, _quantity, _issuerParty, _issuerRef)
|
||||
val _issuerRef: OpaqueBytes
|
||||
) : CommonSchemaV1.FungibleState(_participants.toMutableSet(), _owner, _quantity, _issuerParty, _issuerRef.bytes)
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package net.corda.finance.schemas
|
||||
|
||||
import net.corda.core.contracts.MAX_ISSUER_REF_SIZE
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.schemas.PersistentState
|
||||
import org.hibernate.annotations.Type
|
||||
import javax.persistence.Column
|
||||
import javax.persistence.ElementCollection
|
||||
import javax.persistence.Entity
|
||||
@ -37,7 +39,8 @@ object SampleCashSchemaV3 : MappedSchema(schemaFamily = CashSchema.javaClass, ve
|
||||
@Column(name = "issuer_name")
|
||||
var issuer: AbstractParty,
|
||||
|
||||
@Column(name = "issuer_ref")
|
||||
@Column(name = "issuer_ref", length = MAX_ISSUER_REF_SIZE)
|
||||
@Type(type = "corda-wrapper-binary")
|
||||
var issuerRef: ByteArray
|
||||
) : PersistentState()
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package net.corda.finance.schemas
|
||||
|
||||
import net.corda.core.contracts.MAX_ISSUER_REF_SIZE
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.schemas.PersistentState
|
||||
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
|
||||
import org.hibernate.annotations.Type
|
||||
import java.time.Instant
|
||||
import javax.persistence.Column
|
||||
import javax.persistence.Entity
|
||||
@ -46,7 +48,8 @@ object SampleCommercialPaperSchemaV1 : MappedSchema(schemaFamily = CommercialPap
|
||||
@Column(name = "face_value_issuer_key_hash", length = MAX_HASH_HEX_SIZE)
|
||||
var faceValueIssuerPartyHash: String,
|
||||
|
||||
@Column(name = "face_value_issuer_ref")
|
||||
@Column(name = "face_value_issuer_ref", length = MAX_ISSUER_REF_SIZE)
|
||||
@Type(type = "corda-wrapper-binary")
|
||||
var faceValueIssuerRef: ByteArray
|
||||
) : PersistentState()
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
package net.corda.finance.schemas
|
||||
|
||||
import net.corda.core.contracts.MAX_ISSUER_REF_SIZE
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.schemas.CommonSchemaV1
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.utilities.MAX_HASH_HEX_SIZE
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import org.hibernate.annotations.Type
|
||||
import java.time.Instant
|
||||
import javax.persistence.Column
|
||||
import javax.persistence.Entity
|
||||
@ -30,7 +33,8 @@ object SampleCommercialPaperSchemaV2 : MappedSchema(schemaFamily = CommercialPap
|
||||
@Column(name = "face_value_issuer_key_hash", length = MAX_HASH_HEX_SIZE)
|
||||
var faceValueIssuerPartyHash: String,
|
||||
|
||||
@Column(name = "face_value_issuer_ref")
|
||||
@Column(name = "face_value_issuer_ref", length = MAX_ISSUER_REF_SIZE)
|
||||
@Type(type = "corda-wrapper-binary")
|
||||
var faceValueIssuerRef: ByteArray,
|
||||
|
||||
/** parent attributes */
|
||||
@ -44,6 +48,6 @@ object SampleCommercialPaperSchemaV2 : MappedSchema(schemaFamily = CommercialPap
|
||||
@Transient
|
||||
val _issuerParty: AbstractParty,
|
||||
@Transient
|
||||
val _issuerRef: ByteArray
|
||||
) : CommonSchemaV1.FungibleState(_participants.toMutableSet(), _owner, _quantity, _issuerParty, _issuerRef)
|
||||
val _issuerRef: OpaqueBytes
|
||||
) : CommonSchemaV1.FungibleState(_participants.toMutableSet(), _owner, _quantity, _issuerParty, _issuerRef.bytes)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package net.corda.node.services.persistence
|
||||
import net.corda.core.internal.castIfPossible
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.toHexString
|
||||
import net.corda.node.services.api.SchemaService
|
||||
@ -21,6 +22,7 @@ import org.hibernate.type.AbstractSingleColumnStandardBasicType
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor
|
||||
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor
|
||||
import org.hibernate.type.descriptor.sql.VarbinaryTypeDescriptor
|
||||
import java.sql.Connection
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
@ -83,6 +85,7 @@ class HibernateConfiguration(val schemaService: SchemaService, private val datab
|
||||
// Register a tweaked version of `org.hibernate.type.MaterializedBlobType` that truncates logged messages.
|
||||
// to avoid OOM when large blobs might get logged.
|
||||
applyBasicType(CordaMaterializedBlobType, CordaMaterializedBlobType.name)
|
||||
applyBasicType(CordaWrapperBinaryType, CordaWrapperBinaryType.name)
|
||||
build()
|
||||
}
|
||||
|
||||
@ -139,4 +142,15 @@ class HibernateConfiguration(val schemaService: SchemaService, private val datab
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A tweaked version of `org.hibernate.type.WrapperBinaryType` that deals with ByteArray (java primitive byte[] type).
|
||||
private object CordaWrapperBinaryType : AbstractSingleColumnStandardBasicType<ByteArray>(VarbinaryTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE) {
|
||||
override fun getRegistrationKeys(): Array<String> {
|
||||
return arrayOf(name, "ByteArray", ByteArray::class.java.name)
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return "corda-wrapper-binary"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package net.corda.node.services.vault
|
||||
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.MAX_ISSUER_REF_SIZE
|
||||
import net.corda.core.contracts.UniqueIdentifier
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.Party
|
||||
@ -9,6 +10,7 @@ import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.schemas.PersistentState
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import org.hibernate.annotations.Type
|
||||
import java.io.Serializable
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
@ -131,7 +133,8 @@ object VaultSchemaV1 : MappedSchema(schemaFamily = VaultSchema.javaClass, versio
|
||||
@Column(name = "issuer_name")
|
||||
var issuer: AbstractParty,
|
||||
|
||||
@Column(name = "issuer_reference")
|
||||
@Column(name = "issuer_ref", length = MAX_ISSUER_REF_SIZE)
|
||||
@Type(type = "corda-wrapper-binary")
|
||||
var issuerRef: ByteArray
|
||||
) : PersistentState() {
|
||||
constructor(_owner: AbstractParty, _quantity: Long, _issuerParty: AbstractParty, _issuerRef: OpaqueBytes, _participants: List<AbstractParty>) :
|
||||
|
Loading…
Reference in New Issue
Block a user