mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
ENT-6548: Ensure LazyMappedList is realised with correct SerializationContext. (#7028)
This commit is contained in:
parent
265a293666
commit
4f1a07cbcc
@ -56,7 +56,9 @@ import java.security.cert.TrustAnchor
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Duration
|
||||
import java.time.temporal.Temporal
|
||||
import java.util.*
|
||||
import java.util.Collections
|
||||
import java.util.PrimitiveIterator
|
||||
import java.util.Spliterator
|
||||
import java.util.Spliterator.DISTINCT
|
||||
import java.util.Spliterator.IMMUTABLE
|
||||
import java.util.Spliterator.NONNULL
|
||||
@ -64,6 +66,7 @@ import java.util.Spliterator.ORDERED
|
||||
import java.util.Spliterator.SIZED
|
||||
import java.util.Spliterator.SORTED
|
||||
import java.util.Spliterator.SUBSIZED
|
||||
import java.util.Spliterators
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.stream.Collectors
|
||||
|
@ -114,14 +114,14 @@ fun deserialiseCommands(
|
||||
componentGroups: List<ComponentGroup>,
|
||||
forceDeserialize: Boolean = false,
|
||||
factory: SerializationFactory = SerializationFactory.defaultFactory,
|
||||
@Suppress("UNUSED_PARAMETER") context: SerializationContext = factory.defaultContext,
|
||||
context: SerializationContext = factory.defaultContext,
|
||||
digestService: DigestService = DigestService.sha2_256
|
||||
): List<Command<*>> {
|
||||
// TODO: we could avoid deserialising unrelated signers.
|
||||
// However, current approach ensures the transaction is not malformed
|
||||
// and it will throw if any of the signers objects is not List of public keys).
|
||||
val signersList: List<List<PublicKey>> = uncheckedCast(deserialiseComponentGroup(componentGroups, List::class, ComponentGroupEnum.SIGNERS_GROUP, forceDeserialize))
|
||||
val commandDataList: List<CommandData> = deserialiseComponentGroup(componentGroups, CommandData::class, ComponentGroupEnum.COMMANDS_GROUP, forceDeserialize)
|
||||
val signersList: List<List<PublicKey>> = uncheckedCast(deserialiseComponentGroup(componentGroups, List::class, ComponentGroupEnum.SIGNERS_GROUP, forceDeserialize, factory, context))
|
||||
val commandDataList: List<CommandData> = deserialiseComponentGroup(componentGroups, CommandData::class, ComponentGroupEnum.COMMANDS_GROUP, forceDeserialize, factory, context)
|
||||
val group = componentGroups.firstOrNull { it.groupIndex == ComponentGroupEnum.COMMANDS_GROUP.ordinal }
|
||||
return if (group is FilteredComponentGroup) {
|
||||
check(commandDataList.size <= signersList.size) {
|
||||
@ -154,8 +154,9 @@ fun createComponentGroups(inputs: List<StateRef>,
|
||||
timeWindow: TimeWindow?,
|
||||
references: List<StateRef>,
|
||||
networkParametersHash: SecureHash?): List<ComponentGroup> {
|
||||
val serializationContext = SerializationFactory.defaultFactory.defaultContext
|
||||
val serialize = { value: Any, _: Int -> value.serialize(context = serializationContext) }
|
||||
val serializationFactory = SerializationFactory.defaultFactory
|
||||
val serializationContext = serializationFactory.defaultContext
|
||||
val serialize = { value: Any, _: Int -> value.serialize(serializationFactory, serializationContext) }
|
||||
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)))
|
||||
@ -178,7 +179,11 @@ fun createComponentGroups(inputs: List<StateRef>,
|
||||
*/
|
||||
@KeepForDJVM
|
||||
data class SerializedStateAndRef(val serializedState: SerializedBytes<TransactionState<ContractState>>, val ref: StateRef) {
|
||||
fun toStateAndRef(): StateAndRef<ContractState> = StateAndRef(serializedState.deserialize(), ref)
|
||||
fun toStateAndRef(factory: SerializationFactory, context: SerializationContext) = StateAndRef(serializedState.deserialize(factory, context), ref)
|
||||
fun toStateAndRef(): StateAndRef<ContractState> {
|
||||
val factory = SerializationFactory.defaultFactory
|
||||
return toStateAndRef(factory, factory.defaultContext)
|
||||
}
|
||||
}
|
||||
|
||||
/** Check that network parameters hash on this transaction is the current hash for the network. */
|
||||
|
@ -15,6 +15,7 @@ import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
||||
import net.corda.core.serialization.SerializationFactory
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.serialization.internal.AttachmentsClassLoaderCache
|
||||
import net.corda.core.serialization.serialize
|
||||
@ -187,19 +188,26 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
): LedgerTransaction {
|
||||
// Look up public keys to authenticated identities.
|
||||
val authenticatedCommands = commands.lazyMapped { cmd, _ ->
|
||||
val parties = cmd.signers.mapNotNull { pk -> resolveIdentity(pk) }
|
||||
val parties = cmd.signers.mapNotNull(resolveIdentity)
|
||||
CommandWithParties(cmd.signers, parties, cmd.value)
|
||||
}
|
||||
|
||||
// Ensure that the lazy mappings will use the correct SerializationContext.
|
||||
val serializationFactory = SerializationFactory.defaultFactory
|
||||
val serializationContext = serializationFactory.defaultContext
|
||||
val toStateAndRef = { ssar: SerializedStateAndRef, _: Int ->
|
||||
ssar.toStateAndRef(serializationFactory, serializationContext)
|
||||
}
|
||||
|
||||
val serializedResolvedInputs = inputs.map { ref ->
|
||||
SerializedStateAndRef(resolveStateRefAsSerialized(ref) ?: throw TransactionResolutionException(ref.txhash), ref)
|
||||
}
|
||||
val resolvedInputs = serializedResolvedInputs.lazyMapped { star, _ -> star.toStateAndRef() }
|
||||
val resolvedInputs = serializedResolvedInputs.lazyMapped(toStateAndRef)
|
||||
|
||||
val serializedResolvedReferences = references.map { ref ->
|
||||
SerializedStateAndRef(resolveStateRefAsSerialized(ref) ?: throw TransactionResolutionException(ref.txhash), ref)
|
||||
}
|
||||
val resolvedReferences = serializedResolvedReferences.lazyMapped { star, _ -> star.toStateAndRef() }
|
||||
val resolvedReferences = serializedResolvedReferences.lazyMapped(toStateAndRef)
|
||||
|
||||
val resolvedAttachments = attachments.lazyMapped { att, _ -> resolveAttachment(att) ?: throw AttachmentResolutionException(att) }
|
||||
|
||||
|
@ -30,7 +30,7 @@ import java.security.PublicKey
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.CertificateFactory
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
import java.util.Collections
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KMutableProperty
|
||||
@ -509,6 +509,7 @@ class ThrowableSerializer<T>(kryo: Kryo, type: Class<T>) : Serializer<Throwable>
|
||||
@ThreadSafe
|
||||
@SuppressWarnings("ALL")
|
||||
object LazyMappedListSerializer : Serializer<List<*>>() {
|
||||
override fun write(kryo: Kryo, output: Output, obj: List<*>) = kryo.writeClassAndObject(output, obj.toList())
|
||||
override fun read(kryo: Kryo, input: Input, type: Class<List<*>>) = kryo.readClassAndObject(input) as List<*>
|
||||
// Using a MutableList so that Kryo will always write an instance of java.util.ArrayList.
|
||||
override fun write(kryo: Kryo, output: Output, obj: List<*>) = kryo.writeClassAndObject(output, obj.toMutableList())
|
||||
override fun read(kryo: Kryo, input: Input, type: Class<List<*>>) = kryo.readClassAndObject(input) as? List<*>
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
package net.corda.node
|
||||
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.flows.CashIssueAndPaymentFlow
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.driver.internal.incrementalPortAllocation
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.internal.findCordapp
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.assertDoesNotThrow
|
||||
|
||||
/**
|
||||
* Execute a flow with sub-flows, including the finality flow.
|
||||
* This operation should checkpoint, and have its checkpoint restored.
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
class CashIssueAndPaymentTest {
|
||||
companion object {
|
||||
private val logger = loggerFor<CashIssueAndPaymentTest>()
|
||||
|
||||
private val configOverrides = mapOf(NodeConfiguration::reloadCheckpointAfterSuspend.name to true)
|
||||
private val CASH_AMOUNT = 500.DOLLARS
|
||||
|
||||
fun parametersFor(): DriverParameters {
|
||||
return DriverParameters(
|
||||
systemProperties = mapOf("co.paralleluniverse.fibers.verifyInstrumentation" to "false"),
|
||||
portAllocation = incrementalPortAllocation(),
|
||||
startNodesInProcess = false,
|
||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, startInProcess = false, validating = true)),
|
||||
notaryCustomOverrides = configOverrides,
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("net.corda.finance.contracts"),
|
||||
findCordapp("net.corda.finance.workflows")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 300_000)
|
||||
fun `test can issue cash`() {
|
||||
driver(parametersFor()) {
|
||||
val alice = startNode(providedName = ALICE_NAME, customOverrides = configOverrides).getOrThrow()
|
||||
val aliceParty = alice.nodeInfo.singleIdentity()
|
||||
val notaryParty = notaryHandles.single().identity
|
||||
val result = assertDoesNotThrow {
|
||||
alice.rpc.startFlow(::CashIssueAndPaymentFlow,
|
||||
CASH_AMOUNT,
|
||||
OpaqueBytes.of(0x01),
|
||||
aliceParty,
|
||||
false,
|
||||
notaryParty
|
||||
).use { flowHandle ->
|
||||
flowHandle.returnValue.getOrThrow()
|
||||
}
|
||||
}
|
||||
logger.info("TXN={}, recipient={}", result.stx, result.recipient)
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import net.corda.core.utilities.loggerFor
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.flows.CashIssueAndPaymentFlow
|
||||
import net.corda.node.DeterministicSourcesRule
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.singleIdentity
|
||||
@ -22,20 +23,21 @@ import org.junit.jupiter.api.assertDoesNotThrow
|
||||
@Suppress("FunctionName")
|
||||
class DeterministicCashIssueAndPaymentTest {
|
||||
companion object {
|
||||
val logger = loggerFor<DeterministicCashIssueAndPaymentTest>()
|
||||
private val logger = loggerFor<DeterministicCashIssueAndPaymentTest>()
|
||||
|
||||
private val configOverrides = mapOf(NodeConfiguration::reloadCheckpointAfterSuspend.name to true)
|
||||
private val CASH_AMOUNT = 500.DOLLARS
|
||||
|
||||
@ClassRule
|
||||
@JvmField
|
||||
val djvmSources = DeterministicSourcesRule()
|
||||
|
||||
@JvmField
|
||||
val CASH_AMOUNT = 500.DOLLARS
|
||||
|
||||
fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters {
|
||||
return DriverParameters(
|
||||
portAllocation = incrementalPortAllocation(),
|
||||
startNodesInProcess = false,
|
||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
||||
notaryCustomOverrides = configOverrides,
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("net.corda.finance.contracts"),
|
||||
findCordapp("net.corda.finance.workflows")
|
||||
@ -50,7 +52,7 @@ class DeterministicCashIssueAndPaymentTest {
|
||||
fun `test DJVM can issue cash`() {
|
||||
val reference = OpaqueBytes.of(0x01)
|
||||
driver(parametersFor(djvmSources)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val alice = startNode(providedName = ALICE_NAME, customOverrides = configOverrides).getOrThrow()
|
||||
val aliceParty = alice.nodeInfo.singleIdentity()
|
||||
val notaryParty = notaryHandles.single().identity
|
||||
val txId = assertDoesNotThrow {
|
||||
@ -60,7 +62,9 @@ class DeterministicCashIssueAndPaymentTest {
|
||||
aliceParty,
|
||||
false,
|
||||
notaryParty
|
||||
).returnValue.getOrThrow()
|
||||
).use { flowHandle ->
|
||||
flowHandle.returnValue.getOrThrow()
|
||||
}
|
||||
}
|
||||
logger.info("TX-ID: {}", txId)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user