Introduce MockNodeParameters/Args (#1923)

This commit is contained in:
Andrzej Cichocki 2017-10-24 11:58:32 +01:00 committed by GitHub
parent 7f6608deb5
commit 927924498b
13 changed files with 134 additions and 221 deletions

View File

@ -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<MockNetwork.MockNode> {
override fun create(config: NodeConfiguration, network: MockNetwork,
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
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<MockNetwork.MockNode> {
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)

View File

@ -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<MockNetwork.MockNode> {
override fun create(config: NodeConfiguration, network: MockNetwork,
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): MockNetwork.MockNode {
return object : MockNetwork.MockNode(config, network, id, notaryIdentity, entropyRoot) {
client = mockNet.createNode(MockNodeParameters(client.internals.id), object : MockNetwork.Factory<MockNetwork.MockNode> {
override fun create(args: MockNodeArgs): MockNetwork.MockNode {
return object : MockNetwork.MockNode(args) {
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
}
}

View File

@ -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.

View File

@ -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.
}

View File

@ -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<MockNetwork.MockNode> {
override fun create(config: NodeConfiguration, network: MockNetwork,
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, 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<MockNetwork.MockNode> {
// Create a node in the mock network ...
return mockNet.createNode(nodeFactory = object : MockNetwork.Factory<MockNetwork.MockNode> {
override fun create(config: NodeConfiguration,
network: MockNetwork,
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): MockNetwork.MockNode {
return object : MockNetwork.MockNode(config, network, id, notaryIdentity, entropyRoot) {
return mockNet.createNode(MockNodeParameters(legalName = name), nodeFactory = object : MockNetwork.Factory<MockNetwork.MockNode> {
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

View File

@ -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)

View File

@ -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<PingPongFlow>()
// 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 <reified P : FlowLogic<*>> StartedNode<MockNode>.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

View File

@ -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()

View File

@ -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

View File

@ -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<MockNetwork.MockNode> {
override fun create(config: NodeConfiguration, network: MockNetwork, id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, 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 {

View File

@ -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<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger)
: MockNetwork.MockNode(config, mockNet, id, notaryIdentity, entropyRoot) {
open class SimulatedNode(args: MockNodeArgs) : MockNetwork.MockNode(args) {
override val started: StartedNode<SimulatedNode>? get() = uncheckedCast(super.started)
override fun findMyLocation(): WorldMapLocation? {
return configuration.myLegalName.locality.let { CityDatabase[it] }
}
}
inner class BankFactory : MockNetwork.Factory<SimulatedNode> {
var counter = 0
override fun create(config: NodeConfiguration, network: MockNetwork,
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, 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<SimulatedNode> {
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<SimulatedNode> {
override fun create(config: NodeConfiguration, network: MockNetwork,
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, 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<SimulatedNode> {
override fun create(args: MockNodeArgs) = SimulatedNode(args)
}
object RatesOracleFactory : MockNetwork.Factory<SimulatedNode> {
// 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<ServiceInfo, KeyPair>?, 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<SimulatedNode> {
override fun create(config: NodeConfiguration, network: MockNetwork,
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, 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<SimulatedNode> = listOf(notary.internals, ratesOracle)
val banks: List<SimulatedNode> = bankFactory.createAll()
val banks: List<SimulatedNode> = 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.

View File

@ -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<MockableNodeConfiguration>().also {
doReturn(true).whenever(it).noNetworkMapServiceMode
@ -68,7 +66,7 @@ fun testNodeConfiguration(
doReturn("cordacadevpass").whenever(it).keyStorePassword
doReturn("trustpass").whenever(it).trustStorePassword
doReturn(emptyList<User>()).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

View File

@ -77,6 +77,34 @@ data class MockNetworkParameters(
fun setCordappPackages(cordappPackages: List<String>) = 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<ServiceInfo, KeyPair>? = 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<ServiceInfo, KeyPair>?) = 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<ServiceInfo, KeyPair>?,
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<out N : MockNode> {
/**
* @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<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): N
fun create(args: MockNodeArgs): N
}
object DefaultFactory : Factory<MockNode> {
override fun create(config: NodeConfiguration, network: MockNetwork,
id: Int, notaryIdentity: Pair<ServiceInfo, KeyPair>?, 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<ServiceInfo, KeyPair>?,
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<MockNode>()
override val serverThread: AffinityExecutor =
@ -279,56 +292,28 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
}
}
fun createUnstartedNode(forcedID: Int? = null,
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
configOverrides: (NodeConfiguration) -> Any? = {}): MockNode {
return createUnstartedNode(forcedID, defaultFactory, legalName, notaryIdentity, entropyRoot, configOverrides = configOverrides)
}
fun <N : MockNode> createUnstartedNode(forcedID: Int? = null, nodeFactory: Factory<N>,
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = 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<ServiceInfo, KeyPair>? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode<MockNode> {
return createNode(forcedID, defaultFactory, legalName, notaryIdentity, entropyRoot, configOverrides = configOverrides)
fun createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters()) = createUnstartedNode(parameters, defaultFactory)
fun <N : MockNode> createUnstartedNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: Factory<N>): N {
return createNodeImpl(parameters, nodeFactory, false)
}
fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedNode<MockNode> = createNode(parameters, defaultFactory)
/** Like the other [createNode] but takes a [Factory] and propagates its [MockNode] subtype. */
fun <N : MockNode> createNode(forcedID: Int? = null, nodeFactory: Factory<N>,
legalName: CordaX500Name? = null, notaryIdentity: Pair<ServiceInfo, KeyPair>? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
configOverrides: (NodeConfiguration) -> Any? = {}): StartedNode<N> {
return uncheckedCast(createNodeImpl(forcedID, nodeFactory, true, legalName, notaryIdentity, entropyRoot, configOverrides).started!!
.also { ensureAllNetworkMapCachesHaveAllNodeInfos() })
fun <N : MockNode> createNode(parameters: MockNodeParameters = MockNodeParameters(), nodeFactory: Factory<N>): StartedNode<N> {
val node: StartedNode<N> = uncheckedCast(createNodeImpl(parameters, nodeFactory, true).started)!!
ensureAllNetworkMapCachesHaveAllNodeInfos()
return node
}
private fun <N : MockNode> createNodeImpl(forcedID: Int?, nodeFactory: Factory<N>,
start: Boolean, legalName: CordaX500Name?, notaryIdentity: Pair<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger,
configOverrides: (NodeConfiguration) -> Any?): N {
val id = forcedID ?: nextNodeId++
private fun <N : MockNode> createNodeImpl(parameters: MockNodeParameters, nodeFactory: Factory<N>, 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<MockNode> {
return createNode(legalName = legalName, configOverrides = {
return createNode(MockNodeParameters(legalName = legalName, configOverrides = {
doReturn(NotaryConfig(validating)).whenever(it).notary
})
}))
}
fun <N : MockNode> createNotaryNode(legalName: CordaX500Name = DUMMY_NOTARY.name,
fun <N : MockNode> createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name),
validating: Boolean = true,
nodeFactory: Factory<N>): StartedNode<N> {
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<ServiceInfo, KeyPair>? = null): StartedNode<MockNode> {
return createNode(legalName = legalName, notaryIdentity = notaryIdentity)
return createNode(MockNodeParameters(legalName = legalName, notaryIdentity = notaryIdentity))
}
@Suppress("unused") // This is used from the network visualiser tool.