mirror of
https://github.com/corda/corda.git
synced 2024-12-19 21:17:58 +00:00
CORDA-961 Wire up and enforce max transaction size (#2465)
* wire up and enforce max transaction size * fixup after rebase moved network parameter from AbstractNode to NodeProperties * removed TODO * fix broken import * address PR issues * remove API breaking change address PR issue * added max transaction size to driver and mock network. address PR issues * fix failing test * added TODO * fix verifier test * fix spring driver build error
This commit is contained in:
parent
1902a4f11e
commit
c8cf46c657
@ -280,6 +280,7 @@ public static final class net.corda.core.contracts.AmountTransfer$Companion exte
|
||||
@net.corda.core.serialization.CordaSerializable public interface net.corda.core.contracts.Attachment extends net.corda.core.contracts.NamedByHash
|
||||
public abstract void extractFile(String, java.io.OutputStream)
|
||||
@org.jetbrains.annotations.NotNull public abstract List getSigners()
|
||||
public abstract int getSize()
|
||||
@org.jetbrains.annotations.NotNull public abstract java.io.InputStream open()
|
||||
@org.jetbrains.annotations.NotNull public abstract jar.JarInputStream openAsJAR()
|
||||
##
|
||||
@ -347,6 +348,7 @@ public final class net.corda.core.contracts.ComponentGroupEnum extends java.lang
|
||||
@org.jetbrains.annotations.NotNull public final String getContract()
|
||||
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
|
||||
@org.jetbrains.annotations.NotNull public List getSigners()
|
||||
public int getSize()
|
||||
@org.jetbrains.annotations.NotNull public java.io.InputStream open()
|
||||
@org.jetbrains.annotations.NotNull public jar.JarInputStream openAsJAR()
|
||||
##
|
||||
@ -3584,7 +3586,7 @@ public static final class net.corda.client.jackson.StringToMethodCallParser$Unpa
|
||||
##
|
||||
public final class net.corda.testing.driver.Driver extends java.lang.Object
|
||||
public static final Object driver(net.corda.testing.driver.DriverParameters, kotlin.jvm.functions.Function1)
|
||||
public static final Object driver(net.corda.testing.driver.DriverParameters, boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, Map, boolean, boolean, boolean, boolean, List, List, net.corda.testing.driver.JmxPolicy, kotlin.jvm.functions.Function1)
|
||||
public static final Object driver(net.corda.testing.driver.DriverParameters, boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, Map, boolean, boolean, boolean, boolean, List, List, net.corda.testing.driver.JmxPolicy, int, kotlin.jvm.functions.Function1)
|
||||
##
|
||||
@net.corda.core.DoNotImplement public interface net.corda.testing.driver.DriverDSL
|
||||
@org.jetbrains.annotations.NotNull public abstract java.nio.file.Path baseDirectory(net.corda.core.identity.CordaX500Name)
|
||||
@ -3599,11 +3601,12 @@ public final class net.corda.testing.driver.Driver extends java.lang.Object
|
||||
##
|
||||
public final class net.corda.testing.driver.DriverParameters extends java.lang.Object
|
||||
public <init>()
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, Map, boolean, boolean, boolean, boolean, List, List, net.corda.testing.driver.JmxPolicy)
|
||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, Map, boolean, boolean, boolean, boolean, List, List, net.corda.testing.driver.JmxPolicy, int)
|
||||
public final boolean component1()
|
||||
@org.jetbrains.annotations.NotNull public final List component10()
|
||||
@org.jetbrains.annotations.NotNull public final List component11()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.JmxPolicy component12()
|
||||
public final int component13()
|
||||
@org.jetbrains.annotations.NotNull public final java.nio.file.Path component2()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.PortAllocation component3()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.PortAllocation component4()
|
||||
@ -3612,13 +3615,14 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O
|
||||
public final boolean component7()
|
||||
public final boolean component8()
|
||||
public final boolean component9()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, Map, boolean, boolean, boolean, boolean, List, List, net.corda.testing.driver.JmxPolicy)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters copy(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, Map, boolean, boolean, boolean, boolean, List, List, net.corda.testing.driver.JmxPolicy, int)
|
||||
public boolean equals(Object)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.PortAllocation getDebugPortAllocation()
|
||||
@org.jetbrains.annotations.NotNull public final java.nio.file.Path getDriverDirectory()
|
||||
@org.jetbrains.annotations.NotNull public final List getExtraCordappPackagesToScan()
|
||||
public final boolean getInitialiseSerialization()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.JmxPolicy getJmxPolicy()
|
||||
public final int getMaxTransactionSize()
|
||||
@org.jetbrains.annotations.NotNull public final List getNotarySpecs()
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.PortAllocation getPortAllocation()
|
||||
public final boolean getStartNodesInProcess()
|
||||
@ -3892,7 +3896,7 @@ public final class net.corda.testing.node.MockKeyManagementService extends net.c
|
||||
public class net.corda.testing.node.MockNetwork extends java.lang.Object
|
||||
public <init>(List)
|
||||
public <init>(List, net.corda.testing.node.MockNetworkParameters)
|
||||
public <init>(List, net.corda.testing.node.MockNetworkParameters, boolean, boolean, net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy, kotlin.jvm.functions.Function1, boolean, List)
|
||||
public <init>(List, net.corda.testing.node.MockNetworkParameters, boolean, boolean, net.corda.testing.node.InMemoryMessagingNetwork$ServicePeerAllocationStrategy, kotlin.jvm.functions.Function1, boolean, List, int)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockNetwork$MockNode addressToNode(net.corda.core.messaging.MessageRecipients)
|
||||
@org.jetbrains.annotations.NotNull public final java.nio.file.Path baseDirectory(int)
|
||||
@org.jetbrains.annotations.NotNull public final net.corda.node.internal.StartedNode createNode(net.corda.testing.node.MockNodeParameters)
|
||||
|
@ -50,4 +50,9 @@ interface Attachment : NamedByHash {
|
||||
* Can be empty, for example non-contract attachments won't be necessarily be signed.
|
||||
*/
|
||||
val signers: List<Party>
|
||||
|
||||
/**
|
||||
* Attachment size in bytes.
|
||||
*/
|
||||
val size: Int
|
||||
}
|
||||
|
@ -27,6 +27,10 @@ abstract class AbstractAttachment(dataLoader: () -> ByteArray) : Attachment {
|
||||
}
|
||||
|
||||
protected val attachmentData: ByteArray by lazy(dataLoader)
|
||||
|
||||
// TODO: read file size information from metadata instead of loading the data.
|
||||
override val size: Int get() = attachmentData.size
|
||||
|
||||
override fun open(): InputStream = attachmentData.inputStream()
|
||||
override val signers by lazy {
|
||||
// Can't start with empty set if we're doing intersections. Logically the null means "all possible signers":
|
||||
|
@ -0,0 +1,14 @@
|
||||
package net.corda.core.internal
|
||||
|
||||
import net.corda.core.node.NetworkParameters
|
||||
|
||||
// TODO: This will cause problems when we run tests in parallel, make each node have its own properties.
|
||||
object GlobalProperties {
|
||||
private var _networkParameters: NetworkParameters? = null
|
||||
|
||||
var networkParameters: NetworkParameters
|
||||
get() = checkNotNull(_networkParameters) { "Property 'networkParameters' has not been initialised." }
|
||||
set(value) {
|
||||
_networkParameters = value
|
||||
}
|
||||
}
|
@ -17,7 +17,6 @@ import java.time.Instant
|
||||
*/
|
||||
// TODO Add eventHorizon - how many days a node can be offline before being automatically ejected from the network.
|
||||
// It needs separate design.
|
||||
// TODO Currently maxTransactionSize is not wired.
|
||||
@CordaSerializable
|
||||
data class NetworkParameters(
|
||||
val minimumPlatformVersion: Int,
|
||||
|
@ -5,6 +5,7 @@ import net.corda.core.contracts.ComponentGroupEnum.*
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.Emoji
|
||||
import net.corda.core.internal.GlobalProperties
|
||||
import net.corda.core.node.ServicesForResolution
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
@ -118,7 +119,24 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
val contractAttachments = findAttachmentContracts(resolvedInputs, resolveContractAttachment, resolveAttachment)
|
||||
// Order of attachments is important since contracts may refer to indexes so only append automatic attachments
|
||||
val attachments = (attachments.map { resolveAttachment(it) ?: throw AttachmentResolutionException(it) } + contractAttachments).distinct()
|
||||
return LedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, notary, timeWindow, privacySalt)
|
||||
val ltx = LedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, notary, timeWindow, privacySalt)
|
||||
checkTransactionSize(ltx)
|
||||
return ltx
|
||||
}
|
||||
|
||||
private fun checkTransactionSize(ltx: LedgerTransaction) {
|
||||
var remainingTransactionSize = GlobalProperties.networkParameters.maxTransactionSize
|
||||
|
||||
fun minus(size: Int) {
|
||||
require(remainingTransactionSize > size) { "Transaction exceeded network's maximum transaction size limit : ${GlobalProperties.networkParameters.maxTransactionSize} bytes." }
|
||||
remainingTransactionSize -= size
|
||||
}
|
||||
|
||||
// Check attachment size first as they are most likely to go over the limit.
|
||||
ltx.attachments.forEach { minus(it.size) }
|
||||
minus(ltx.inputs.serialize().size)
|
||||
minus(ltx.commands.serialize().size)
|
||||
minus(ltx.outputs.serialize().size)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,6 +31,7 @@ class AttachmentTest {
|
||||
override val id get() = throw UnsupportedOperationException()
|
||||
override fun open() = inputStream
|
||||
override val signers get() = throw UnsupportedOperationException()
|
||||
override val size: Int = 512
|
||||
}
|
||||
try {
|
||||
attachment.openAsJAR()
|
||||
|
@ -114,6 +114,7 @@ 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 size get() = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
private class CustomAttachmentLogic(serverIdentity: Party, private val attachmentId: SecureHash, private val customContent: String) : ClientLogic(serverIdentity) {
|
||||
|
@ -75,6 +75,9 @@ UNRELEASED
|
||||
* Moved ``NodeInfoSchema`` to internal package as the node info's database schema is not part of the public API. This
|
||||
was needed to allow changes to the schema.
|
||||
|
||||
* Introduced max transaction size limit on transactions. The max transaction size parameter is set by the compatibility zone
|
||||
operator. The parameter is distributed to Corda nodes by network map service as part of the ``NetworkParameters``.
|
||||
|
||||
* Support for external user credentials data source and password encryption [CORDA-827].
|
||||
|
||||
* Exporting additional JMX metrics (artemis, hibernate statistics) and loading Jolokia agent at JVM startup when using
|
||||
|
@ -71,6 +71,7 @@ The current set of network parameters:
|
||||
:maxMessageSize: Maximum allowed size in bytes of an individual message sent over the wire. Note that attachments are
|
||||
a special case and may be fragmented for streaming transfer, however, an individual transaction or flow message
|
||||
may not be larger than this value.
|
||||
:maxTransactionSize: Maximum allowed size in bytes of a transaction. This is the size of the transaction object and its attachments.
|
||||
:modifiedTime: The time when the network parameters were last modified by the compatibility zone operator.
|
||||
:epoch: Version number of the network parameters. Starting from 1, this will always increment whenever any of the
|
||||
parameters change.
|
||||
|
@ -13,13 +13,19 @@ import net.corda.core.flows.*
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.FlowStateMachine
|
||||
import net.corda.core.internal.GlobalProperties
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.concurrent.map
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.messaging.*
|
||||
import net.corda.core.node.*
|
||||
import net.corda.core.node.services.*
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.debug
|
||||
@ -118,7 +124,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
// low-performance prototyping period.
|
||||
protected abstract val serverThread: AffinityExecutor
|
||||
|
||||
protected lateinit var networkParameters: NetworkParameters
|
||||
private val cordappServices = MutableClassToInstanceMap.create<SerializeAsToken>()
|
||||
private val flowFactories = ConcurrentHashMap<Class<out FlowLogic<*>>, InitiatedFlowFactory<*>>()
|
||||
|
||||
@ -166,7 +171,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
initCertificate()
|
||||
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
|
||||
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
||||
return initialiseDatabasePersistence(schemaService, makeIdentityService(identity.certificate)) { database ->
|
||||
return initialiseDatabasePersistence(schemaService, makeIdentityService(identity.certificate)) { database ->
|
||||
// TODO The fact that we need to specify an empty list of notaries just to generate our node info looks like
|
||||
// a code smell.
|
||||
val persistentNetworkMapCache = PersistentNetworkMapCache(database, notaries = emptyList())
|
||||
@ -189,13 +194,13 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
||||
val identityService = makeIdentityService(identity.certificate)
|
||||
networkMapClient = configuration.compatibilityZoneURL?.let { NetworkMapClient(it, identityService.trustRoot) }
|
||||
networkParameters = NetworkParametersReader(identityService.trustRoot, networkMapClient, configuration.baseDirectory).networkParameters
|
||||
check(networkParameters.minimumPlatformVersion <= versionInfo.platformVersion) {
|
||||
GlobalProperties.networkParameters = NetworkParametersReader(identityService.trustRoot, networkMapClient, configuration.baseDirectory).networkParameters
|
||||
check(GlobalProperties.networkParameters.minimumPlatformVersion <= versionInfo.platformVersion) {
|
||||
"Node's platform version is lower than network's required minimumPlatformVersion"
|
||||
}
|
||||
// Do all of this in a database transaction so anything that might need a connection has one.
|
||||
val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database ->
|
||||
val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries).start(), identityService)
|
||||
val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, GlobalProperties.networkParameters.notaries).start(), identityService)
|
||||
val (keyPairs, info) = initNodeInfo(networkMapCache, identity, identityKeyPair)
|
||||
identityService.loadIdentities(info.legalIdentitiesAndCerts)
|
||||
val transactionStorage = makeTransactionStorage(database, configuration.transactionCacheSizeBytes)
|
||||
@ -234,7 +239,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
networkMapUpdater = NetworkMapUpdater(services.networkMapCache,
|
||||
NodeInfoWatcher(configuration.baseDirectory, getRxIoScheduler(), Duration.ofMillis(configuration.additionalNodeInfoPollingFrequencyMsec)),
|
||||
networkMapClient,
|
||||
networkParameters.serialize().hash,
|
||||
GlobalProperties.networkParameters.serialize().hash,
|
||||
configuration.baseDirectory)
|
||||
runOnStop += networkMapUpdater::close
|
||||
|
||||
@ -520,7 +525,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
* Builds node internal, advertised, and plugin services.
|
||||
* Returns a list of tokenizable services to be added to the serialisation context.
|
||||
*/
|
||||
private fun makeServices(keyPairs: Set<KeyPair>, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList<Any> {
|
||||
private fun makeServices(keyPairs: Set<KeyPair>, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList<Any> {
|
||||
checkpointStorage = DBCheckpointStorage()
|
||||
val metrics = MetricRegistry()
|
||||
attachments = NodeAttachmentService(metrics, configuration.attachmentContentCacheSizeBytes, configuration.attachmentCacheBound)
|
||||
|
@ -2,6 +2,7 @@ package net.corda.node.internal
|
||||
|
||||
import com.codahale.metrics.JmxReporter
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.internal.GlobalProperties.networkParameters
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.concurrent.thenMatch
|
||||
import net.corda.core.internal.div
|
||||
|
@ -0,0 +1,135 @@
|
||||
package net.corda.node.services.transactions
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.InputStreamAndHash
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.api.StartedNodeServices
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.contracts.DummyState
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.dummyCommand
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.node.startFlow
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class MaxTransactionSizeTests {
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var notaryServices: StartedNodeServices
|
||||
private lateinit var aliceServices: StartedNodeServices
|
||||
private lateinit var notary: Party
|
||||
private lateinit var alice: Party
|
||||
private lateinit var bob: Party
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = MockNetwork(listOf("net.corda.testing.contracts", "net.corda.node.services.transactions"), maxTransactionSize = 3_000_000)
|
||||
val aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME))
|
||||
val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME))
|
||||
notaryServices = mockNet.defaultNotaryNode.services
|
||||
aliceServices = aliceNode.services
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
alice = aliceNode.info.singleIdentity()
|
||||
bob = bobNode.info.singleIdentity()
|
||||
}
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
mockNet.stopNodes()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check transaction will fail when exceed max transaction size limit`() {
|
||||
// These 4 attachments yield a transaction that's got ~ 4mb, which will exceed the 3mb max transaction size limit
|
||||
val bigFile1 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 0)
|
||||
val bigFile2 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 1)
|
||||
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 2)
|
||||
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 3)
|
||||
val flow = aliceServices.database.transaction {
|
||||
val hash1 = aliceServices.attachments.importAttachment(bigFile1.inputStream)
|
||||
val hash2 = aliceServices.attachments.importAttachment(bigFile2.inputStream)
|
||||
val hash3 = aliceServices.attachments.importAttachment(bigFile3.inputStream)
|
||||
val hash4 = aliceServices.attachments.importAttachment(bigFile4.inputStream)
|
||||
assertEquals(hash1, bigFile1.sha256)
|
||||
SendLargeTransactionFlow(notary, bob, hash1, hash2, hash3, hash4)
|
||||
}
|
||||
val exception = assertFailsWith<IllegalArgumentException> {
|
||||
val future = aliceServices.startFlow(flow)
|
||||
mockNet.runNetwork()
|
||||
future.getOrThrow()
|
||||
}
|
||||
assertThat(exception).hasMessageContaining("Transaction exceeded network's maximum transaction size limit")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check transaction will be rejected by counterparty when exceed max transaction size limit`() {
|
||||
// These 4 attachments yield a transaction that's got ~ 4mb, which will exceed the 3mb max transaction size limit
|
||||
val bigFile1 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 0)
|
||||
val bigFile2 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 1)
|
||||
val bigFile3 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 2)
|
||||
val bigFile4 = InputStreamAndHash.createInMemoryTestZip(1024 * 1024, 3)
|
||||
val flow = aliceServices.database.transaction {
|
||||
val hash1 = aliceServices.attachments.importAttachment(bigFile1.inputStream)
|
||||
val hash2 = aliceServices.attachments.importAttachment(bigFile2.inputStream)
|
||||
val hash3 = aliceServices.attachments.importAttachment(bigFile3.inputStream)
|
||||
val hash4 = aliceServices.attachments.importAttachment(bigFile4.inputStream)
|
||||
assertEquals(hash1, bigFile1.sha256)
|
||||
SendLargeTransactionFlow(notary, bob, hash1, hash2, hash3, hash4, verify = false)
|
||||
}
|
||||
val ex = assertFailsWith<UnexpectedFlowEndException> {
|
||||
val future = aliceServices.startFlow(flow)
|
||||
mockNet.runNetwork()
|
||||
future.getOrThrow()
|
||||
}
|
||||
assertThat(ex).hasMessageContaining("Counterparty flow on O=Bob Plc, L=Rome, C=IT had an internal error and has terminated")
|
||||
}
|
||||
|
||||
@StartableByRPC
|
||||
@InitiatingFlow
|
||||
class SendLargeTransactionFlow(private val notary: Party,
|
||||
private val otherSide: Party,
|
||||
private val hash1: SecureHash,
|
||||
private val hash2: SecureHash,
|
||||
private val hash3: SecureHash,
|
||||
private val hash4: SecureHash,
|
||||
private val verify: Boolean = true) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
val tx = TransactionBuilder(notary = notary)
|
||||
.addOutputState(DummyState(), DummyContract.PROGRAM_ID)
|
||||
.addCommand(dummyCommand(ourIdentity.owningKey))
|
||||
.addAttachment(hash1)
|
||||
.addAttachment(hash2)
|
||||
.addAttachment(hash3)
|
||||
.addAttachment(hash4)
|
||||
val stx = serviceHub.signInitialTransaction(tx, ourIdentity.owningKey)
|
||||
if (verify) stx.verify(serviceHub, checkSufficientSignatures = false)
|
||||
// Send to the other side and wait for it to trigger resolution from us.
|
||||
val otherSideSession = initiateFlow(otherSide)
|
||||
subFlow(SendTransactionFlow(otherSideSession, stx))
|
||||
otherSideSession.receive<Unit>()
|
||||
}
|
||||
}
|
||||
|
||||
@InitiatedBy(SendLargeTransactionFlow::class)
|
||||
@Suppress("UNUSED")
|
||||
class ReceiveLargeTransactionFlow(private val otherSide: FlowSession) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
subFlow(ReceiveTransactionFlow(otherSide))
|
||||
// Unblock the other side by sending some dummy object (Unit is fine here as it's a singleton).
|
||||
otherSide.send(Unit)
|
||||
}
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ fun <A> springDriver(
|
||||
startNodesInProcess: Boolean = defaultParameters.startNodesInProcess,
|
||||
notarySpecs: List<NotarySpec> = defaultParameters.notarySpecs,
|
||||
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
||||
maxTransactionSize: Int = defaultParameters.maxTransactionSize,
|
||||
dsl: SpringBootDriverDSL.() -> A
|
||||
): A {
|
||||
return genericDriver(
|
||||
@ -44,6 +45,7 @@ fun <A> springDriver(
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
notarySpecs = notarySpecs,
|
||||
driverDslWrapper = { driverDSL: DriverDSLImpl -> SpringBootDriverDSL(driverDSL) },
|
||||
maxTransactionSize = maxTransactionSize,
|
||||
coerce = { it }, dsl = dsl
|
||||
)
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ data class NodeParameters(
|
||||
|
||||
data class JmxPolicy(val startJmxHttpServer: Boolean = false,
|
||||
val jmxHttpServerPortAllocation: PortAllocation? =
|
||||
if (startJmxHttpServer) PortAllocation.Incremental(7005) else null)
|
||||
if (startJmxHttpServer) PortAllocation.Incremental(7005) else null)
|
||||
|
||||
/**
|
||||
* [driver] allows one to start up nodes like this:
|
||||
@ -174,7 +174,7 @@ data class JmxPolicy(val startJmxHttpServer: Boolean = false,
|
||||
* @param useTestClock If true the test clock will be used in Node.
|
||||
* @param startNodesInProcess Provides the default behaviour of whether new nodes should start inside this process or
|
||||
* not. Note that this may be overridden in [DriverDSL.startNode].
|
||||
* @param waitForAllNodesToFinish If true, the nodes will not shut down automatically after executing the code in the driver DSL block.
|
||||
* @param waitForAllNodesToFinish If true, the nodes will not shut down automatically after executing the code in the driver DSL block.
|
||||
* It will wait for them to be shut down externally instead.
|
||||
* @param notarySpecs The notaries advertised for this network. These nodes will be started automatically and will be
|
||||
* available from [DriverDSL.notaryHandles]. Defaults to a simple validating notary.
|
||||
@ -198,6 +198,7 @@ fun <A> driver(
|
||||
notarySpecs: List<NotarySpec> = defaultParameters.notarySpecs,
|
||||
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
||||
jmxPolicy: JmxPolicy = defaultParameters.jmxPolicy,
|
||||
maxTransactionSize: Int = defaultParameters.maxTransactionSize,
|
||||
dsl: DriverDSL.() -> A
|
||||
): A {
|
||||
return genericDriver(
|
||||
@ -213,7 +214,8 @@ fun <A> driver(
|
||||
notarySpecs = notarySpecs,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
jmxPolicy = jmxPolicy,
|
||||
compatibilityZone = null
|
||||
compatibilityZone = null,
|
||||
maxTransactionSize = maxTransactionSize
|
||||
),
|
||||
coerce = { it },
|
||||
dsl = dsl,
|
||||
@ -249,7 +251,8 @@ data class DriverParameters(
|
||||
val waitForAllNodesToFinish: Boolean = false,
|
||||
val notarySpecs: List<NotarySpec> = listOf(NotarySpec(DUMMY_NOTARY_NAME)),
|
||||
val extraCordappPackagesToScan: List<String> = emptyList(),
|
||||
val jmxPolicy: JmxPolicy = JmxPolicy()
|
||||
val jmxPolicy: JmxPolicy = JmxPolicy(),
|
||||
val maxTransactionSize: Int = Int.MAX_VALUE
|
||||
) {
|
||||
fun setIsDebug(isDebug: Boolean) = copy(isDebug = isDebug)
|
||||
fun setDriverDirectory(driverDirectory: Path) = copy(driverDirectory = driverDirectory)
|
||||
|
@ -19,6 +19,7 @@ import net.corda.core.messaging.MessageRecipients
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.messaging.SingleMessageRecipient
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.KeyManagementService
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
@ -42,16 +43,15 @@ import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||
import net.corda.nodeapi.internal.config.User
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.setGlobalSerialization
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.internal.testThreadFactory
|
||||
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||
import net.corda.testing.core.setGlobalSerialization
|
||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||
import org.apache.sshd.common.util.security.SecurityUtils
|
||||
import rx.internal.schedulers.CachedThreadScheduler
|
||||
@ -133,7 +133,8 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
||||
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
|
||||
private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory,
|
||||
initialiseSerialization: Boolean = defaultParameters.initialiseSerialization,
|
||||
private val notarySpecs: List<NotarySpec> = defaultParameters.notarySpecs) {
|
||||
private val notarySpecs: List<NotarySpec> = defaultParameters.notarySpecs,
|
||||
maxTransactionSize: Int = Int.MAX_VALUE) {
|
||||
/** Helper constructor for creating a [MockNetwork] with custom parameters from Java. */
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters)
|
||||
@ -228,7 +229,7 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
||||
filesystem.getPath("/nodes").createDirectory()
|
||||
val notaryInfos = generateNotaryIdentities()
|
||||
// The network parameters must be serialised before starting any of the nodes
|
||||
networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos))
|
||||
networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos, maxTransactionSize = maxTransactionSize))
|
||||
@Suppress("LeakingThis")
|
||||
notaryNodes = createNotaries()
|
||||
} catch (t: Throwable) {
|
||||
|
@ -6,6 +6,7 @@ import net.corda.core.crypto.*
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.GlobalProperties
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.messaging.FlowHandle
|
||||
import net.corda.core.messaging.FlowProgressHandle
|
||||
@ -31,6 +32,7 @@ import net.corda.node.services.vault.NodeVaultService
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.DEV_ROOT_CA
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.services.MockAttachmentStorage
|
||||
|
@ -9,11 +9,13 @@ import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.GlobalProperties
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.serialization.internal.effectiveSerializationEnv
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.api.StartedNodeServices
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.core.chooseIdentity
|
||||
@ -34,6 +36,7 @@ fun ServiceHub.ledger(
|
||||
false
|
||||
}
|
||||
return LedgerDSL(TestLedgerDSLInterpreter(this), notary).apply {
|
||||
GlobalProperties.networkParameters = testNetworkParameters(emptyList())
|
||||
if (serializationExists) {
|
||||
script()
|
||||
} else {
|
||||
|
@ -14,6 +14,7 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.concurrent.*
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.NetworkMapCache
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.toFuture
|
||||
@ -37,7 +38,6 @@ import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
@ -87,7 +87,8 @@ class DriverDSLImpl(
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
val jmxPolicy: JmxPolicy,
|
||||
val notarySpecs: List<NotarySpec>,
|
||||
val compatibilityZone: CompatibilityZoneParams?
|
||||
val compatibilityZone: CompatibilityZoneParams?,
|
||||
val maxTransactionSize: Int
|
||||
) : InternalDriverDSL {
|
||||
private var _executorService: ScheduledExecutorService? = null
|
||||
val executorService get() = _executorService!!
|
||||
@ -699,7 +700,7 @@ class DriverDSLImpl(
|
||||
* The local version of the network map, which is a bunch of classes that copy the relevant files to the node directories.
|
||||
*/
|
||||
private inner class LocalNetworkMap(notaryInfos: List<NotaryInfo>) {
|
||||
val networkParametersCopier = NetworkParametersCopier(testNetworkParameters(notaryInfos))
|
||||
val networkParametersCopier = NetworkParametersCopier(testNetworkParameters(notaryInfos, maxTransactionSize = maxTransactionSize))
|
||||
// TODO: this object will copy NodeInfo files from started nodes to other nodes additional-node-infos/
|
||||
// This uses the FileSystem and adds a delay (~5 seconds) given by the time we wait before polling the file system.
|
||||
// Investigate whether we can avoid that.
|
||||
@ -955,6 +956,7 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
||||
notarySpecs: List<NotarySpec>,
|
||||
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
||||
jmxPolicy: JmxPolicy = JmxPolicy(),
|
||||
maxTransactionSize: Int,
|
||||
driverDslWrapper: (DriverDSLImpl) -> D,
|
||||
coerce: (D) -> DI, dsl: DI.() -> A
|
||||
): A {
|
||||
@ -972,7 +974,8 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
jmxPolicy = jmxPolicy,
|
||||
notarySpecs = notarySpecs,
|
||||
compatibilityZone = null
|
||||
compatibilityZone = null,
|
||||
maxTransactionSize = maxTransactionSize
|
||||
)
|
||||
)
|
||||
val shutdownHook = addShutdownHook(driverDsl::shutdown)
|
||||
@ -1014,6 +1017,7 @@ fun <A> internalDriver(
|
||||
notarySpecs: List<NotarySpec> = DriverParameters().notarySpecs,
|
||||
extraCordappPackagesToScan: List<String> = DriverParameters().extraCordappPackagesToScan,
|
||||
jmxPolicy: JmxPolicy = DriverParameters().jmxPolicy,
|
||||
maxTransactionSize: Int = DriverParameters().maxTransactionSize,
|
||||
compatibilityZone: CompatibilityZoneParams? = null,
|
||||
dsl: DriverDSLImpl.() -> A
|
||||
): A {
|
||||
@ -1030,7 +1034,8 @@ fun <A> internalDriver(
|
||||
notarySpecs = notarySpecs,
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
jmxPolicy = jmxPolicy,
|
||||
compatibilityZone = compatibilityZone
|
||||
compatibilityZone = compatibilityZone,
|
||||
maxTransactionSize = maxTransactionSize
|
||||
),
|
||||
coerce = { it },
|
||||
dsl = dsl,
|
||||
|
@ -106,8 +106,9 @@ fun <A> rpcDriver(
|
||||
notarySpecs: List<NotarySpec> = emptyList(),
|
||||
externalTrace: Trace? = null,
|
||||
jmxPolicy: JmxPolicy = JmxPolicy(),
|
||||
maxTransactionSize: Int = Int.MAX_VALUE,
|
||||
dsl: RPCDriverDSL.() -> A
|
||||
) : A {
|
||||
): A {
|
||||
return genericDriver(
|
||||
driverDsl = RPCDriverDSL(
|
||||
DriverDSLImpl(
|
||||
@ -122,7 +123,8 @@ fun <A> rpcDriver(
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
notarySpecs = notarySpecs,
|
||||
jmxPolicy = jmxPolicy,
|
||||
compatibilityZone = null
|
||||
compatibilityZone = null,
|
||||
maxTransactionSize = maxTransactionSize
|
||||
), externalTrace
|
||||
),
|
||||
coerce = { it },
|
||||
@ -434,7 +436,7 @@ data class RPCDriverDSL(
|
||||
minLargeMessageSize = MAX_MESSAGE_SIZE
|
||||
isUseGlobalPools = false
|
||||
}
|
||||
val rpcSecurityManager = RPCSecurityManagerImpl.fromUserList(users = listOf(InternalUser(rpcUser.username, rpcUser.password, rpcUser.permissions)) , id = AuthServiceId("TEST_SECURITY_MANAGER"))
|
||||
val rpcSecurityManager = RPCSecurityManagerImpl.fromUserList(users = listOf(InternalUser(rpcUser.username, rpcUser.password, rpcUser.permissions)), id = AuthServiceId("TEST_SECURITY_MANAGER"))
|
||||
val rpcServer = RPCServer(
|
||||
ops,
|
||||
rpcUser.username,
|
||||
|
@ -9,7 +9,8 @@ fun testNetworkParameters(
|
||||
minimumPlatformVersion: Int = 1,
|
||||
modifiedTime: Instant = Instant.now(),
|
||||
maxMessageSize: Int = 10485760,
|
||||
maxTransactionSize: Int = 40000,
|
||||
// TODO: Make this configurable and consistence across driver, bootstrapper, demobench and NetworkMapServer
|
||||
maxTransactionSize: Int = Int.MAX_VALUE,
|
||||
epoch: Int = 1
|
||||
): NetworkParameters {
|
||||
return NetworkParameters(
|
||||
|
@ -143,7 +143,7 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
|
||||
notaries = listOf(NotaryInfo(identity, config.nodeConfig.notary!!.validating)),
|
||||
modifiedTime = Instant.now(),
|
||||
maxMessageSize = 10485760,
|
||||
maxTransactionSize = 40000,
|
||||
maxTransactionSize = Int.MAX_VALUE,
|
||||
epoch = 1
|
||||
))
|
||||
notaryIdentity = identity
|
||||
|
@ -5,6 +5,7 @@ import com.typesafe.config.ConfigFactory
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.GlobalProperties
|
||||
import net.corda.core.internal.concurrent.OpenFuture
|
||||
import net.corda.core.internal.concurrent.doneFuture
|
||||
import net.corda.core.internal.concurrent.fork
|
||||
@ -22,6 +23,7 @@ import net.corda.nodeapi.VerifierApi
|
||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.driver.JmxPolicy
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
@ -61,6 +63,7 @@ fun <A> verifierDriver(
|
||||
extraCordappPackagesToScan: List<String> = emptyList(),
|
||||
notarySpecs: List<NotarySpec> = emptyList(),
|
||||
jmxPolicy: JmxPolicy = JmxPolicy(),
|
||||
maxTransactionSize: Int = Int.MAX_VALUE,
|
||||
dsl: VerifierDriverDSL.() -> A
|
||||
) = genericDriver(
|
||||
driverDsl = VerifierDriverDSL(
|
||||
@ -76,7 +79,8 @@ fun <A> verifierDriver(
|
||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||
notarySpecs = notarySpecs,
|
||||
jmxPolicy = jmxPolicy,
|
||||
compatibilityZone = null
|
||||
compatibilityZone = null,
|
||||
maxTransactionSize = maxTransactionSize
|
||||
)
|
||||
),
|
||||
coerce = { it },
|
||||
@ -162,6 +166,7 @@ data class VerifierDriverDSL(private val driverDSL: DriverDSLImpl) : InternalDri
|
||||
/** Starts a lightweight verification requestor that implements the Node's Verifier API */
|
||||
fun startVerificationRequestor(name: CordaX500Name): CordaFuture<VerificationRequestorHandle> {
|
||||
val hostAndPort = driverDSL.portAllocation.nextHostAndPort()
|
||||
GlobalProperties.networkParameters = testNetworkParameters(emptyList(), maxTransactionSize = driverDSL.maxTransactionSize)
|
||||
return driverDSL.executorService.fork {
|
||||
startVerificationRequestorInternal(name, hostAndPort)
|
||||
}
|
||||
@ -184,6 +189,7 @@ data class VerifierDriverDSL(private val driverDSL: DriverDSLImpl) : InternalDri
|
||||
val securityManager = object : ActiveMQSecurityManager {
|
||||
// We don't need auth, SSL is good enough
|
||||
override fun validateUser(user: String?, password: String?) = true
|
||||
|
||||
override fun validateUserAndRole(user: String?, password: String?, roles: MutableSet<Role>?, checkType: CheckType?) = true
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user