mirror of
https://github.com/corda/corda.git
synced 2025-06-19 07:38:22 +00:00
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:
committed by
Katelyn Baker
parent
d32dbc33c5
commit
da74263f42
@ -2994,17 +2994,15 @@ public static final class net.corda.core.serialization.SingletonSerializationTok
|
|||||||
@org.jetbrains.annotations.NotNull public final String getReason()
|
@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
|
@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)
|
public <init>(Map, Map)
|
||||||
@org.jetbrains.annotations.NotNull public final List component1()
|
@org.jetbrains.annotations.NotNull public final Map component1()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party component2()
|
@org.jetbrains.annotations.NotNull public final Map 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(Map, Map)
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeFilteredTransaction copy(List, net.corda.core.identity.Party, net.corda.core.crypto.SecureHash)
|
|
||||||
public boolean equals(Object)
|
public boolean equals(Object)
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
|
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
|
||||||
@org.jetbrains.annotations.NotNull public List getInputs()
|
@org.jetbrains.annotations.NotNull public List getInputs()
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.identity.Party getNotary()
|
@org.jetbrains.annotations.NotNull public net.corda.core.identity.Party getNotary()
|
||||||
@org.jetbrains.annotations.NotNull public List getOutputs()
|
@org.jetbrains.annotations.NotNull public List getOutputs()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getRest()
|
|
||||||
public int hashCode()
|
public int hashCode()
|
||||||
public String toString()
|
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 final net.corda.core.contracts.PrivacySalt getPrivacySalt()
|
||||||
@org.jetbrains.annotations.NotNull public Set getRequiredSigningKeys()
|
@org.jetbrains.annotations.NotNull public Set getRequiredSigningKeys()
|
||||||
@org.jetbrains.annotations.NotNull public List getSigs()
|
@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 net.corda.core.contracts.Attachment getUpgradedContractAttachment()
|
||||||
|
@org.jetbrains.annotations.NotNull public final String getUpgradedContractClassName()
|
||||||
public int hashCode()
|
public int hashCode()
|
||||||
public String toString()
|
public String toString()
|
||||||
public void verifyRequiredSignatures()
|
public void verifyRequiredSignatures()
|
||||||
public void verifySignaturesExcept(Collection)
|
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
|
@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 net.corda.core.transactions.ContractUpgradeFilteredTransaction buildFilteredTransaction()
|
||||||
@org.jetbrains.annotations.NotNull public final List component1()
|
@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.contracts.PrivacySalt component2()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash component3()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeWireTransaction copy(List, net.corda.core.contracts.PrivacySalt)
|
||||||
@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)
|
|
||||||
public boolean equals(Object)
|
public boolean equals(Object)
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
|
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
|
||||||
@org.jetbrains.annotations.NotNull public List getInputs()
|
@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 net.corda.core.identity.Party getNotary()
|
||||||
@org.jetbrains.annotations.NotNull public List getOutputs()
|
@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 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 net.corda.core.crypto.SecureHash getUpgradedContractAttachmentId()
|
||||||
|
@org.jetbrains.annotations.NotNull public final String getUpgradedContractClassName()
|
||||||
public int hashCode()
|
public int hashCode()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeLedgerTransaction resolve(net.corda.core.node.ServicesForResolution, List)
|
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.ContractUpgradeLedgerTransaction resolve(net.corda.core.node.ServicesForResolution, List)
|
||||||
public String toString()
|
public String toString()
|
||||||
@ -3190,11 +3184,9 @@ public static final class net.corda.core.transactions.LedgerTransaction$InOutGro
|
|||||||
public void verifySignaturesExcept(Collection)
|
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
|
@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 List component1()
|
||||||
@org.jetbrains.annotations.NotNull public final net.corda.core.identity.Party component2()
|
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.NotaryChangeWireTransaction copy(List)
|
||||||
@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)
|
|
||||||
public boolean equals(Object)
|
public boolean equals(Object)
|
||||||
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
|
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
|
||||||
@org.jetbrains.annotations.NotNull public List getInputs()
|
@org.jetbrains.annotations.NotNull public List getInputs()
|
||||||
|
@ -222,7 +222,11 @@ fun componentHash(opaqueBytes: OpaqueBytes, privacySalt: PrivacySalt, componentG
|
|||||||
/** Return the SHA256(SHA256(nonce || serializedComponent)). */
|
/** Return the SHA256(SHA256(nonce || serializedComponent)). */
|
||||||
fun componentHash(nonce: SecureHash, opaqueBytes: OpaqueBytes): SecureHash = SecureHash.sha256Twice(nonce.bytes + opaqueBytes.bytes)
|
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()
|
fun <T : Any> serializedHash(x: T): SecureHash = x.serialize(context = SerializationDefaults.P2P_CONTEXT.withoutReferences()).bytes.sha256()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,7 +7,7 @@ import net.corda.core.crypto.Crypto
|
|||||||
import net.corda.core.crypto.SignableData
|
import net.corda.core.crypto.SignableData
|
||||||
import net.corda.core.crypto.SignatureMetadata
|
import net.corda.core.crypto.SignatureMetadata
|
||||||
import net.corda.core.identity.Party
|
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.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
|
|
||||||
@ -30,11 +30,11 @@ class NotaryChangeFlow<out T : ContractState>(
|
|||||||
override fun assembleTx(): AbstractStateReplacementFlow.UpgradeTx {
|
override fun assembleTx(): AbstractStateReplacementFlow.UpgradeTx {
|
||||||
val inputs = resolveEncumbrances(originalState)
|
val inputs = resolveEncumbrances(originalState)
|
||||||
|
|
||||||
val tx = NotaryChangeWireTransaction(
|
val tx = NotaryChangeTransactionBuilder(
|
||||||
inputs.map { it.ref },
|
inputs.map { it.ref },
|
||||||
originalState.state.notary,
|
originalState.state.notary,
|
||||||
modification
|
modification
|
||||||
)
|
).build()
|
||||||
|
|
||||||
val participantKeys = inputs.flatMap { it.state.data.participants }.map { it.owningKey }.toSet()
|
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
|
// TODO: We need a much faster way of finding our key in the transaction
|
||||||
|
@ -21,14 +21,14 @@ object ContractUpgradeUtils {
|
|||||||
val upgradedContractAttachmentId = getContractAttachmentId(upgradedContractClass.name, services)
|
val upgradedContractAttachmentId = getContractAttachmentId(upgradedContractClass.name, services)
|
||||||
|
|
||||||
val inputs = listOf(stateAndRef.ref)
|
val inputs = listOf(stateAndRef.ref)
|
||||||
return ContractUpgradeWireTransaction(
|
return ContractUpgradeTransactionBuilder(
|
||||||
inputs,
|
inputs,
|
||||||
stateAndRef.state.notary,
|
stateAndRef.state.notary,
|
||||||
legacyContractAttachmentId,
|
legacyContractAttachmentId,
|
||||||
upgradedContractClass.name,
|
upgradedContractClass.name,
|
||||||
upgradedContractAttachmentId,
|
upgradedContractAttachmentId,
|
||||||
privacySalt
|
privacySalt
|
||||||
)
|
).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getContractAttachmentId(name: ContractClassName, services: ServicesForResolution): AttachmentId {
|
private fun getContractAttachmentId(name: ContractClassName, services: ServicesForResolution): AttachmentId {
|
||||||
|
@ -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()
|
||||||
|
}
|
@ -3,12 +3,18 @@ package net.corda.core.transactions
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.TransactionSignature
|
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.identity.Party
|
||||||
import net.corda.core.internal.AttachmentWithContext
|
import net.corda.core.internal.AttachmentWithContext
|
||||||
|
import net.corda.core.internal.combinedHash
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
import net.corda.core.serialization.CordaSerializable
|
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 net.corda.core.utilities.toBase58String
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
@ -18,13 +24,20 @@ import java.security.PublicKey
|
|||||||
/** A special transaction for upgrading the contract of a state. */
|
/** A special transaction for upgrading the contract of a state. */
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
data class ContractUpgradeWireTransaction(
|
data class ContractUpgradeWireTransaction(
|
||||||
override val inputs: List<StateRef>,
|
/**
|
||||||
override val notary: Party,
|
* Contains all of the transaction components in serialized form.
|
||||||
val legacyContractAttachmentId: SecureHash,
|
* This is used for calculating the transaction id in a deterministic fashion, since re-serializing properties
|
||||||
val upgradeContractClassName: ContractClassName,
|
* may result in a different byte sequence depending on the serialization context.
|
||||||
val upgradedContractAttachmentId: SecureHash,
|
*/
|
||||||
|
val serializedComponents: List<OpaqueBytes>,
|
||||||
|
/** Required for hiding components in [ContractUpgradeFilteredTransaction]. */
|
||||||
val privacySalt: PrivacySalt = PrivacySalt()
|
val privacySalt: PrivacySalt = PrivacySalt()
|
||||||
) : CoreTransaction() {
|
) : 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 {
|
init {
|
||||||
check(inputs.isNotEmpty()) { "A contract upgrade transaction must have inputs" }
|
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, " +
|
get() = throw UnsupportedOperationException("ContractUpgradeWireTransaction does not contain output states, " +
|
||||||
"outputs can only be obtained from a resolved ContractUpgradeLedgerTransaction")
|
"outputs can only be obtained from a resolved ContractUpgradeLedgerTransaction")
|
||||||
|
|
||||||
/** Hash of the list of components that are hidden in the [ContractUpgradeFilteredTransaction]. */
|
override val id: SecureHash by lazy {
|
||||||
private val hiddenComponentHash: SecureHash
|
val componentHashes =serializedComponents.mapIndexed { index, component ->
|
||||||
get() = serializedHash(listOf(legacyContractAttachmentId, upgradeContractClassName, privacySalt))
|
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. */
|
/** Resolves input states and contract attachments, and builds a ContractUpgradeLedgerTransaction. */
|
||||||
fun resolve(services: ServicesForResolution, sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
fun resolve(services: ServicesForResolution, sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
||||||
@ -56,7 +75,7 @@ data class ContractUpgradeWireTransaction(
|
|||||||
resolvedInputs,
|
resolvedInputs,
|
||||||
notary,
|
notary,
|
||||||
legacyContractAttachment,
|
legacyContractAttachment,
|
||||||
upgradeContractClassName,
|
upgradedContractClassName,
|
||||||
upgradedContractAttachment,
|
upgradedContractAttachment,
|
||||||
id,
|
id,
|
||||||
privacySalt,
|
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 {
|
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
|
* 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
|
* 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.
|
* 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
|
@CordaSerializable
|
||||||
data class ContractUpgradeFilteredTransaction(
|
data class ContractUpgradeFilteredTransaction(
|
||||||
override val inputs: List<StateRef>,
|
/** Transaction components that are exposed. */
|
||||||
override val notary: Party,
|
val visibleComponents: Map<Int, FilteredComponent>,
|
||||||
val rest: SecureHash
|
/**
|
||||||
|
* Hashes of the transaction components that are not revealed in this transaction.
|
||||||
|
* Required for computing the transaction id.
|
||||||
|
*/
|
||||||
|
val hiddenComponents: Map<Int, SecureHash>
|
||||||
) : CoreTransaction() {
|
) : 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()
|
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 inputs: List<StateAndRef<ContractState>>,
|
||||||
override val notary: Party,
|
override val notary: Party,
|
||||||
val legacyContractAttachment: Attachment,
|
val legacyContractAttachment: Attachment,
|
||||||
val upgradeContractClassName: ContractClassName,
|
val upgradedContractClassName: ContractClassName,
|
||||||
val upgradedContractAttachment: Attachment,
|
val upgradedContractAttachment: Attachment,
|
||||||
override val id: SecureHash,
|
override val id: SecureHash,
|
||||||
val privacySalt: PrivacySalt,
|
val privacySalt: PrivacySalt,
|
||||||
@ -165,7 +223,7 @@ data class ContractUpgradeLedgerTransaction(
|
|||||||
// TODO: re-map encumbrance pointers
|
// TODO: re-map encumbrance pointers
|
||||||
input.state.copy(
|
input.state.copy(
|
||||||
data = upgradedState,
|
data = upgradedState,
|
||||||
contract = upgradeContractClassName,
|
contract = upgradedContractClassName,
|
||||||
constraint = outputConstraint
|
constraint = outputConstraint
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -182,7 +240,7 @@ data class ContractUpgradeLedgerTransaction(
|
|||||||
private fun loadUpgradedContract(): UpgradedContract<ContractState, *> {
|
private fun loadUpgradedContract(): UpgradedContract<ContractState, *> {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return this::class.java.classLoader
|
return this::class.java.classLoader
|
||||||
.loadClass(upgradeContractClassName)
|
.loadClass(upgradedContractClassName)
|
||||||
.asSubclass(Contract::class.java)
|
.asSubclass(Contract::class.java)
|
||||||
.getConstructor()
|
.getConstructor()
|
||||||
.newInstance() as UpgradedContract<ContractState, *>
|
.newInstance() as UpgradedContract<ContractState, *>
|
||||||
|
@ -3,11 +3,15 @@ package net.corda.core.transactions
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.TransactionSignature
|
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.identity.Party
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
import net.corda.core.serialization.CordaSerializable
|
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 net.corda.core.utilities.toBase58String
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
@ -18,10 +22,18 @@ import java.security.PublicKey
|
|||||||
*/
|
*/
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
data class NotaryChangeWireTransaction(
|
data class NotaryChangeWireTransaction(
|
||||||
override val inputs: List<StateRef>,
|
/**
|
||||||
override val notary: Party,
|
* Contains all of the transaction components in serialized form.
|
||||||
val newNotary: Party
|
* 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() {
|
) : 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
|
* This transaction does not contain any output states, outputs can be obtained by resolving a
|
||||||
* [NotaryChangeLedgerTransaction] and applying the notary modification to inputs.
|
* [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
|
* 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.
|
* 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]. */
|
/** 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()
|
val resolvedInputs = services.loadStates(inputs.toSet()).toList()
|
||||||
return NotaryChangeLedgerTransaction(resolvedInputs, notary, newNotary, id, sigs)
|
return NotaryChangeLedgerTransaction(resolvedInputs, notary, newNotary, id, sigs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resolves input states and builds a [NotaryChangeLedgerTransaction]. */
|
/** Resolves input states and builds a [NotaryChangeLedgerTransaction]. */
|
||||||
fun resolve(services: ServiceHub, sigs: List<TransactionSignature>) = resolve(services as ServicesForResolution, sigs)
|
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() })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,10 +22,7 @@ import net.corda.core.serialization.MissingAttachmentsException
|
|||||||
import net.corda.core.serialization.SerializationWhitelist
|
import net.corda.core.serialization.SerializationWhitelist
|
||||||
import net.corda.core.serialization.SerializeAsToken
|
import net.corda.core.serialization.SerializeAsToken
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import net.corda.core.transactions.ContractUpgradeWireTransaction
|
import net.corda.core.transactions.*
|
||||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
|
||||||
import net.corda.core.transactions.WireTransaction
|
|
||||||
import net.corda.core.utilities.NonEmptySet
|
import net.corda.core.utilities.NonEmptySet
|
||||||
import net.corda.core.utilities.toNonEmptySet
|
import net.corda.core.utilities.toNonEmptySet
|
||||||
import net.corda.nodeapi.internal.serialization.CordaClassResolver
|
import net.corda.nodeapi.internal.serialization.CordaClassResolver
|
||||||
@ -130,6 +127,7 @@ object DefaultKryoCustomizer {
|
|||||||
register(java.lang.invoke.SerializedLambda::class.java)
|
register(java.lang.invoke.SerializedLambda::class.java)
|
||||||
register(ClosureSerializer.Closure::class.java, CordaClosureBlacklistSerializer)
|
register(ClosureSerializer.Closure::class.java, CordaClosureBlacklistSerializer)
|
||||||
register(ContractUpgradeWireTransaction::class.java, ContractUpgradeWireTransactionSerializer)
|
register(ContractUpgradeWireTransaction::class.java, ContractUpgradeWireTransactionSerializer)
|
||||||
|
register(ContractUpgradeFilteredTransaction::class.java, ContractUpgradeFilteredTransactionSerializer)
|
||||||
|
|
||||||
for (whitelistProvider in serializationWhitelists) {
|
for (whitelistProvider in serializationWhitelists) {
|
||||||
val types = whitelistProvider.whitelist
|
val types = whitelistProvider.whitelist
|
||||||
|
@ -8,14 +8,10 @@ import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer
|
|||||||
import com.esotericsoftware.kryo.serializers.FieldSerializer
|
import com.esotericsoftware.kryo.serializers.FieldSerializer
|
||||||
import com.esotericsoftware.kryo.util.MapReferenceResolver
|
import com.esotericsoftware.kryo.util.MapReferenceResolver
|
||||||
import net.corda.core.concurrent.CordaFuture
|
import net.corda.core.concurrent.CordaFuture
|
||||||
import net.corda.core.contracts.ContractState
|
|
||||||
import net.corda.core.contracts.PrivacySalt
|
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.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.TransactionSignature
|
import net.corda.core.crypto.TransactionSignature
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.internal.uncheckedCast
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
import net.corda.core.serialization.SerializationContext.UseCase.Checkpoint
|
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.toFuture
|
||||||
import net.corda.core.toObservable
|
import net.corda.core.toObservable
|
||||||
import net.corda.core.transactions.*
|
import net.corda.core.transactions.*
|
||||||
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.serialization.CordaClassResolver
|
import net.corda.nodeapi.internal.serialization.CordaClassResolver
|
||||||
import net.corda.nodeapi.internal.serialization.serializationContextKey
|
import net.corda.nodeapi.internal.serialization.serializationContextKey
|
||||||
@ -254,40 +251,41 @@ object WireTransactionSerializer : Serializer<WireTransaction>() {
|
|||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
object NotaryChangeWireTransactionSerializer : Serializer<NotaryChangeWireTransaction>() {
|
object NotaryChangeWireTransactionSerializer : Serializer<NotaryChangeWireTransaction>() {
|
||||||
override fun write(kryo: Kryo, output: Output, obj: NotaryChangeWireTransaction) {
|
override fun write(kryo: Kryo, output: Output, obj: NotaryChangeWireTransaction) {
|
||||||
kryo.writeClassAndObject(output, obj.inputs)
|
kryo.writeClassAndObject(output, obj.serializedComponents)
|
||||||
kryo.writeClassAndObject(output, obj.notary)
|
|
||||||
kryo.writeClassAndObject(output, obj.newNotary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(kryo: Kryo, input: Input, type: Class<NotaryChangeWireTransaction>): NotaryChangeWireTransaction {
|
override fun read(kryo: Kryo, input: Input, type: Class<NotaryChangeWireTransaction>): NotaryChangeWireTransaction {
|
||||||
val inputs: List<StateRef> = uncheckedCast(kryo.readClassAndObject(input))
|
val components : List<OpaqueBytes> = uncheckedCast(kryo.readClassAndObject(input))
|
||||||
val notary = kryo.readClassAndObject(input) as Party
|
return NotaryChangeWireTransaction(components)
|
||||||
val newNotary = kryo.readClassAndObject(input) as Party
|
|
||||||
|
|
||||||
return NotaryChangeWireTransaction(inputs, notary, newNotary)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
object ContractUpgradeWireTransactionSerializer : Serializer<ContractUpgradeWireTransaction>() {
|
object ContractUpgradeWireTransactionSerializer : Serializer<ContractUpgradeWireTransaction>() {
|
||||||
override fun write(kryo: Kryo, output: Output, obj: ContractUpgradeWireTransaction) {
|
override fun write(kryo: Kryo, output: Output, obj: ContractUpgradeWireTransaction) {
|
||||||
kryo.writeClassAndObject(output, obj.inputs)
|
kryo.writeClassAndObject(output, obj.serializedComponents)
|
||||||
kryo.writeClassAndObject(output, obj.notary)
|
|
||||||
kryo.writeClassAndObject(output, obj.legacyContractAttachmentId)
|
|
||||||
kryo.writeClassAndObject(output, obj.upgradeContractClassName)
|
|
||||||
kryo.writeClassAndObject(output, obj.upgradedContractAttachmentId)
|
|
||||||
kryo.writeClassAndObject(output, obj.privacySalt)
|
kryo.writeClassAndObject(output, obj.privacySalt)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(kryo: Kryo, input: Input, type: Class<ContractUpgradeWireTransaction>): ContractUpgradeWireTransaction {
|
override fun read(kryo: Kryo, input: Input, type: Class<ContractUpgradeWireTransaction>): ContractUpgradeWireTransaction {
|
||||||
val inputs: List<StateRef> = uncheckedCast(kryo.readClassAndObject(input))
|
val components: List<OpaqueBytes> = 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 privacySalt = kryo.readClassAndObject(input) as PrivacySalt
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,10 +4,14 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import com.nhaarman.mockito_kotlin.argThat
|
import com.nhaarman.mockito_kotlin.argThat
|
||||||
import com.nhaarman.mockito_kotlin.doNothing
|
import com.nhaarman.mockito_kotlin.doNothing
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
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.NullKeys
|
||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.identity.*
|
import net.corda.core.identity.*
|
||||||
|
import net.corda.core.internal.NotaryChangeTransactionBuilder
|
||||||
import net.corda.core.internal.packageName
|
import net.corda.core.internal.packageName
|
||||||
import net.corda.core.node.StatesToRecord
|
import net.corda.core.node.StatesToRecord
|
||||||
import net.corda.core.node.services.StatesNotAvailableException
|
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.PageSpecification
|
||||||
import net.corda.core.node.services.vault.QueryCriteria
|
import net.corda.core.node.services.vault.QueryCriteria
|
||||||
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.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.NonEmptySet
|
import net.corda.core.utilities.NonEmptySet
|
||||||
@ -607,7 +610,7 @@ class NodeVaultServiceTest {
|
|||||||
// Change notary
|
// Change notary
|
||||||
services.identityService.verifyAndRegisterIdentity(DUMMY_NOTARY_IDENTITY)
|
services.identityService.verifyAndRegisterIdentity(DUMMY_NOTARY_IDENTITY)
|
||||||
val newNotary = DUMMY_NOTARY
|
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))
|
val cashStateWithNewNotary = StateAndRef(initialCashState.state.copy(notary = newNotary), StateRef(changeNotaryTx.id, 0))
|
||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
|
Reference in New Issue
Block a user