mirror of
https://github.com/corda/corda.git
synced 2025-06-05 00:50:52 +00:00
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:
parent
3e124b0b48
commit
37c918a8f5
@ -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) {
|
||||||
|
@ -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() }
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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("#")
|
||||||
|
@ -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
|
||||||
|
@ -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())
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user