From c18bc8758ffb6117522efcc8635a51bcee8b2fce Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Fri, 9 Feb 2018 17:24:15 +0000 Subject: [PATCH] CORDA-961 Wire up and enforce max transaction size (#2465) (#2493) * 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 (cherry picked from commit c8cf46c) --- .ci/api-current.txt | 2 + .../net/corda/core/contracts/Attachment.kt | 5 + .../corda/core/internal/AbstractAttachment.kt | 4 + .../corda/core/internal/GlobalProperties.kt | 14 ++ .../net/corda/core/node/NetworkParameters.kt | 1 - .../core/transactions/WireTransaction.kt | 20 ++- .../corda/core/contracts/StructuresTests.kt | 1 + .../AttachmentSerializationTest.kt | 1 + docs/source/changelog.rst | 3 + docs/source/network-map.rst | 1 + .../net/corda/node/internal/AbstractNode.kt | 23 +-- .../kotlin/net/corda/node/internal/Node.kt | 1 + .../transactions/MaxTransactionSizeTests.kt | 135 ++++++++++++++++++ .../net/corda/test/spring/SpringDriver.kt | 2 + .../kotlin/net/corda/testing/driver/Driver.kt | 11 +- .../kotlin/net/corda/testing/node/MockNode.kt | 11 +- .../net/corda/testing/node/MockServices.kt | 2 + .../net/corda/testing/node/NodeTestUtils.kt | 3 + .../testing/node/internal/DriverDSLImpl.kt | 15 +- .../corda/testing/node/internal/RPCDriver.kt | 8 +- .../common/internal/ParametersUtilities.kt | 3 +- .../corda/demobench/model/NodeController.kt | 2 +- .../net/corda/verifier/VerifierDriver.kt | 8 +- 23 files changed, 245 insertions(+), 31 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/internal/GlobalProperties.kt create mode 100644 node/src/test/kotlin/net/corda/node/services/transactions/MaxTransactionSizeTests.kt diff --git a/.ci/api-current.txt b/.ci/api-current.txt index faac058fcc..0e705ec491 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -272,6 +272,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() ## @@ -339,6 +340,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() ## diff --git a/core/src/main/kotlin/net/corda/core/contracts/Attachment.kt b/core/src/main/kotlin/net/corda/core/contracts/Attachment.kt index 641e730417..654fd51a89 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/Attachment.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/Attachment.kt @@ -50,4 +50,9 @@ interface Attachment : NamedByHash { * Can be empty, for example non-contract attachments won't be necessarily be signed. */ val signers: List + + /** + * Attachment size in bytes. + */ + val size: Int } diff --git a/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt b/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt index 07c82039df..d48b98f290 100644 --- a/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt +++ b/core/src/main/kotlin/net/corda/core/internal/AbstractAttachment.kt @@ -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": diff --git a/core/src/main/kotlin/net/corda/core/internal/GlobalProperties.kt b/core/src/main/kotlin/net/corda/core/internal/GlobalProperties.kt new file mode 100644 index 0000000000..bedac0269a --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/internal/GlobalProperties.kt @@ -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 + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt b/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt index f7076a8316..cca7c1433b 100644 --- a/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt +++ b/core/src/main/kotlin/net/corda/core/node/NetworkParameters.kt @@ -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, diff --git a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt index 7112369667..d973152262 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt @@ -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, 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) } /** diff --git a/core/src/test/kotlin/net/corda/core/contracts/StructuresTests.kt b/core/src/test/kotlin/net/corda/core/contracts/StructuresTests.kt index 47f69945dd..7afd26a416 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/StructuresTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/StructuresTests.kt @@ -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() diff --git a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt index 81b70a6c2b..56959a0cec 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -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) { diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 1e38ca9fee..bccaf5c085 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -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 diff --git a/docs/source/network-map.rst b/docs/source/network-map.rst index 0c8da04e1f..7ec1d0e53c 100644 --- a/docs/source/network-map.rst +++ b/docs/source/network-map.rst @@ -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. diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 10a863648d..23d1f05709 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -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 @@ -121,7 +127,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() private val flowFactories = ConcurrentHashMap>, InitiatedFlowFactory<*>>() @@ -169,7 +174,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()) @@ -192,13 +197,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) @@ -237,7 +242,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 @@ -521,7 +526,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, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList { + private fun makeServices(keyPairs: Set, schemaService: SchemaService, transactionStorage: WritableTransactionStorage, database: CordaPersistence, info: NodeInfo, identityService: IdentityServiceInternal, networkMapCache: NetworkMapCacheInternal): MutableList { checkpointStorage = DBCheckpointStorage() val metrics = MetricRegistry() attachments = NodeAttachmentService(metrics, configuration.attachmentContentCacheSizeBytes, configuration.attachmentCacheBound) diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 8d5dbad49b..1911b89df9 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -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 diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/MaxTransactionSizeTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/MaxTransactionSizeTests.kt new file mode 100644 index 0000000000..7b6a077b8d --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/services/transactions/MaxTransactionSizeTests.kt @@ -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 { + 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 { + 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() { + @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() + } + } + + @InitiatedBy(SendLargeTransactionFlow::class) + @Suppress("UNUSED") + class ReceiveLargeTransactionFlow(private val otherSide: FlowSession) : FlowLogic() { + @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) + } + } +} \ No newline at end of file diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt index cba6ba4da3..4d05a61d85 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/test/spring/SpringDriver.kt @@ -29,6 +29,7 @@ fun springDriver( startNodesInProcess: Boolean = defaultParameters.startNodesInProcess, notarySpecs: List = defaultParameters.notarySpecs, extraCordappPackagesToScan: List = defaultParameters.extraCordappPackagesToScan, + maxTransactionSize: Int = defaultParameters.maxTransactionSize, dsl: SpringBootDriverDSL.() -> A ): A { return genericDriver( @@ -44,6 +45,7 @@ fun springDriver( extraCordappPackagesToScan = extraCordappPackagesToScan, notarySpecs = notarySpecs, driverDslWrapper = { driverDSL: DriverDSLImpl -> SpringBootDriverDSL(driverDSL) }, + maxTransactionSize = maxTransactionSize, coerce = { it }, dsl = dsl ) } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 16e6b0d88d..826a49f16c 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -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 driver( notarySpecs: List = defaultParameters.notarySpecs, extraCordappPackagesToScan: List = defaultParameters.extraCordappPackagesToScan, jmxPolicy: JmxPolicy = defaultParameters.jmxPolicy, + maxTransactionSize: Int = defaultParameters.maxTransactionSize, dsl: DriverDSL.() -> A ): A { return genericDriver( @@ -213,7 +214,8 @@ fun 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 = listOf(NotarySpec(DUMMY_NOTARY_NAME)), val extraCordappPackagesToScan: List = 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) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 1b68a4444a..5db5bb6fcd 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -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, servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory, initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, - private val notarySpecs: List = defaultParameters.notarySpecs) { + private val notarySpecs: List = defaultParameters.notarySpecs, + maxTransactionSize: Int = Int.MAX_VALUE) { /** Helper constructor for creating a [MockNetwork] with custom parameters from Java. */ @JvmOverloads constructor(cordappPackages: List, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters) @@ -228,7 +229,7 @@ open class MockNetwork(private val cordappPackages: List, 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) { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index 5d1eae0305..a33b51879e 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -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 diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt index 1980b11136..ecb1d3c462 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/NodeTestUtils.kt @@ -10,12 +10,14 @@ import net.corda.core.context.Origin 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.internal.FlowStateMachine 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 @@ -36,6 +38,7 @@ fun ServiceHub.ledger( false } return LedgerDSL(TestLedgerDSLInterpreter(this), notary).apply { + GlobalProperties.networkParameters = testNetworkParameters(emptyList()) if (serializationExists) { script() } else { diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index 46d5731e9a..bdd40ada5a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -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, val jmxPolicy: JmxPolicy, val notarySpecs: List, - 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) { - 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 genericDriver( notarySpecs: List, extraCordappPackagesToScan: List = defaultParameters.extraCordappPackagesToScan, jmxPolicy: JmxPolicy = JmxPolicy(), + maxTransactionSize: Int, driverDslWrapper: (DriverDSLImpl) -> D, coerce: (D) -> DI, dsl: DI.() -> A ): A { @@ -972,7 +974,8 @@ fun genericDriver( extraCordappPackagesToScan = extraCordappPackagesToScan, jmxPolicy = jmxPolicy, notarySpecs = notarySpecs, - compatibilityZone = null + compatibilityZone = null, + maxTransactionSize = maxTransactionSize ) ) val shutdownHook = addShutdownHook(driverDsl::shutdown) @@ -1014,6 +1017,7 @@ fun internalDriver( notarySpecs: List = DriverParameters().notarySpecs, extraCordappPackagesToScan: List = DriverParameters().extraCordappPackagesToScan, jmxPolicy: JmxPolicy = DriverParameters().jmxPolicy, + maxTransactionSize: Int = DriverParameters().maxTransactionSize, compatibilityZone: CompatibilityZoneParams? = null, dsl: DriverDSLImpl.() -> A ): A { @@ -1030,7 +1034,8 @@ fun internalDriver( notarySpecs = notarySpecs, extraCordappPackagesToScan = extraCordappPackagesToScan, jmxPolicy = jmxPolicy, - compatibilityZone = compatibilityZone + compatibilityZone = compatibilityZone, + maxTransactionSize = maxTransactionSize ), coerce = { it }, dsl = dsl, diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt index e41c1916ce..585f170fd3 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt @@ -106,8 +106,9 @@ fun rpcDriver( notarySpecs: List = 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 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, diff --git a/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt index b0215cf83c..ea089257c2 100644 --- a/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt +++ b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt @@ -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( diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt index 37faeae80a..1c53fdd82f 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt @@ -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 diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt index 791df21211..ad436e30d6 100644 --- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt +++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt @@ -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 verifierDriver( extraCordappPackagesToScan: List = emptyList(), notarySpecs: List = emptyList(), jmxPolicy: JmxPolicy = JmxPolicy(), + maxTransactionSize: Int = Int.MAX_VALUE, dsl: VerifierDriverDSL.() -> A ) = genericDriver( driverDsl = VerifierDriverDSL( @@ -76,7 +79,8 @@ fun 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 { 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?, checkType: CheckType?) = true }