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

* CORDA-696: Ensure deterministic transaction id calculation for contract upgrade and notary change transactions.

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.

* Support forwards compatibility for new hidden or visible properties

* Some refactoring and updating api docs

* Fix tests & add custom serializer in case the transaction is captured in a checkpoint

* Update id calculation for notary change transactions as well - no filtering is involved

* Use computeNonce

* More refactoring

* Use helper for computing component hashes

* Optimise id calculation
This commit is contained in:
Andrius Dagys
2018-03-08 17:59:25 +00:00
committed by Katelyn Baker
parent d32dbc33c5
commit da74263f42
10 changed files with 208 additions and 85 deletions

View File

@ -2994,17 +2994,15 @@ public static final class net.corda.core.serialization.SingletonSerializationTok
@org.jetbrains.annotations.NotNull public final String getReason()
##
@net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement 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()
##
@ -3031,23 +3029,19 @@ 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()
public void verifySignaturesExcept(Collection)
##
@net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement 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()
@ -3055,8 +3049,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()
@ -3190,11 +3184,9 @@ public static final class net.corda.core.transactions.LedgerTransaction$InOutGro
public void verifySignaturesExcept(Collection)
##
@net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement 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,14 +21,14 @@ 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 {

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
@ -130,6 +127,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 {