CORDA-696 - Ensure deterministic transaction id calculation for contra… (#2676)

The problem with the previous implementation is that the transaction would be deserialized with the schema specified
in the serialized form, but the calculation of the id would involve re-serializing properties using a local serialization context
which might produce a different result.
This commit is contained in:
Andrius Dagys 2018-03-08 17:59:25 +00:00
parent af60848da7
commit a3bf4577f3
10 changed files with 208 additions and 85 deletions

View File

@ -3001,17 +3001,15 @@ public static final class net.corda.core.serialization.SingletonSerializationTok
@org.jetbrains.annotations.NotNull public final String getReason()
##
@net.corda.core.DoNotImplement @net.corda.core.serialization.CordaSerializable public final class net.corda.core.transactions.ContractUpgradeFilteredTransaction extends net.corda.core.transactions.CoreTransaction
public <init>(List, net.corda.core.identity.Party, net.corda.core.crypto.SecureHash)
@org.jetbrains.annotations.NotNull public final List component1()
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party component2()
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash component3()
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeFilteredTransaction copy(List, net.corda.core.identity.Party, net.corda.core.crypto.SecureHash)
public <init>(Map, Map)
@org.jetbrains.annotations.NotNull public final Map component1()
@org.jetbrains.annotations.NotNull public final Map component2()
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeFilteredTransaction copy(Map, Map)
public boolean equals(Object)
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
@org.jetbrains.annotations.NotNull public List getInputs()
@org.jetbrains.annotations.NotNull public net.corda.core.identity.Party getNotary()
@org.jetbrains.annotations.NotNull public List getOutputs()
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getRest()
public int hashCode()
public String toString()
##
@ -3038,8 +3036,8 @@ public static final class net.corda.core.serialization.SingletonSerializationTok
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.PrivacySalt getPrivacySalt()
@org.jetbrains.annotations.NotNull public Set getRequiredSigningKeys()
@org.jetbrains.annotations.NotNull public List getSigs()
@org.jetbrains.annotations.NotNull public final String getUpgradeContractClassName()
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.Attachment getUpgradedContractAttachment()
@org.jetbrains.annotations.NotNull public final String getUpgradedContractClassName()
public int hashCode()
public String toString()
public void verifyRequiredSignatures()
@ -3047,15 +3045,11 @@ public static final class net.corda.core.serialization.SingletonSerializationTok
public void verifySignaturesExcept(java.security.PublicKey...)
##
@net.corda.core.DoNotImplement @net.corda.core.serialization.CordaSerializable public final class net.corda.core.transactions.ContractUpgradeWireTransaction extends net.corda.core.transactions.CoreTransaction
public <init>(List, net.corda.core.identity.Party, net.corda.core.crypto.SecureHash, String, net.corda.core.crypto.SecureHash, net.corda.core.contracts.PrivacySalt)
public <init>(List, net.corda.core.contracts.PrivacySalt)
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeFilteredTransaction buildFilteredTransaction()
@org.jetbrains.annotations.NotNull public final List component1()
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party component2()
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash component3()
@org.jetbrains.annotations.NotNull public final String component4()
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash component5()
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.PrivacySalt component6()
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeWireTransaction copy(List, net.corda.core.identity.Party, net.corda.core.crypto.SecureHash, String, net.corda.core.crypto.SecureHash, net.corda.core.contracts.PrivacySalt)
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.PrivacySalt component2()
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeWireTransaction copy(List, net.corda.core.contracts.PrivacySalt)
public boolean equals(Object)
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
@org.jetbrains.annotations.NotNull public List getInputs()
@ -3063,8 +3057,8 @@ public static final class net.corda.core.serialization.SingletonSerializationTok
@org.jetbrains.annotations.NotNull public net.corda.core.identity.Party getNotary()
@org.jetbrains.annotations.NotNull public List getOutputs()
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.PrivacySalt getPrivacySalt()
@org.jetbrains.annotations.NotNull public final String getUpgradeContractClassName()
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getUpgradedContractAttachmentId()
@org.jetbrains.annotations.NotNull public final String getUpgradedContractClassName()
public int hashCode()
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeLedgerTransaction resolve(net.corda.core.node.ServicesForResolution, List)
public String toString()
@ -3199,11 +3193,9 @@ public static final class net.corda.core.transactions.LedgerTransaction$InOutGro
public void verifySignaturesExcept(java.security.PublicKey...)
##
@net.corda.core.DoNotImplement @net.corda.core.serialization.CordaSerializable public final class net.corda.core.transactions.NotaryChangeWireTransaction extends net.corda.core.transactions.CoreTransaction
public <init>(List, net.corda.core.identity.Party, net.corda.core.identity.Party)
public <init>(List)
@org.jetbrains.annotations.NotNull public final List component1()
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party component2()
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party component3()
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeWireTransaction copy(List, net.corda.core.identity.Party, net.corda.core.identity.Party)
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeWireTransaction copy(List)
public boolean equals(Object)
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
@org.jetbrains.annotations.NotNull public List getInputs()

View File

@ -222,7 +222,11 @@ fun componentHash(opaqueBytes: OpaqueBytes, privacySalt: PrivacySalt, componentG
/** Return the SHA256(SHA256(nonce || serializedComponent)). */
fun componentHash(nonce: SecureHash, opaqueBytes: OpaqueBytes): SecureHash = SecureHash.sha256Twice(nonce.bytes + opaqueBytes.bytes)
/** Serialise the object and return the hash of the serialized bytes. */
/**
* Serialise the object and return the hash of the serialized bytes. Note that the resulting hash may not be deterministic
* across platform versions: serialization can produce different values if any of the types being serialized have changed,
* or if the version of serialization specified by the context changes.
*/
fun <T : Any> serializedHash(x: T): SecureHash = x.serialize(context = SerializationDefaults.P2P_CONTEXT.withoutReferences()).bytes.sha256()
/**

View File

@ -7,7 +7,7 @@ import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignableData
import net.corda.core.crypto.SignatureMetadata
import net.corda.core.identity.Party
import net.corda.core.transactions.NotaryChangeWireTransaction
import net.corda.core.internal.NotaryChangeTransactionBuilder
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
@ -30,11 +30,11 @@ class NotaryChangeFlow<out T : ContractState>(
override fun assembleTx(): AbstractStateReplacementFlow.UpgradeTx {
val inputs = resolveEncumbrances(originalState)
val tx = NotaryChangeWireTransaction(
val tx = NotaryChangeTransactionBuilder(
inputs.map { it.ref },
originalState.state.notary,
modification
)
).build()
val participantKeys = inputs.flatMap { it.state.data.participants }.map { it.owningKey }.toSet()
// TODO: We need a much faster way of finding our key in the transaction

View File

@ -21,18 +21,18 @@ object ContractUpgradeUtils {
val upgradedContractAttachmentId = getContractAttachmentId(upgradedContractClass.name, services)
val inputs = listOf(stateAndRef.ref)
return ContractUpgradeWireTransaction(
return ContractUpgradeTransactionBuilder(
inputs,
stateAndRef.state.notary,
legacyContractAttachmentId,
upgradedContractClass.name,
upgradedContractAttachmentId,
privacySalt
)
).build()
}
private fun getContractAttachmentId(name: ContractClassName, services: ServicesForResolution): AttachmentId {
return services.cordappProvider.getContractAttachmentID(name)
?: throw IllegalStateException("Attachment not found for contract: $name")
}
}
}

View File

@ -0,0 +1,45 @@
package net.corda.core.internal
import net.corda.core.contracts.ContractClassName
import net.corda.core.contracts.PrivacySalt
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.identity.Party
import net.corda.core.serialization.serialize
import net.corda.core.transactions.ContractUpgradeWireTransaction
import net.corda.core.transactions.NotaryChangeWireTransaction
import java.io.ByteArrayOutputStream
/** Constructs a [NotaryChangeWireTransaction]. */
class NotaryChangeTransactionBuilder(val inputs: List<StateRef>,
val notary: Party,
val newNotary: Party) {
fun build(): NotaryChangeWireTransaction {
val components = listOf(inputs, notary, newNotary).map { it.serialize() }
return NotaryChangeWireTransaction(components)
}
}
/** Constructs a [ContractUpgradeWireTransaction]. */
class ContractUpgradeTransactionBuilder(
val inputs: List<StateRef>,
val notary: Party,
val legacyContractAttachmentId: SecureHash,
val upgradedContractClassName: ContractClassName,
val upgradedContractAttachmentId: SecureHash,
val privacySalt: PrivacySalt = PrivacySalt()) {
fun build(): ContractUpgradeWireTransaction {
val components = listOf(inputs, notary, legacyContractAttachmentId, upgradedContractClassName, upgradedContractAttachmentId).map { it.serialize() }
return ContractUpgradeWireTransaction(components, privacySalt)
}
}
/** Concatenates the hash components into a single [ByteArray] and returns its hash. */
fun combinedHash(components: Iterable<SecureHash>): SecureHash {
val stream = ByteArrayOutputStream()
components.forEach {
stream.write(it.bytes)
}
return stream.toByteArray().sha256()
}

View File

@ -3,12 +3,18 @@ package net.corda.core.transactions
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.serializedHash
import net.corda.core.crypto.componentHash
import net.corda.core.crypto.computeNonce
import net.corda.core.identity.Party
import net.corda.core.internal.AttachmentWithContext
import net.corda.core.internal.combinedHash
import net.corda.core.node.NetworkParameters
import net.corda.core.node.ServicesForResolution
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.deserialize
import net.corda.core.transactions.ContractUpgradeFilteredTransaction.FilteredComponent
import net.corda.core.transactions.ContractUpgradeWireTransaction.Component.*
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.toBase58String
import java.security.PublicKey
@ -18,13 +24,20 @@ import java.security.PublicKey
/** A special transaction for upgrading the contract of a state. */
@CordaSerializable
data class ContractUpgradeWireTransaction(
override val inputs: List<StateRef>,
override val notary: Party,
val legacyContractAttachmentId: SecureHash,
val upgradeContractClassName: ContractClassName,
val upgradedContractAttachmentId: SecureHash,
/**
* Contains all of the transaction components in serialized form.
* This is used for calculating the transaction id in a deterministic fashion, since re-serializing properties
* may result in a different byte sequence depending on the serialization context.
*/
val serializedComponents: List<OpaqueBytes>,
/** Required for hiding components in [ContractUpgradeFilteredTransaction]. */
val privacySalt: PrivacySalt = PrivacySalt()
) : CoreTransaction() {
override val inputs: List<StateRef> = serializedComponents[INPUTS.ordinal].deserialize()
override val notary: Party by lazy { serializedComponents[NOTARY.ordinal].deserialize<Party>() }
val legacyContractAttachmentId: SecureHash by lazy { serializedComponents[LEGACY_ATTACHMENT.ordinal].deserialize<SecureHash>() }
val upgradedContractClassName: ContractClassName by lazy { serializedComponents[UPGRADED_CONTRACT.ordinal].deserialize<ContractClassName>() }
val upgradedContractAttachmentId: SecureHash by lazy { serializedComponents[UPGRADED_ATTACHMENT.ordinal].deserialize<SecureHash>() }
init {
check(inputs.isNotEmpty()) { "A contract upgrade transaction must have inputs" }
@ -39,11 +52,17 @@ data class ContractUpgradeWireTransaction(
get() = throw UnsupportedOperationException("ContractUpgradeWireTransaction does not contain output states, " +
"outputs can only be obtained from a resolved ContractUpgradeLedgerTransaction")
/** Hash of the list of components that are hidden in the [ContractUpgradeFilteredTransaction]. */
private val hiddenComponentHash: SecureHash
get() = serializedHash(listOf(legacyContractAttachmentId, upgradeContractClassName, privacySalt))
override val id: SecureHash by lazy {
val componentHashes =serializedComponents.mapIndexed { index, component ->
componentHash(nonces[index], component)
}
combinedHash(componentHashes)
}
override val id: SecureHash by lazy { serializedHash(inputs + notary).hashConcat(hiddenComponentHash) }
/** Required for filtering transaction components. */
private val nonces = (0 until serializedComponents.size).map {
computeNonce(privacySalt, it, 0)
}
/** Resolves input states and contract attachments, and builds a ContractUpgradeLedgerTransaction. */
fun resolve(services: ServicesForResolution, sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
@ -56,7 +75,7 @@ data class ContractUpgradeWireTransaction(
resolvedInputs,
notary,
legacyContractAttachment,
upgradeContractClassName,
upgradedContractClassName,
upgradedContractAttachment,
id,
privacySalt,
@ -65,8 +84,23 @@ data class ContractUpgradeWireTransaction(
)
}
/** Constructs a filtered transaction: the inputs and the notary party are always visible, while the rest are hidden. */
fun buildFilteredTransaction(): ContractUpgradeFilteredTransaction {
return ContractUpgradeFilteredTransaction(inputs, notary, hiddenComponentHash)
val totalComponents = (0 until serializedComponents.size).toSet()
val visibleComponents = mapOf(
INPUTS.ordinal to FilteredComponent(serializedComponents[INPUTS.ordinal], nonces[INPUTS.ordinal]),
NOTARY.ordinal to FilteredComponent(serializedComponents[NOTARY.ordinal], nonces[NOTARY.ordinal])
)
val hiddenComponents = (totalComponents - visibleComponents.keys).map { index ->
val hash = componentHash(nonces[index], serializedComponents[index])
index to hash
}.toMap()
return ContractUpgradeFilteredTransaction(visibleComponents, hiddenComponents)
}
enum class Component {
INPUTS, NOTARY, LEGACY_ATTACHMENT, UPGRADED_CONTRACT, UPGRADED_ATTACHMENT
}
}
@ -74,19 +108,43 @@ data class ContractUpgradeWireTransaction(
* A filtered version of the [ContractUpgradeWireTransaction]. In comparison with a regular [FilteredTransaction], there
* is no flexibility on what parts of the transaction to reveal the inputs and notary field are always visible and the
* rest of the transaction is always hidden. Its only purpose is to hide transaction data when using a non-validating notary.
*
* @property inputs The inputs of this transaction.
* @property notary The notary for this transaction.
* @property rest Hash of the hidden components of the [ContractUpgradeWireTransaction].
*/
@CordaSerializable
data class ContractUpgradeFilteredTransaction(
override val inputs: List<StateRef>,
override val notary: Party,
val rest: SecureHash
/** Transaction components that are exposed. */
val visibleComponents: Map<Int, FilteredComponent>,
/**
* Hashes of the transaction components that are not revealed in this transaction.
* Required for computing the transaction id.
*/
val hiddenComponents: Map<Int, SecureHash>
) : CoreTransaction() {
override val id: SecureHash get() = serializedHash(inputs + notary).hashConcat(rest)
override val inputs: List<StateRef> by lazy {
visibleComponents[INPUTS.ordinal]?.component?.deserialize<List<StateRef>>()
?: throw IllegalArgumentException("Inputs not specified")
}
override val notary: Party by lazy {
visibleComponents[NOTARY.ordinal]?.component?.deserialize<Party>()
?: throw IllegalArgumentException("Notary not specified")
}
override val id: SecureHash by lazy {
val totalComponents = visibleComponents.size + hiddenComponents.size
val hashList = (0 until totalComponents).map { i ->
when {
visibleComponents.containsKey(i) -> {
componentHash(visibleComponents[i]!!.nonce, visibleComponents[i]!!.component)
}
hiddenComponents.containsKey(i) -> hiddenComponents[i]!!
else -> throw IllegalStateException("Missing component hashes")
}
}
combinedHash(hashList)
}
override val outputs: List<TransactionState<ContractState>> get() = emptyList()
/** Contains the serialized component and the associated nonce for computing the transaction id. */
@CordaSerializable
class FilteredComponent(val component: OpaqueBytes, val nonce: SecureHash)
}
/**
@ -103,7 +161,7 @@ data class ContractUpgradeLedgerTransaction(
override val inputs: List<StateAndRef<ContractState>>,
override val notary: Party,
val legacyContractAttachment: Attachment,
val upgradeContractClassName: ContractClassName,
val upgradedContractClassName: ContractClassName,
val upgradedContractAttachment: Attachment,
override val id: SecureHash,
val privacySalt: PrivacySalt,
@ -165,7 +223,7 @@ data class ContractUpgradeLedgerTransaction(
// TODO: re-map encumbrance pointers
input.state.copy(
data = upgradedState,
contract = upgradeContractClassName,
contract = upgradedContractClassName,
constraint = outputConstraint
)
}
@ -182,7 +240,7 @@ data class ContractUpgradeLedgerTransaction(
private fun loadUpgradedContract(): UpgradedContract<ContractState, *> {
@Suppress("UNCHECKED_CAST")
return this::class.java.classLoader
.loadClass(upgradeContractClassName)
.loadClass(upgradedContractClassName)
.asSubclass(Contract::class.java)
.getConstructor()
.newInstance() as UpgradedContract<ContractState, *>

View File

@ -3,11 +3,15 @@ package net.corda.core.transactions
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.serializedHash
import net.corda.core.crypto.sha256
import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub
import net.corda.core.node.ServicesForResolution
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.transactions.NotaryChangeWireTransaction.Component.*
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.toBase58String
import java.security.PublicKey
@ -18,10 +22,18 @@ import java.security.PublicKey
*/
@CordaSerializable
data class NotaryChangeWireTransaction(
override val inputs: List<StateRef>,
override val notary: Party,
val newNotary: Party
/**
* Contains all of the transaction components in serialized form.
* This is used for calculating the transaction id in a deterministic fashion, since re-serializing properties
* may result in a different byte sequence depending on the serialization context.
*/
val serializedComponents: List<OpaqueBytes>
) : CoreTransaction() {
override val inputs: List<StateRef> = serializedComponents[INPUTS.ordinal].deserialize()
override val notary: Party = serializedComponents[NOTARY.ordinal].deserialize()
/** Identity of the notary service to reassign the states to.*/
val newNotary: Party = serializedComponents[NEW_NOTARY.ordinal].deserialize()
/**
* This transaction does not contain any output states, outputs can be obtained by resolving a
* [NotaryChangeLedgerTransaction] and applying the notary modification to inputs.
@ -39,16 +51,29 @@ data class NotaryChangeWireTransaction(
* A privacy salt is not really required in this case, because we already used nonces in normal transactions and
* thus input state refs will always be unique. Also, filtering doesn't apply on this type of transactions.
*/
override val id: SecureHash by lazy { serializedHash(inputs + notary + newNotary) }
override val id: SecureHash by lazy {
serializedComponents.map { component ->
component.bytes.sha256()
}.reduce { combinedHash, componentHash ->
combinedHash.hashConcat(componentHash)
}
}
/** Resolves input states and builds a [NotaryChangeLedgerTransaction]. */
fun resolve(services: ServicesForResolution, sigs: List<TransactionSignature>) : NotaryChangeLedgerTransaction {
fun resolve(services: ServicesForResolution, sigs: List<TransactionSignature>): NotaryChangeLedgerTransaction {
val resolvedInputs = services.loadStates(inputs.toSet()).toList()
return NotaryChangeLedgerTransaction(resolvedInputs, notary, newNotary, id, sigs)
}
/** Resolves input states and builds a [NotaryChangeLedgerTransaction]. */
fun resolve(services: ServiceHub, sigs: List<TransactionSignature>) = resolve(services as ServicesForResolution, sigs)
enum class Component {
INPUTS, NOTARY, NEW_NOTARY
}
@Deprecated("Required only for backwards compatibility purposes. This type of transaction should not be constructed outside Corda code.", ReplaceWith("NotaryChangeTransactionBuilder"), DeprecationLevel.WARNING)
constructor(inputs: List<StateRef>, notary: Party, newNotary: Party) : this(listOf(inputs, notary, newNotary).map { it.serialize() })
}
/**

View File

@ -22,10 +22,7 @@ import net.corda.core.serialization.MissingAttachmentsException
import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SerializedBytes
import net.corda.core.transactions.ContractUpgradeWireTransaction
import net.corda.core.transactions.NotaryChangeWireTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.transactions.*
import net.corda.core.utilities.NonEmptySet
import net.corda.core.utilities.toNonEmptySet
import net.corda.nodeapi.internal.serialization.CordaClassResolver
@ -129,6 +126,7 @@ object DefaultKryoCustomizer {
register(java.lang.invoke.SerializedLambda::class.java)
register(ClosureSerializer.Closure::class.java, CordaClosureBlacklistSerializer)
register(ContractUpgradeWireTransaction::class.java, ContractUpgradeWireTransactionSerializer)
register(ContractUpgradeFilteredTransaction::class.java, ContractUpgradeFilteredTransactionSerializer)
for (whitelistProvider in serializationWhitelists) {
val types = whitelistProvider.whitelist

View File

@ -8,14 +8,10 @@ import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer
import com.esotericsoftware.kryo.serializers.FieldSerializer
import com.esotericsoftware.kryo.util.MapReferenceResolver
import net.corda.core.concurrent.CordaFuture
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.PrivacySalt
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.TransactionSignature
import net.corda.core.identity.Party
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationContext.UseCase.Checkpoint
@ -25,6 +21,7 @@ import net.corda.core.serialization.SerializedBytes
import net.corda.core.toFuture
import net.corda.core.toObservable
import net.corda.core.transactions.*
import net.corda.core.utilities.OpaqueBytes
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.serialization.CordaClassResolver
import net.corda.nodeapi.internal.serialization.serializationContextKey
@ -254,40 +251,41 @@ object WireTransactionSerializer : Serializer<WireTransaction>() {
@ThreadSafe
object NotaryChangeWireTransactionSerializer : Serializer<NotaryChangeWireTransaction>() {
override fun write(kryo: Kryo, output: Output, obj: NotaryChangeWireTransaction) {
kryo.writeClassAndObject(output, obj.inputs)
kryo.writeClassAndObject(output, obj.notary)
kryo.writeClassAndObject(output, obj.newNotary)
kryo.writeClassAndObject(output, obj.serializedComponents)
}
override fun read(kryo: Kryo, input: Input, type: Class<NotaryChangeWireTransaction>): NotaryChangeWireTransaction {
val inputs: List<StateRef> = uncheckedCast(kryo.readClassAndObject(input))
val notary = kryo.readClassAndObject(input) as Party
val newNotary = kryo.readClassAndObject(input) as Party
return NotaryChangeWireTransaction(inputs, notary, newNotary)
val components : List<OpaqueBytes> = uncheckedCast(kryo.readClassAndObject(input))
return NotaryChangeWireTransaction(components)
}
}
@ThreadSafe
object ContractUpgradeWireTransactionSerializer : Serializer<ContractUpgradeWireTransaction>() {
override fun write(kryo: Kryo, output: Output, obj: ContractUpgradeWireTransaction) {
kryo.writeClassAndObject(output, obj.inputs)
kryo.writeClassAndObject(output, obj.notary)
kryo.writeClassAndObject(output, obj.legacyContractAttachmentId)
kryo.writeClassAndObject(output, obj.upgradeContractClassName)
kryo.writeClassAndObject(output, obj.upgradedContractAttachmentId)
kryo.writeClassAndObject(output, obj.serializedComponents)
kryo.writeClassAndObject(output, obj.privacySalt)
}
override fun read(kryo: Kryo, input: Input, type: Class<ContractUpgradeWireTransaction>): ContractUpgradeWireTransaction {
val inputs: List<StateRef> = uncheckedCast(kryo.readClassAndObject(input))
val notary = kryo.readClassAndObject(input) as Party
val legacyContractAttachment = kryo.readClassAndObject(input) as SecureHash
val upgradeContractClassName = kryo.readClassAndObject(input) as String
val upgradedContractAttachment = kryo.readClassAndObject(input) as SecureHash
val components: List<OpaqueBytes> = uncheckedCast(kryo.readClassAndObject(input))
val privacySalt = kryo.readClassAndObject(input) as PrivacySalt
return ContractUpgradeWireTransaction(inputs, notary, legacyContractAttachment, upgradeContractClassName, upgradedContractAttachment, privacySalt)
return ContractUpgradeWireTransaction(components, privacySalt)
}
}
@ThreadSafe
object ContractUpgradeFilteredTransactionSerializer : Serializer<ContractUpgradeFilteredTransaction>() {
override fun write(kryo: Kryo, output: Output, obj: ContractUpgradeFilteredTransaction) {
kryo.writeClassAndObject(output, obj.visibleComponents)
kryo.writeClassAndObject(output, obj.hiddenComponents)
}
override fun read(kryo: Kryo, input: Input, type: Class<ContractUpgradeFilteredTransaction>): ContractUpgradeFilteredTransaction {
val visibleComponents: Map<Int, ContractUpgradeFilteredTransaction.FilteredComponent> = uncheckedCast(kryo.readClassAndObject(input))
val hiddenComponents: Map<Int, SecureHash> = uncheckedCast(kryo.readClassAndObject(input))
return ContractUpgradeFilteredTransaction(visibleComponents, hiddenComponents)
}
}

View File

@ -4,10 +4,14 @@ import co.paralleluniverse.fibers.Suspendable
import com.nhaarman.mockito_kotlin.argThat
import com.nhaarman.mockito_kotlin.doNothing
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.*
import net.corda.core.contracts.Amount
import net.corda.core.contracts.Issued
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.NullKeys
import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.*
import net.corda.core.internal.NotaryChangeTransactionBuilder
import net.corda.core.internal.packageName
import net.corda.core.node.StatesToRecord
import net.corda.core.node.services.StatesNotAvailableException
@ -17,7 +21,6 @@ import net.corda.core.node.services.queryBy
import net.corda.core.node.services.vault.PageSpecification
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.node.services.vault.QueryCriteria.*
import net.corda.core.transactions.NotaryChangeWireTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.NonEmptySet
@ -607,7 +610,7 @@ class NodeVaultServiceTest {
// Change notary
services.identityService.verifyAndRegisterIdentity(DUMMY_NOTARY_IDENTITY)
val newNotary = DUMMY_NOTARY
val changeNotaryTx = NotaryChangeWireTransaction(listOf(initialCashState.ref), issueStx.notary!!, newNotary)
val changeNotaryTx = NotaryChangeTransactionBuilder(listOf(initialCashState.ref), issueStx.notary!!, newNotary).build()
val cashStateWithNewNotary = StateAndRef(initialCashState.state.copy(notary = newNotary), StateRef(changeNotaryTx.id, 0))
database.transaction {