BFT tests no longer use NodeBasedTest (#917)

* Move Raft config to the interface
* Inline method only used in 1 test
This commit is contained in:
Andrzej Cichocki 2017-06-27 11:49:19 +01:00 committed by GitHub
parent 3e124b0b48
commit 37c918a8f5
10 changed files with 132 additions and 115 deletions

View File

@ -1,55 +1,63 @@
package net.corda.node.services package net.corda.node.services
import com.google.common.util.concurrent.Futures import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.ListenableFuture import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.* import net.corda.core.*
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.appendToCommonName
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.utilities.ALICE
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.flows.NotaryError import net.corda.flows.NotaryError
import net.corda.flows.NotaryException import net.corda.flows.NotaryException
import net.corda.flows.NotaryFlow import net.corda.flows.NotaryFlow
import net.corda.node.internal.AbstractNode import net.corda.node.internal.AbstractNode
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.BFTNonValidatingNotaryService import net.corda.node.services.transactions.BFTNonValidatingNotaryService
import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minClusterSize
import net.corda.node.services.transactions.minCorrectReplicas import net.corda.node.services.transactions.minCorrectReplicas
import net.corda.node.utilities.ServiceIdentityGenerator import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction
import net.corda.testing.node.NodeBasedTest import net.corda.testing.node.MockNetwork
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.junit.Ignore import org.junit.Ignore
import org.junit.After
import org.junit.Test import org.junit.Test
import java.nio.file.Files import java.nio.file.Files
import kotlin.test.* import kotlin.test.*
class BFTNotaryServiceTests : NodeBasedTest() { class BFTNotaryServiceTests {
companion object { companion object {
private val clusterName = X500Name("CN=BFT,O=R3,OU=corda,L=Zurich,C=CH") private val clusterName = X500Name("CN=BFT,O=R3,OU=corda,L=Zurich,C=CH")
private val serviceType = BFTNonValidatingNotaryService.type private val serviceType = BFTNonValidatingNotaryService.type
} }
private fun bftNotaryCluster(clusterSize: Int): ListenableFuture<Party> { private val mockNet = MockNetwork()
private val node = mockNet.createNode(advertisedServices = ServiceInfo(NetworkMapService.type))
@After
fun stopNodes() {
mockNet.stopNodes()
}
private fun bftNotaryCluster(clusterSize: Int): Party {
Files.deleteIfExists("config" / "currentView") // XXX: Make config object warn if this exists? Files.deleteIfExists("config" / "currentView") // XXX: Make config object warn if this exists?
val replicaIds = (0 until clusterSize) val replicaIds = (0 until clusterSize)
val replicaNames = replicaIds.map { DUMMY_NOTARY.name.appendToCommonName(" $it") }
val party = ServiceIdentityGenerator.generateToDisk( val party = ServiceIdentityGenerator.generateToDisk(
replicaNames.map { baseDirectory(it) }, replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
serviceType.id, serviceType.id,
clusterName) clusterName)
val advertisedServices = setOf(ServiceInfo(serviceType, clusterName)) val bftNotaryService = ServiceInfo(serviceType, clusterName)
val config = mapOf("notaryClusterAddresses" to replicaIds.map { "localhost:${11000 + it * 10}" }) val notaryClusterAddresses = replicaIds.map { HostAndPort.fromParts("localhost", 11000 + it * 10) }
return Futures.allAsList(replicaIds.map { replicaIds.forEach { replicaId ->
startNode( mockNet.createNode(
replicaNames[it], node.info.address,
advertisedServices = advertisedServices, advertisedServices = bftNotaryService,
configOverrides = mapOf("bftReplicaId" to it) + config configOverrides = {
) whenever(it.bftReplicaId).thenReturn(replicaId)
}).map { party } whenever(it.notaryClusterAddresses).thenReturn(notaryClusterAddresses)
})
}
return party
} }
@Test @Test
@ -66,9 +74,8 @@ class BFTNotaryServiceTests : NodeBasedTest() {
private fun detectDoubleSpend(faultyReplicas: Int) { private fun detectDoubleSpend(faultyReplicas: Int) {
val clusterSize = minClusterSize(faultyReplicas) val clusterSize = minClusterSize(faultyReplicas)
val aliceFuture = startNode(ALICE.name) val notary = bftNotaryCluster(clusterSize)
val notary = bftNotaryCluster(clusterSize).getOrThrow() node.run {
aliceFuture.getOrThrow().run {
val issueTx = signInitialTransaction(notary) { val issueTx = signInitialTransaction(notary) {
addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity)) addOutputState(DummyContract.SingleOwnerState(owner = info.legalIdentity))
} }
@ -83,6 +90,7 @@ class BFTNotaryServiceTests : NodeBasedTest() {
assertEquals(spendTxs.size, spendTxs.map { it.id }.distinct().size) assertEquals(spendTxs.size, spendTxs.map { it.id }.distinct().size)
val flows = spendTxs.map { NotaryFlow.Client(it) } val flows = spendTxs.map { NotaryFlow.Client(it) }
val stateMachines = flows.map { services.startFlow(it) } val stateMachines = flows.map { services.startFlow(it) }
mockNet.runNetwork()
val results = stateMachines.map { ErrorOr.catch { it.resultFuture.getOrThrow() } } val results = stateMachines.map { ErrorOr.catch { it.resultFuture.getOrThrow() } }
val successfulIndex = results.mapIndexedNotNull { index, result -> val successfulIndex = results.mapIndexedNotNull { index, result ->
if (result.error == null) { if (result.error == null) {

View File

@ -1,6 +1,7 @@
package net.corda.services.messaging package net.corda.services.messaging
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.crypto.X509Utilities import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.cert import net.corda.core.crypto.cert
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
@ -16,9 +17,9 @@ import net.corda.node.services.network.NetworkMapService.RegistrationRequest
import net.corda.node.services.network.NodeRegistration import net.corda.node.services.network.NodeRegistration
import net.corda.node.utilities.AddOrRemove import net.corda.node.utilities.AddOrRemove
import net.corda.testing.MOCK_VERSION_INFO import net.corda.testing.MOCK_VERSION_INFO
import net.corda.testing.TestNodeConfiguration
import net.corda.testing.node.NodeBasedTest import net.corda.testing.node.NodeBasedTest
import net.corda.testing.node.SimpleNode import net.corda.testing.node.SimpleNode
import net.corda.testing.testNodeConfiguration
import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
@ -57,10 +58,11 @@ class P2PSecurityTest : NodeBasedTest() {
private fun startSimpleNode(legalName: X500Name, private fun startSimpleNode(legalName: X500Name,
trustRoot: X509Certificate): SimpleNode { trustRoot: X509Certificate): SimpleNode {
val config = TestNodeConfiguration( val config = testNodeConfiguration(
baseDirectory = baseDirectory(legalName), baseDirectory = baseDirectory(legalName),
myLegalName = legalName, myLegalName = legalName).also {
networkMapService = NetworkMapInfo(networkMapNode.configuration.p2pAddress, networkMapNode.info.legalIdentity.name)) whenever(it.networkMapService).thenReturn(NetworkMapInfo(networkMapNode.configuration.p2pAddress, networkMapNode.info.legalIdentity.name))
}
config.configureWithDevSSLCertificate() // This creates the node's TLS cert with the CN as the legal name config.configureWithDevSSLCertificate() // This creates the node's TLS cert with the CN as the legal name
return SimpleNode(config, trustRoot = trustRoot).apply { start() } return SimpleNode(config, trustRoot = trustRoot).apply { start() }
} }

View File

@ -29,7 +29,6 @@ import net.corda.core.utilities.getTestPartyAndCertificate
import net.corda.flows.* import net.corda.flows.*
import net.corda.node.services.* import net.corda.node.services.*
import net.corda.node.services.api.* import net.corda.node.services.api.*
import net.corda.node.services.config.FullNodeConfiguration
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.config.configureWithDevSSLCertificate
import net.corda.node.services.database.HibernateConfiguration import net.corda.node.services.database.HibernateConfiguration
@ -686,7 +685,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
ValidatingNotaryService.type -> ValidatingNotaryService(timeWindowChecker, uniquenessProvider) ValidatingNotaryService.type -> ValidatingNotaryService(timeWindowChecker, uniquenessProvider)
RaftNonValidatingNotaryService.type -> RaftNonValidatingNotaryService(timeWindowChecker, uniquenessProvider as RaftUniquenessProvider) RaftNonValidatingNotaryService.type -> RaftNonValidatingNotaryService(timeWindowChecker, uniquenessProvider as RaftUniquenessProvider)
RaftValidatingNotaryService.type -> RaftValidatingNotaryService(timeWindowChecker, uniquenessProvider as RaftUniquenessProvider) RaftValidatingNotaryService.type -> RaftValidatingNotaryService(timeWindowChecker, uniquenessProvider as RaftUniquenessProvider)
BFTNonValidatingNotaryService.type -> with(configuration as FullNodeConfiguration) { BFTNonValidatingNotaryService.type -> with(configuration) {
val replicaId = bftReplicaId ?: throw IllegalArgumentException("bftReplicaId value must be specified in the configuration") val replicaId = bftReplicaId ?: throw IllegalArgumentException("bftReplicaId value must be specified in the configuration")
BFTSMaRtConfig(notaryClusterAddresses).use { config -> BFTSMaRtConfig(notaryClusterAddresses).use { config ->
BFTNonValidatingNotaryService(config, services, timeWindowChecker, replicaId, database).also { BFTNonValidatingNotaryService(config, services, timeWindowChecker, replicaId, database).also {

View File

@ -26,6 +26,9 @@ interface NodeConfiguration : NodeSSLConfiguration {
val certificateChainCheckPolicies: List<CertChainPolicyConfig> val certificateChainCheckPolicies: List<CertChainPolicyConfig>
val verifierType: VerifierType val verifierType: VerifierType
val messageRedeliveryDelaySeconds: Int val messageRedeliveryDelaySeconds: Int
val bftReplicaId: Int?
val notaryNodeAddress: HostAndPort?
val notaryClusterAddresses: List<HostAndPort>
} }
data class FullNodeConfiguration( data class FullNodeConfiguration(
@ -53,9 +56,9 @@ data class FullNodeConfiguration(
// Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one // Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one
val messagingServerAddress: HostAndPort?, val messagingServerAddress: HostAndPort?,
val extraAdvertisedServiceIds: List<String>, val extraAdvertisedServiceIds: List<String>,
val bftReplicaId: Int?, override val bftReplicaId: Int?,
val notaryNodeAddress: HostAndPort?, override val notaryNodeAddress: HostAndPort?,
val notaryClusterAddresses: List<HostAndPort>, override val notaryClusterAddresses: List<HostAndPort>,
override val certificateChainCheckPolicies: List<CertChainPolicyConfig>, override val certificateChainCheckPolicies: List<CertChainPolicyConfig>,
override val devMode: Boolean = false, override val devMode: Boolean = false,
val useTestClock: Boolean = false, val useTestClock: Boolean = false,

View File

@ -1,21 +1,43 @@
package net.corda.node.services.config package net.corda.node.services.config
import com.google.common.net.HostAndPort
import net.corda.core.crypto.commonName
import net.corda.core.utilities.ALICE import net.corda.core.utilities.ALICE
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.testing.testConfiguration import net.corda.testing.node.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.bouncycastle.asn1.x500.X500Name
import org.junit.Test import org.junit.Test
import java.net.URL
import java.nio.file.Paths import java.nio.file.Paths
class FullNodeConfigurationTest { class FullNodeConfigurationTest {
@Test @Test
fun `Artemis special characters not permitted in RPC usernames`() { fun `Artemis special characters not permitted in RPC usernames`() {
fun configWithRPCUsername(username: String): FullNodeConfiguration { val testConfiguration = FullNodeConfiguration(
return testConfiguration(Paths.get("."), ALICE.name, 0).copy( basedir = Paths.get("."),
rpcUsers = listOf(User(username, "pass", emptySet()))) myLegalName = ALICE.name,
} networkMapService = null,
emailAddress = "",
keyStorePassword = "cordacadevpass",
trustStorePassword = "trustpass",
dataSourceProperties = makeTestDataSourceProperties(ALICE.name.commonName),
certificateSigningService = URL("http://localhost"),
rpcUsers = emptyList(),
verifierType = VerifierType.InMemory,
useHTTPS = false,
p2pAddress = HostAndPort.fromParts("localhost", 0),
rpcAddress = HostAndPort.fromParts("localhost", 1),
messagingServerAddress = null,
extraAdvertisedServiceIds = emptyList(),
bftReplicaId = null,
notaryNodeAddress = null,
notaryClusterAddresses = emptyList(),
certificateChainCheckPolicies = emptyList(),
devMode = true)
fun configWithRPCUsername(username: String) {
testConfiguration.copy(rpcUsers = listOf(User(username, "pass", emptySet())))
}
assertThatThrownBy { configWithRPCUsername("user.1") }.hasMessageContaining(".") assertThatThrownBy { configWithRPCUsername("user.1") }.hasMessageContaining(".")
assertThatThrownBy { configWithRPCUsername("user*1") }.hasMessageContaining("*") assertThatThrownBy { configWithRPCUsername("user*1") }.hasMessageContaining("*")
assertThatThrownBy { configWithRPCUsername("user#1") }.hasMessageContaining("#") assertThatThrownBy { configWithRPCUsername("user#1") }.hasMessageContaining("#")

View File

@ -22,10 +22,10 @@ import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction
import net.corda.testing.MOCK_VERSION_INFO import net.corda.testing.MOCK_VERSION_INFO
import net.corda.testing.TestNodeConfiguration
import net.corda.testing.freeLocalHostAndPort import net.corda.testing.freeLocalHostAndPort
import net.corda.testing.freePort import net.corda.testing.freePort
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import net.corda.testing.testNodeConfiguration
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
@ -70,10 +70,9 @@ class ArtemisMessagingTests {
fun setUp() { fun setUp() {
val baseDirectory = temporaryFolder.root.toPath() val baseDirectory = temporaryFolder.root.toPath()
userService = RPCUserServiceImpl(emptyList()) userService = RPCUserServiceImpl(emptyList())
config = TestNodeConfiguration( config = testNodeConfiguration(
baseDirectory = baseDirectory, baseDirectory = baseDirectory,
myLegalName = ALICE.name, myLegalName = ALICE.name)
networkMapService = null)
LogHelper.setLevel(PersistentUniquenessProvider::class) LogHelper.setLevel(PersistentUniquenessProvider::class)
val dataSourceAndDatabase = configureDatabase(makeTestDataSourceProperties()) val dataSourceAndDatabase = configureDatabase(makeTestDataSourceProperties())
dataSource = dataSourceAndDatabase.first dataSource = dataSourceAndDatabase.first

View File

@ -7,8 +7,8 @@ import net.corda.core.crypto.*
import net.corda.core.exists import net.corda.core.exists
import net.corda.core.toTypedArray import net.corda.core.toTypedArray
import net.corda.core.utilities.ALICE import net.corda.core.utilities.ALICE
import net.corda.testing.TestNodeConfiguration
import net.corda.testing.getTestX509Name import net.corda.testing.getTestX509Name
import net.corda.testing.testNodeConfiguration
import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509CertificateHolder
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -38,10 +38,9 @@ class NetworkRegistrationHelperTest {
on { retrieveCertificates(eq(id)) }.then { certs } on { retrieveCertificates(eq(id)) }.then { certs }
} }
val config = TestNodeConfiguration( val config = testNodeConfiguration(
baseDirectory = tempFolder.root.toPath(), baseDirectory = tempFolder.root.toPath(),
myLegalName = ALICE.name, myLegalName = ALICE.name)
networkMapService = null)
assertFalse(config.nodeKeystore.exists()) assertFalse(config.nodeKeystore.exists())
assertFalse(config.sslKeystore.exists()) assertFalse(config.sslKeystore.exists())

View File

@ -20,11 +20,11 @@ import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.statemachine.StateMachineManager
import net.corda.node.services.transactions.SimpleNotaryService import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction
import net.corda.testing.TestNodeConfiguration
import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.InMemoryMessagingNetwork
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.TestClock import net.corda.testing.node.TestClock
import net.corda.testing.node.setTo import net.corda.testing.node.setTo
import net.corda.testing.testNodeConfiguration
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
@ -71,10 +71,9 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
val letter = 'A' + counter val letter = 'A' + counter
val (city, country) = bankLocations[counter++ % bankLocations.size] val (city, country) = bankLocations[counter++ % bankLocations.size]
val cfg = TestNodeConfiguration( val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory, baseDirectory = config.baseDirectory,
myLegalName = X500Name("CN=Bank $letter,O=Bank $letter,L=$city,C=$country"), myLegalName = X500Name("CN=Bank $letter,O=Bank $letter,L=$city,C=$country"))
networkMapService = null)
return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot)
} }
@ -93,10 +92,9 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
advertisedServices: Set<ServiceInfo>, id: Int, overrideServices: Map<ServiceInfo, KeyPair>?, advertisedServices: Set<ServiceInfo>, id: Int, overrideServices: Map<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): MockNetwork.MockNode { entropyRoot: BigInteger): MockNetwork.MockNode {
require(advertisedServices.containsType(NetworkMapService.type)) require(advertisedServices.containsType(NetworkMapService.type))
val cfg = TestNodeConfiguration( val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory, baseDirectory = config.baseDirectory,
myLegalName = DUMMY_MAP.name, myLegalName = DUMMY_MAP.name)
networkMapService = null)
return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) {} return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) {}
} }
} }
@ -106,10 +104,9 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
advertisedServices: Set<ServiceInfo>, id: Int, overrideServices: Map<ServiceInfo, KeyPair>?, advertisedServices: Set<ServiceInfo>, id: Int, overrideServices: Map<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): MockNetwork.MockNode { entropyRoot: BigInteger): MockNetwork.MockNode {
require(advertisedServices.containsType(SimpleNotaryService.type)) require(advertisedServices.containsType(SimpleNotaryService.type))
val cfg = TestNodeConfiguration( val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory, baseDirectory = config.baseDirectory,
myLegalName = DUMMY_NOTARY.name, myLegalName = DUMMY_NOTARY.name)
networkMapService = null)
return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot)
} }
} }
@ -122,10 +119,9 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
advertisedServices: Set<ServiceInfo>, id: Int, overrideServices: Map<ServiceInfo, KeyPair>?, advertisedServices: Set<ServiceInfo>, id: Int, overrideServices: Map<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): MockNetwork.MockNode { entropyRoot: BigInteger): MockNetwork.MockNode {
require(advertisedServices.containsType(NodeInterestRates.Oracle.type)) require(advertisedServices.containsType(NodeInterestRates.Oracle.type))
val cfg = TestNodeConfiguration( val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory, baseDirectory = config.baseDirectory,
myLegalName = RATES_SERVICE_NAME, myLegalName = RATES_SERVICE_NAME)
networkMapService = null)
return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) { return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) {
override fun start(): MockNetwork.MockNode { override fun start(): MockNetwork.MockNode {
super.start() super.start()
@ -146,10 +142,9 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceInfo>, id: Int, overrideServices: Map<ServiceInfo, KeyPair>?, advertisedServices: Set<ServiceInfo>, id: Int, overrideServices: Map<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): MockNetwork.MockNode { entropyRoot: BigInteger): MockNetwork.MockNode {
val cfg = TestNodeConfiguration( val cfg = testNodeConfiguration(
baseDirectory = config.baseDirectory, baseDirectory = config.baseDirectory,
myLegalName = DUMMY_REGULATOR.name, myLegalName = DUMMY_REGULATOR.name)
networkMapService = null)
return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) { return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) {
// TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request. // 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. // So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it.

View File

@ -4,7 +4,13 @@
package net.corda.testing package net.corda.testing
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.nhaarman.mockito_kotlin.spy
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.commonName
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
@ -14,10 +20,8 @@ import net.corda.core.node.services.IdentityService
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.* import net.corda.core.utilities.*
import net.corda.node.internal.NetworkMapInfo
import net.corda.node.services.config.* import net.corda.node.services.config.*
import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.nodeapi.User
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.config.SSLConfiguration
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
@ -29,7 +33,6 @@ import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
import java.util.*
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
/** /**
@ -142,47 +145,26 @@ fun getFreeLocalPorts(hostName: String, numberToAlloc: Int): List<HostAndPort> {
dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail
) = ledger { this.transaction(transactionLabel, transactionBuilder, dsl) } ) = ledger { this.transaction(transactionLabel, transactionBuilder, dsl) }
// TODO Replace this with testConfiguration fun testNodeConfiguration(
data class TestNodeConfiguration( baseDirectory: Path,
override val baseDirectory: Path, myLegalName: X500Name): NodeConfiguration {
override val myLegalName: X500Name, abstract class MockableNodeConfiguration : NodeConfiguration // Otherwise Mockito is defeated by val getters.
override val networkMapService: NetworkMapInfo?, val nc = spy<MockableNodeConfiguration>()
override val minimumPlatformVersion: Int = 1, whenever(nc.baseDirectory).thenReturn(baseDirectory)
override val keyStorePassword: String = "cordacadevpass", whenever(nc.myLegalName).thenReturn(myLegalName)
override val trustStorePassword: String = "trustpass", whenever(nc.minimumPlatformVersion).thenReturn(1)
override val rpcUsers: List<User> = emptyList(), whenever(nc.keyStorePassword).thenReturn("cordacadevpass")
override val dataSourceProperties: Properties = makeTestDataSourceProperties(myLegalName.commonName), whenever(nc.trustStorePassword).thenReturn("trustpass")
override val emailAddress: String = "", whenever(nc.rpcUsers).thenReturn(emptyList())
override val exportJMXto: String = "", whenever(nc.dataSourceProperties).thenReturn(makeTestDataSourceProperties(myLegalName.commonName))
override val devMode: Boolean = true, whenever(nc.emailAddress).thenReturn("")
override val certificateSigningService: URL = URL("http://localhost"), whenever(nc.exportJMXto).thenReturn("")
override val certificateChainCheckPolicies: List<CertChainPolicyConfig> = emptyList(), whenever(nc.devMode).thenReturn(true)
override val verifierType: VerifierType = VerifierType.InMemory, whenever(nc.certificateSigningService).thenReturn(URL("http://localhost"))
override val messageRedeliveryDelaySeconds: Int = 5) : NodeConfiguration { whenever(nc.certificateChainCheckPolicies).thenReturn(emptyList())
} whenever(nc.verifierType).thenReturn(VerifierType.InMemory)
whenever(nc.messageRedeliveryDelaySeconds).thenReturn(5)
fun testConfiguration(baseDirectory: Path, legalName: X500Name, basePort: Int): FullNodeConfiguration { return nc
return FullNodeConfiguration(
basedir = baseDirectory,
myLegalName = legalName,
networkMapService = null,
emailAddress = "",
keyStorePassword = "cordacadevpass",
trustStorePassword = "trustpass",
dataSourceProperties = makeTestDataSourceProperties(legalName.commonName),
certificateSigningService = URL("http://localhost"),
rpcUsers = emptyList(),
verifierType = VerifierType.InMemory,
useHTTPS = false,
p2pAddress = HostAndPort.fromParts("localhost", basePort),
rpcAddress = HostAndPort.fromParts("localhost", basePort + 1),
messagingServerAddress = null,
extraAdvertisedServiceIds = emptyList(),
bftReplicaId = null,
notaryNodeAddress = null,
notaryClusterAddresses = emptyList(),
certificateChainCheckPolicies = emptyList(),
devMode = true)
} }
@JvmOverloads @JvmOverloads

View File

@ -4,6 +4,7 @@ import com.google.common.jimfs.Configuration.unix
import com.google.common.jimfs.Jimfs import com.google.common.jimfs.Jimfs
import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.* import net.corda.core.*
import net.corda.core.crypto.CertificateAndKeyPair import net.corda.core.crypto.CertificateAndKeyPair
import net.corda.core.crypto.cert import net.corda.core.crypto.cert
@ -35,8 +36,8 @@ import net.corda.node.services.vault.NodeVaultService
import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import net.corda.testing.MOCK_VERSION_INFO import net.corda.testing.MOCK_VERSION_INFO
import net.corda.testing.TestNodeConfiguration
import net.corda.testing.getTestX509Name import net.corda.testing.getTestX509Name
import net.corda.testing.testNodeConfiguration
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.slf4j.Logger import org.slf4j.Logger
@ -66,7 +67,9 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy =
InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random(), InMemoryMessagingNetwork.ServicePeerAllocationStrategy.Random(),
private val defaultFactory: Factory = MockNetwork.DefaultFactory) { private val defaultFactory: Factory = MockNetwork.DefaultFactory) {
private var nextNodeId = 0 val nextNodeId
get() = _nextNodeId
private var _nextNodeId = 0
val filesystem: FileSystem = Jimfs.newFileSystem(unix()) val filesystem: FileSystem = Jimfs.newFileSystem(unix())
private val busyLatch: ReusableLatch = ReusableLatch() private val busyLatch: ReusableLatch = ReusableLatch()
val messagingNetwork = InMemoryMessagingNetwork(networkSendManuallyPumped, servicePeerAllocationStrategy, busyLatch) val messagingNetwork = InMemoryMessagingNetwork(networkSendManuallyPumped, servicePeerAllocationStrategy, busyLatch)
@ -275,24 +278,27 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
* @param overrideServices a set of service entries to use in place of the node's default service entries, * @param overrideServices 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. * 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, * @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. * 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(networkMapAddress: SingleMessageRecipient? = null, forcedID: Int = -1, nodeFactory: Factory = defaultFactory, fun createNode(networkMapAddress: SingleMessageRecipient? = null, forcedID: Int = -1, nodeFactory: Factory = defaultFactory,
start: Boolean = true, legalName: X500Name? = null, overrideServices: Map<ServiceInfo, KeyPair>? = null, start: Boolean = true, legalName: X500Name? = null, overrideServices: Map<ServiceInfo, KeyPair>? = null,
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()), entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
vararg advertisedServices: ServiceInfo): MockNode { vararg advertisedServices: ServiceInfo,
configOverrides: (NodeConfiguration) -> Any? = {}): MockNode {
val newNode = forcedID == -1 val newNode = forcedID == -1
val id = if (newNode) nextNodeId++ else forcedID val id = if (newNode) _nextNodeId++ else forcedID
val path = filesystem.getPath("/nodes/$id") val path = baseDirectory(id)
if (newNode) if (newNode)
(path / "attachments").createDirectories() (path / "attachments").createDirectories()
val config = TestNodeConfiguration( val config = testNodeConfiguration(
baseDirectory = path, baseDirectory = path,
myLegalName = legalName ?: getTestX509Name("Mock Company $id"), myLegalName = legalName ?: getTestX509Name("Mock Company $id")).also {
networkMapService = null, whenever(it.dataSourceProperties).thenReturn(makeTestDataSourceProperties("node_${id}_net_$networkId"))
dataSourceProperties = makeTestDataSourceProperties("node_${id}_net_$networkId")) configOverrides(it)
}
val node = nodeFactory.create(config, this, networkMapAddress, advertisedServices.toSet(), id, overrideServices, entropyRoot) val node = nodeFactory.create(config, this, networkMapAddress, advertisedServices.toSet(), id, overrideServices, entropyRoot)
if (start) { if (start) {
node.setup().start() node.setup().start()
@ -305,6 +311,8 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
return node return node
} }
fun baseDirectory(nodeId: Int) = filesystem.getPath("/nodes/$nodeId")
/** /**
* Asks every node in order to process any queued up inbound messages. This may in turn result in nodes * Asks every node in order to process any queued up inbound messages. This may in turn result in nodes
* sending more messages to each other, thus, a typical usage is to call runNetwork with the [rounds] * sending more messages to each other, thus, a typical usage is to call runNetwork with the [rounds]