mirror of
https://github.com/corda/corda.git
synced 2025-04-19 00:27:13 +00:00
Merge pull request #1595 from corda/shams-os-merge-231118
O/S merge until e14421b
This commit is contained in:
commit
cccdb2882a
@ -431,7 +431,9 @@ public static final class net.corda.core.contracts.AmountTransfer$Companion exte
|
||||
public interface net.corda.core.contracts.Attachment extends net.corda.core.contracts.NamedByHash
|
||||
public void extractFile(String, java.io.OutputStream)
|
||||
@NotNull
|
||||
public abstract java.util.List<java.security.PublicKey> getSigners()
|
||||
public abstract java.util.List<java.security.PublicKey> getSignerKeys()
|
||||
@NotNull
|
||||
public abstract java.util.List<net.corda.core.identity.Party> getSigners()
|
||||
public abstract int getSize()
|
||||
@NotNull
|
||||
public abstract java.io.InputStream open()
|
||||
@ -542,7 +544,7 @@ public final class net.corda.core.contracts.ContractAttachment extends java.lang
|
||||
@NotNull
|
||||
public net.corda.core.crypto.SecureHash getId()
|
||||
@NotNull
|
||||
public java.util.List<java.security.PublicKey> getSigners()
|
||||
public java.util.List<net.corda.core.identity.Party> getSigners()
|
||||
public int getSize()
|
||||
@Nullable
|
||||
public final String getUploader()
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
@ -234,7 +234,7 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
|
||||
val attachment = rigorousMock<ContractAttachment>()
|
||||
doReturn(attachment).whenever(attachmentStorage).openAttachment(attachmentId)
|
||||
doReturn(attachmentId).whenever(attachment).id
|
||||
doReturn(emptyList<Party>()).whenever(attachment).signers
|
||||
doReturn(emptyList<Party>()).whenever(attachment).signerKeys
|
||||
doReturn(setOf(DummyContract.PROGRAM_ID)).whenever(attachment).allContracts
|
||||
doReturn("app").whenever(attachment).uploader
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
@ -40,10 +40,12 @@ class AttachmentTest {
|
||||
@Before
|
||||
fun setup() {
|
||||
attachment = object : Attachment {
|
||||
override val signerKeys: List<PublicKey>
|
||||
get() = listOf(ALICE_KEY)
|
||||
override val id: SecureHash
|
||||
get() = SecureHash.allOnesHash
|
||||
override val signers: List<PublicKey>
|
||||
get() = listOf(ALICE_KEY)
|
||||
override val signers: List<Party>
|
||||
get() = listOf(ALICE)
|
||||
override val size: Int
|
||||
get() = jarData.size
|
||||
|
||||
|
@ -3,13 +3,19 @@ package net.corda.deterministic.verifier
|
||||
import net.corda.core.contracts.Attachment
|
||||
import net.corda.core.contracts.ContractClassName
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.InputStream
|
||||
import java.security.PublicKey
|
||||
|
||||
@CordaSerializable
|
||||
class MockContractAttachment(override val id: SecureHash = SecureHash.zeroHash, val contract: ContractClassName, override val signers: List<PublicKey> = emptyList()) : Attachment {
|
||||
class MockContractAttachment(
|
||||
override val id: SecureHash = SecureHash.zeroHash,
|
||||
val contract: ContractClassName,
|
||||
override val signerKeys: List<PublicKey> = emptyList(),
|
||||
override val signers: List<Party> = emptyList()
|
||||
) : Attachment {
|
||||
override fun open(): InputStream = ByteArrayInputStream(id.bytes)
|
||||
override val size = id.size
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.corda.core.contracts
|
||||
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.extractFile
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import java.io.FileNotFoundException
|
||||
@ -31,6 +33,7 @@ import java.util.jar.JarInputStream
|
||||
*/
|
||||
@KeepForDJVM
|
||||
@CordaSerializable
|
||||
@DoNotImplement
|
||||
interface Attachment : NamedByHash {
|
||||
fun open(): InputStream
|
||||
|
||||
@ -51,11 +54,20 @@ interface Attachment : NamedByHash {
|
||||
@JvmDefault
|
||||
fun extractFile(path: String, outputTo: OutputStream) = openAsJAR().use { it.extractFile(path, outputTo) }
|
||||
|
||||
/**
|
||||
* The parties that have correctly signed the whole attachment.
|
||||
* Even though this returns a list of party objects, it is not required that these parties exist on the network, but rather they are a mapping from the signing key to the X.500 name.
|
||||
*
|
||||
* Note: Anyone can sign attachments, not only Corda parties. It's recommended to use [signerKeys].
|
||||
*/
|
||||
@Deprecated("Use signerKeys. There is no requirement that attachment signers are Corda parties.")
|
||||
val signers: List<Party>
|
||||
|
||||
/**
|
||||
* The keys that have correctly signed the whole attachment.
|
||||
* Can be empty, for example non-contract attachments won't be necessarily be signed.
|
||||
*/
|
||||
val signers: List<PublicKey>
|
||||
val signerKeys: List<PublicKey>
|
||||
|
||||
/**
|
||||
* Attachment size in bytes.
|
||||
|
@ -72,7 +72,7 @@ interface AttachmentConstraint {
|
||||
|
||||
// You can transition from the WhitelistConstraint to the SignatureConstraint only if all signers of the JAR are required to sign in the future.
|
||||
input is WhitelistedByZoneAttachmentConstraint && output is SignatureAttachmentConstraint ->
|
||||
attachment.signers.isNotEmpty() && output.key.keys.containsAll(attachment.signers)
|
||||
attachment.signerKeys.isNotEmpty() && output.key.keys.containsAll(attachment.signerKeys)
|
||||
|
||||
else -> false
|
||||
}
|
||||
@ -180,5 +180,5 @@ data class SignatureAttachmentConstraint(
|
||||
val key: PublicKey
|
||||
) : AttachmentConstraint {
|
||||
override fun isSatisfiedBy(attachment: Attachment): Boolean =
|
||||
key.isFulfilledBy(attachment.signers.map { it })
|
||||
key.isFulfilledBy(attachment.signerKeys.map { it })
|
||||
}
|
@ -18,7 +18,7 @@ class ContractAttachment @JvmOverloads constructor(
|
||||
val contract: ContractClassName,
|
||||
val additionalContracts: Set<ContractClassName> = emptySet(),
|
||||
val uploader: String? = null,
|
||||
override val signers: List<PublicKey> = emptyList()) : Attachment by attachment {
|
||||
override val signerKeys: List<PublicKey> = emptyList()) : Attachment by attachment {
|
||||
|
||||
val allContracts: Set<ContractClassName> get() = additionalContracts + contract
|
||||
|
||||
|
@ -6,6 +6,7 @@ import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.contracts.Attachment
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.serialization.MissingAttachmentsException
|
||||
import net.corda.core.serialization.SerializeAsTokenContext
|
||||
import java.io.FileNotFoundException
|
||||
@ -47,10 +48,15 @@ abstract class AbstractAttachment(dataLoader: () -> ByteArray) : Attachment {
|
||||
override val size: Int get() = attachmentData.size
|
||||
|
||||
override fun open(): InputStream = attachmentData.inputStream()
|
||||
override val signers: List<PublicKey> by lazy {
|
||||
|
||||
override val signerKeys: List<PublicKey> by lazy {
|
||||
openAsJAR().use(JarSignatureCollector::collectSigners)
|
||||
}
|
||||
|
||||
override val signers: List<Party> by lazy {
|
||||
openAsJAR().use(JarSignatureCollector::collectSigningParties)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?) = other === this || other is Attachment && other.id == this.id
|
||||
override fun hashCode() = id.hashCode()
|
||||
override fun toString() = "${javaClass.simpleName}(id=$id)"
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ data class ContractUpgradeLedgerTransaction(
|
||||
private fun verifyConstraints() {
|
||||
val attachmentForConstraintVerification = AttachmentWithContext(
|
||||
legacyContractAttachment as? ContractAttachment
|
||||
?: ContractAttachment(legacyContractAttachment, legacyContractClassName, signers = legacyContractAttachment.signers),
|
||||
?: ContractAttachment(legacyContractAttachment, legacyContractClassName, signerKeys = legacyContractAttachment.signerKeys),
|
||||
upgradedContract.legacyContract,
|
||||
networkParameters.whitelistedContractImplementations
|
||||
)
|
||||
|
@ -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)
|
||||
@ -166,7 +150,7 @@ data class LedgerTransaction private constructor(
|
||||
|
||||
contractsAndOwners.forEach { contract, owner ->
|
||||
val attachment = contractAttachmentsByContract[contract]!!
|
||||
if (!owner.isFulfilledBy(attachment.signers)) {
|
||||
if (!owner.isFulfilledBy(attachment.signerKeys)) {
|
||||
throw TransactionVerificationException.ContractAttachmentNotSignedByPackageOwnerException(this.id, id, contract)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -24,7 +20,6 @@ import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.node.services.KeyManagementService
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializationFactory
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.warnOnce
|
||||
import java.security.PublicKey
|
||||
@ -165,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
|
||||
)
|
||||
}
|
||||
@ -347,7 +343,7 @@ open class TransactionBuilder @JvmOverloads constructor(
|
||||
attachmentToUse: ContractAttachment,
|
||||
services: ServicesForResolution): AttachmentConstraint = when {
|
||||
inputStates != null -> attachmentConstraintsTransition(inputStates.groupBy { it.constraint }.keys, attachmentToUse)
|
||||
attachmentToUse.signers.isNotEmpty() && services.networkParameters.minimumPlatformVersion < 4 -> {
|
||||
attachmentToUse.signerKeys.isNotEmpty() && services.networkParameters.minimumPlatformVersion < 4 -> {
|
||||
log.warnOnce("Signature constraints not available on network requiring a minimum platform version of 4. Current is: ${services.networkParameters.minimumPlatformVersion}.")
|
||||
if (useWhitelistedByZoneAttachmentConstraint(contractClassName, services.networkParameters)) {
|
||||
log.warnOnce("Reverting back to using whitelisted zone constraints for contract $contractClassName")
|
||||
@ -357,7 +353,7 @@ open class TransactionBuilder @JvmOverloads constructor(
|
||||
HashAttachmentConstraint(attachmentToUse.id)
|
||||
}
|
||||
}
|
||||
attachmentToUse.signers.isNotEmpty() -> makeSignatureAttachmentConstraint(attachmentToUse.signers)
|
||||
attachmentToUse.signerKeys.isNotEmpty() -> makeSignatureAttachmentConstraint(attachmentToUse.signerKeys)
|
||||
useWhitelistedByZoneAttachmentConstraint(contractClassName, services.networkParameters) -> WhitelistedByZoneAttachmentConstraint
|
||||
else -> HashAttachmentConstraint(attachmentToUse.id)
|
||||
}
|
||||
@ -398,8 +394,8 @@ open class TransactionBuilder @JvmOverloads constructor(
|
||||
constraints.any { it is SignatureAttachmentConstraint } && constraints.any { it is WhitelistedByZoneAttachmentConstraint } -> {
|
||||
val signatureConstraint = constraints.mapNotNull { it as? SignatureAttachmentConstraint }.single()
|
||||
when {
|
||||
attachmentToUse.signers.isEmpty() -> throw IllegalArgumentException("Cannot mix a state with the WhitelistedByZoneAttachmentConstraint and a state with the SignatureAttachmentConstraint, when the latest attachment is not signed. Please contact your Zone operator.")
|
||||
signatureConstraint.key.keys.containsAll(attachmentToUse.signers) -> signatureConstraint
|
||||
attachmentToUse.signerKeys.isEmpty() -> throw IllegalArgumentException("Cannot mix a state with the WhitelistedByZoneAttachmentConstraint and a state with the SignatureAttachmentConstraint, when the latest attachment is not signed. Please contact your Zone operator.")
|
||||
signatureConstraint.key.keys.containsAll(attachmentToUse.signerKeys) -> signatureConstraint
|
||||
else -> throw IllegalArgumentException("Attempting to transition a WhitelistedByZoneAttachmentConstraint state backed by an attachment signed by multiple parties to a weaker SignatureConstraint that does not require all those signatures. Please contact your Zone operator.")
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.MissingContractAttachments
|
||||
import net.corda.finance.POUNDS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
@ -223,7 +222,7 @@ class ConstraintsPropagationTests {
|
||||
fun `Attachment canBeTransitionedFrom behaves as expected`() {
|
||||
|
||||
val attachment = mock<ContractAttachment>()
|
||||
whenever(attachment.signers).thenReturn(listOf(ALICE_PARTY.owningKey))
|
||||
whenever(attachment.signerKeys).thenReturn(listOf(ALICE_PARTY.owningKey))
|
||||
|
||||
// Exhaustive positive check
|
||||
assertTrue(HashAttachmentConstraint(SecureHash.randomSHA256()).canBeTransitionedFrom(SignatureAttachmentConstraint(ALICE_PUBKEY), attachment))
|
||||
|
@ -3,6 +3,7 @@ package net.corda.core.contracts
|
||||
import com.nhaarman.mockito_kotlin.doAnswer
|
||||
import com.nhaarman.mockito_kotlin.spy
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.identity.Party
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
@ -30,7 +31,8 @@ class AttachmentTest {
|
||||
val attachment = object : Attachment {
|
||||
override val id get() = throw UnsupportedOperationException()
|
||||
override fun open() = inputStream
|
||||
override val signers get() = throw UnsupportedOperationException()
|
||||
override val signerKeys get() = throw UnsupportedOperationException()
|
||||
override val signers: List<Party> get() = throw UnsupportedOperationException()
|
||||
override val size: Int = 512
|
||||
}
|
||||
try {
|
||||
|
@ -111,7 +111,8 @@ class AttachmentSerializationTest {
|
||||
|
||||
private class CustomAttachment(override val id: SecureHash, internal val customContent: String) : Attachment {
|
||||
override fun open() = throw UnsupportedOperationException("Not implemented.")
|
||||
override val signers get() = throw UnsupportedOperationException()
|
||||
override val signerKeys get() = throw UnsupportedOperationException()
|
||||
override val signers: List<Party> get() = throw UnsupportedOperationException()
|
||||
override val size get() = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -50,7 +50,7 @@ class TransactionBuilderTest {
|
||||
doReturn(contractAttachmentId).whenever(attachment).id
|
||||
doReturn(setOf(DummyContract.PROGRAM_ID)).whenever(attachment).allContracts
|
||||
doReturn("app").whenever(attachment).uploader
|
||||
doReturn(emptyList<Party>()).whenever(attachment).signers
|
||||
doReturn(emptyList<Party>()).whenever(attachment).signerKeys
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -128,12 +128,12 @@ class TransactionBuilderTest {
|
||||
private val unsignedAttachment = ContractAttachment(object : AbstractAttachment({ byteArrayOf() }) {
|
||||
override val id: SecureHash get() = throw UnsupportedOperationException()
|
||||
|
||||
override val signers: List<PublicKey> get() = emptyList()
|
||||
override val signerKeys: List<PublicKey> get() = emptyList()
|
||||
}, DummyContract.PROGRAM_ID)
|
||||
|
||||
private fun signedAttachment(vararg parties: Party) = ContractAttachment(object : AbstractAttachment({ byteArrayOf() }) {
|
||||
override val id: SecureHash get() = throw UnsupportedOperationException()
|
||||
|
||||
override val signers: List<PublicKey> get() = parties.map { it.owningKey }
|
||||
}, DummyContract.PROGRAM_ID, signers = parties.map { it.owningKey })
|
||||
override val signerKeys: List<PublicKey> get() = parties.map { it.owningKey }
|
||||
}, DummyContract.PROGRAM_ID, signerKeys = parties.map { it.owningKey })
|
||||
}
|
||||
|
@ -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,13 +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.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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 ``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 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
|
||||
=======================
|
||||
|
||||
|
@ -1,44 +1,38 @@
|
||||
Testing Corda
|
||||
=============
|
||||
Testing your changes
|
||||
====================
|
||||
|
||||
Automated Tests
|
||||
Automated tests
|
||||
---------------
|
||||
Corda has a suite of tests that any contributing developers must maintain and extend when adding new code.
|
||||
|
||||
Corda has a maintained suite of tests that any contributing developers must maintain and add to if new code has been added.
|
||||
There are several test suites:
|
||||
|
||||
There are several distinct test suites each with a different purpose;
|
||||
* **Unit tests**: These are traditional unit tests that should only test a single code unit, typically a method or class.
|
||||
* **Integration tests**: These tests should test the integration of small numbers of units, preferably with mocked out services.
|
||||
* **Smoke tests**: These are full end to end tests which start a full set of Corda nodes and verify broader behaviour.
|
||||
* **Other**: These include tests such as performance tests, stress tests, etc, and may be in an external repo.
|
||||
|
||||
**Unit tests**: These are traditional unit tests that should only test a single code unit, typically a method or class.
|
||||
Running the automated tests
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
These tests are mostly written with JUnit and can be run via ``gradle``:
|
||||
|
||||
**Integration tests**: These tests should test the integration of small numbers of units, preferably with mocked out services.
|
||||
|
||||
**Smoke tests**: These are full end to end tests which start a full set of Corda nodes and verify broader behaviour.
|
||||
|
||||
**Other**: These include tests such as performance tests, stress tests, etc, and may be in an external repo.
|
||||
|
||||
These tests are mostly written with JUnit and can be run via ``gradle``. On windows run ``gradlew test integrationTest
|
||||
smokeTest`` and on unix run ``./gradlw test integrationTest smokeTest`` or any combination of these three arguments.
|
||||
* **Windows**: Run ``gradlew test integrationTest smokeTest``
|
||||
* **Unix/Mac OSX**: Run ``./gradlew test integrationTest smokeTest``
|
||||
|
||||
Before creating a pull request please make sure these pass.
|
||||
|
||||
Manual Testing
|
||||
Manual testing
|
||||
--------------
|
||||
You should manually test anything that would be impacted by your changes. The areas that usually need to be manually tested and when are
|
||||
as follows:
|
||||
|
||||
Manual testing would ideally be done for every set of changes merged into master, but practically you should manually test
|
||||
anything that would be impacted by your changes. The areas that usually need to be manually tested and when are below;
|
||||
* **Node startup** - changes in the ``node`` or ``node:capsule`` project in both the Kotlin or gradle or the ``cordformation`` gradle plugin.
|
||||
* **Sample project** - changes in the ``samples`` project. eg; changing the IRS demo means you should manually test the IRS demo.
|
||||
* **Explorer** - changes to the ``tools/explorer`` project.
|
||||
* **Demobench** - changes to the ``tools/demobench`` project.
|
||||
|
||||
**Node startup** - changes in the ``node`` or ``node:capsule`` project in both the Kotlin or gradle or the ``cordformation`` gradle plugin.
|
||||
|
||||
**Sample project** - changes in the ``samples`` project. eg; changing the IRS demo means you should manually test the IRS demo.
|
||||
|
||||
**Explorer** - changes to the ``tools/explorer`` project.
|
||||
|
||||
**Demobench** - changes to the ``tools/demobench`` project.
|
||||
|
||||
How to manually test each of these areas differs and is currently not fully specified. For now the best thing to do is
|
||||
ensure the program starts, that you can interact with it, and that no exceptions are generated in normal operation.
|
||||
|
||||
TODO: Add instructions on manual testing
|
||||
How to manually test each of these areas differs and is currently not fully specified. For now the best thing to do is to ensure the
|
||||
program starts, that you can interact with it, and that no exceptions are generated in normal operation.
|
||||
|
||||
External Database Testing
|
||||
-------------------------
|
||||
|
@ -87,7 +87,7 @@ class AttachmentsClassLoaderStaticContractTests {
|
||||
doReturn(it.cordappProvider.getContractAttachmentID(AttachmentDummyContract.ATTACHMENT_PROGRAM_ID)).whenever(attachment).id
|
||||
doReturn(setOf(AttachmentDummyContract.ATTACHMENT_PROGRAM_ID)).whenever(attachment).allContracts
|
||||
doReturn("app").whenever(attachment).uploader
|
||||
doReturn(emptyList<Party>()).whenever(attachment).signers
|
||||
doReturn(emptyList<Party>()).whenever(attachment).signerKeys
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -211,7 +211,7 @@ object DefaultKryoCustomizer {
|
||||
output.writeString(obj.contract)
|
||||
kryo.writeClassAndObject(output, obj.additionalContracts)
|
||||
output.writeString(obj.uploader)
|
||||
kryo.writeClassAndObject(output, obj.signers)
|
||||
kryo.writeClassAndObject(output, obj.signerKeys)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
@ -11,13 +11,10 @@ import net.corda.node.services.config.rpc.NodeRpcOptions
|
||||
import net.corda.node.services.config.schema.v1.V1NodeConfigurationSpec
|
||||
import net.corda.node.services.keys.cryptoservice.BCCryptoService
|
||||
import net.corda.node.services.keys.cryptoservice.SupportedCryptoServices
|
||||
import net.corda.node.services.keys.cryptoservice.utimaco.UtimacoCryptoService
|
||||
import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier
|
||||
import net.corda.nodeapi.internal.config.MutualSslConfiguration
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.node.services.keys.cryptoservice.utimaco.UtimacoCryptoService
|
||||
import net.corda.nodeapi.BrokerRpcSslOptions
|
||||
import net.corda.nodeapi.internal.DEV_PUB_KEY_HASHES
|
||||
import net.corda.nodeapi.internal.config.*
|
||||
import net.corda.nodeapi.internal.cryptoservice.CryptoService
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.tools.shell.SSHDConfiguration
|
||||
|
@ -45,7 +45,8 @@ class NodeSchemaService(private val extraSchemas: Set<MappedSchema> = emptySet()
|
||||
PersistentIdentityService.PersistentIdentityNames::class.java,
|
||||
ContractUpgradeServiceImpl.DBContractUpgrade::class.java,
|
||||
RunOnceService.MutualExclusion::class.java,
|
||||
PersistentKeyManagementService.PublicKeyHashToExternalId::class.java)) {
|
||||
PersistentKeyManagementService.PublicKeyHashToExternalId::class.java
|
||||
)) {
|
||||
override val migrationResource = "node-core.changelog-master"
|
||||
}
|
||||
|
||||
|
@ -12,5 +12,4 @@
|
||||
<include file="migration/vault-schema.changelog-pkey-swap.xml"/>
|
||||
<include file="migration/vault-schema.changelog-v7.xml"/>
|
||||
<include file="migration/vault-schema.changelog-v8.xml"/>
|
||||
|
||||
</databaseChangeLog>
|
||||
|
@ -278,7 +278,7 @@ class NodeConfigurationImplTest {
|
||||
fun `jmxReporterType is null and defaults to Jokolia`() {
|
||||
val rawConfig = getConfig("working-config.conf", ConfigFactory.parseMap(mapOf("devMode" to true)))
|
||||
val nodeConfig = rawConfig.parseAsNodeConfiguration().value()
|
||||
assertTrue(JmxReporterType.JOLOKIA.toString() == nodeConfig.jmxReporterType.toString())
|
||||
assertEquals(JmxReporterType.JOLOKIA.toString(), nodeConfig.jmxReporterType.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -286,7 +286,7 @@ class NodeConfigurationImplTest {
|
||||
var rawConfig = getConfig("working-config.conf", ConfigFactory.parseMap(mapOf("devMode" to true)))
|
||||
rawConfig = rawConfig.withValue("jmxReporterType", ConfigValueFactory.fromAnyRef("NEW_RELIC"))
|
||||
val nodeConfig = rawConfig.parseAsNodeConfiguration().value()
|
||||
assertTrue(JmxReporterType.NEW_RELIC.toString() == nodeConfig.jmxReporterType.toString())
|
||||
assertEquals(JmxReporterType.NEW_RELIC.toString(), nodeConfig.jmxReporterType.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -294,7 +294,7 @@ class NodeConfigurationImplTest {
|
||||
var rawConfig = getConfig("working-config.conf", ConfigFactory.parseMap(mapOf("devMode" to true)))
|
||||
rawConfig = rawConfig.withValue("jmxReporterType", ConfigValueFactory.fromAnyRef("JOLOKIA"))
|
||||
val nodeConfig = rawConfig.parseAsNodeConfiguration().value()
|
||||
assertTrue(JmxReporterType.JOLOKIA.toString() == nodeConfig.jmxReporterType.toString())
|
||||
assertEquals(JmxReporterType.JOLOKIA.toString(), nodeConfig.jmxReporterType.toString())
|
||||
}
|
||||
|
||||
private fun configDebugOptions(devMode: Boolean, devModeOptions: DevModeOptions?): NodeConfigurationImpl {
|
||||
|
@ -91,7 +91,7 @@ class NodeAttachmentServiceTest {
|
||||
val signedJar = jarAndSigner.first
|
||||
signedJar.inputStream().use { jarStream ->
|
||||
val attachmentId = storage.importAttachment(jarStream, "test", null)
|
||||
assertEquals(listOf(jarAndSigner.second.hash), storage.openAttachment(attachmentId)!!.signers.map { it.hash })
|
||||
assertEquals(listOf(jarAndSigner.second.hash), storage.openAttachment(attachmentId)!!.signerKeys.map { it.hash })
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,7 +102,7 @@ class NodeAttachmentServiceTest {
|
||||
val jarName = makeTestContractJar(it.path, "com.example.MyContract")
|
||||
it.path.resolve(jarName).inputStream().use { jarStream ->
|
||||
val attachmentId = storage.importAttachment(jarStream, "test", null)
|
||||
assertEquals(0, storage.openAttachment(attachmentId)!!.signers.size)
|
||||
assertEquals(0, storage.openAttachment(attachmentId)!!.signerKeys.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class ContractAttachmentSerializer(factory: SerializerFactory) : CustomSerialize
|
||||
} catch (e: Exception) {
|
||||
throw MissingAttachmentsException(listOf(obj.id))
|
||||
}
|
||||
return ContractAttachmentProxy(GeneratedAttachment(bytes), obj.contract, obj.additionalContracts, obj.uploader, obj.signers)
|
||||
return ContractAttachmentProxy(GeneratedAttachment(bytes), obj.contract, obj.additionalContracts, obj.uploader, obj.signerKeys)
|
||||
}
|
||||
|
||||
override fun fromProxy(proxy: ContractAttachmentProxy): ContractAttachment {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ class MockAttachmentStorage : AttachmentStorage, SingletonSerializeAsToken() {
|
||||
|
||||
fun getAttachmentIdAndBytes(jar: InputStream): Pair<AttachmentId, ByteArray> = jar.readFully().let { bytes -> Pair(bytes.sha256(), bytes) }
|
||||
|
||||
private class MockAttachment(dataLoader: () -> ByteArray, override val id: SecureHash, override val signers: List<PublicKey>) : AbstractAttachment(dataLoader)
|
||||
private class MockAttachment(dataLoader: () -> ByteArray, override val id: SecureHash, override val signerKeys: List<PublicKey>) : AbstractAttachment(dataLoader)
|
||||
|
||||
private fun importAttachmentInternal(jar: InputStream, uploader: String, contractClassNames: List<ContractClassName>? = null, attachmentId: AttachmentId? = null, signers: List<PublicKey> = emptyList()): AttachmentId {
|
||||
// JIS makes read()/readBytes() return bytes of the current file, but we want to hash the entire container here.
|
||||
|
@ -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