mirror of
https://github.com/corda/corda.git
synced 2025-04-19 00:27:13 +00:00
Merge branch 'merge-point-e14421b' into shams-os-merge-231118
This commit is contained in:
commit
04054f7c68
@ -203,6 +203,7 @@ see changes to this list.
|
||||
* tomtau
|
||||
* Tudor Malene (R3)
|
||||
* Tushar Singh Bora (Accenture)
|
||||
* Vardan Nadkarni (Persistent Systems Limited)
|
||||
* varunkm
|
||||
* Venelin Stoykov (INDUSTRIA)
|
||||
* verymahler
|
||||
|
@ -27,6 +27,7 @@ import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.PartialMerkleTree.PartialTree
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.internal.DigitalSignatureWithCert
|
||||
import net.corda.core.internal.createComponentGroups
|
||||
import net.corda.core.internal.kotlinObjectInstance
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
@ -187,6 +188,7 @@ private class WireTransactionSerializer : JsonSerializer<WireTransaction>() {
|
||||
value.commands,
|
||||
value.timeWindow,
|
||||
value.attachments,
|
||||
value.references,
|
||||
value.privacySalt
|
||||
))
|
||||
}
|
||||
@ -195,13 +197,14 @@ private class WireTransactionSerializer : JsonSerializer<WireTransaction>() {
|
||||
private class WireTransactionDeserializer : JsonDeserializer<WireTransaction>() {
|
||||
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): WireTransaction {
|
||||
val wrapper = parser.readValueAs<WireTransactionJson>()
|
||||
val componentGroups = WireTransaction.createComponentGroups(
|
||||
val componentGroups = createComponentGroups(
|
||||
wrapper.inputs,
|
||||
wrapper.outputs,
|
||||
wrapper.commands,
|
||||
wrapper.attachments,
|
||||
wrapper.notary,
|
||||
wrapper.timeWindow
|
||||
wrapper.timeWindow,
|
||||
wrapper.references
|
||||
)
|
||||
return WireTransaction(componentGroups, wrapper.privacySalt)
|
||||
}
|
||||
@ -214,6 +217,7 @@ private class WireTransactionJson(val id: SecureHash,
|
||||
val commands: List<Command<*>>,
|
||||
val timeWindow: TimeWindow?,
|
||||
val attachments: List<SecureHash>,
|
||||
val references: List<StateRef>,
|
||||
val privacySalt: PrivacySalt)
|
||||
|
||||
private interface TransactionStateMixin {
|
||||
|
@ -93,7 +93,7 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
|
||||
services = rigorousMock()
|
||||
cordappProvider = rigorousMock()
|
||||
doReturn(cordappProvider).whenever(services).cordappProvider
|
||||
doReturn(testNetworkParameters()).whenever(services).networkParameters
|
||||
doReturn(testNetworkParameters(minimumPlatformVersion = 4)).whenever(services).networkParameters
|
||||
doReturn(attachments).whenever(services).attachments
|
||||
}
|
||||
|
||||
@ -245,6 +245,7 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
|
||||
outputs = mutableListOf(createTransactionState()),
|
||||
commands = mutableListOf(Command(DummyCommandData, listOf(BOB_PUBKEY))),
|
||||
window = TimeWindow.fromStartAndDuration(Instant.now(), 1.hours),
|
||||
references = mutableListOf(StateRef(SecureHash.randomSHA256(), 0)),
|
||||
privacySalt = net.corda.core.contracts.PrivacySalt()
|
||||
).toWireTransaction(services)
|
||||
val stx = sign(wtx)
|
||||
@ -253,7 +254,7 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
|
||||
println(mapper.writeValueAsString(json))
|
||||
val (wtxJson, signaturesJson) = json.assertHasOnlyFields("wire", "signatures")
|
||||
assertThat(signaturesJson.childrenAs<TransactionSignature>(mapper)).isEqualTo(stx.sigs)
|
||||
val wtxFields = wtxJson.assertHasOnlyFields("id", "notary", "inputs", "attachments", "outputs", "commands", "timeWindow", "privacySalt")
|
||||
val wtxFields = wtxJson.assertHasOnlyFields("id", "notary", "inputs", "attachments", "outputs", "commands", "timeWindow", "references", "privacySalt")
|
||||
assertThat(wtxFields[0].valueAs<SecureHash>(mapper)).isEqualTo(wtx.id)
|
||||
assertThat(wtxFields[1].valueAs<Party>(mapper)).isEqualTo(wtx.notary)
|
||||
assertThat(wtxFields[2].childrenAs<StateRef>(mapper)).isEqualTo(wtx.inputs)
|
||||
@ -261,7 +262,8 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
|
||||
assertThat(wtxFields[4].childrenAs<TransactionState<*>>(mapper)).isEqualTo(wtx.outputs)
|
||||
assertThat(wtxFields[5].childrenAs<Command<*>>(mapper)).isEqualTo(wtx.commands)
|
||||
assertThat(wtxFields[6].valueAs<TimeWindow>(mapper)).isEqualTo(wtx.timeWindow)
|
||||
assertThat(wtxFields[7].valueAs<PrivacySalt>(mapper)).isEqualTo(wtx.privacySalt)
|
||||
assertThat(wtxFields[7].childrenAs<StateRef>(mapper)).isEqualTo(wtx.references)
|
||||
assertThat(wtxFields[8].valueAs<PrivacySalt>(mapper)).isEqualTo(wtx.privacySalt)
|
||||
assertThat(mapper.convertValue<WireTransaction>(wtxJson)).isEqualTo(wtx)
|
||||
assertThat(mapper.convertValue<SignedTransaction>(json)).isEqualTo(stx)
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
package net.corda.core.internal
|
||||
|
||||
import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TransactionState
|
||||
import net.corda.core.cordapp.Cordapp
|
||||
import net.corda.core.cordapp.CordappConfig
|
||||
import net.corda.core.cordapp.CordappContext
|
||||
@ -12,14 +8,11 @@ import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.node.ZoneVersionTooLowException
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import org.slf4j.MDC
|
||||
|
||||
// *Internal* Corda-specific utilities
|
||||
@ -80,11 +73,3 @@ class LazyMappedList<T, U>(val originalList: List<T>, val transform: (T, Int) ->
|
||||
override fun get(index: Int) = partialResolvedList[index]
|
||||
?: transform(originalList[index], index).also { computed -> partialResolvedList[index] = computed }
|
||||
}
|
||||
|
||||
/**
|
||||
* A SerializedStateAndRef is a pair (BinaryStateRepresentation, StateRef).
|
||||
* The [serializedState] is the actual component from the original transaction.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
@CordaSerializable
|
||||
data class SerializedStateAndRef(val serializedState: SerializedBytes<TransactionState<ContractState>>, val ref: StateRef)
|
@ -1,18 +1,13 @@
|
||||
package net.corda.core.internal
|
||||
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.componentHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.serialization.MissingAttachmentsException
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializationFactory
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.ComponentGroup
|
||||
import net.corda.core.transactions.ContractUpgradeWireTransaction
|
||||
import net.corda.core.transactions.FilteredComponentGroup
|
||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.transactions.*
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.lazyMapped
|
||||
import java.io.ByteArrayOutputStream
|
||||
@ -121,4 +116,40 @@ fun deserialiseCommands(componentGroups: List<ComponentGroup>,
|
||||
}
|
||||
commandDataList.lazyMapped { commandData, index -> Command(commandData, signersList[index]) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating list of [ComponentGroup] used in one of the constructors of [WireTransaction] required
|
||||
* for backwards compatibility purposes.
|
||||
*/
|
||||
fun createComponentGroups(inputs: List<StateRef>,
|
||||
outputs: List<TransactionState<ContractState>>,
|
||||
commands: List<Command<*>>,
|
||||
attachments: List<SecureHash>,
|
||||
notary: Party?,
|
||||
timeWindow: TimeWindow?,
|
||||
references: List<StateRef>): List<ComponentGroup> {
|
||||
val serialize = { value: Any, _: Int -> value.serialize() }
|
||||
val componentGroupMap: MutableList<ComponentGroup> = mutableListOf()
|
||||
if (inputs.isNotEmpty()) componentGroupMap.add(ComponentGroup(ComponentGroupEnum.INPUTS_GROUP.ordinal, inputs.lazyMapped(serialize)))
|
||||
if (references.isNotEmpty()) componentGroupMap.add(ComponentGroup(ComponentGroupEnum.REFERENCES_GROUP.ordinal, references.lazyMapped(serialize)))
|
||||
if (outputs.isNotEmpty()) componentGroupMap.add(ComponentGroup(ComponentGroupEnum.OUTPUTS_GROUP.ordinal, outputs.lazyMapped(serialize)))
|
||||
// Adding commandData only to the commands group. Signers are added in their own group.
|
||||
if (commands.isNotEmpty()) componentGroupMap.add(ComponentGroup(ComponentGroupEnum.COMMANDS_GROUP.ordinal, commands.map { it.value }.lazyMapped(serialize)))
|
||||
if (attachments.isNotEmpty()) componentGroupMap.add(ComponentGroup(ComponentGroupEnum.ATTACHMENTS_GROUP.ordinal, attachments.lazyMapped(serialize)))
|
||||
if (notary != null) componentGroupMap.add(ComponentGroup(ComponentGroupEnum.NOTARY_GROUP.ordinal, listOf(notary).lazyMapped(serialize)))
|
||||
if (timeWindow != null) componentGroupMap.add(ComponentGroup(ComponentGroupEnum.TIMEWINDOW_GROUP.ordinal, listOf(timeWindow).lazyMapped(serialize)))
|
||||
// Adding signers to their own group. This is required for command visibility purposes: a party receiving
|
||||
// a FilteredTransaction can now verify it sees all the commands it should sign.
|
||||
if (commands.isNotEmpty()) componentGroupMap.add(ComponentGroup(ComponentGroupEnum.SIGNERS_GROUP.ordinal, commands.map { it.signers }.lazyMapped(serialize)))
|
||||
return componentGroupMap
|
||||
}
|
||||
|
||||
/**
|
||||
* A SerializedStateAndRef is a pair (BinaryStateRepresentation, StateRef).
|
||||
* The [serializedState] is the actual component from the original wire transaction.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
data class SerializedStateAndRef(val serializedState: SerializedBytes<TransactionState<ContractState>>, val ref: StateRef) {
|
||||
fun toStateAndRef(): StateAndRef<ContractState> = StateAndRef(serializedState.deserialize(), ref)
|
||||
}
|
||||
|
@ -8,10 +8,11 @@ import net.corda.core.crypto.isFulfilledBy
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.serialization.ConstructorForDeserialization
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
||||
import net.corda.core.serialization.internal.AttachmentsClassLoaderBuilder
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.warnOnce
|
||||
import java.util.*
|
||||
import java.util.function.Predicate
|
||||
@ -28,12 +29,13 @@ import kotlin.collections.HashSet
|
||||
*
|
||||
* All the above refer to inputs using a (txhash, output index) pair.
|
||||
*/
|
||||
// TODO LedgerTransaction is not supposed to be serialisable as it references attachments, etc. The verification logic
|
||||
// currently sends this across to out-of-process verifiers. We'll need to change that first.
|
||||
// DOCSTART 1
|
||||
@KeepForDJVM
|
||||
@CordaSerializable
|
||||
data class LedgerTransaction private constructor(
|
||||
class LedgerTransaction
|
||||
@ConstructorForDeserialization
|
||||
// LedgerTransaction is not meant to be created directly from client code, but rather via WireTransaction.toLedgerTransaction
|
||||
private constructor(
|
||||
// DOCSTART 1
|
||||
/** The resolved input states which will be consumed/invalidated by the execution of this transaction. */
|
||||
override val inputs: List<StateAndRef<ContractState>>,
|
||||
override val outputs: List<TransactionState<ContractState>>,
|
||||
@ -46,39 +48,15 @@ data class LedgerTransaction private constructor(
|
||||
override val notary: Party?,
|
||||
val timeWindow: TimeWindow?,
|
||||
val privacySalt: PrivacySalt,
|
||||
private val networkParameters: NetworkParameters?,
|
||||
override val references: List<StateAndRef<ContractState>>,
|
||||
val componentGroups: List<ComponentGroup>?,
|
||||
val resolvedInputBytes: List<SerializedStateAndRef>?,
|
||||
val resolvedReferenceBytes: List<SerializedStateAndRef>?
|
||||
val networkParameters: NetworkParameters?,
|
||||
override val references: List<StateAndRef<ContractState>>
|
||||
//DOCEND 1
|
||||
) : FullTransaction() {
|
||||
// These are not part of the c'tor above as that defines LedgerTransaction's serialisation format
|
||||
private var componentGroups: List<ComponentGroup>? = null
|
||||
private var serializedInputs: List<SerializedStateAndRef>? = null
|
||||
private var serializedReferences: List<SerializedStateAndRef>? = null
|
||||
|
||||
@Deprecated("Client code should not instantiate LedgerTransaction.")
|
||||
constructor(
|
||||
inputs: List<StateAndRef<ContractState>>,
|
||||
outputs: List<TransactionState<ContractState>>,
|
||||
commands: List<CommandWithParties<CommandData>>,
|
||||
attachments: List<Attachment>,
|
||||
id: SecureHash,
|
||||
notary: Party?,
|
||||
timeWindow: TimeWindow?,
|
||||
privacySalt: PrivacySalt
|
||||
) : this(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, null, emptyList(), null, null, null)
|
||||
|
||||
@Deprecated("Client code should not instantiate LedgerTransaction.")
|
||||
constructor(
|
||||
inputs: List<StateAndRef<ContractState>>,
|
||||
outputs: List<TransactionState<ContractState>>,
|
||||
commands: List<CommandWithParties<CommandData>>,
|
||||
attachments: List<Attachment>,
|
||||
id: SecureHash,
|
||||
notary: Party?,
|
||||
timeWindow: TimeWindow?,
|
||||
privacySalt: PrivacySalt,
|
||||
networkParameters: NetworkParameters?
|
||||
) : this(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, networkParameters, emptyList(), null, null, null)
|
||||
|
||||
//DOCEND 1
|
||||
init {
|
||||
checkBaseInvariants()
|
||||
if (timeWindow != null) check(notary != null) { "Transactions with time-windows must be notarised" }
|
||||
@ -87,10 +65,10 @@ data class LedgerTransaction private constructor(
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val logger = loggerFor<LedgerTransaction>()
|
||||
private val logger = contextLogger()
|
||||
|
||||
@CordaInternal
|
||||
internal fun makeLedgerTransaction(
|
||||
internal fun create(
|
||||
inputs: List<StateAndRef<ContractState>>,
|
||||
outputs: List<TransactionState<ContractState>>,
|
||||
commands: List<CommandWithParties<CommandData>>,
|
||||
@ -101,10 +79,16 @@ data class LedgerTransaction private constructor(
|
||||
privacySalt: PrivacySalt,
|
||||
networkParameters: NetworkParameters?,
|
||||
references: List<StateAndRef<ContractState>>,
|
||||
componentGroups: List<ComponentGroup>,
|
||||
resolvedInputBytes: List<SerializedStateAndRef>,
|
||||
resolvedReferenceBytes: List<SerializedStateAndRef>
|
||||
) = LedgerTransaction(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, networkParameters, references, componentGroups, resolvedInputBytes, resolvedReferenceBytes)
|
||||
componentGroups: List<ComponentGroup>? = null,
|
||||
serializedInputs: List<SerializedStateAndRef>? = null,
|
||||
serializedReferences: List<SerializedStateAndRef>? = null
|
||||
): LedgerTransaction {
|
||||
return LedgerTransaction(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, networkParameters, references).apply {
|
||||
this.componentGroups = componentGroups
|
||||
this.serializedInputs = serializedInputs
|
||||
this.serializedReferences = serializedReferences
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val inputStates: List<ContractState> get() = inputs.map { it.state.data }
|
||||
@ -137,7 +121,7 @@ data class LedgerTransaction private constructor(
|
||||
|
||||
AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext(this.attachments) { transactionClassLoader ->
|
||||
|
||||
val internalTx = createInternalLedgerTransaction()
|
||||
val internalTx = createLtxForVerification()
|
||||
|
||||
// TODO - verify for version downgrade
|
||||
validatePackageOwnership(contractAttachmentsByContract)
|
||||
@ -195,7 +179,9 @@ data class LedgerTransaction private constructor(
|
||||
* * Constraints should be one of the valid supported ones.
|
||||
* * Constraints should propagate correctly if not marked otherwise.
|
||||
*/
|
||||
private fun verifyConstraintsValidity(internalTx: LedgerTransaction, contractAttachmentsByContract: Map<ContractClassName, ContractAttachment>, transactionClassLoader: ClassLoader) {
|
||||
private fun verifyConstraintsValidity(internalTx: LedgerTransaction,
|
||||
contractAttachmentsByContract: Map<ContractClassName, ContractAttachment>,
|
||||
transactionClassLoader: ClassLoader) {
|
||||
// First check that the constraints are valid.
|
||||
for (state in internalTx.allStates) {
|
||||
checkConstraintValidity(state)
|
||||
@ -277,30 +263,38 @@ data class LedgerTransaction private constructor(
|
||||
throw TransactionVerificationException.ContractCreationError(id, className, e)
|
||||
}
|
||||
|
||||
private fun createInternalLedgerTransaction(): LedgerTransaction {
|
||||
return if (resolvedInputBytes != null && resolvedReferenceBytes != null && componentGroups != null) {
|
||||
private fun createLtxForVerification(): LedgerTransaction {
|
||||
val serializedInputs = this.serializedInputs
|
||||
val serializedReferences = this.serializedReferences
|
||||
val componentGroups = this.componentGroups
|
||||
|
||||
return if (serializedInputs != null && serializedReferences != null && componentGroups != null) {
|
||||
// Deserialize all relevant classes in the transaction classloader.
|
||||
val resolvedDeserializedInputs = resolvedInputBytes.map { StateAndRef(it.serializedState.deserialize(), it.ref) }
|
||||
val resolvedDeserializedReferences = resolvedReferenceBytes.map { StateAndRef(it.serializedState.deserialize(), it.ref) }
|
||||
val deserializedInputs = serializedInputs.map { it.toStateAndRef() }
|
||||
val deserializedReferences = serializedReferences.map { it.toStateAndRef() }
|
||||
val deserializedOutputs = deserialiseComponentGroup(componentGroups, TransactionState::class, ComponentGroupEnum.OUTPUTS_GROUP, forceDeserialize = true)
|
||||
val deserializedCommands = deserialiseCommands(this.componentGroups, forceDeserialize = true)
|
||||
val authenticatedArgs = deserializedCommands.map { cmd ->
|
||||
val deserializedCommands = deserialiseCommands(componentGroups, forceDeserialize = true)
|
||||
val authenticatedDeserializedCommands = deserializedCommands.map { cmd ->
|
||||
val parties = commands.find { it.value.javaClass.name == cmd.value.javaClass.name }!!.signingParties
|
||||
CommandWithParties(cmd.signers, parties, cmd.value)
|
||||
}
|
||||
|
||||
val ledgerTransactionToVerify = this.copy(
|
||||
inputs = resolvedDeserializedInputs,
|
||||
LedgerTransaction(
|
||||
inputs = deserializedInputs,
|
||||
outputs = deserializedOutputs,
|
||||
commands = authenticatedArgs,
|
||||
references = resolvedDeserializedReferences)
|
||||
|
||||
ledgerTransactionToVerify
|
||||
commands = authenticatedDeserializedCommands,
|
||||
attachments = this.attachments,
|
||||
id = this.id,
|
||||
notary = this.notary,
|
||||
timeWindow = this.timeWindow,
|
||||
privacySalt = this.privacySalt,
|
||||
networkParameters = this.networkParameters,
|
||||
references = deserializedReferences
|
||||
)
|
||||
} else {
|
||||
// This branch is only present for backwards compatibility.
|
||||
// TODO - it should be removed once the constructor of LedgerTransaction is no longer public api.
|
||||
logger.warn("The LedgerTransaction should not be instantiated directly from client code. Please use WireTransaction.toLedgerTransaction. The result of the verify method might not be accurate.")
|
||||
logger.warn("The LedgerTransaction should not be instantiated directly from client code. Please use WireTransaction.toLedgerTransaction." +
|
||||
"The result of the verify method might not be accurate.")
|
||||
this
|
||||
}
|
||||
}
|
||||
@ -766,7 +760,92 @@ data class LedgerTransaction private constructor(
|
||||
*/
|
||||
fun getAttachment(id: SecureHash): Attachment = attachments.first { it.id == id }
|
||||
|
||||
@JvmOverloads
|
||||
operator fun component1(): List<StateAndRef<ContractState>> = inputs
|
||||
operator fun component2(): List<TransactionState<ContractState>> = outputs
|
||||
operator fun component3(): List<CommandWithParties<CommandData>> = commands
|
||||
operator fun component4(): List<Attachment> = attachments
|
||||
operator fun component5(): SecureHash = id
|
||||
operator fun component6(): Party? = notary
|
||||
operator fun component7(): TimeWindow? = timeWindow
|
||||
operator fun component8(): PrivacySalt = privacySalt
|
||||
operator fun component9(): NetworkParameters? = networkParameters
|
||||
operator fun component10(): List<StateAndRef<ContractState>> = references
|
||||
|
||||
override fun equals(other: Any?): Boolean = this === other || other is LedgerTransaction && this.id == other.id
|
||||
|
||||
override fun hashCode(): Int = id.hashCode()
|
||||
|
||||
override fun toString(): String {
|
||||
return """LedgerTransaction(
|
||||
| id=$id
|
||||
| inputs=$inputs
|
||||
| outputs=$outputs
|
||||
| commands=$commands
|
||||
| attachments=$attachments
|
||||
| notary=$notary
|
||||
| timeWindow=$timeWindow
|
||||
| references=$references
|
||||
| networkParameters=$networkParameters
|
||||
| privacySalt=$privacySalt
|
||||
|)""".trimMargin()
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Stuff that we can't remove and so is deprecated instead
|
||||
//
|
||||
|
||||
@Deprecated("LedgerTransaction should not be created directly, use WireTransaction.toLedgerTransaction instead.")
|
||||
constructor(
|
||||
inputs: List<StateAndRef<ContractState>>,
|
||||
outputs: List<TransactionState<ContractState>>,
|
||||
commands: List<CommandWithParties<CommandData>>,
|
||||
attachments: List<Attachment>,
|
||||
id: SecureHash,
|
||||
notary: Party?,
|
||||
timeWindow: TimeWindow?,
|
||||
privacySalt: PrivacySalt
|
||||
) : this(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, null, emptyList())
|
||||
|
||||
@Deprecated("LedgerTransaction should not be created directly, use WireTransaction.toLedgerTransaction instead.")
|
||||
@DeprecatedConstructorForDeserialization(1)
|
||||
constructor(
|
||||
inputs: List<StateAndRef<ContractState>>,
|
||||
outputs: List<TransactionState<ContractState>>,
|
||||
commands: List<CommandWithParties<CommandData>>,
|
||||
attachments: List<Attachment>,
|
||||
id: SecureHash,
|
||||
notary: Party?,
|
||||
timeWindow: TimeWindow?,
|
||||
privacySalt: PrivacySalt,
|
||||
networkParameters: NetworkParameters?
|
||||
) : this(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, networkParameters, emptyList())
|
||||
|
||||
@Deprecated("LedgerTransactions should not be created directly, use WireTransaction.toLedgerTransaction instead.")
|
||||
fun copy(inputs: List<StateAndRef<ContractState>>,
|
||||
outputs: List<TransactionState<ContractState>>,
|
||||
commands: List<CommandWithParties<CommandData>>,
|
||||
attachments: List<Attachment>,
|
||||
id: SecureHash,
|
||||
notary: Party?,
|
||||
timeWindow: TimeWindow?,
|
||||
privacySalt: PrivacySalt
|
||||
): LedgerTransaction {
|
||||
return LedgerTransaction(
|
||||
inputs = inputs,
|
||||
outputs = outputs,
|
||||
commands = commands,
|
||||
attachments = attachments,
|
||||
id = id,
|
||||
notary = notary,
|
||||
timeWindow = timeWindow,
|
||||
privacySalt = privacySalt,
|
||||
networkParameters = networkParameters,
|
||||
references = references
|
||||
)
|
||||
}
|
||||
|
||||
@Deprecated("LedgerTransactions should not be created directly, use WireTransaction.toLedgerTransaction instead.")
|
||||
fun copy(inputs: List<StateAndRef<ContractState>> = this.inputs,
|
||||
outputs: List<TransactionState<ContractState>> = this.outputs,
|
||||
commands: List<CommandWithParties<CommandData>> = this.commands,
|
||||
@ -776,16 +855,18 @@ data class LedgerTransaction private constructor(
|
||||
timeWindow: TimeWindow? = this.timeWindow,
|
||||
privacySalt: PrivacySalt = this.privacySalt,
|
||||
networkParameters: NetworkParameters? = this.networkParameters
|
||||
) = copy(inputs = inputs,
|
||||
outputs = outputs,
|
||||
commands = commands,
|
||||
attachments = attachments,
|
||||
id = id,
|
||||
notary = notary,
|
||||
timeWindow = timeWindow,
|
||||
privacySalt = privacySalt,
|
||||
networkParameters = networkParameters,
|
||||
references = references
|
||||
)
|
||||
): LedgerTransaction {
|
||||
return LedgerTransaction(
|
||||
inputs = inputs,
|
||||
outputs = outputs,
|
||||
commands = commands,
|
||||
attachments = attachments,
|
||||
id = id,
|
||||
notary = notary,
|
||||
timeWindow = timeWindow,
|
||||
privacySalt = privacySalt,
|
||||
networkParameters = networkParameters,
|
||||
references = references
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,11 +11,7 @@ import net.corda.core.crypto.SignatureMetadata
|
||||
import net.corda.core.crypto.keys
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.AttachmentWithContext
|
||||
import net.corda.core.internal.FlowStateMachine
|
||||
import net.corda.core.internal.StatePointerSearch
|
||||
import net.corda.core.internal.ensureMinimumPlatformVersion
|
||||
import net.corda.core.internal.isUploaderTrusted
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
@ -164,14 +160,15 @@ open class TransactionBuilder @JvmOverloads constructor(
|
||||
|
||||
return SerializationFactory.defaultFactory.withCurrentContext(serializationContext) {
|
||||
WireTransaction(
|
||||
WireTransaction.createComponentGroups(
|
||||
createComponentGroups(
|
||||
inputStates(),
|
||||
resolvedOutputs,
|
||||
commands,
|
||||
(allContractAttachments + attachments).toSortedSet().toList(), // Sort the attachments to ensure transaction builds are stable.
|
||||
notary,
|
||||
window,
|
||||
referenceStates),
|
||||
referenceStates
|
||||
),
|
||||
privacySalt
|
||||
)
|
||||
}
|
||||
|
@ -4,18 +4,19 @@ import net.corda.core.CordaInternal
|
||||
import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.ComponentGroupEnum.*
|
||||
import net.corda.core.contracts.ComponentGroupEnum.COMMANDS_GROUP
|
||||
import net.corda.core.contracts.ComponentGroupEnum.OUTPUTS_GROUP
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.SerializedStateAndRef
|
||||
import net.corda.core.internal.Emoji
|
||||
import net.corda.core.internal.SerializedStateAndRef
|
||||
import net.corda.core.internal.createComponentGroups
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.lazyMapped
|
||||
@ -53,7 +54,8 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
@DeleteForDJVM
|
||||
constructor(componentGroups: List<ComponentGroup>) : this(componentGroups, PrivacySalt())
|
||||
|
||||
@Deprecated("Required only in some unit-tests and for backwards compatibility purposes.", ReplaceWith("WireTransaction(val componentGroups: List<ComponentGroup>, override val privacySalt: PrivacySalt)"), DeprecationLevel.WARNING)
|
||||
@Deprecated("Required only in some unit-tests and for backwards compatibility purposes.",
|
||||
ReplaceWith("WireTransaction(val componentGroups: List<ComponentGroup>, override val privacySalt: PrivacySalt)"), DeprecationLevel.WARNING)
|
||||
@DeleteForDJVM
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
@ -64,7 +66,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
notary: Party?,
|
||||
timeWindow: TimeWindow?,
|
||||
privacySalt: PrivacySalt = PrivacySalt()
|
||||
) : this(createComponentGroups(inputs, outputs, commands, attachments, notary, timeWindow), privacySalt)
|
||||
) : this(createComponentGroups(inputs, outputs, commands, attachments, notary, timeWindow, emptyList()), privacySalt)
|
||||
|
||||
init {
|
||||
check(componentGroups.all { it.components.isNotEmpty() }) { "Empty component groups are not allowed" }
|
||||
@ -103,7 +105,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
return toLedgerTransactionInternal(
|
||||
resolveIdentity = { services.identityService.partyFromKey(it) },
|
||||
resolveAttachment = { services.attachments.openAttachment(it) },
|
||||
resolveStateRefComponent = { resolveStateRefBinaryComponent(it, services) },
|
||||
resolveStateRefAsSerialized = { resolveStateRefBinaryComponent(it, services) },
|
||||
networkParameters = services.networkParameters
|
||||
)
|
||||
}
|
||||
@ -130,38 +132,44 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
private fun toLedgerTransactionInternal(
|
||||
resolveIdentity: (PublicKey) -> Party?,
|
||||
resolveAttachment: (SecureHash) -> Attachment?,
|
||||
resolveStateRefComponent: (StateRef) -> SerializedBytes<TransactionState<ContractState>>?,
|
||||
resolveStateRefAsSerialized: (StateRef) -> SerializedBytes<TransactionState<ContractState>>?,
|
||||
networkParameters: NetworkParameters?
|
||||
): LedgerTransaction {
|
||||
// Look up public keys to authenticated identities.
|
||||
val authenticatedArgs = commands.lazyMapped { cmd, _ ->
|
||||
val authenticatedCommands = commands.lazyMapped { cmd, _ ->
|
||||
val parties = cmd.signers.mapNotNull { pk -> resolveIdentity(pk) }
|
||||
CommandWithParties(cmd.signers, parties, cmd.value)
|
||||
}
|
||||
|
||||
val resolvedInputBytes = inputs.map { ref ->
|
||||
SerializedStateAndRef(resolveStateRefComponent(ref)
|
||||
?: throw TransactionResolutionException(ref.txhash), ref)
|
||||
}
|
||||
val resolvedInputs = resolvedInputBytes.lazyMapped { (serialized, ref), _ ->
|
||||
StateAndRef(serialized.deserialize(), ref)
|
||||
val serializedResolvedInputs = inputs.map { ref ->
|
||||
SerializedStateAndRef(resolveStateRefAsSerialized(ref) ?: throw TransactionResolutionException(ref.txhash), ref)
|
||||
}
|
||||
val resolvedInputs = serializedResolvedInputs.lazyMapped { star, _ -> star.toStateAndRef() }
|
||||
|
||||
val resolvedReferenceBytes = references.map { ref ->
|
||||
SerializedStateAndRef(resolveStateRefComponent(ref)
|
||||
?: throw TransactionResolutionException(ref.txhash), ref)
|
||||
}
|
||||
val resolvedReferences = resolvedReferenceBytes.lazyMapped { (serialized, ref), _ ->
|
||||
StateAndRef(serialized.deserialize(), ref)
|
||||
val serializedResolvedReferences = references.map { ref ->
|
||||
SerializedStateAndRef(resolveStateRefAsSerialized(ref) ?: throw TransactionResolutionException(ref.txhash), ref)
|
||||
}
|
||||
val resolvedReferences = serializedResolvedReferences.lazyMapped { star, _ -> star.toStateAndRef() }
|
||||
|
||||
val attachments = attachments.lazyMapped { att, _ ->
|
||||
resolveAttachment(att) ?: throw AttachmentResolutionException(att)
|
||||
}
|
||||
val resolvedAttachments = attachments.lazyMapped { att, _ -> resolveAttachment(att) ?: throw AttachmentResolutionException(att) }
|
||||
|
||||
val ltx = LedgerTransaction.makeLedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, notary, timeWindow, privacySalt, networkParameters, resolvedReferences, componentGroups, resolvedInputBytes, resolvedReferenceBytes)
|
||||
val ltx = LedgerTransaction.create(
|
||||
resolvedInputs,
|
||||
outputs,
|
||||
authenticatedCommands,
|
||||
resolvedAttachments,
|
||||
id,
|
||||
notary,
|
||||
timeWindow,
|
||||
privacySalt,
|
||||
networkParameters,
|
||||
resolvedReferences,
|
||||
componentGroups,
|
||||
serializedResolvedInputs,
|
||||
serializedResolvedReferences
|
||||
)
|
||||
|
||||
checkTransactionSize(ltx, networkParameters?.maxTransactionSize ?: DEFAULT_MAX_TX_SIZE)
|
||||
checkTransactionSize(ltx, networkParameters?.maxTransactionSize ?: DEFAULT_MAX_TX_SIZE, serializedResolvedInputs, serializedResolvedReferences)
|
||||
|
||||
return ltx
|
||||
}
|
||||
@ -170,7 +178,10 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
* Deterministic function that checks if the transaction is below the maximum allowed size.
|
||||
* It uses the binary representation of transactions.
|
||||
*/
|
||||
private fun checkTransactionSize(ltx: LedgerTransaction, maxTransactionSize: Int) {
|
||||
private fun checkTransactionSize(ltx: LedgerTransaction,
|
||||
maxTransactionSize: Int,
|
||||
resolvedSerializedInputs: List<SerializedStateAndRef>,
|
||||
resolvedSerializedReferences: List<SerializedStateAndRef>) {
|
||||
var remainingTransactionSize = maxTransactionSize
|
||||
|
||||
fun minus(size: Int) {
|
||||
@ -187,8 +198,8 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
// it's likely that the same underlying Attachment CorDapp will occur more than once so we dedup on the attachment id.
|
||||
ltx.attachments.distinctBy { it.id }.forEach { minus(it.size) }
|
||||
|
||||
minus(ltx.resolvedInputBytes!!.sumBy { it.serializedState.size })
|
||||
minus(ltx.resolvedReferenceBytes!!.sumBy { it.serializedState.size })
|
||||
minus(resolvedSerializedInputs.sumBy { it.serializedState.size })
|
||||
minus(resolvedSerializedReferences.sumBy { it.serializedState.size })
|
||||
|
||||
// For Commands and outputs we can use the component groups as they are already serialized.
|
||||
minus(componentGroupSize(COMMANDS_GROUP))
|
||||
@ -277,33 +288,15 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
companion object {
|
||||
private const val DEFAULT_MAX_TX_SIZE = 10485760
|
||||
|
||||
/**
|
||||
* Creating list of [ComponentGroup] used in one of the constructors of [WireTransaction] required
|
||||
* for backwards compatibility purposes.
|
||||
*/
|
||||
@JvmOverloads
|
||||
@CordaInternal
|
||||
@Deprecated("Do not use, this is internal API")
|
||||
fun createComponentGroups(inputs: List<StateRef>,
|
||||
outputs: List<TransactionState<ContractState>>,
|
||||
commands: List<Command<*>>,
|
||||
attachments: List<SecureHash>,
|
||||
notary: Party?,
|
||||
timeWindow: TimeWindow?,
|
||||
references: List<StateRef> = emptyList()): List<ComponentGroup> {
|
||||
val serialize = { value: Any, _: Int -> value.serialize() }
|
||||
val componentGroupMap: MutableList<ComponentGroup> = mutableListOf()
|
||||
if (inputs.isNotEmpty()) componentGroupMap.add(ComponentGroup(INPUTS_GROUP.ordinal, inputs.lazyMapped(serialize)))
|
||||
if (references.isNotEmpty()) componentGroupMap.add(ComponentGroup(REFERENCES_GROUP.ordinal, references.lazyMapped(serialize)))
|
||||
if (outputs.isNotEmpty()) componentGroupMap.add(ComponentGroup(OUTPUTS_GROUP.ordinal, outputs.lazyMapped(serialize)))
|
||||
// Adding commandData only to the commands group. Signers are added in their own group.
|
||||
if (commands.isNotEmpty()) componentGroupMap.add(ComponentGroup(COMMANDS_GROUP.ordinal, commands.map { it.value }.lazyMapped(serialize)))
|
||||
if (attachments.isNotEmpty()) componentGroupMap.add(ComponentGroup(ATTACHMENTS_GROUP.ordinal, attachments.lazyMapped(serialize)))
|
||||
if (notary != null) componentGroupMap.add(ComponentGroup(NOTARY_GROUP.ordinal, listOf(notary).lazyMapped(serialize)))
|
||||
if (timeWindow != null) componentGroupMap.add(ComponentGroup(TIMEWINDOW_GROUP.ordinal, listOf(timeWindow).lazyMapped(serialize)))
|
||||
// Adding signers to their own group. This is required for command visibility purposes: a party receiving
|
||||
// a FilteredTransaction can now verify it sees all the commands it should sign.
|
||||
if (commands.isNotEmpty()) componentGroupMap.add(ComponentGroup(SIGNERS_GROUP.ordinal, commands.map { it.signers }.lazyMapped(serialize)))
|
||||
return componentGroupMap
|
||||
timeWindow: TimeWindow?): List<ComponentGroup> {
|
||||
return createComponentGroups(inputs, outputs, commands, attachments, notary, timeWindow, emptyList())
|
||||
}
|
||||
|
||||
/**
|
||||
@ -312,7 +305,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
* For [ContractUpgradeWireTransaction] or [NotaryChangeWireTransaction] it knows how to recreate the output state in the correct classloader independent of the node's classpath.
|
||||
*/
|
||||
@CordaInternal
|
||||
fun resolveStateRefBinaryComponent(stateRef: StateRef, services: ServicesForResolution): SerializedBytes<TransactionState<ContractState>>? {
|
||||
internal fun resolveStateRefBinaryComponent(stateRef: StateRef, services: ServicesForResolution): SerializedBytes<TransactionState<ContractState>>? {
|
||||
return if (services is ServiceHub) {
|
||||
val coreTransaction = services.validatedTransactions.getTransaction(stateRef.txhash)?.coreTransaction
|
||||
?: throw TransactionResolutionException(stateRef.txhash)
|
||||
@ -333,11 +326,26 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
override fun toString(): String {
|
||||
val buf = StringBuilder()
|
||||
buf.appendln("Transaction:")
|
||||
for (reference in references) buf.appendln("${Emoji.rightArrow}REFS: $reference")
|
||||
for (input in inputs) buf.appendln("${Emoji.rightArrow}INPUT: $input")
|
||||
for ((data) in outputs) buf.appendln("${Emoji.leftArrow}OUTPUT: $data")
|
||||
for (command in commands) buf.appendln("${Emoji.diamond}COMMAND: $command")
|
||||
for (attachment in attachments) buf.appendln("${Emoji.paperclip}ATTACHMENT: $attachment")
|
||||
for (reference in references) {
|
||||
val emoji = Emoji.rightArrow
|
||||
buf.appendln("${emoji}REFS: $reference")
|
||||
}
|
||||
for (input in inputs) {
|
||||
val emoji = Emoji.rightArrow
|
||||
buf.appendln("${emoji}INPUT: $input")
|
||||
}
|
||||
for ((data) in outputs) {
|
||||
val emoji = Emoji.leftArrow
|
||||
buf.appendln("${emoji}OUTPUT: $data")
|
||||
}
|
||||
for (command in commands) {
|
||||
val emoji = Emoji.diamond
|
||||
buf.appendln("${emoji}COMMAND: $command")
|
||||
}
|
||||
for (attachment in attachments) {
|
||||
val emoji = Emoji.paperclip
|
||||
buf.appendln("${emoji}ATTACHMENT: $attachment")
|
||||
}
|
||||
return buf.toString()
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package net.corda.core.transactions
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.ComponentGroupEnum.*
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.internal.createComponentGroups
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
@ -124,7 +125,7 @@ class CompatibleTransactionTests {
|
||||
|
||||
@Test
|
||||
fun `WireTransaction constructors and compatibility`() {
|
||||
val groups = WireTransaction.createComponentGroups(inputs, outputs, commands, attachments, notary, timeWindow)
|
||||
val groups = createComponentGroups(inputs, outputs, commands, attachments, notary, timeWindow, emptyList())
|
||||
val wireTransactionOldConstructor = WireTransaction(groups, privacySalt)
|
||||
assertEquals(wireTransactionA, wireTransactionOldConstructor)
|
||||
|
||||
|
@ -124,7 +124,7 @@ class TransactionTests {
|
||||
val id = SecureHash.randomSHA256()
|
||||
val timeWindow: TimeWindow? = null
|
||||
val privacySalt = PrivacySalt()
|
||||
val transaction = LedgerTransaction(
|
||||
val transaction = LedgerTransaction.create(
|
||||
inputs,
|
||||
outputs,
|
||||
commands,
|
||||
@ -133,7 +133,8 @@ class TransactionTests {
|
||||
null,
|
||||
timeWindow,
|
||||
privacySalt,
|
||||
testNetworkParameters()
|
||||
testNetworkParameters(),
|
||||
emptyList()
|
||||
)
|
||||
|
||||
transaction.verify()
|
||||
@ -166,7 +167,7 @@ class TransactionTests {
|
||||
val id = SecureHash.randomSHA256()
|
||||
val timeWindow: TimeWindow? = null
|
||||
val privacySalt = PrivacySalt()
|
||||
fun buildTransaction() = LedgerTransaction(
|
||||
fun buildTransaction() = LedgerTransaction.create(
|
||||
inputs,
|
||||
outputs,
|
||||
commands,
|
||||
@ -174,7 +175,9 @@ class TransactionTests {
|
||||
id,
|
||||
notary,
|
||||
timeWindow,
|
||||
privacySalt
|
||||
privacySalt,
|
||||
null,
|
||||
emptyList()
|
||||
)
|
||||
|
||||
assertFailsWith<TransactionVerificationException.NotaryChangeInWrongTransactionType> { buildTransaction() }
|
||||
|
@ -7,16 +7,17 @@ release, see :doc:`upgrade-notes`.
|
||||
Unreleased
|
||||
----------
|
||||
|
||||
* Marked the `Attachment` interface as `@DoNotImplement` because it is not meant to be extended by CorDapp developers. If you have already done so,
|
||||
please get in contact on the usual communication channels.
|
||||
* Marked the ``Attachment`` interface as ``@DoNotImplement`` because it is not meant to be extended by CorDapp developers. If you have already
|
||||
done so, please get in contact on the usual communication channels.
|
||||
|
||||
* Added auto-acceptance of network parameters for network updates. This behaviour is available for a subset of the network parameters
|
||||
and is configurable via the node config. See :doc:`network-map` for more information.
|
||||
and is configurable via the node config. See :doc:`network-map` for more information.
|
||||
|
||||
* Deprecated `SerializationContext.withAttachmentsClassLoader`. This functionality has always been disabled by flags
|
||||
and there is no reason for a CorDapp developer to use it. It is just an internal implementation detail of Corda.
|
||||
* Deprecated ``SerializationContext.withAttachmentsClassLoader``. This functionality has always been disabled by flags
|
||||
and there is no reason for a CorDapp developer to use it. It is just an internal implementation detail of Corda.
|
||||
|
||||
* Deprecated the `LedgerTransaction` constructor. No client code should call it directly. LedgerTransactions can be created from WireTransactions if required.
|
||||
* Deprecated all means to directly create a ``LedgerTransaction`` instance, as client code is only meant to get hold of a ``LedgerTransaction``
|
||||
via ``WireTransaction.toLedgerTransaction``.
|
||||
|
||||
* Introduced new optional network bootstrapper command line options (--register-package-owner, --unregister-package-owner)
|
||||
to register/unregister a java package namespace with an associated owner in the network parameter packageOwnership whitelist.
|
||||
|
@ -1,3 +1,9 @@
|
||||
.. highlight:: kotlin
|
||||
.. raw:: html
|
||||
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/codesets.js"></script>
|
||||
|
||||
Default Class Evolution
|
||||
=======================
|
||||
|
||||
|
@ -10,6 +10,7 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.NamedCacheFactory
|
||||
import net.corda.core.internal.createComponentGroups
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.utilities.loggerFor
|
||||
@ -154,7 +155,7 @@ fun createWireTransaction(inputs: List<StateRef>,
|
||||
notary: Party?,
|
||||
timeWindow: TimeWindow?,
|
||||
privacySalt: PrivacySalt = PrivacySalt()): WireTransaction {
|
||||
val componentGroups = WireTransaction.createComponentGroups(inputs, outputs, commands, attachments, notary, timeWindow)
|
||||
val componentGroups = createComponentGroups(inputs, outputs, commands, attachments, notary, timeWindow, emptyList())
|
||||
return WireTransaction(componentGroups, privacySalt)
|
||||
}
|
||||
|
||||
|
@ -165,6 +165,7 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
|
||||
nodeInfoFilesCopier.reset()
|
||||
notaryIdentity = null
|
||||
networkParametersCopier = null
|
||||
SuggestedDetails.reset()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,7 +27,20 @@ object SuggestedDetails {
|
||||
|
||||
private var cursor = 0
|
||||
|
||||
val nextBank: Pair<String, String> get() = banks[cursor++ % banks.size]
|
||||
fun nextBank(exists: (String) -> Boolean): Pair<String, String> {
|
||||
for (i in 0 until banks.size) {
|
||||
val bank = banks[cursor]
|
||||
if (!exists(bank.first)) {
|
||||
return bank
|
||||
}
|
||||
cursor = (cursor + 1) % banks.size
|
||||
}
|
||||
return banks[cursor]
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
cursor = 0
|
||||
}
|
||||
}
|
||||
|
||||
class NodeData {
|
||||
|
@ -206,7 +206,8 @@ class NodeTabView : Fragment() {
|
||||
model.webPort.value = nodeController.nextPort
|
||||
model.h2Port.value = nodeController.nextPort
|
||||
|
||||
val defaults = SuggestedDetails.nextBank
|
||||
val defaults = SuggestedDetails.nextBank(nodeController::nameExists)
|
||||
|
||||
model.legalName.value = defaults.first
|
||||
model.nearestCity.value = CityDatabase[defaults.second]
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user