Merge commit 'd8bf1019b6c7ddbe709cff7c730e66eb576f9ad5' into christians/merge-CORDA-1336

This commit is contained in:
Christian Sailer 2018-04-13 16:39:00 +01:00
commit a0bf0261bb
14 changed files with 158 additions and 84 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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