ENT-1584: Subscribe to private network maps using UUIDs (#2922)

Client private network map implementation

Add private network maps UUIDs to config as extraNetworkMapKeys. Adjust NetworkMapServer implementation accordingly.
Change NetworkMapUpdaterTest to use NetworkMapServer instead of mock
This commit is contained in:
Katarzyna Streich 2018-04-13 10:52:45 +01:00 committed by GitHub
parent 02913b284e
commit 91c52af5ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 136 additions and 81 deletions

View File

@ -108,6 +108,7 @@ private fun Config.getSingleValue(path: String, type: KType): Any? {
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()
@ -142,6 +143,7 @@ private fun Config.getCollectionValue(path: String, type: KType): Collection<Any
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) {
@ -191,7 +193,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<*>) {
@ -226,6 +228,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

@ -84,6 +84,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")
@ -289,6 +294,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

@ -278,7 +278,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

@ -53,6 +53,7 @@ interface NodeConfiguration : NodeSSLConfiguration {
val attachmentCacheBound: Long get() = defaultAttachmentCacheBound
// 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>
@ -144,6 +145,7 @@ data class NodeConfigurationImpl(
private val transactionCacheSizeMegaBytes: Int? = null,
private val attachmentContentCacheSizeMegaBytes: Int? = null,
override val attachmentCacheBound: Long = NodeConfiguration.defaultAttachmentCacheBound,
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

@ -21,6 +21,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 {
@ -43,13 +44,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

@ -18,9 +18,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
@ -29,7 +31,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()
@ -76,16 +79,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)
@ -100,7 +112,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

@ -25,6 +25,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 {
@ -84,11 +85,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

@ -2,41 +2,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
@ -46,24 +42,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
@ -91,11 +93,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)
@ -124,12 +124,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())
@ -168,7 +169,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(
@ -185,49 +186,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

@ -6,6 +6,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
@ -366,6 +367,7 @@ open class InternalMockNetwork(private val cordappPackages: List<String>,
doReturn(baseDirectory(id).createDirectories()).whenever(it).baseDirectory
doReturn(parameters.legalName ?: CordaX500Name("Mock Company $id", "London", "GB")).whenever(it).myLegalName
doReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties
doReturn(emptyList<SecureHash>()).whenever(it).extraNetworkMapKeys
parameters.configOverrides(it)
}
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version))

View File

@ -2,6 +2,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
@ -26,6 +27,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
@ -95,13 +97,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) }
@ -111,8 +120,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) {
@ -134,22 +146,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 {