mirror of
https://github.com/corda/corda.git
synced 2024-12-27 08:22:35 +00:00
Merge commit 'd8bf1019b6c7ddbe709cff7c730e66eb576f9ad5' into christians/merge-CORDA-1336
This commit is contained in:
commit
a0bf0261bb
@ -1000,5 +1000,17 @@ object Crypto {
|
||||
} else {
|
||||
txId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to force registering all [Crypto]-related cryptography [Provider]s.
|
||||
* It is recommended that it is invoked first thing on `main` functions, so the [Provider]s are in place before any
|
||||
* cryptographic operation is requested outside [Crypto] (i.e., SecureRandom, KeyStore, cert-path validation,
|
||||
* CRL & CSR checks etc.).
|
||||
*/
|
||||
// TODO: perform all cryptographic operations via Crypto.
|
||||
@JvmStatic
|
||||
fun registerProviders() {
|
||||
providerMap
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ internal val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
|
||||
}
|
||||
internal val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply {
|
||||
require(name == "BCPQC") // The constant it comes from is not final.
|
||||
}.also {
|
||||
Security.addProvider(it)
|
||||
}
|
||||
// This map is required to defend against users that forcibly call Security.addProvider / Security.removeProvider
|
||||
// that could cause unexpected and suspicious behaviour.
|
||||
|
@ -75,7 +75,10 @@ class ArtemisTcpTransport {
|
||||
// TODO further investigate how to ensure we use a well defined wire level protocol for Node to Node communications.
|
||||
TransportConstants.PROTOCOLS_PROP_NAME to "CORE,AMQP",
|
||||
TransportConstants.USE_GLOBAL_WORKER_POOL_PROP_NAME to (nodeSerializationEnv != null),
|
||||
TransportConstants.REMOTING_THREADS_PROPNAME to (if (nodeSerializationEnv != null) -1 else 1)
|
||||
TransportConstants.REMOTING_THREADS_PROPNAME to (if (nodeSerializationEnv != null) -1 else 1),
|
||||
// turn off direct delivery in Artemis - this is latency optimisation that can lead to
|
||||
//hick-ups under high load (CORDA-1336)
|
||||
TransportConstants.DIRECT_DELIVER to false
|
||||
)
|
||||
|
||||
if (config != null && enableSSL) {
|
||||
|
@ -120,6 +120,7 @@ private fun Config.getSingleValue(path: String, type: KType, strict: Boolean = t
|
||||
NetworkHostAndPort::class -> NetworkHostAndPort.parse(getString(path))
|
||||
Path::class -> Paths.get(getString(path))
|
||||
URL::class -> URL(getString(path))
|
||||
UUID::class -> UUID.fromString(getString(path))
|
||||
CordaX500Name::class -> {
|
||||
when (getValue(path).valueType()) {
|
||||
ConfigValueType.OBJECT -> getConfig(path).parseAs(strict)
|
||||
@ -154,6 +155,7 @@ private fun Config.getCollectionValue(path: String, type: KType, strict: Boolean
|
||||
NetworkHostAndPort::class -> getStringList(path).map(NetworkHostAndPort.Companion::parse)
|
||||
Path::class -> getStringList(path).map { Paths.get(it) }
|
||||
URL::class -> getStringList(path).map(::URL)
|
||||
UUID::class -> getStringList(path).map { UUID.fromString(it) }
|
||||
CordaX500Name::class -> getStringList(path).map(CordaX500Name.Companion::parse)
|
||||
Properties::class -> getConfigList(path).map(Config::toProperties)
|
||||
else -> if (elementClass.java.isEnum) {
|
||||
@ -203,7 +205,7 @@ private fun Any.toConfigMap(): Map<String, Any> {
|
||||
val configValue = if (value is String || value is Boolean || value is Number) {
|
||||
// These types are supported by Config as use as is
|
||||
value
|
||||
} else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name || value is Path || value is URL) {
|
||||
} else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name || value is Path || value is URL || value is UUID) {
|
||||
// These types make sense to be represented as Strings and the exact inverse parsing function for use in parseAs
|
||||
value.toString()
|
||||
} else if (value is Enum<*>) {
|
||||
@ -238,6 +240,7 @@ private fun Iterable<*>.toConfigIterable(field: Field): Iterable<Any?> {
|
||||
NetworkHostAndPort::class.java -> map(Any?::toString)
|
||||
Path::class.java -> map(Any?::toString)
|
||||
URL::class.java -> map(Any?::toString)
|
||||
UUID::class.java -> map(Any?::toString)
|
||||
CordaX500Name::class.java -> map(Any?::toString)
|
||||
Properties::class.java -> map { ConfigFactory.parseMap(uncheckedCast(it)).root() }
|
||||
else -> if (elementType.isEnum) {
|
||||
|
@ -94,6 +94,11 @@ class ConfigParsingTest {
|
||||
testPropertyType<URLData, URLListData, URL>(URL("http://localhost:1234"), URL("http://localhost:1235"), valuesToString = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UUID`() {
|
||||
testPropertyType<UUIDData, UUIDListData, UUID>(UUID.randomUUID(), UUID.randomUUID(), valuesToString = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun CordaX500Name() {
|
||||
val name1 = CordaX500Name(organisation = "Mock Party", locality = "London", country = "GB")
|
||||
@ -299,6 +304,8 @@ class ConfigParsingTest {
|
||||
data class PathListData(override val values: List<Path>) : ListData<Path>
|
||||
data class URLData(override val value: URL) : SingleData<URL>
|
||||
data class URLListData(override val values: List<URL>) : ListData<URL>
|
||||
data class UUIDData(override val value: UUID) : SingleData<UUID>
|
||||
data class UUIDListData(override val values: List<UUID>) : ListData<UUID>
|
||||
data class CordaX500NameData(override val value: CordaX500Name) : SingleData<CordaX500Name>
|
||||
data class CordaX500NameListData(override val values: List<CordaX500Name>) : ListData<CordaX500Name>
|
||||
data class PropertiesData(override val value: Properties) : SingleData<Properties>
|
||||
|
@ -19,7 +19,9 @@ import kotlin.system.exitProcess
|
||||
import net.corda.node.internal.EnterpriseNode
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
Crypto.findProvider(CordaSecurityProvider.PROVIDER_NAME) // Install our SecureRandom before e.g. UUID asks for one.
|
||||
// Register all cryptography [Provider]s first thing on boot.
|
||||
// Required to install our [SecureRandom] before e.g., UUID asks for one.
|
||||
Crypto.registerProviders()
|
||||
// Pass the arguments to the Node factory. In the Enterprise edition, this line is modified to point to a subclass.
|
||||
// It will exit the process in case of startup failure and is not intended to be used by embedders. If you want
|
||||
// to embed Node in your own container, instantiate it directly and set up the configuration objects yourself.
|
||||
|
@ -284,7 +284,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
NodeInfoWatcher(configuration.baseDirectory, getRxIoScheduler(), Duration.ofMillis(configuration.additionalNodeInfoPollingFrequencyMsec)),
|
||||
networkMapClient,
|
||||
networkParameters.serialize().hash,
|
||||
configuration.baseDirectory)
|
||||
configuration.baseDirectory,
|
||||
configuration.extraNetworkMapKeys)
|
||||
runOnStop += networkMapUpdater::close
|
||||
|
||||
networkMapUpdater.subscribeToNetworkMap()
|
||||
|
@ -68,6 +68,7 @@ interface NodeConfiguration : NodeSSLConfiguration {
|
||||
|
||||
// do not change this value without syncing it with ScheduledFlowsDrainingModeTest
|
||||
val drainingModePollPeriod: Duration get() = Duration.ofSeconds(5)
|
||||
val extraNetworkMapKeys: List<UUID>
|
||||
|
||||
fun validate(): List<String>
|
||||
|
||||
@ -181,6 +182,7 @@ data class NodeConfigurationImpl(
|
||||
private val attachmentContentCacheSizeMegaBytes: Int? = null,
|
||||
override val attachmentCacheBound: Long = NodeConfiguration.defaultAttachmentCacheBound,
|
||||
override val graphiteOptions: GraphiteOptions? = null,
|
||||
override val extraNetworkMapKeys: List<UUID> = emptyList(),
|
||||
// do not use or remove (breaks DemoBench together with rejection of unknown configuration keys during parsing)
|
||||
private val h2port: Int = 0,
|
||||
// do not use or remove (used by Capsule)
|
||||
|
@ -31,6 +31,7 @@ import java.io.BufferedReader
|
||||
import java.net.URL
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
|
||||
class NetworkMapClient(compatibilityZoneURL: URL, val trustedRoot: X509Certificate) {
|
||||
companion object {
|
||||
@ -53,13 +54,14 @@ class NetworkMapClient(compatibilityZoneURL: URL, val trustedRoot: X509Certifica
|
||||
logger.trace { "Sent network parameters approval to $ackURL successfully." }
|
||||
}
|
||||
|
||||
fun getNetworkMap(): NetworkMapResponse {
|
||||
logger.trace { "Fetching network map update from $networkMapUrl." }
|
||||
val connection = networkMapUrl.openHttpConnection()
|
||||
fun getNetworkMap(networkMapKey: UUID? = null): NetworkMapResponse {
|
||||
val url = networkMapKey?.let { URL("$networkMapUrl/$networkMapKey") } ?: networkMapUrl
|
||||
logger.trace { "Fetching network map update from $url." }
|
||||
val connection = url.openHttpConnection()
|
||||
val signedNetworkMap = connection.responseAs<SignedNetworkMap>()
|
||||
val networkMap = signedNetworkMap.verifiedNetworkMapCert(trustedRoot)
|
||||
val timeout = connection.cacheControl.maxAgeSeconds().seconds
|
||||
logger.trace { "Fetched network map update from $networkMapUrl successfully: $networkMap" }
|
||||
logger.trace { "Fetched network map update from $url successfully: $networkMap" }
|
||||
return NetworkMapResponse(networkMap, timeout)
|
||||
}
|
||||
|
||||
|
@ -28,9 +28,11 @@ import net.corda.nodeapi.exceptions.OutdatedNetworkParameterHashException
|
||||
import net.corda.nodeapi.internal.network.*
|
||||
import rx.Subscription
|
||||
import rx.subjects.PublishSubject
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.system.exitProcess
|
||||
@ -39,7 +41,8 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
|
||||
private val fileWatcher: NodeInfoWatcher,
|
||||
private val networkMapClient: NetworkMapClient?,
|
||||
private val currentParametersHash: SecureHash,
|
||||
private val baseDirectory: Path
|
||||
private val baseDirectory: Path,
|
||||
private val extraNetworkMapKeys: List<UUID>
|
||||
) : AutoCloseable {
|
||||
companion object {
|
||||
private val logger = contextLogger()
|
||||
@ -86,16 +89,25 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
|
||||
}
|
||||
|
||||
private fun updateNetworkMapCache(networkMapClient: NetworkMapClient): Duration {
|
||||
val (networkMap, cacheTimeout) = networkMapClient.getNetworkMap()
|
||||
networkMap.parametersUpdate?.let { handleUpdateNetworkParameters(networkMapClient, it) }
|
||||
val (globalNetworkMap, cacheTimeout) = networkMapClient.getNetworkMap()
|
||||
globalNetworkMap.parametersUpdate?.let { handleUpdateNetworkParameters(networkMapClient, it) }
|
||||
val additionalHashes = extraNetworkMapKeys.flatMap {
|
||||
try {
|
||||
networkMapClient.getNetworkMap(it).payload.nodeInfoHashes
|
||||
} catch (e: Exception) {
|
||||
// Failure to retrieve one network map using UUID shouldn't stop the whole update.
|
||||
logger.warn("Error encountered when downloading network map with uuid '$it', skipping...", e)
|
||||
emptyList<SecureHash>()
|
||||
}
|
||||
}
|
||||
val allHashesFromNetworkMap = (globalNetworkMap.nodeInfoHashes + additionalHashes).toSet()
|
||||
|
||||
if (currentParametersHash != networkMap.networkParameterHash) {
|
||||
exitOnParametersMismatch(networkMap)
|
||||
if (currentParametersHash != globalNetworkMap.networkParameterHash) {
|
||||
exitOnParametersMismatch(globalNetworkMap)
|
||||
}
|
||||
|
||||
val currentNodeHashes = networkMapCache.allNodeHashes
|
||||
val hashesFromNetworkMap = networkMap.nodeInfoHashes
|
||||
(hashesFromNetworkMap - currentNodeHashes).mapNotNull {
|
||||
(allHashesFromNetworkMap - currentNodeHashes).mapNotNull {
|
||||
// Download new node info from network map
|
||||
try {
|
||||
networkMapClient.getNodeInfo(it)
|
||||
@ -110,7 +122,7 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
|
||||
}
|
||||
|
||||
// Remove node info from network map.
|
||||
(currentNodeHashes - hashesFromNetworkMap - fileWatcher.processedNodeInfoHashes)
|
||||
(currentNodeHashes - allHashesFromNetworkMap - fileWatcher.processedNodeInfoHashes)
|
||||
.mapNotNull(networkMapCache::getNodeByHash)
|
||||
.forEach(networkMapCache::removeNode)
|
||||
|
||||
|
@ -35,6 +35,7 @@ import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class NetworkMapClientTest {
|
||||
@ -94,11 +95,11 @@ class NetworkMapClientTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `download NetworkParameter correctly`() {
|
||||
fun `download NetworkParameters correctly`() {
|
||||
// The test server returns same network parameter for any hash.
|
||||
val parametersHash = server.networkParameters.serialize().hash
|
||||
val networkParameter = networkMapClient.getNetworkParameters(parametersHash).verified()
|
||||
assertEquals(server.networkParameters, networkParameter)
|
||||
val networkParameters = networkMapClient.getNetworkParameters(parametersHash).verified()
|
||||
assertEquals(server.networkParameters, networkParameters)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -12,41 +12,37 @@ package net.corda.node.services.network
|
||||
|
||||
import com.google.common.jimfs.Configuration.unix
|
||||
import com.google.common.jimfs.Jimfs
|
||||
import com.nhaarman.mockito_kotlin.any
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.nhaarman.mockito_kotlin.times
|
||||
import com.nhaarman.mockito_kotlin.verify
|
||||
import com.nhaarman.mockito_kotlin.*
|
||||
import net.corda.cordform.CordformNode.NODE_INFO_DIRECTORY
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.messaging.ParametersUpdateInfo
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.millis
|
||||
import net.corda.node.services.api.NetworkMapCacheInternal
|
||||
import net.corda.nodeapi.internal.NodeInfoAndSigned
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import net.corda.nodeapi.internal.createDevNetworkMapCa
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.network.*
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.core.expect
|
||||
import net.corda.testing.core.expectEvents
|
||||
import net.corda.testing.core.sequence
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.driver.PortAllocation
|
||||
import net.corda.testing.internal.DEV_ROOT_CA
|
||||
import net.corda.testing.internal.createNodeInfoAndSigned
|
||||
import net.corda.testing.node.internal.network.NetworkMapServer
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import rx.schedulers.TestScheduler
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.test.assertEquals
|
||||
@ -56,24 +52,30 @@ class NetworkMapUpdaterTest {
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule(true)
|
||||
|
||||
private val cacheExpiryMs = 1000
|
||||
private val privateNetUUID = UUID.randomUUID()
|
||||
private val fs = Jimfs.newFileSystem(unix())
|
||||
private val baseDir = fs.getPath("/node")
|
||||
private val networkMapCache = createMockNetworkMapCache()
|
||||
private val nodeInfoMap = ConcurrentHashMap<SecureHash, SignedNodeInfo>()
|
||||
private val networkParamsMap = HashMap<SecureHash, NetworkParameters>()
|
||||
private val networkMapCertAndKeyPair: CertificateAndKeyPair = createDevNetworkMapCa()
|
||||
private val cacheExpiryMs = 100
|
||||
private val networkMapClient = createMockNetworkMapClient()
|
||||
private val scheduler = TestScheduler()
|
||||
private val networkParametersHash = SecureHash.randomSHA256()
|
||||
private val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
||||
private val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient, networkParametersHash, baseDir)
|
||||
private var parametersUpdate: ParametersUpdate? = null
|
||||
private val networkMapCache = createMockNetworkMapCache()
|
||||
private lateinit var server: NetworkMapServer
|
||||
private lateinit var networkMapClient: NetworkMapClient
|
||||
private lateinit var updater: NetworkMapUpdater
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
server = NetworkMapServer(cacheExpiryMs.millis, PortAllocation.Incremental(10000).nextHostAndPort())
|
||||
val hostAndPort = server.start()
|
||||
networkMapClient = NetworkMapClient(URL("http://${hostAndPort.host}:${hostAndPort.port}"), DEV_ROOT_CA.certificate)
|
||||
updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient, server.networkParameters.serialize().hash, baseDir, listOf(privateNetUUID))
|
||||
}
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
updater.close()
|
||||
fs.close()
|
||||
server.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -101,11 +103,9 @@ class NetworkMapUpdaterTest {
|
||||
NodeInfoWatcher.saveToFile(baseDir / NODE_INFO_DIRECTORY, fileNodeInfoAndSigned)
|
||||
networkMapClient.publish(signedNodeInfo3)
|
||||
networkMapClient.publish(signedNodeInfo4)
|
||||
|
||||
scheduler.advanceTimeBy(10, TimeUnit.SECONDS)
|
||||
// TODO: Remove sleep in unit test.
|
||||
Thread.sleep(2L * cacheExpiryMs)
|
||||
|
||||
// 4 node info from network map, and 1 from file.
|
||||
verify(networkMapCache, times(5)).addNode(any())
|
||||
verify(networkMapCache, times(1)).addNode(nodeInfo3)
|
||||
@ -134,12 +134,13 @@ class NetworkMapUpdaterTest {
|
||||
Thread.sleep(2L * cacheExpiryMs)
|
||||
|
||||
// 4 node info from network map, and 1 from file.
|
||||
assertThat(nodeInfoMap).hasSize(4)
|
||||
verify(networkMapCache, times(5)).addNode(any())
|
||||
verify(networkMapCache, times(1)).addNode(fileNodeInfoAndSigned.nodeInfo)
|
||||
|
||||
// Test remove node.
|
||||
nodeInfoMap.clear()
|
||||
listOf(nodeInfo1, nodeInfo2, nodeInfo3, nodeInfo4).forEach {
|
||||
server.removeNodeInfo(it)
|
||||
}
|
||||
// TODO: Remove sleep in unit test.
|
||||
Thread.sleep(2L * cacheExpiryMs)
|
||||
verify(networkMapCache, times(4)).removeNode(any())
|
||||
@ -178,7 +179,7 @@ class NetworkMapUpdaterTest {
|
||||
assertEquals(null, snapshot)
|
||||
val newParameters = testNetworkParameters(epoch = 2)
|
||||
val updateDeadline = Instant.now().plus(1, ChronoUnit.DAYS)
|
||||
scheduleParametersUpdate(newParameters, "Test update", updateDeadline)
|
||||
server.scheduleParametersUpdate(newParameters, "Test update", updateDeadline)
|
||||
updater.subscribeToNetworkMap()
|
||||
updates.expectEvents(isStrict = false) {
|
||||
sequence(
|
||||
@ -195,49 +196,33 @@ class NetworkMapUpdaterTest {
|
||||
@Test
|
||||
fun `ack network parameters update`() {
|
||||
val newParameters = testNetworkParameters(epoch = 314)
|
||||
scheduleParametersUpdate(newParameters, "Test update", Instant.MIN)
|
||||
server.scheduleParametersUpdate(newParameters, "Test update", Instant.MIN)
|
||||
updater.subscribeToNetworkMap()
|
||||
// TODO: Remove sleep in unit test.
|
||||
Thread.sleep(2L * cacheExpiryMs)
|
||||
val newHash = newParameters.serialize().hash
|
||||
val keyPair = Crypto.generateKeyPair()
|
||||
updater.acceptNewNetworkParameters(newHash, { hash -> hash.serialize().sign(keyPair)})
|
||||
verify(networkMapClient).ackNetworkParametersUpdate(any())
|
||||
val updateFile = baseDir / NETWORK_PARAMS_UPDATE_FILE_NAME
|
||||
val signedNetworkParams = updateFile.readObject<SignedNetworkParameters>()
|
||||
val paramsFromFile = signedNetworkParams.verifiedNetworkMapCert(DEV_ROOT_CA.certificate)
|
||||
assertEquals(newParameters, paramsFromFile)
|
||||
}
|
||||
|
||||
private fun scheduleParametersUpdate(nextParameters: NetworkParameters, description: String, updateDeadline: Instant) {
|
||||
val nextParamsHash = nextParameters.serialize().hash
|
||||
networkParamsMap[nextParamsHash] = nextParameters
|
||||
parametersUpdate = ParametersUpdate(nextParamsHash, description, updateDeadline)
|
||||
}
|
||||
|
||||
private fun createMockNetworkMapClient(): NetworkMapClient {
|
||||
return mock {
|
||||
on { trustedRoot }.then {
|
||||
DEV_ROOT_CA.certificate
|
||||
}
|
||||
on { publish(any()) }.then {
|
||||
val signedNodeInfo: SignedNodeInfo = uncheckedCast(it.arguments[0])
|
||||
nodeInfoMap.put(signedNodeInfo.verified().serialize().hash, signedNodeInfo)
|
||||
}
|
||||
on { getNetworkMap() }.then {
|
||||
NetworkMapResponse(NetworkMap(nodeInfoMap.keys.toList(), networkParametersHash, parametersUpdate), cacheExpiryMs.millis)
|
||||
}
|
||||
on { getNodeInfo(any()) }.then {
|
||||
nodeInfoMap[it.arguments[0]]?.verified()
|
||||
}
|
||||
on { getNetworkParameters(any()) }.then {
|
||||
val paramsHash: SecureHash = uncheckedCast(it.arguments[0])
|
||||
networkParamsMap[paramsHash]?.let { networkMapCertAndKeyPair.sign(it) }
|
||||
}
|
||||
on { ackNetworkParametersUpdate(any()) }.then {
|
||||
Unit
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun `fetch nodes from private network`() {
|
||||
server.addNodesToPrivateNetwork(privateNetUUID, listOf(ALICE_NAME))
|
||||
Assertions.assertThatThrownBy { networkMapClient.getNetworkMap(privateNetUUID).payload.nodeInfoHashes }
|
||||
.isInstanceOf(IOException::class.java)
|
||||
.hasMessageContaining("Response Code 404")
|
||||
val (aliceInfo, signedAliceInfo) = createNodeInfoAndSigned(ALICE_NAME) // Goes to private network map
|
||||
val aliceHash = aliceInfo.serialize().hash
|
||||
val (bobInfo, signedBobInfo) = createNodeInfoAndSigned(BOB_NAME) // Goes to global network map
|
||||
networkMapClient.publish(signedAliceInfo)
|
||||
networkMapClient.publish(signedBobInfo)
|
||||
assertThat(networkMapClient.getNetworkMap().payload.nodeInfoHashes).containsExactly(bobInfo.serialize().hash)
|
||||
assertThat(networkMapClient.getNetworkMap(privateNetUUID).payload.nodeInfoHashes).containsExactly(aliceHash)
|
||||
assertEquals(aliceInfo, networkMapClient.getNodeInfo(aliceHash))
|
||||
}
|
||||
|
||||
private fun createMockNetworkMapCache(): NetworkMapCacheInternal {
|
||||
|
@ -16,6 +16,7 @@ import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
@ -377,6 +378,7 @@ open class InternalMockNetwork(private val cordappPackages: List<String>,
|
||||
doReturn(parameters.legalName ?: CordaX500Name("Mock Company $id", "London", "GB")).whenever(it).myLegalName
|
||||
doReturn(makeTestDataSourceProperties("node_$id","net_$networkId")).whenever(it).dataSourceProperties
|
||||
doReturn(makeTestDatabaseProperties("node_$id")).whenever(it).database
|
||||
doReturn(emptyList<SecureHash>()).whenever(it).extraNetworkMapKeys
|
||||
parameters.configOverrides(it)
|
||||
}
|
||||
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version))
|
||||
|
@ -12,6 +12,7 @@ package net.corda.testing.node.internal.network
|
||||
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.readObject
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NodeInfo
|
||||
@ -36,6 +37,7 @@ import java.security.PublicKey
|
||||
import java.security.SignatureException
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import javax.ws.rs.*
|
||||
import javax.ws.rs.core.MediaType
|
||||
import javax.ws.rs.core.Response
|
||||
@ -105,13 +107,20 @@ class NetworkMapServer(private val pollInterval: Duration,
|
||||
return service.latestAcceptedParametersMap[publicKey]
|
||||
}
|
||||
|
||||
fun addNodesToPrivateNetwork(networkUUID: UUID, nodesNames: List<CordaX500Name>) {
|
||||
service.addNodesToPrivateNetwork(networkUUID, nodesNames)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
server.stop()
|
||||
}
|
||||
|
||||
@Path("network-map")
|
||||
inner class InMemoryNetworkMapService {
|
||||
private val nodeNamesUUID = mutableMapOf<CordaX500Name, UUID>()
|
||||
private val nodeInfoMap = mutableMapOf<SecureHash, SignedNodeInfo>()
|
||||
// Mapping from the UUID of the network (null for global one) to hashes of the nodes in network
|
||||
private val networkMaps = mutableMapOf<UUID?, MutableSet<SecureHash>>()
|
||||
val latestAcceptedParametersMap = mutableMapOf<PublicKey, SecureHash>()
|
||||
private val signedNetParams by lazy { networkMapCertAndKeyPair.sign(networkParameters) }
|
||||
|
||||
@ -121,8 +130,11 @@ class NetworkMapServer(private val pollInterval: Duration,
|
||||
fun publishNodeInfo(input: InputStream): Response {
|
||||
return try {
|
||||
val signedNodeInfo = input.readObject<SignedNodeInfo>()
|
||||
signedNodeInfo.verified()
|
||||
nodeInfoMap[signedNodeInfo.raw.hash] = signedNodeInfo
|
||||
val hash = signedNodeInfo.raw.hash
|
||||
val nodeInfo = signedNodeInfo.verified()
|
||||
val privateNetwork = nodeNamesUUID[nodeInfo.legalIdentities[0].name]
|
||||
networkMaps.computeIfAbsent(privateNetwork, { mutableSetOf() }).add(hash)
|
||||
nodeInfoMap[hash] = signedNodeInfo
|
||||
ok()
|
||||
} catch (e: Exception) {
|
||||
when (e) {
|
||||
@ -144,22 +156,50 @@ class NetworkMapServer(private val pollInterval: Duration,
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||
fun getNetworkMap(): Response {
|
||||
val networkMap = NetworkMap(nodeInfoMap.keys.toList(), signedNetParams.raw.hash, parametersUpdate)
|
||||
fun getGlobalNetworkMap(): Response {
|
||||
val nodeInfoHashes = networkMaps[null]?.toList() ?: emptyList()
|
||||
return networkMapResponse(nodeInfoHashes)
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{var}")
|
||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||
fun getPrivateNetworkMap(@PathParam("var") extraUUID: String): Response {
|
||||
val uuid = UUID.fromString(extraUUID)
|
||||
val nodeInfoHashes = networkMaps[uuid] ?: return Response.status(Response.Status.NOT_FOUND).build()
|
||||
return networkMapResponse(nodeInfoHashes.toList())
|
||||
}
|
||||
|
||||
private fun networkMapResponse(nodeInfoHashes: List<SecureHash>): Response {
|
||||
val networkMap = NetworkMap(nodeInfoHashes, signedNetParams.raw.hash, parametersUpdate)
|
||||
val signedNetworkMap = networkMapCertAndKeyPair.sign(networkMap)
|
||||
return Response.ok(signedNetworkMap.serialize().bytes).header("Cache-Control", "max-age=${pollInterval.seconds}").build()
|
||||
}
|
||||
|
||||
// Remove nodeInfo for testing.
|
||||
fun removeNodeInfo(nodeInfo: NodeInfo) {
|
||||
nodeInfoMap.remove(nodeInfo.serialize().hash)
|
||||
val hash = nodeInfo.serialize().hash
|
||||
networkMaps.forEach {
|
||||
it.value.remove(hash)
|
||||
}
|
||||
nodeInfoMap.remove(hash)
|
||||
}
|
||||
|
||||
fun addNodesToPrivateNetwork(networkUUID: UUID, nodeNames: List<CordaX500Name>) {
|
||||
for (name in nodeNames) {
|
||||
check(name !in nodeInfoMap.values.flatMap { it.verified().legalIdentities.map { it.name } }) {
|
||||
throw IllegalArgumentException("Node with name: $name was already published to global network map")
|
||||
}
|
||||
}
|
||||
nodeNames.forEach { nodeNamesUUID[it] = networkUUID }
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("node-info/{var}")
|
||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||
fun getNodeInfo(@PathParam("var") nodeInfoHash: String): Response {
|
||||
val signedNodeInfo = nodeInfoMap[SecureHash.parse(nodeInfoHash)]
|
||||
val hash = SecureHash.parse(nodeInfoHash)
|
||||
val signedNodeInfo = nodeInfoMap[hash]
|
||||
return if (signedNodeInfo != null) {
|
||||
Response.ok(signedNodeInfo.serialize().bytes)
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user