From 927924498b017e6382208b2b80fe0e075ab35c7c Mon Sep 17 00:00:00 2001 From: Andrzej Cichocki Date: Tue, 24 Oct 2017 11:58:32 +0100 Subject: [PATCH] Introduce MockNodeParameters/Args (#1923) --- .../net/corda/core/flows/AttachmentTests.kt | 17 +-- .../AttachmentSerializationTest.kt | 13 +- .../contracts/asset/CashSelectionH2Test.kt | 7 +- .../node/services/BFTNotaryServiceTests.kt | 5 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 28 +--- .../services/network/NetworkMapCacheTest.kt | 5 +- .../statemachine/FlowFrameworkTests.kt | 21 ++- .../transactions/NotaryServiceTests.kt | 3 +- .../ValidatingNotaryServiceTests.kt | 3 +- .../vault/VaultSoftLockManagerTest.kt | 12 +- .../net/corda/netmap/simulation/Simulation.kt | 101 ++++--------- .../kotlin/net/corda/testing/NodeTestUtils.kt | 6 +- .../kotlin/net/corda/testing/node/MockNode.kt | 134 ++++++++---------- 13 files changed, 134 insertions(+), 221 deletions(-) diff --git a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt index a9ba82d71b..81e67b3b6e 100644 --- a/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt +++ b/core/src/test/kotlin/net/corda/core/flows/AttachmentTests.kt @@ -9,21 +9,19 @@ import net.corda.core.internal.FetchAttachmentsFlow import net.corda.core.internal.FetchDataFlow import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.persistence.NodeAttachmentService -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.ALICE import net.corda.testing.ALICE_NAME import net.corda.testing.BOB import net.corda.testing.node.MockNetwork +import net.corda.testing.node.MockNodeArgs +import net.corda.testing.node.MockNodeParameters import net.corda.testing.singleIdentity import org.junit.After import org.junit.Before import org.junit.Test import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream -import java.math.BigInteger -import java.security.KeyPair import java.util.jar.JarOutputStream import java.util.zip.ZipEntry import kotlin.test.assertEquals @@ -113,17 +111,14 @@ class AttachmentTests { @Test fun `malicious response`() { // Make a node that doesn't do sanity checking at load time. - val aliceNode = mockNet.createNotaryNode(legalName = ALICE.name, nodeFactory = object : MockNetwork.Factory { - override fun create(config: NodeConfiguration, network: MockNetwork, - id: Int, notaryIdentity: Pair?, - entropyRoot: BigInteger): MockNetwork.MockNode { - return object : MockNetwork.MockNode(config, network, id, notaryIdentity, entropyRoot) { + val aliceNode = mockNet.createNotaryNode(MockNodeParameters(legalName = ALICE.name), nodeFactory = object : MockNetwork.Factory { + override fun create(args: MockNodeArgs): MockNetwork.MockNode { + return object : MockNetwork.MockNode(args) { override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = false } } } }, validating = false) - val bobNode = mockNet.createNode(legalName = BOB.name) - + val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name)) mockNet.runNetwork() val alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME) 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 62d9b0bded..8057164bcf 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -13,19 +13,17 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.StartedNode -import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.persistence.NodeAttachmentService import net.corda.node.utilities.currentDBSession -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork +import net.corda.testing.node.MockNodeArgs +import net.corda.testing.node.MockNodeParameters import org.junit.After import org.junit.Before import org.junit.Test import java.io.ByteArrayOutputStream -import java.math.BigInteger import java.nio.charset.StandardCharsets.UTF_8 -import java.security.KeyPair import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream import kotlin.test.assertEquals @@ -158,10 +156,9 @@ class AttachmentSerializationTest { private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String { client.dispose() - client = mockNet.createNode(client.internals.id, object : MockNetwork.Factory { - override fun create(config: NodeConfiguration, network: MockNetwork, - id: Int, notaryIdentity: Pair?, entropyRoot: BigInteger): MockNetwork.MockNode { - return object : MockNetwork.MockNode(config, network, id, notaryIdentity, entropyRoot) { + client = mockNet.createNode(MockNodeParameters(client.internals.id), object : MockNetwork.Factory { + override fun create(args: MockNodeArgs): MockNetwork.MockNode { + return object : MockNetwork.MockNode(args) { override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad } } } diff --git a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashSelectionH2Test.kt b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashSelectionH2Test.kt index e0cb3297b5..2a69ac0631 100644 --- a/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashSelectionH2Test.kt +++ b/finance/src/test/kotlin/net/corda/finance/contracts/asset/CashSelectionH2Test.kt @@ -8,6 +8,7 @@ import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.schemas.CashSchemaV1 import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork +import net.corda.testing.node.MockNodeParameters import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test @@ -19,12 +20,10 @@ class CashSelectionH2Test { val mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance.contracts.asset", CashSchemaV1::class.packageName)) try { val notaryNode = mockNet.createNotaryNode() - val bankA = mockNet.createNode(configOverrides = { existingConfig -> + val bankA = mockNet.createNode(MockNodeParameters(configOverrides = { existingConfig -> // Tweak connections to be minimal to make this easier (1 results in a hung node during start up, so use 2 connections). existingConfig.dataSourceProperties.setProperty("maximumPoolSize", "2") - existingConfig - }) - + })) mockNet.startNodes() // Start more cash spends than we have connections. If spend leaks a connection on retry, we will run out of connections. diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index 735a926462..3605ab14a4 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -30,6 +30,7 @@ import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand import net.corda.testing.getDefaultNotary import net.corda.testing.node.MockNetwork +import net.corda.testing.node.MockNodeParameters import org.junit.After import org.junit.Test import java.nio.file.Paths @@ -57,10 +58,10 @@ class BFTNotaryServiceTests { clusterName) val clusterAddresses = replicaIds.map { NetworkHostAndPort("localhost", 11000 + it * 10) } replicaIds.forEach { replicaId -> - mockNet.createNode(configOverrides = { + mockNet.createNode(MockNodeParameters(configOverrides = { val notary = NotaryConfig(validating = false, bftSMaRt = BFTSMaRtConfiguration(replicaId, clusterAddresses, exposeRaces = exposeRaces)) doReturn(notary).whenever(it).notary - }) + })) } mockNet.runNetwork() // Exchange initial network map registration messages. } diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 89e4c49cf5..c5f893965e 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -34,17 +34,12 @@ import net.corda.finance.flows.TwoPartyTradeFlow.Buyer import net.corda.finance.flows.TwoPartyTradeFlow.Seller import net.corda.node.internal.StartedNode import net.corda.node.services.api.WritableTransactionStorage -import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.checkpoints import net.corda.node.utilities.CordaPersistence -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.* import net.corda.testing.contracts.fillWithSomeTestCash -import net.corda.testing.node.InMemoryMessagingNetwork -import net.corda.testing.node.MockNetwork -import net.corda.testing.node.MockServices -import net.corda.testing.node.pumpReceive +import net.corda.testing.node.* import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before @@ -54,8 +49,6 @@ import org.junit.runners.Parameterized import rx.Observable import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream -import java.math.BigInteger -import java.security.KeyPair import java.util.* import java.util.jar.JarOutputStream import java.util.zip.ZipEntry @@ -267,13 +260,7 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { // ... bring the node back up ... the act of constructing the SMM will re-register the message handlers // that Bob was waiting on before the reboot occurred. - bobNode = mockNet.createNode(bobAddr.id, object : MockNetwork.Factory { - override fun create(config: NodeConfiguration, network: MockNetwork, - id: Int, notaryIdentity: Pair?, entropyRoot: BigInteger): MockNetwork.MockNode { - return MockNetwork.MockNode(config, network, bobAddr.id, notaryIdentity, entropyRoot) - } - }, BOB_NAME) - + bobNode = mockNet.createNode(MockNodeParameters(bobAddr.id, BOB_NAME)) // Find the future representing the result of this state machine again. val bobFuture = bobNode.smm.findStateMachines(BuyerAcceptor::class.java).single().second @@ -307,19 +294,16 @@ class TwoPartyTradeFlowTests(val anonymous: Boolean) { // of gets and puts. private fun makeNodeWithTracking(name: CordaX500Name): StartedNode { // Create a node in the mock network ... - return mockNet.createNode(nodeFactory = object : MockNetwork.Factory { - override fun create(config: NodeConfiguration, - network: MockNetwork, - id: Int, notaryIdentity: Pair?, - entropyRoot: BigInteger): MockNetwork.MockNode { - return object : MockNetwork.MockNode(config, network, id, notaryIdentity, entropyRoot) { + return mockNet.createNode(MockNodeParameters(legalName = name), nodeFactory = object : MockNetwork.Factory { + override fun create(args: MockNodeArgs): MockNetwork.MockNode { + return object : MockNetwork.MockNode(args) { // That constructs a recording tx storage override fun makeTransactionStorage(): WritableTransactionStorage { return RecordingTransactionStorage(database, super.makeTransactionStorage()) } } } - }, legalName = name) + }) } @Test diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt index 9ec782a048..b493466645 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapCacheTest.kt @@ -5,6 +5,7 @@ import net.corda.testing.ALICE import net.corda.testing.BOB import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork +import net.corda.testing.node.MockNodeParameters import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Test @@ -22,12 +23,12 @@ class NetworkMapCacheTest { @Test fun `key collision`() { val entropy = BigInteger.valueOf(24012017L) - val aliceNode = mockNet.createNode(nodeFactory = MockNetwork.DefaultFactory, legalName = ALICE.name, entropyRoot = entropy) + val aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE.name, entropyRoot = entropy)) mockNet.runNetwork() // Node A currently knows only about itself, so this returns node A assertEquals(aliceNode.services.networkMapCache.getNodesByLegalIdentityKey(aliceNode.info.chooseIdentity().owningKey).singleOrNull(), aliceNode.info) - val bobNode = mockNet.createNode(nodeFactory = MockNetwork.DefaultFactory, legalName = BOB.name, entropyRoot = entropy) + val bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB.name, entropyRoot = entropy)) assertEquals(aliceNode.info.chooseIdentity(), bobNode.info.chooseIdentity()) aliceNode.services.networkMapCache.addNode(bobNode.info) diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 76b1ea9e3f..044c953422 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -33,6 +33,7 @@ import net.corda.testing.node.InMemoryMessagingNetwork.MessageTransfer import net.corda.testing.node.InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork.MockNode +import net.corda.testing.node.MockNodeParameters import net.corda.testing.node.pumpReceive import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy @@ -72,9 +73,8 @@ class FlowFrameworkTests { @Before fun start() { mockNet = MockNetwork(servicePeerAllocationStrategy = RoundRobin(), cordappPackages = listOf("net.corda.finance.contracts", "net.corda.testing.contracts")) - aliceNode = mockNet.createNode(legalName = ALICE_NAME) - bobNode = mockNet.createNode(legalName = BOB_NAME) - + aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)) + bobNode = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME)) mockNet.runNetwork() // We intentionally create our own notary and ignore the one provided by the network @@ -176,8 +176,7 @@ class FlowFrameworkTests { var sentCount = 0 mockNet.messagingNetwork.sentMessages.toSessionTransfers().filter { it.isPayloadTransfer }.forEach { sentCount++ } - - val charlieNode = mockNet.createNode(legalName = CHARLIE_NAME) + val charlieNode = mockNet.createNode(MockNodeParameters(legalName = CHARLIE_NAME)) val secondFlow = charlieNode.registerFlowFactory(PingPongFlow::class) { PingPongFlow(it, payload2) } mockNet.runNetwork() val charlie = charlieNode.info.singleIdentity() @@ -196,7 +195,7 @@ class FlowFrameworkTests { assertEquals(1, bobNode.checkpointStorage.checkpoints().size) // confirm checkpoint bobNode.services.networkMapCache.clearNetworkMapCache() } - val node2b = mockNet.createNode(bobNode.internals.id) + val node2b = mockNet.createNode(MockNodeParameters(bobNode.internals.id)) bobNode.internals.manuallyCloseDB() val (firstAgain, fut1) = node2b.getSingleFlow() // Run the network which will also fire up the second flow. First message should get deduped. So message data stays in sync. @@ -223,7 +222,7 @@ class FlowFrameworkTests { @Test fun `sending to multiple parties`() { - val charlieNode = mockNet.createNode(legalName = CHARLIE_NAME) + val charlieNode = mockNet.createNode(MockNodeParameters(legalName = CHARLIE_NAME)) mockNet.runNetwork() val charlie = charlieNode.info.singleIdentity() bobNode.registerFlowFactory(SendFlow::class) { InitiatedReceiveFlow(it).nonTerminating() } @@ -256,7 +255,7 @@ class FlowFrameworkTests { @Test fun `receiving from multiple parties`() { - val charlieNode = mockNet.createNode(legalName = CHARLIE_NAME) + val charlieNode = mockNet.createNode(MockNodeParameters(legalName = CHARLIE_NAME)) mockNet.runNetwork() val charlie = charlieNode.info.singleIdentity() val bobPayload = "Test 1" @@ -410,7 +409,7 @@ class FlowFrameworkTests { @Test fun `FlowException propagated in invocation chain`() { - val charlieNode = mockNet.createNode(legalName = CHARLIE_NAME) + val charlieNode = mockNet.createNode(MockNodeParameters(legalName = CHARLIE_NAME)) mockNet.runNetwork() val charlie = charlieNode.info.singleIdentity() @@ -425,7 +424,7 @@ class FlowFrameworkTests { @Test fun `FlowException thrown and there is a 3rd unrelated party flow`() { - val charlieNode = mockNet.createNode(legalName = CHARLIE_NAME) + val charlieNode = mockNet.createNode(MockNodeParameters(legalName = CHARLIE_NAME)) mockNet.runNetwork() val charlie = charlieNode.info.singleIdentity() @@ -674,7 +673,7 @@ class FlowFrameworkTests { private inline fun > StartedNode.restartAndGetRestoredFlow() = internals.run { disableDBCloseOnStop() // Handover DB to new node copy stop() - val newNode = mockNet.createNode(id) + val newNode = mockNet.createNode(MockNodeParameters(id)) newNode.internals.acceptableLiveFiberCountOnStop = 1 manuallyCloseDB() mockNet.runNetwork() // allow NetworkMapService messages to stabilise and thus start the state machine diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt index 87a5085cda..2510497167 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/NotaryServiceTests.kt @@ -17,6 +17,7 @@ import net.corda.node.services.api.StartedNodeServices import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork +import net.corda.testing.node.MockNodeParameters import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before @@ -37,7 +38,7 @@ class NotaryServiceTests { fun setup() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts")) val notaryNode = mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name, validating = false) - aliceServices = mockNet.createNode(legalName = ALICE_NAME).services + aliceServices = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)).services mockNet.runNetwork() // Clear network map registration messages notaryServices = notaryNode.services notary = notaryServices.getDefaultNotary() diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt index 139997f87b..1a1c74ea07 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/ValidatingNotaryServiceTests.kt @@ -18,6 +18,7 @@ import net.corda.node.services.issueInvalidState import net.corda.testing.* import net.corda.testing.contracts.DummyContract import net.corda.testing.node.MockNetwork +import net.corda.testing.node.MockNodeParameters import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before @@ -37,7 +38,7 @@ class ValidatingNotaryServiceTests { fun setup() { mockNet = MockNetwork(cordappPackages = listOf("net.corda.testing.contracts")) val notaryNode = mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name) - val aliceNode = mockNet.createNode(legalName = ALICE_NAME) + val aliceNode = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME)) mockNet.runNetwork() // Clear network map registration messages notaryServices = notaryNode.services aliceServices = aliceNode.services diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt index 29ebdc3c5e..ae1ac4da8f 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt @@ -25,15 +25,13 @@ import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.services.api.VaultServiceInternal -import net.corda.node.services.config.NodeConfiguration -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.chooseIdentity import net.corda.testing.node.MockNetwork import net.corda.testing.rigorousMock +import net.corda.testing.node.MockNodeArgs +import net.corda.testing.node.MockNodeParameters import org.junit.After import org.junit.Test -import java.math.BigInteger -import java.security.KeyPair import java.util.* import java.util.concurrent.atomic.AtomicBoolean import kotlin.reflect.jvm.jvmName @@ -72,7 +70,7 @@ class NodePair(private val mockNet: MockNetwork) { while (!serverRunning.get()) mockNet.runNetwork(1) if (rebootClient) { client.dispose() - client = mockNet.createNode(client.internals.id) + client = mockNet.createNode(MockNodeParameters(client.internals.id)) } return uncheckedCast(client.smm.allStateMachines.single().stateMachine) } @@ -83,8 +81,8 @@ class VaultSoftLockManagerTest { doNothing().whenever(it).softLockRelease(any(), anyOrNull()) } private val mockNet = MockNetwork(cordappPackages = listOf(ContractImpl::class.packageName), defaultFactory = object : MockNetwork.Factory { - override fun create(config: NodeConfiguration, network: MockNetwork, id: Int, notaryIdentity: Pair?, entropyRoot: BigInteger): MockNetwork.MockNode { - return object : MockNetwork.MockNode(config, network, id, notaryIdentity, entropyRoot) { + override fun create(args: MockNodeArgs): MockNetwork.MockNode { + return object : MockNetwork.MockNode(args) { override fun makeVaultService(keyManagementService: KeyManagementService, stateLoader: StateLoader): VaultServiceInternal { val realVault = super.makeVaultService(keyManagementService, stateLoader) return object : VaultServiceInternal by realVault { diff --git a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt index c16edfcf7c..157f93eb58 100644 --- a/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt +++ b/samples/network-visualiser/src/main/kotlin/net/corda/netmap/simulation/Simulation.kt @@ -1,5 +1,7 @@ package net.corda.netmap.simulation +import com.nhaarman.mockito_kotlin.doReturn +import com.nhaarman.mockito_kotlin.whenever import net.corda.core.flows.FlowLogic import net.corda.core.identity.CordaX500Name import net.corda.core.internal.uncheckedCast @@ -8,20 +10,14 @@ import net.corda.finance.utils.CityDatabase import net.corda.finance.utils.WorldMapLocation import net.corda.irs.api.NodeInterestRates import net.corda.node.internal.StartedNode -import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.statemachine.StateMachineManager -import net.corda.nodeapi.internal.ServiceInfo import net.corda.testing.DUMMY_NOTARY import net.corda.testing.DUMMY_REGULATOR -import net.corda.testing.node.InMemoryMessagingNetwork -import net.corda.testing.node.MockNetwork -import net.corda.testing.node.TestClock -import net.corda.testing.node.setTo -import net.corda.testing.testNodeConfiguration +import net.corda.testing.node.* +import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import rx.Observable import rx.subjects.PublishSubject import java.math.BigInteger -import java.security.KeyPair import java.time.LocalDate import java.time.LocalDateTime import java.time.ZoneOffset @@ -39,6 +35,13 @@ import java.util.concurrent.Future abstract class Simulation(val networkSendManuallyPumped: Boolean, runAsync: Boolean, latencyInjector: InMemoryMessagingNetwork.LatencyCalculator?) { + companion object { + private val defaultParams // The get() is necessary so that entropyRoot isn't shared. + get() = MockNodeParameters(configOverrides = { + doReturn(makeTestDataSourceProperties(it.myLegalName.organisation)).whenever(it).dataSourceProperties + }) + } + init { if (!runAsync && latencyInjector != null) throw IllegalArgumentException("The latency injector is only useful when using manual pumping.") @@ -47,63 +50,23 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, val bankLocations = listOf(Pair("London", "GB"), Pair("Frankfurt", "DE"), Pair("Rome", "IT")) // This puts together a mock network of SimulatedNodes. - - open class SimulatedNode(config: NodeConfiguration, mockNet: MockNetwork, - id: Int, notaryIdentity: Pair?, - entropyRoot: BigInteger) - : MockNetwork.MockNode(config, mockNet, id, notaryIdentity, entropyRoot) { + open class SimulatedNode(args: MockNodeArgs) : MockNetwork.MockNode(args) { override val started: StartedNode? get() = uncheckedCast(super.started) override fun findMyLocation(): WorldMapLocation? { return configuration.myLegalName.locality.let { CityDatabase[it] } } } - inner class BankFactory : MockNetwork.Factory { - var counter = 0 - - override fun create(config: NodeConfiguration, network: MockNetwork, - id: Int, notaryIdentity: Pair?, entropyRoot: BigInteger): SimulatedNode { - val letter = 'A' + counter - val (city, country) = bankLocations[counter++ % bankLocations.size] - - val cfg = testNodeConfiguration( - baseDirectory = config.baseDirectory, - myLegalName = CordaX500Name(organisation = "Bank $letter", locality = city, country = country)) - return SimulatedNode(cfg, network, id, notaryIdentity, entropyRoot) - } - - fun createAll(): List { - return bankLocations.mapIndexed { i, _ -> - // Use deterministic seeds so the simulation is stable. Needed so that party owning keys are stable. - mockNet.createUnstartedNode(nodeFactory = this, entropyRoot = BigInteger.valueOf(i.toLong())) - } - } - } - - val bankFactory = BankFactory() - - object NotaryNodeFactory : MockNetwork.Factory { - override fun create(config: NodeConfiguration, network: MockNetwork, - id: Int, notaryIdentity: Pair?, entropyRoot: BigInteger): SimulatedNode { - requireNotNull(config.notary) - val cfg = testNodeConfiguration( - baseDirectory = config.baseDirectory, - myLegalName = DUMMY_NOTARY.name, - notaryConfig = config.notary) - return SimulatedNode(cfg, network, id, notaryIdentity, entropyRoot) - } + private object SimulatedNodeFactory : MockNetwork.Factory { + override fun create(args: MockNodeArgs) = SimulatedNode(args) } object RatesOracleFactory : MockNetwork.Factory { // TODO: Make a more realistic legal name val RATES_SERVICE_NAME = CordaX500Name(organisation = "Rates Service Provider", locality = "Madrid", country = "ES") - override fun create(config: NodeConfiguration, network: MockNetwork, - id: Int, notaryIdentity: Pair?, entropyRoot: BigInteger): SimulatedNode { - val cfg = testNodeConfiguration( - baseDirectory = config.baseDirectory, - myLegalName = RATES_SERVICE_NAME) - return object : SimulatedNode(cfg, network, id, notaryIdentity, entropyRoot) { + override fun create(args: MockNodeArgs): SimulatedNode { + return object : SimulatedNode(args) { override fun start() = super.start().apply { registerInitiatedFlow(NodeInterestRates.FixQueryHandler::class.java) registerInitiatedFlow(NodeInterestRates.FixSignHandler::class.java) @@ -117,33 +80,23 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, } } - object RegulatorFactory : MockNetwork.Factory { - override fun create(config: NodeConfiguration, network: MockNetwork, - id: Int, notaryIdentity: Pair?, entropyRoot: BigInteger): SimulatedNode { - val cfg = testNodeConfiguration( - baseDirectory = config.baseDirectory, - myLegalName = DUMMY_REGULATOR.name) - return object : SimulatedNode(cfg, network, id, notaryIdentity, entropyRoot) { - // TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request. - // So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it. - // But that's fine for visualisation purposes. - } - } - } - val mockNet = MockNetwork( networkSendManuallyPumped = networkSendManuallyPumped, threadPerNode = runAsync, cordappPackages = listOf("net.corda.irs.contract", "net.corda.finance.contract", "net.corda.irs")) - // This one must come first. - val notary = mockNet.createNotaryNode(validating = false, nodeFactory = NotaryNodeFactory) - val regulators = listOf(mockNet.createUnstartedNode(nodeFactory = RegulatorFactory)) - val ratesOracle = mockNet.createUnstartedNode(nodeFactory = RatesOracleFactory) - + val notary = mockNet.createNotaryNode(defaultParams.copy(legalName = DUMMY_NOTARY.name), false, SimulatedNodeFactory) + // TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request. + // So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it. + // But that's fine for visualisation purposes. + val regulators = listOf(mockNet.createUnstartedNode(defaultParams.copy(legalName = DUMMY_REGULATOR.name), SimulatedNodeFactory)) + val ratesOracle = mockNet.createUnstartedNode(defaultParams.copy(legalName = RatesOracleFactory.RATES_SERVICE_NAME), RatesOracleFactory) // All nodes must be in one of these two lists for the purposes of the visualiser tool. val serviceProviders: List = listOf(notary.internals, ratesOracle) - val banks: List = bankFactory.createAll() - + val banks: List = bankLocations.mapIndexed { i, (city, country) -> + val legalName = CordaX500Name(organisation = "Bank ${'A' + i}", locality = city, country = country) + // Use deterministic seeds so the simulation is stable. Needed so that party owning keys are stable. + mockNet.createUnstartedNode(defaultParams.copy(legalName = legalName, entropyRoot = BigInteger.valueOf(i.toLong())), SimulatedNodeFactory) + } val clocks = (serviceProviders + regulators + banks).map { it.platformClock as TestClock } // These are used from the network visualiser tool. diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt index e4c680da42..768b7771c6 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/NodeTestUtils.kt @@ -10,7 +10,6 @@ import net.corda.core.node.ServiceHub import net.corda.core.transactions.TransactionBuilder import net.corda.node.services.config.CertChainPolicyConfig import net.corda.node.services.config.NodeConfiguration -import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.VerifierType import net.corda.nodeapi.User import net.corda.testing.node.MockServices @@ -57,8 +56,7 @@ fun transaction( fun testNodeConfiguration( baseDirectory: Path, - myLegalName: CordaX500Name, - notaryConfig: NotaryConfig? = null): NodeConfiguration { + myLegalName: CordaX500Name): NodeConfiguration { abstract class MockableNodeConfiguration : NodeConfiguration // Otherwise Mockito is defeated by val getters. return rigorousMock().also { doReturn(true).whenever(it).noNetworkMapServiceMode @@ -68,7 +66,7 @@ fun testNodeConfiguration( doReturn("cordacadevpass").whenever(it).keyStorePassword doReturn("trustpass").whenever(it).trustStorePassword doReturn(emptyList()).whenever(it).rpcUsers - doReturn(notaryConfig).whenever(it).notary + doReturn(null).whenever(it).notary doReturn(makeTestDataSourceProperties(myLegalName.organisation)).whenever(it).dataSourceProperties doReturn(makeTestDatabaseProperties()).whenever(it).database doReturn("").whenever(it).emailAddress 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 2c87d33a9b..cf9be80a2b 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 @@ -77,6 +77,34 @@ data class MockNetworkParameters( fun setCordappPackages(cordappPackages: List) = copy(cordappPackages = cordappPackages) } +/** + * @param notaryIdentity a set of service entries to use in place of the node's default service entries, + * for example where a node's service is part of a cluster. + * @param entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value, + * but can be overridden to cause nodes to have stable or colliding identity/service keys. + * @param configOverrides add/override behaviour of the [NodeConfiguration] mock object. + */ +@Suppress("unused") +data class MockNodeParameters( + val forcedID: Int? = null, + val legalName: CordaX500Name? = null, + val notaryIdentity: Pair? = null, + val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), + val configOverrides: (NodeConfiguration) -> Any? = {}) { + fun setForcedID(forcedID: Int?) = copy(forcedID = forcedID) + fun setLegalName(legalName: CordaX500Name?) = copy(legalName = legalName) + fun setNotaryIdentity(notaryIdentity: Pair?) = copy(notaryIdentity = notaryIdentity) + fun setEntropyRoot(entropyRoot: BigInteger) = copy(entropyRoot = entropyRoot) + fun setConfigOverrides(configOverrides: (NodeConfiguration) -> Any?) = copy(configOverrides = configOverrides) +} + +data class MockNodeArgs( + val config: NodeConfiguration, + val network: MockNetwork, + val id: Int, + val notaryIdentity: Pair?, + val entropyRoot: BigInteger) + /** * A mock node brings up a suite of in-memory services in a fast manner suitable for unit testing. * Components that do IO are either swapped out for mocks, or pointed to a [Jimfs] in memory filesystem or an in @@ -118,24 +146,11 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete /** Allows customisation of how nodes are created. */ interface Factory { - /** - * @param config the configuration of the node to be created - * @param network a reference to the [MockNetwork] owning the node. - * @param id a unique identifier for the node. - * @param notaryIdentity is an additional override to use in place of the node's default notary service, - * main usage is for when the node is part of a notary cluster. - * @param entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value, - * but can be overriden to cause nodes to have stable or colliding identity/service keys. - */ - fun create(config: NodeConfiguration, network: MockNetwork, id: Int, - notaryIdentity: Pair?, entropyRoot: BigInteger): N + fun create(args: MockNodeArgs): N } object DefaultFactory : Factory { - override fun create(config: NodeConfiguration, network: MockNetwork, - id: Int, notaryIdentity: Pair?, entropyRoot: BigInteger): MockNode { - return MockNode(config, network, id, notaryIdentity, entropyRoot) - } + override fun create(args: MockNodeArgs) = MockNode(args) } /** @@ -161,19 +176,17 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete } } - /** - * @param notaryIdentity is an additional override to use in place of the node's default notary service, - * main usage is for when the node is part of a notary cluster. - * @param entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value, - * but can be overriden to cause nodes to have stable or colliding identity/service keys. - */ - open class MockNode(config: NodeConfiguration, - val mockNet: MockNetwork, - val id: Int, - internal val notaryIdentity: Pair?, - val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue())) : - AbstractNode(config, TestClock(), MOCK_VERSION_INFO, CordappLoader.createDefaultWithTestPackages(config, mockNet.cordappPackages), mockNet.busyLatch) { + open class MockNode(args: MockNodeArgs) : AbstractNode( + args.config, + TestClock(), + MOCK_VERSION_INFO, + CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages), + args.network.busyLatch) { + val mockNet = args.network override val networkMapAddress = null + val id = args.id + internal val notaryIdentity = args.notaryIdentity + val entropyRoot = args.entropyRoot var counter = entropyRoot override val log: Logger = loggerFor() override val serverThread: AffinityExecutor = @@ -279,56 +292,28 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete } } - fun createUnstartedNode(forcedID: Int? = null, - legalName: CordaX500Name? = null, notaryIdentity: Pair? = null, - entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), - configOverrides: (NodeConfiguration) -> Any? = {}): MockNode { - return createUnstartedNode(forcedID, defaultFactory, legalName, notaryIdentity, entropyRoot, configOverrides = configOverrides) - } - - fun createUnstartedNode(forcedID: Int? = null, nodeFactory: Factory, - legalName: CordaX500Name? = null, notaryIdentity: Pair? = null, - entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), - configOverrides: (NodeConfiguration) -> Any? = {}): N { - return createNodeImpl(forcedID, nodeFactory, false, legalName, notaryIdentity, entropyRoot, configOverrides) - } - - /** - * Returns a node, optionally created by the passed factory method. - * @param notaryIdentity a set of service entries to use in place of the node's default service entries, - * for example where a node's service is part of a cluster. - * @param entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value, - * but can be overridden to cause nodes to have stable or colliding identity/service keys. - * @param configOverrides add/override behaviour of the [NodeConfiguration] mock object. - */ - fun createNode(forcedID: Int? = null, - legalName: CordaX500Name? = null, notaryIdentity: Pair? = null, - entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), - configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode { - return createNode(forcedID, defaultFactory, legalName, notaryIdentity, entropyRoot, configOverrides = configOverrides) + fun createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters()) = createUnstartedNode(parameters, defaultFactory) + fun createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: Factory): N { + return createNodeImpl(parameters, nodeFactory, false) } + fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedNode = createNode(parameters, defaultFactory) /** Like the other [createNode] but takes a [Factory] and propagates its [MockNode] subtype. */ - fun createNode(forcedID: Int? = null, nodeFactory: Factory, - legalName: CordaX500Name? = null, notaryIdentity: Pair? = null, - entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), - configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode { - return uncheckedCast(createNodeImpl(forcedID, nodeFactory, true, legalName, notaryIdentity, entropyRoot, configOverrides).started!! - .also { ensureAllNetworkMapCachesHaveAllNodeInfos() }) + fun createNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: Factory): StartedNode { + val node: StartedNode = uncheckedCast(createNodeImpl(parameters, nodeFactory, true).started)!! + ensureAllNetworkMapCachesHaveAllNodeInfos() + return node } - private fun createNodeImpl(forcedID: Int?, nodeFactory: Factory, - start: Boolean, legalName: CordaX500Name?, notaryIdentity: Pair?, - entropyRoot: BigInteger, - configOverrides: (NodeConfiguration) -> Any?): N { - val id = forcedID ?: nextNodeId++ + private fun createNodeImpl(parameters: MockNodeParameters, nodeFactory: Factory, start: Boolean): N { + val id = parameters.forcedID ?: nextNodeId++ val config = testNodeConfiguration( baseDirectory = baseDirectory(id).createDirectories(), - myLegalName = legalName ?: CordaX500Name(organisation = "Mock Company $id", locality = "London", country = "GB")).also { + myLegalName = parameters.legalName ?: CordaX500Name(organisation = "Mock Company $id", locality = "London", country = "GB")).also { doReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties - configOverrides(it) + parameters.configOverrides(it) } - return nodeFactory.create(config, this, id, notaryIdentity, entropyRoot).apply { + return nodeFactory.create(MockNodeArgs(config, this, id, parameters.notaryIdentity, parameters.entropyRoot)).apply { if (start) { start() if (threadPerNode) nodeReadyFuture.getOrThrow() // XXX: What about manually-started nodes? @@ -364,23 +349,24 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete @JvmOverloads fun createNotaryNode(legalName: CordaX500Name = DUMMY_NOTARY.name, validating: Boolean = true): StartedNode { - return createNode(legalName = legalName, configOverrides = { + return createNode(MockNodeParameters(legalName = legalName, configOverrides = { doReturn(NotaryConfig(validating)).whenever(it).notary - }) + })) } - fun createNotaryNode(legalName: CordaX500Name = DUMMY_NOTARY.name, + fun createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name), validating: Boolean = true, nodeFactory: Factory): StartedNode { - return createNode(legalName = legalName, nodeFactory = nodeFactory, configOverrides = { + return createNode(parameters.copy(configOverrides = { doReturn(NotaryConfig(validating)).whenever(it).notary - }) + parameters.configOverrides(it) + }), nodeFactory) } @JvmOverloads fun createPartyNode(legalName: CordaX500Name? = null, notaryIdentity: Pair? = null): StartedNode { - return createNode(legalName = legalName, notaryIdentity = notaryIdentity) + return createNode(MockNodeParameters(legalName = legalName, notaryIdentity = notaryIdentity)) } @Suppress("unused") // This is used from the network visualiser tool.