mirror of
https://github.com/corda/corda.git
synced 2025-02-09 20:31:18 +00:00
Wire part of network parameters (#2187)
* Take maximum message size from network parameters * Add epoch handling * Add handling of network parameters mismatch Change NetworkMapClient and updater, add handle in AbstractNode that results in node shutdown on parameters mismatch. Later on we should implement proper handling of parameters updates. Add tests of NetworkParameters wiring. When node starts with compatibilityZone url configured it takes networkParameters from the networkMap. * Permit only one network parameters file On node startup network parameters are read from node's base directory, we permit only zero or one files to be there. If network map server is configured the parameters can be downloaded at startup (if not present in the directory already). * Update docs on network map endpoints
This commit is contained in:
parent
90f6cd1fe7
commit
550469ea38
@ -2,7 +2,7 @@ Network Map
|
|||||||
===========
|
===========
|
||||||
|
|
||||||
The network map stores a collection of ``NodeInfo`` objects, each representing another node with which the node can interact.
|
The network map stores a collection of ``NodeInfo`` objects, each representing another node with which the node can interact.
|
||||||
There two sources from which a Corda node can retrieve ``NodeInfo`` objects:
|
There are two sources from which a Corda node can retrieve ``NodeInfo`` objects:
|
||||||
|
|
||||||
1. the REST protocol with the network map service, which also provides a publishing API,
|
1. the REST protocol with the network map service, which also provides a publishing API,
|
||||||
|
|
||||||
@ -25,7 +25,18 @@ Node side network map update protocol:
|
|||||||
|
|
||||||
* The Corda node will query the network map service periodically according to the ``Expires`` attribute in the HTTP header.
|
* The Corda node will query the network map service periodically according to the ``Expires`` attribute in the HTTP header.
|
||||||
|
|
||||||
* The network map service returns a signed ``NetworkMap`` object, containing list of node info hashes and the network parameters hashes.
|
* The network map service returns a signed ``NetworkMap`` object which looks as follows:
|
||||||
|
|
||||||
|
.. container:: codeset
|
||||||
|
|
||||||
|
.. sourcecode:: kotlin
|
||||||
|
|
||||||
|
data class NetworkMap {
|
||||||
|
val nodeInfoHashes: List<SecureHash>,
|
||||||
|
val networkParametersHash: SecureHash
|
||||||
|
}
|
||||||
|
|
||||||
|
The object contains list of node info hashes and hash of the network parameters data structure (without the signatures).
|
||||||
|
|
||||||
* The node updates its local copy of ``NodeInfos`` if it is different from the newly downloaded ``NetworkMap``.
|
* The node updates its local copy of ``NodeInfos`` if it is different from the newly downloaded ``NetworkMap``.
|
||||||
|
|
||||||
@ -34,13 +45,13 @@ Network Map service REST API:
|
|||||||
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
| Request method | Path | Description |
|
| Request method | Path | Description |
|
||||||
+================+===================================+========================================================================================================================================================+
|
+================+===================================+========================================================================================================================================================+
|
||||||
| POST | /api/network-map/publish | Publish new ``NodeInfo`` to the network map service, the legal identity in ``NodeInfo`` must match with the identity registered with the doorman. |
|
| POST | /network-map/publish | Publish new ``NodeInfo`` to the network map service, the legal identity in ``NodeInfo`` must match with the identity registered with the doorman. |
|
||||||
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
| GET | /api/network-map | Retrieve ``NetworkMap`` from the server, the ``NetworkMap`` object contains list of node info hashes and NetworkParameters hash. |
|
| GET | /network-map | Retrieve ``NetworkMap`` from the server, the ``NetworkMap`` object contains list of node info hashes and ``NetworkParameters`` hash. |
|
||||||
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
| GET | /api/network-map/node-info/{hash} | Retrieve ``NodeInfo`` object with the same hash. |
|
| GET | /network-map/node-info/{hash} | Retrieve ``NodeInfo`` object with the same hash. |
|
||||||
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
| GET | /api/network-map/parameters/{hash}| Retrieve ``NetworkParameters`` object with the same hash. |
|
| GET | /network-map/parameters/{hash} | Retrieve ``NetworkParameters`` object with the same hash. |
|
||||||
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
+----------------+-----------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
TODO: Access control of the network map will be added in the future.
|
TODO: Access control of the network map will be added in the future.
|
||||||
@ -55,3 +66,19 @@ Nodes expect to find a serialized ``SignedData<NodeInfo>`` object, the same obje
|
|||||||
Whenever a node starts it writes on disk a file containing its own ``NodeInfo``, this file is called ``nodeInfo-XXX`` where ``XXX`` is a long string.
|
Whenever a node starts it writes on disk a file containing its own ``NodeInfo``, this file is called ``nodeInfo-XXX`` where ``XXX`` is a long string.
|
||||||
|
|
||||||
Hence if an operator wants node A to see node B they can pick B's ``NodeInfo`` file from B base directory and drop it into A's ``additional-node-infos`` directory.
|
Hence if an operator wants node A to see node B they can pick B's ``NodeInfo`` file from B base directory and drop it into A's ``additional-node-infos`` directory.
|
||||||
|
|
||||||
|
|
||||||
|
Network parameters
|
||||||
|
------------------
|
||||||
|
Network parameters are constants that every node participating in the network needs to agree on and use for interop purposes.
|
||||||
|
The structure is distributed as a file containing serialized ``SignedData<NetworkParameters>`` with a signature from
|
||||||
|
a sub-key of the compatibility zone root cert. Network map advertises the hash of currently used network parameters.
|
||||||
|
The ``NetworkParameters`` structure contains:
|
||||||
|
* ``minimumPlatformVersion`` - minimum version of Corda platform that is required for nodes in the network.
|
||||||
|
* ``notaries`` - list of well known and trusted notary identities with information on validation type.
|
||||||
|
* ``maxMessageSize`` - maximum P2P message size sent over the wire in bytes.
|
||||||
|
* ``maxTransactionSize`` - maximum permitted transaction size in bytes.
|
||||||
|
* ``modifiedTime`` - the time the network parameters were created by the CZ operator.
|
||||||
|
* ``epoch`` - version number of the network parameters. Starting from 1, this will always increment on each new set of parameters.
|
||||||
|
|
||||||
|
The set of parameters is still under development and we may find the need to add additional fields.
|
||||||
|
@ -11,9 +11,9 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
|
|||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import java.security.cert.CertPathValidatorException
|
import java.security.cert.CertPathValidatorException
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Duration
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
|
const val NETWORK_PARAMS_FILE_NAME = "network-parameters"
|
||||||
// TODO: Need more discussion on rather we should move this class out of internal.
|
// TODO: Need more discussion on rather we should move this class out of internal.
|
||||||
/**
|
/**
|
||||||
* Data class containing hash of [NetworkParameters] and network participant's [NodeInfo] hashes.
|
* Data class containing hash of [NetworkParameters] and network participant's [NodeInfo] hashes.
|
||||||
@ -22,21 +22,21 @@ import java.time.Instant
|
|||||||
data class NetworkMap(val nodeInfoHashes: List<SecureHash>, val networkParameterHash: SecureHash)
|
data class NetworkMap(val nodeInfoHashes: List<SecureHash>, val networkParameterHash: SecureHash)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property minimumPlatformVersion
|
* @property minimumPlatformVersion Minimum version of Corda platform that is required for nodes in the network.
|
||||||
* @property notaries
|
* @property notaries List of well known and trusted notary identities with information on validation type.
|
||||||
* @property eventHorizon
|
|
||||||
* @property maxMessageSize Maximum P2P message sent over the wire in bytes.
|
* @property maxMessageSize Maximum P2P message sent over the wire in bytes.
|
||||||
* @property maxTransactionSize Maximum permitted transaction size in bytes.
|
* @property maxTransactionSize Maximum permitted transaction size in bytes.
|
||||||
* @property modifiedTime
|
* @property modifiedTime
|
||||||
* @property epoch Version number of the network parameters. Starting from 1, this will always increment on each new set
|
* @property epoch Version number of the network parameters. Starting from 1, this will always increment on each new set
|
||||||
* of parameters.
|
* of parameters.
|
||||||
*/
|
*/
|
||||||
// TODO Wire up the parameters
|
// TODO Add eventHorizon - how many days a node can be offline before being automatically ejected from the network.
|
||||||
|
// It needs separate design.
|
||||||
|
// TODO Currently maxTransactionSize is not wired.
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
data class NetworkParameters(
|
data class NetworkParameters(
|
||||||
val minimumPlatformVersion: Int,
|
val minimumPlatformVersion: Int,
|
||||||
val notaries: List<NotaryInfo>,
|
val notaries: List<NotaryInfo>,
|
||||||
val eventHorizon: Duration,
|
|
||||||
val maxMessageSize: Int,
|
val maxMessageSize: Int,
|
||||||
val maxTransactionSize: Int,
|
val maxTransactionSize: Int,
|
||||||
val modifiedTime: Instant,
|
val modifiedTime: Instant,
|
||||||
@ -46,6 +46,8 @@ data class NetworkParameters(
|
|||||||
require(minimumPlatformVersion > 0) { "minimumPlatformVersion must be at least 1" }
|
require(minimumPlatformVersion > 0) { "minimumPlatformVersion must be at least 1" }
|
||||||
require(notaries.distinctBy { it.identity } == notaries) { "Duplicate notary identities" }
|
require(notaries.distinctBy { it.identity } == notaries) { "Duplicate notary identities" }
|
||||||
require(epoch > 0) { "epoch must be at least 1" }
|
require(epoch > 0) { "epoch must be at least 1" }
|
||||||
|
require(maxMessageSize > 0) { "maxMessageSize must be at least 1" }
|
||||||
|
require(maxTransactionSize > 0) { "maxTransactionSize must be at least 1" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import net.corda.core.crypto.sign
|
|||||||
import net.corda.core.internal.copyTo
|
import net.corda.core.internal.copyTo
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.nodeapi.internal.NetworkParameters
|
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.nio.file.FileAlreadyExistsException
|
import java.nio.file.FileAlreadyExistsException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -24,7 +23,7 @@ class NetworkParametersCopier(networkParameters: NetworkParameters) {
|
|||||||
|
|
||||||
fun install(dir: Path) {
|
fun install(dir: Path) {
|
||||||
try {
|
try {
|
||||||
serializedNetworkParameters.open().copyTo(dir / "network-parameters")
|
serializedNetworkParameters.open().copyTo(dir / NETWORK_PARAMS_FILE_NAME)
|
||||||
} catch (e: FileAlreadyExistsException) {
|
} catch (e: FileAlreadyExistsException) {
|
||||||
// Leave the file untouched if it already exists
|
// Leave the file untouched if it already exists
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,6 @@ class NetworkParametersGenerator {
|
|||||||
minimumPlatformVersion = 1,
|
minimumPlatformVersion = 1,
|
||||||
notaries = notaryInfos,
|
notaries = notaryInfos,
|
||||||
modifiedTime = Instant.now(),
|
modifiedTime = Instant.now(),
|
||||||
eventHorizon = 10000.days,
|
|
||||||
maxMessageSize = 40000,
|
maxMessageSize = 40000,
|
||||||
maxTransactionSize = 40000,
|
maxTransactionSize = 40000,
|
||||||
epoch = 1
|
epoch = 1
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
package net.corda.node.services.network
|
package net.corda.node.services.network
|
||||||
|
|
||||||
|
import net.corda.core.crypto.SignedData
|
||||||
|
import net.corda.core.internal.readAll
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
|
import net.corda.core.serialization.deserialize
|
||||||
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
|
import net.corda.nodeapi.internal.NETWORK_PARAMS_FILE_NAME
|
||||||
|
import net.corda.nodeapi.internal.NetworkParameters
|
||||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
import net.corda.testing.node.internal.CompatibilityZoneParams
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.BOB_NAME
|
import net.corda.testing.BOB_NAME
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
@ -12,10 +19,17 @@ import net.corda.testing.node.network.NetworkMapServer
|
|||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
import java.nio.file.Files
|
||||||
|
import kotlin.streams.toList
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class NetworkMapTest {
|
class NetworkMapTest {
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
private val cacheTimeout = 1.seconds
|
private val cacheTimeout = 1.seconds
|
||||||
private val portAllocation = PortAllocation.Incremental(10000)
|
private val portAllocation = PortAllocation.Incremental(10000)
|
||||||
|
|
||||||
@ -34,9 +48,20 @@ class NetworkMapTest {
|
|||||||
networkMapServer.close()
|
networkMapServer.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `node correctly downloads and saves network parameters file on startup`() {
|
||||||
|
internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone, initialiseSerialization = false) {
|
||||||
|
val aliceDir = baseDirectory(ALICE_NAME)
|
||||||
|
startNode(providedName = ALICE_NAME).getOrThrow()
|
||||||
|
val networkParameters = Files.list(aliceDir).toList().single { NETWORK_PARAMS_FILE_NAME == it.fileName.toString() }
|
||||||
|
.readAll().deserialize<SignedData<NetworkParameters>>().verified()
|
||||||
|
assertEquals(NetworkMapServer.stubNetworkParameter, networkParameters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `nodes can see each other using the http network map`() {
|
fun `nodes can see each other using the http network map`() {
|
||||||
internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone) {
|
internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone, initialiseSerialization = false) {
|
||||||
val alice = startNode(providedName = ALICE_NAME)
|
val alice = startNode(providedName = ALICE_NAME)
|
||||||
val bob = startNode(providedName = BOB_NAME)
|
val bob = startNode(providedName = BOB_NAME)
|
||||||
val notaryNode = defaultNotaryNode.get()
|
val notaryNode = defaultNotaryNode.get()
|
||||||
@ -51,7 +76,7 @@ class NetworkMapTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `nodes process network map add updates correctly when adding new node to network map`() {
|
fun `nodes process network map add updates correctly when adding new node to network map`() {
|
||||||
internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone) {
|
internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone, initialiseSerialization = false) {
|
||||||
val alice = startNode(providedName = ALICE_NAME)
|
val alice = startNode(providedName = ALICE_NAME)
|
||||||
val notaryNode = defaultNotaryNode.get()
|
val notaryNode = defaultNotaryNode.get()
|
||||||
val aliceNode = alice.get()
|
val aliceNode = alice.get()
|
||||||
@ -72,7 +97,7 @@ class NetworkMapTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `nodes process network map remove updates correctly`() {
|
fun `nodes process network map remove updates correctly`() {
|
||||||
internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone) {
|
internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone, initialiseSerialization = false) {
|
||||||
val alice = startNode(providedName = ALICE_NAME)
|
val alice = startNode(providedName = ALICE_NAME)
|
||||||
val bob = startNode(providedName = BOB_NAME)
|
val bob = startNode(providedName = BOB_NAME)
|
||||||
val notaryNode = defaultNotaryNode.get()
|
val notaryNode = defaultNotaryNode.get()
|
||||||
|
@ -14,6 +14,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
|
|||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||||
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
import net.corda.testing.node.internal.CompatibilityZoneParams
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.node.internal.internalDriver
|
import net.corda.testing.node.internal.internalDriver
|
||||||
@ -24,6 +25,7 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
|||||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@ -38,6 +40,9 @@ import javax.ws.rs.core.MediaType
|
|||||||
import javax.ws.rs.core.Response
|
import javax.ws.rs.core.Response
|
||||||
|
|
||||||
class NodeRegistrationTest {
|
class NodeRegistrationTest {
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
private val portAllocation = PortAllocation.Incremental(13000)
|
private val portAllocation = PortAllocation.Incremental(13000)
|
||||||
private val rootCertAndKeyPair = createSelfKeyAndSelfSignedCertificate()
|
private val rootCertAndKeyPair = createSelfKeyAndSelfSignedCertificate()
|
||||||
private val registrationHandler = RegistrationHandler(rootCertAndKeyPair)
|
private val registrationHandler = RegistrationHandler(rootCertAndKeyPair)
|
||||||
@ -47,7 +52,7 @@ class NodeRegistrationTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun startServer() {
|
fun startServer() {
|
||||||
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), registrationHandler)
|
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), rootCertAndKeyPair, registrationHandler)
|
||||||
serverHostAndPort = server.start()
|
serverHostAndPort = server.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +69,8 @@ class NodeRegistrationTest {
|
|||||||
internalDriver(
|
internalDriver(
|
||||||
portAllocation = portAllocation,
|
portAllocation = portAllocation,
|
||||||
notarySpecs = emptyList(),
|
notarySpecs = emptyList(),
|
||||||
compatibilityZone = compatibilityZone
|
compatibilityZone = compatibilityZone,
|
||||||
|
initialiseSerialization = false
|
||||||
) {
|
) {
|
||||||
startNode(providedName = CordaX500Name("Alice", "London", "GB")).getOrThrow()
|
startNode(providedName = CordaX500Name("Alice", "London", "GB")).getOrThrow()
|
||||||
assertThat(registrationHandler.idsPolled).contains("Alice")
|
assertThat(registrationHandler.idsPolled).contains("Alice")
|
||||||
|
@ -57,6 +57,7 @@ import net.corda.node.services.vault.NodeVaultService
|
|||||||
import net.corda.node.services.vault.VaultSoftLockManager
|
import net.corda.node.services.vault.VaultSoftLockManager
|
||||||
import net.corda.node.shell.InteractiveShell
|
import net.corda.node.shell.InteractiveShell
|
||||||
import net.corda.node.utilities.AffinityExecutor
|
import net.corda.node.utilities.AffinityExecutor
|
||||||
|
import net.corda.nodeapi.internal.NETWORK_PARAMS_FILE_NAME
|
||||||
import net.corda.nodeapi.internal.NetworkParameters
|
import net.corda.nodeapi.internal.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
@ -69,6 +70,7 @@ import rx.Observable
|
|||||||
import rx.Scheduler
|
import rx.Scheduler
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
|
import java.nio.file.Files
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.KeyStoreException
|
import java.security.KeyStoreException
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -82,6 +84,7 @@ import java.util.concurrent.ExecutorService
|
|||||||
import java.util.concurrent.TimeUnit.SECONDS
|
import java.util.concurrent.TimeUnit.SECONDS
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.streams.toList
|
||||||
import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
|
import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -135,11 +138,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
protected lateinit var network: MessagingService
|
protected lateinit var network: MessagingService
|
||||||
protected val runOnStop = ArrayList<() -> Any?>()
|
protected val runOnStop = ArrayList<() -> Any?>()
|
||||||
protected val _nodeReadyFuture = openFuture<Unit>()
|
protected val _nodeReadyFuture = openFuture<Unit>()
|
||||||
protected val networkMapClient: NetworkMapClient? by lazy {
|
protected var networkMapClient: NetworkMapClient? = null
|
||||||
configuration.compatibilityZoneURL?.let {
|
|
||||||
NetworkMapClient(it, services.identityService.trustRoot)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lateinit var securityManager: RPCSecurityManager get
|
lateinit var securityManager: RPCSecurityManager get
|
||||||
|
|
||||||
@ -197,10 +196,13 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
check(started == null) { "Node has already been started" }
|
check(started == null) { "Node has already been started" }
|
||||||
log.info("Node starting up ...")
|
log.info("Node starting up ...")
|
||||||
initCertificate()
|
initCertificate()
|
||||||
readNetworkParameters()
|
|
||||||
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
|
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
|
||||||
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
||||||
val identityService = makeIdentityService(identity.certificate)
|
val identityService = makeIdentityService(identity.certificate)
|
||||||
|
networkMapClient = configuration.compatibilityZoneURL?.let {
|
||||||
|
NetworkMapClient(it, identityService.trustRoot)
|
||||||
|
}
|
||||||
|
readNetworkParameters()
|
||||||
// Do all of this in a database transaction so anything that might need a connection has one.
|
// Do all of this in a database transaction so anything that might need a connection has one.
|
||||||
val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database ->
|
val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database ->
|
||||||
val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries), identityService)
|
val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries), identityService)
|
||||||
@ -238,10 +240,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
startShell(rpcOps)
|
startShell(rpcOps)
|
||||||
Pair(StartedNodeImpl(this, _services, info, checkpointStorage, smm, attachments, network, database, rpcOps, flowStarter, notaryService), schedulerService)
|
Pair(StartedNodeImpl(this, _services, info, checkpointStorage, smm, attachments, network, database, rpcOps, flowStarter, notaryService), schedulerService)
|
||||||
}
|
}
|
||||||
|
|
||||||
val networkMapUpdater = NetworkMapUpdater(services.networkMapCache,
|
val networkMapUpdater = NetworkMapUpdater(services.networkMapCache,
|
||||||
NodeInfoWatcher(configuration.baseDirectory, getRxIoScheduler(), Duration.ofMillis(configuration.additionalNodeInfoPollingFrequencyMsec)),
|
NodeInfoWatcher(configuration.baseDirectory, getRxIoScheduler(), Duration.ofMillis(configuration.additionalNodeInfoPollingFrequencyMsec)),
|
||||||
networkMapClient)
|
networkMapClient,
|
||||||
|
networkParameters.serialize().hash)
|
||||||
runOnStop += networkMapUpdater::close
|
runOnStop += networkMapUpdater::close
|
||||||
|
|
||||||
networkMapUpdater.updateNodeInfo(services.myInfo) {
|
networkMapUpdater.updateNodeInfo(services.myInfo) {
|
||||||
@ -636,10 +638,28 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun readNetworkParameters() {
|
private fun readNetworkParameters() {
|
||||||
val file = configuration.baseDirectory / "network-parameters"
|
val files = Files.list(configuration.baseDirectory).filter { NETWORK_PARAMS_FILE_NAME == it.fileName.toString() }.toList()
|
||||||
networkParameters = file.readAll().deserialize<SignedData<NetworkParameters>>().verified()
|
val paramsFromFile = try {
|
||||||
log.info(networkParameters.toString())
|
// It's fine at this point if we don't have network parameters or have corrupted file, later we check if parameters can be downloaded from network map server.
|
||||||
check(networkParameters.minimumPlatformVersion <= versionInfo.platformVersion) { "Node is too old for the network" }
|
files[0].readAll().deserialize<SignedData<NetworkParameters>>().verified()
|
||||||
|
} catch (t: Exception) {
|
||||||
|
log.warn("Couldn't find correct network parameters file in the base directory")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
networkParameters = if (paramsFromFile != null) {
|
||||||
|
paramsFromFile
|
||||||
|
} else if (networkMapClient != null) {
|
||||||
|
log.info("Requesting network parameters from network map server...")
|
||||||
|
val (networkMap, _) = networkMapClient!!.getNetworkMap()
|
||||||
|
val signedParams = networkMapClient!!.getNetworkParameter(networkMap.networkParameterHash) ?: throw IllegalArgumentException("Failed loading network parameters from network map server")
|
||||||
|
val verifiedParams = signedParams.verified() // Verify before saving.
|
||||||
|
signedParams.serialize().open().copyTo(configuration.baseDirectory / NETWORK_PARAMS_FILE_NAME)
|
||||||
|
verifiedParams
|
||||||
|
} else {
|
||||||
|
throw IllegalArgumentException("Couldn't load network parameters file")
|
||||||
|
}
|
||||||
|
log.info("Loaded network parameters $networkParameters")
|
||||||
|
check(networkParameters.minimumPlatformVersion <= versionInfo.platformVersion) { "Node's platform version is lower than network's required minimumPlatformVersion" }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeCoreNotaryService(notaryConfig: NotaryConfig, database: CordaPersistence): NotaryService {
|
private fun makeCoreNotaryService(notaryConfig: NotaryConfig, database: CordaPersistence): NotaryService {
|
||||||
|
@ -144,9 +144,9 @@ open class Node(configuration: NodeConfiguration,
|
|||||||
val advertisedAddress = info.addresses.single()
|
val advertisedAddress = info.addresses.single()
|
||||||
|
|
||||||
printBasicNodeInfo("Incoming connection address", advertisedAddress.toString())
|
printBasicNodeInfo("Incoming connection address", advertisedAddress.toString())
|
||||||
rpcMessagingClient = RPCMessagingClient(configuration, serverAddress)
|
rpcMessagingClient = RPCMessagingClient(configuration, serverAddress, networkParameters.maxMessageSize)
|
||||||
verifierMessagingClient = when (configuration.verifierType) {
|
verifierMessagingClient = when (configuration.verifierType) {
|
||||||
VerifierType.OutOfProcess -> VerifierMessagingClient(configuration, serverAddress, services.monitoringService.metrics)
|
VerifierType.OutOfProcess -> VerifierMessagingClient(configuration, serverAddress, services.monitoringService.metrics, networkParameters.maxMessageSize)
|
||||||
VerifierType.InMemory -> null
|
VerifierType.InMemory -> null
|
||||||
}
|
}
|
||||||
return P2PMessagingClient(
|
return P2PMessagingClient(
|
||||||
@ -156,12 +156,13 @@ open class Node(configuration: NodeConfiguration,
|
|||||||
info.legalIdentities[0].owningKey,
|
info.legalIdentities[0].owningKey,
|
||||||
serverThread,
|
serverThread,
|
||||||
database,
|
database,
|
||||||
advertisedAddress)
|
advertisedAddress,
|
||||||
|
networkParameters.maxMessageSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeLocalMessageBroker(): NetworkHostAndPort {
|
private fun makeLocalMessageBroker(): NetworkHostAndPort {
|
||||||
with(configuration) {
|
with(configuration) {
|
||||||
messageBroker = ArtemisMessagingServer(this, p2pAddress.port, rpcAddress?.port, services.networkMapCache, securityManager)
|
messageBroker = ArtemisMessagingServer(this, p2pAddress.port, rpcAddress?.port, services.networkMapCache, securityManager, networkParameters.maxMessageSize)
|
||||||
return NetworkHostAndPort("localhost", p2pAddress.port)
|
return NetworkHostAndPort("localhost", p2pAddress.port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import net.corda.nodeapi.internal.config.SSLConfiguration
|
|||||||
import org.apache.activemq.artemis.api.core.client.*
|
import org.apache.activemq.artemis.api.core.client.*
|
||||||
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
|
import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE
|
||||||
|
|
||||||
class ArtemisMessagingClient(private val config: SSLConfiguration, private val serverAddress: NetworkHostAndPort) {
|
class ArtemisMessagingClient(private val config: SSLConfiguration, private val serverAddress: NetworkHostAndPort, private val maxMessageSize: Int) {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = loggerFor<ArtemisMessagingClient>()
|
private val log = loggerFor<ArtemisMessagingClient>()
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ class ArtemisMessagingClient(private val config: SSLConfiguration, private val s
|
|||||||
// would be the default and the two lines below can be deleted.
|
// would be the default and the two lines below can be deleted.
|
||||||
connectionTTL = -1
|
connectionTTL = -1
|
||||||
clientFailureCheckPeriod = -1
|
clientFailureCheckPeriod = -1
|
||||||
minLargeMessageSize = ArtemisMessagingServer.MAX_FILE_SIZE
|
minLargeMessageSize = maxMessageSize
|
||||||
isUseGlobalPools = nodeSerializationEnv != null
|
isUseGlobalPools = nodeSerializationEnv != null
|
||||||
}
|
}
|
||||||
val sessionFactory = locator.createSessionFactory()
|
val sessionFactory = locator.createSessionFactory()
|
||||||
|
@ -101,14 +101,11 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
|||||||
private val p2pPort: Int,
|
private val p2pPort: Int,
|
||||||
val rpcPort: Int?,
|
val rpcPort: Int?,
|
||||||
val networkMapCache: NetworkMapCache,
|
val networkMapCache: NetworkMapCache,
|
||||||
val securityManager: RPCSecurityManager) : SingletonSerializeAsToken() {
|
val securityManager: RPCSecurityManager,
|
||||||
|
val maxMessageSize: Int) : SingletonSerializeAsToken() {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
/** 10 MiB maximum allowed file size for attachments, including message headers. TODO: acquire this value from Network Map when supported. */
|
|
||||||
@JvmStatic
|
|
||||||
val MAX_FILE_SIZE = 10485760
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InnerState {
|
private class InnerState {
|
||||||
var running = false
|
var running = false
|
||||||
}
|
}
|
||||||
@ -181,9 +178,9 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
|||||||
idCacheSize = 2000 // Artemis Default duplicate cache size i.e. a guess
|
idCacheSize = 2000 // Artemis Default duplicate cache size i.e. a guess
|
||||||
isPersistIDCache = true
|
isPersistIDCache = true
|
||||||
isPopulateValidatedUser = true
|
isPopulateValidatedUser = true
|
||||||
journalBufferSize_NIO = MAX_FILE_SIZE // Artemis default is 490KiB - required to address IllegalArgumentException (when Artemis uses Java NIO): Record is too large to store.
|
journalBufferSize_NIO = maxMessageSize // Artemis default is 490KiB - required to address IllegalArgumentException (when Artemis uses Java NIO): Record is too large to store.
|
||||||
journalBufferSize_AIO = MAX_FILE_SIZE // Required to address IllegalArgumentException (when Artemis uses Linux Async IO): Record is too large to store.
|
journalBufferSize_AIO = maxMessageSize // Required to address IllegalArgumentException (when Artemis uses Linux Async IO): Record is too large to store.
|
||||||
journalFileSize = MAX_FILE_SIZE // The size of each journal file in bytes. Artemis default is 10MiB.
|
journalFileSize = maxMessageSize // The size of each journal file in bytes. Artemis default is 10MiB.
|
||||||
managementNotificationAddress = SimpleString(NOTIFICATIONS_ADDRESS)
|
managementNotificationAddress = SimpleString(NOTIFICATIONS_ADDRESS)
|
||||||
// Artemis allows multiple servers to be grouped together into a cluster for load balancing purposes. The cluster
|
// Artemis allows multiple servers to be grouped together into a cluster for load balancing purposes. The cluster
|
||||||
// user is used for connecting the nodes together. It has super-user privileges and so it's imperative that its
|
// user is used for connecting the nodes together. It has super-user privileges and so it's imperative that its
|
||||||
@ -211,7 +208,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
|||||||
)
|
)
|
||||||
addressesSettings = mapOf(
|
addressesSettings = mapOf(
|
||||||
"${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.#" to AddressSettings().apply {
|
"${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.#" to AddressSettings().apply {
|
||||||
maxSizeBytes = 10L * MAX_FILE_SIZE
|
maxSizeBytes = 10L * maxMessageSize
|
||||||
addressFullMessagePolicy = AddressFullMessagePolicy.FAIL
|
addressFullMessagePolicy = AddressFullMessagePolicy.FAIL
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -68,7 +68,8 @@ class P2PMessagingClient(config: NodeConfiguration,
|
|||||||
private val myIdentity: PublicKey,
|
private val myIdentity: PublicKey,
|
||||||
private val nodeExecutor: AffinityExecutor.ServiceAffinityExecutor,
|
private val nodeExecutor: AffinityExecutor.ServiceAffinityExecutor,
|
||||||
private val database: CordaPersistence,
|
private val database: CordaPersistence,
|
||||||
advertisedAddress: NetworkHostAndPort = serverAddress
|
advertisedAddress: NetworkHostAndPort = serverAddress,
|
||||||
|
private val maxMessageSize: Int
|
||||||
) : SingletonSerializeAsToken(), MessagingService {
|
) : SingletonSerializeAsToken(), MessagingService {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
@ -146,7 +147,7 @@ class P2PMessagingClient(config: NodeConfiguration,
|
|||||||
|
|
||||||
override val myAddress: SingleMessageRecipient = NodeAddress(myIdentity, advertisedAddress)
|
override val myAddress: SingleMessageRecipient = NodeAddress(myIdentity, advertisedAddress)
|
||||||
private val messageRedeliveryDelaySeconds = config.messageRedeliveryDelaySeconds.toLong()
|
private val messageRedeliveryDelaySeconds = config.messageRedeliveryDelaySeconds.toLong()
|
||||||
private val artemis = ArtemisMessagingClient(config, serverAddress)
|
private val artemis = ArtemisMessagingClient(config, serverAddress, maxMessageSize)
|
||||||
private val state = ThreadBox(InnerState())
|
private val state = ThreadBox(InnerState())
|
||||||
private val handlers = CopyOnWriteArrayList<Handler>()
|
private val handlers = CopyOnWriteArrayList<Handler>()
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ import net.corda.nodeapi.internal.crypto.getX509Certificate
|
|||||||
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||||
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
|
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
|
||||||
|
|
||||||
class RPCMessagingClient(private val config: SSLConfiguration, serverAddress: NetworkHostAndPort) : SingletonSerializeAsToken() {
|
class RPCMessagingClient(private val config: SSLConfiguration, serverAddress: NetworkHostAndPort, private val maxMessageSize: Int) : SingletonSerializeAsToken() {
|
||||||
private val artemis = ArtemisMessagingClient(config, serverAddress)
|
private val artemis = ArtemisMessagingClient(config, serverAddress, maxMessageSize)
|
||||||
private var rpcServer: RPCServer? = null
|
private var rpcServer: RPCServer? = null
|
||||||
|
|
||||||
fun start(rpcOps: RPCOps, securityManager: RPCSecurityManager) = synchronized(this) {
|
fun start(rpcOps: RPCOps, securityManager: RPCSecurityManager) = synchronized(this) {
|
||||||
|
@ -17,13 +17,13 @@ import org.apache.activemq.artemis.api.core.SimpleString
|
|||||||
import org.apache.activemq.artemis.api.core.client.*
|
import org.apache.activemq.artemis.api.core.client.*
|
||||||
import java.util.concurrent.*
|
import java.util.concurrent.*
|
||||||
|
|
||||||
class VerifierMessagingClient(config: SSLConfiguration, serverAddress: NetworkHostAndPort, metrics: MetricRegistry) : SingletonSerializeAsToken() {
|
class VerifierMessagingClient(config: SSLConfiguration, serverAddress: NetworkHostAndPort, metrics: MetricRegistry, private val maxMessageSize: Int) : SingletonSerializeAsToken() {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = loggerFor<VerifierMessagingClient>()
|
private val log = loggerFor<VerifierMessagingClient>()
|
||||||
private val verifierResponseAddress = "$VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX.${random63BitValue()}"
|
private val verifierResponseAddress = "$VERIFICATION_RESPONSES_QUEUE_NAME_PREFIX.${random63BitValue()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val artemis = ArtemisMessagingClient(config, serverAddress)
|
private val artemis = ArtemisMessagingClient(config, serverAddress, maxMessageSize)
|
||||||
/** An executor for sending messages */
|
/** An executor for sending messages */
|
||||||
private val messagingExecutor = AffinityExecutor.ServiceAffinityExecutor("Messaging", 1)
|
private val messagingExecutor = AffinityExecutor.ServiceAffinityExecutor("Messaging", 1)
|
||||||
private var verificationResponseConsumer: ClientConsumer? = null
|
private var verificationResponseConsumer: ClientConsumer? = null
|
||||||
|
@ -66,7 +66,7 @@ class NetworkMapClient(compatibilityZoneURL: URL, private val trustedRoot: X509C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNetworkParameter(networkParameterHash: SecureHash): NetworkParameters? {
|
fun getNetworkParameter(networkParameterHash: SecureHash): SignedData<NetworkParameters>? {
|
||||||
val conn = URL("$networkMapUrl/network-parameter/$networkParameterHash").openHttpConnection()
|
val conn = URL("$networkMapUrl/network-parameter/$networkParameterHash").openHttpConnection()
|
||||||
return if (conn.responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
|
return if (conn.responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||||
null
|
null
|
||||||
@ -85,7 +85,8 @@ data class NetworkMapResponse(val networkMap: NetworkMap, val cacheMaxAge: Durat
|
|||||||
|
|
||||||
class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
|
class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
|
||||||
private val fileWatcher: NodeInfoWatcher,
|
private val fileWatcher: NodeInfoWatcher,
|
||||||
private val networkMapClient: NetworkMapClient?) : Closeable {
|
private val networkMapClient: NetworkMapClient?,
|
||||||
|
private val currentParametersHash: SecureHash) : Closeable {
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = contextLogger()
|
private val logger = contextLogger()
|
||||||
private val retryInterval = 1.minutes
|
private val retryInterval = 1.minutes
|
||||||
@ -125,6 +126,12 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
|
|||||||
override fun run() {
|
override fun run() {
|
||||||
val nextScheduleDelay = try {
|
val nextScheduleDelay = try {
|
||||||
val (networkMap, cacheTimeout) = networkMapClient.getNetworkMap()
|
val (networkMap, cacheTimeout) = networkMapClient.getNetworkMap()
|
||||||
|
// TODO NetworkParameters updates are not implemented yet. Every mismatch should result in node shutdown.
|
||||||
|
if (currentParametersHash != networkMap.networkParameterHash) {
|
||||||
|
logger.error("Node is using parameters with hash: $currentParametersHash but network map is advertising: ${networkMap.networkParameterHash}.\n" +
|
||||||
|
"Please update node to use correct network parameters file.\"")
|
||||||
|
System.exit(1)
|
||||||
|
}
|
||||||
val currentNodeHashes = networkMapCache.allNodeHashes
|
val currentNodeHashes = networkMapCache.allNodeHashes
|
||||||
val hashesFromNetworkMap = networkMap.nodeInfoHashes
|
val hashesFromNetworkMap = networkMap.nodeInfoHashes
|
||||||
(hashesFromNetworkMap - currentNodeHashes).mapNotNull {
|
(hashesFromNetworkMap - currentNodeHashes).mapNotNull {
|
||||||
@ -144,7 +151,6 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
|
|||||||
(currentNodeHashes - hashesFromNetworkMap - fileWatcher.processedNodeInfoHashes)
|
(currentNodeHashes - hashesFromNetworkMap - fileWatcher.processedNodeInfoHashes)
|
||||||
.mapNotNull(networkMapCache::getNodeByHash)
|
.mapNotNull(networkMapCache::getNodeByHash)
|
||||||
.forEach(networkMapCache::removeNode)
|
.forEach(networkMapCache::removeNode)
|
||||||
// TODO: Check NetworkParameter.
|
|
||||||
cacheTimeout
|
cacheTimeout
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
logger.warn("Error encountered while updating network map, will retry in ${retryInterval.seconds} seconds", t)
|
logger.warn("Error encountered while updating network map, will retry in ${retryInterval.seconds} seconds", t)
|
||||||
|
@ -207,9 +207,6 @@ open class PersistentNetworkMapCache(
|
|||||||
getAllInfos(session).map { it.toNodeInfo() }
|
getAllInfos(session).map { it.toNodeInfo() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes related to NetworkMap redesign
|
|
||||||
// TODO It will be properly merged into network map cache after services removal.
|
|
||||||
|
|
||||||
private fun getAllInfos(session: Session): List<NodeInfoSchemaV1.PersistentNodeInfo> {
|
private fun getAllInfos(session: Session): List<NodeInfoSchemaV1.PersistentNodeInfo> {
|
||||||
val criteria = session.criteriaBuilder.createQuery(NodeInfoSchemaV1.PersistentNodeInfo::class.java)
|
val criteria = session.criteriaBuilder.createQuery(NodeInfoSchemaV1.PersistentNodeInfo::class.java)
|
||||||
criteria.select(criteria.from(NodeInfoSchemaV1.PersistentNodeInfo::class.java))
|
criteria.select(criteria.from(NodeInfoSchemaV1.PersistentNodeInfo::class.java))
|
||||||
@ -292,7 +289,6 @@ open class PersistentNetworkMapCache(
|
|||||||
else result.map { it.toNodeInfo() }.singleOrNull() ?: throw IllegalStateException("More than one node with the same host and port")
|
else result.map { it.toNodeInfo() }.singleOrNull() ?: throw IllegalStateException("More than one node with the same host and port")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Object Relational Mapping support. */
|
/** Object Relational Mapping support. */
|
||||||
private fun generateMappedObject(nodeInfo: NodeInfo): NodeInfoSchemaV1.PersistentNodeInfo {
|
private fun generateMappedObject(nodeInfo: NodeInfo): NodeInfoSchemaV1.PersistentNodeInfo {
|
||||||
return NodeInfoSchemaV1.PersistentNodeInfo(
|
return NodeInfoSchemaV1.PersistentNodeInfo(
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
package net.corda.node.internal
|
||||||
|
|
||||||
|
import com.nhaarman.mockito_kotlin.doReturn
|
||||||
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.finance.DOLLARS
|
||||||
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
|
import net.corda.node.services.config.NotaryConfig
|
||||||
|
import net.corda.nodeapi.internal.NetworkParameters
|
||||||
|
import net.corda.nodeapi.internal.NetworkParametersCopier
|
||||||
|
import net.corda.nodeapi.internal.NotaryInfo
|
||||||
|
import net.corda.testing.*
|
||||||
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
|
import net.corda.testing.node.*
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Test
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.test.assertFails
|
||||||
|
import org.assertj.core.api.Assertions.*
|
||||||
|
|
||||||
|
class NetworkParametersTest {
|
||||||
|
private val mockNet = MockNetwork(
|
||||||
|
MockNetworkParameters(networkSendManuallyPumped = true),
|
||||||
|
notarySpecs = listOf(MockNetwork.NotarySpec(DUMMY_NOTARY_NAME)))
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
mockNet.stopNodes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimum Platform Version tests
|
||||||
|
@Test
|
||||||
|
fun `node shutdowns when on lower platform version than network`() {
|
||||||
|
val alice = mockNet.createUnstartedNode(MockNodeParameters(legalName = ALICE_NAME, forcedID = 100, version = MockServices.MOCK_VERSION_INFO.copy(platformVersion = 1)))
|
||||||
|
val aliceDirectory = mockNet.baseDirectory(100)
|
||||||
|
val netParams = testNetworkParameters(
|
||||||
|
notaries = listOf(NotaryInfo(mockNet.defaultNotaryIdentity, true)),
|
||||||
|
minimumPlatformVersion = 2)
|
||||||
|
dropParametersToDir(aliceDirectory, netParams)
|
||||||
|
assertThatThrownBy { alice.start() }.hasMessageContaining("platform version")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `node works fine when on higher platform version`() {
|
||||||
|
val alice = mockNet.createUnstartedNode(MockNodeParameters(legalName = ALICE_NAME, forcedID = 100, version = MockServices.MOCK_VERSION_INFO.copy(platformVersion = 2)))
|
||||||
|
val aliceDirectory = mockNet.baseDirectory(100)
|
||||||
|
val netParams = testNetworkParameters(
|
||||||
|
notaries = listOf(NotaryInfo(mockNet.defaultNotaryIdentity, true)),
|
||||||
|
minimumPlatformVersion = 1)
|
||||||
|
dropParametersToDir(aliceDirectory, netParams)
|
||||||
|
alice.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notaries tests
|
||||||
|
@Test
|
||||||
|
fun `choosing notary not specified in network parameters will fail`() {
|
||||||
|
val fakeNotary = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME, configOverrides = {
|
||||||
|
val notary = NotaryConfig(false)
|
||||||
|
doReturn(notary).whenever(it).notary}))
|
||||||
|
val fakeNotaryId = fakeNotary.info.chooseIdentity()
|
||||||
|
val alice = mockNet.createPartyNode(ALICE_NAME)
|
||||||
|
assertThat(alice.services.networkMapCache.notaryIdentities).doesNotContain(fakeNotaryId)
|
||||||
|
assertFails {
|
||||||
|
alice.services.startFlow(CashIssueFlow(500.DOLLARS, OpaqueBytes.of(0x01), fakeNotaryId)).resultFuture.getOrThrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
private fun dropParametersToDir(dir: Path, params: NetworkParameters) {
|
||||||
|
NetworkParametersCopier(params).install(dir)
|
||||||
|
}
|
||||||
|
}
|
@ -163,7 +163,7 @@ class ArtemisMessagingTests {
|
|||||||
return Pair(messagingClient, receivedMessages)
|
return Pair(messagingClient, receivedMessages)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createMessagingClient(server: NetworkHostAndPort = NetworkHostAndPort("localhost", serverPort), platformVersion: Int = 1): P2PMessagingClient {
|
private fun createMessagingClient(server: NetworkHostAndPort = NetworkHostAndPort("localhost", serverPort), platformVersion: Int = 1, maxMessageSize: Int = MAX_MESSAGE_SIZE): P2PMessagingClient {
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
P2PMessagingClient(
|
P2PMessagingClient(
|
||||||
config,
|
config,
|
||||||
@ -171,16 +171,16 @@ class ArtemisMessagingTests {
|
|||||||
server,
|
server,
|
||||||
identity.public,
|
identity.public,
|
||||||
ServiceAffinityExecutor("ArtemisMessagingTests", 1),
|
ServiceAffinityExecutor("ArtemisMessagingTests", 1),
|
||||||
database
|
database,
|
||||||
).apply {
|
maxMessageSize = maxMessageSize).apply {
|
||||||
config.configureWithDevSSLCertificate()
|
config.configureWithDevSSLCertificate()
|
||||||
messagingClient = this
|
messagingClient = this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createMessagingServer(local: Int = serverPort, rpc: Int = rpcPort): ArtemisMessagingServer {
|
private fun createMessagingServer(local: Int = serverPort, rpc: Int = rpcPort, maxMessageSize: Int = MAX_MESSAGE_SIZE): ArtemisMessagingServer {
|
||||||
return ArtemisMessagingServer(config, local, rpc, networkMapCache, securityManager).apply {
|
return ArtemisMessagingServer(config, local, rpc, networkMapCache, securityManager, maxMessageSize).apply {
|
||||||
config.configureWithDevSSLCertificate()
|
config.configureWithDevSSLCertificate()
|
||||||
messagingServer = this
|
messagingServer = this
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,7 @@ import net.corda.core.internal.cert
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.services.network.TestNodeInfoFactory.createNodeInfo
|
import net.corda.node.services.network.TestNodeInfoFactory.createNodeInfo
|
||||||
import net.corda.testing.DEV_CA
|
|
||||||
import net.corda.testing.DEV_TRUST_ROOT
|
import net.corda.testing.DEV_TRUST_ROOT
|
||||||
import net.corda.testing.ROOT_CA
|
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.node.network.NetworkMapServer
|
import net.corda.testing.node.network.NetworkMapServer
|
||||||
@ -71,7 +69,7 @@ class NetworkMapClientTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `download NetworkParameter correctly`() {
|
fun `download NetworkParameter correctly`() {
|
||||||
// The test server returns same network parameter for any hash.
|
// The test server returns same network parameter for any hash.
|
||||||
val networkParameter = networkMapClient.getNetworkParameter(SecureHash.randomSHA256())
|
val networkParameter = networkMapClient.getNetworkParameter(SecureHash.randomSHA256())?.verified()
|
||||||
assertNotNull(networkParameter)
|
assertNotNull(networkParameter)
|
||||||
assertEquals(NetworkMapServer.stubNetworkParameter, networkParameter)
|
assertEquals(NetworkMapServer.stubNetworkParameter, networkParameter)
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,10 @@ import java.util.concurrent.TimeUnit
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class NetworkMapUpdaterTest {
|
class NetworkMapUpdaterTest {
|
||||||
|
companion object {
|
||||||
|
val NETWORK_PARAMS_HASH = SecureHash.randomSHA256()
|
||||||
|
}
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule(true)
|
val testSerialization = SerializationEnvironmentRule(true)
|
||||||
@ -53,7 +57,7 @@ class NetworkMapUpdaterTest {
|
|||||||
|
|
||||||
val scheduler = TestScheduler()
|
val scheduler = TestScheduler()
|
||||||
val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
||||||
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient)
|
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient, NETWORK_PARAMS_HASH)
|
||||||
|
|
||||||
// Publish node info for the first time.
|
// Publish node info for the first time.
|
||||||
updater.updateNodeInfo(nodeInfo1) { signedNodeInfo }
|
updater.updateNodeInfo(nodeInfo1) { signedNodeInfo }
|
||||||
@ -96,13 +100,13 @@ class NetworkMapUpdaterTest {
|
|||||||
val signedNodeInfo: SignedData<NodeInfo> = uncheckedCast(it.arguments.first())
|
val signedNodeInfo: SignedData<NodeInfo> = uncheckedCast(it.arguments.first())
|
||||||
nodeInfoMap.put(signedNodeInfo.verified().serialize().hash, signedNodeInfo)
|
nodeInfoMap.put(signedNodeInfo.verified().serialize().hash, signedNodeInfo)
|
||||||
}
|
}
|
||||||
on { getNetworkMap() }.then { NetworkMapResponse(NetworkMap(nodeInfoMap.keys.toList(), SecureHash.randomSHA256()), 100.millis) }
|
on { getNetworkMap() }.then { NetworkMapResponse(NetworkMap(nodeInfoMap.keys.toList(), NETWORK_PARAMS_HASH), 100.millis) }
|
||||||
on { getNodeInfo(any()) }.then { nodeInfoMap[it.arguments.first()]?.verified() }
|
on { getNodeInfo(any()) }.then { nodeInfoMap[it.arguments.first()]?.verified() }
|
||||||
}
|
}
|
||||||
|
|
||||||
val scheduler = TestScheduler()
|
val scheduler = TestScheduler()
|
||||||
val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
||||||
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient)
|
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient, NETWORK_PARAMS_HASH)
|
||||||
|
|
||||||
// Test adding new node.
|
// Test adding new node.
|
||||||
networkMapClient.publish(nodeInfo1)
|
networkMapClient.publish(nodeInfo1)
|
||||||
@ -150,13 +154,13 @@ class NetworkMapUpdaterTest {
|
|||||||
val signedNodeInfo: SignedData<NodeInfo> = uncheckedCast(it.arguments.first())
|
val signedNodeInfo: SignedData<NodeInfo> = uncheckedCast(it.arguments.first())
|
||||||
nodeInfoMap.put(signedNodeInfo.verified().serialize().hash, signedNodeInfo)
|
nodeInfoMap.put(signedNodeInfo.verified().serialize().hash, signedNodeInfo)
|
||||||
}
|
}
|
||||||
on { getNetworkMap() }.then { NetworkMapResponse(NetworkMap(nodeInfoMap.keys.toList(), SecureHash.randomSHA256()), 100.millis) }
|
on { getNetworkMap() }.then { NetworkMapResponse(NetworkMap(nodeInfoMap.keys.toList(), NETWORK_PARAMS_HASH), 100.millis) }
|
||||||
on { getNodeInfo(any()) }.then { nodeInfoMap[it.arguments.first()]?.verified() }
|
on { getNodeInfo(any()) }.then { nodeInfoMap[it.arguments.first()]?.verified() }
|
||||||
}
|
}
|
||||||
|
|
||||||
val scheduler = TestScheduler()
|
val scheduler = TestScheduler()
|
||||||
val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
||||||
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient)
|
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, networkMapClient, NETWORK_PARAMS_HASH)
|
||||||
|
|
||||||
// Add all nodes.
|
// Add all nodes.
|
||||||
NodeInfoWatcher.saveToFile(baseDir / CordformNode.NODE_INFO_DIRECTORY, fileNodeInfo)
|
NodeInfoWatcher.saveToFile(baseDir / CordformNode.NODE_INFO_DIRECTORY, fileNodeInfo)
|
||||||
@ -200,7 +204,7 @@ class NetworkMapUpdaterTest {
|
|||||||
|
|
||||||
val scheduler = TestScheduler()
|
val scheduler = TestScheduler()
|
||||||
val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
val fileWatcher = NodeInfoWatcher(baseDir, scheduler)
|
||||||
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, null)
|
val updater = NetworkMapUpdater(networkMapCache, fileWatcher, null, NETWORK_PARAMS_HASH)
|
||||||
|
|
||||||
// Not subscribed yet.
|
// Not subscribed yet.
|
||||||
verify(networkMapCache, times(0)).addNode(any())
|
verify(networkMapCache, times(0)).addNode(any())
|
||||||
|
@ -22,6 +22,7 @@ import net.corda.core.node.services.KeyManagementService
|
|||||||
import net.corda.core.serialization.SerializationWhitelist
|
import net.corda.core.serialization.SerializationWhitelist
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.internal.AbstractNode
|
import net.corda.node.internal.AbstractNode
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
@ -91,7 +92,8 @@ data class MockNodeParameters(
|
|||||||
val forcedID: Int? = null,
|
val forcedID: Int? = null,
|
||||||
val legalName: CordaX500Name? = null,
|
val legalName: CordaX500Name? = null,
|
||||||
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||||
val configOverrides: (NodeConfiguration) -> Any? = {}) {
|
val configOverrides: (NodeConfiguration) -> Any? = {},
|
||||||
|
val version: VersionInfo = MOCK_VERSION_INFO) {
|
||||||
fun setForcedID(forcedID: Int?) = copy(forcedID = forcedID)
|
fun setForcedID(forcedID: Int?) = copy(forcedID = forcedID)
|
||||||
fun setLegalName(legalName: CordaX500Name?) = copy(legalName = legalName)
|
fun setLegalName(legalName: CordaX500Name?) = copy(legalName = legalName)
|
||||||
fun setEntropyRoot(entropyRoot: BigInteger) = copy(entropyRoot = entropyRoot)
|
fun setEntropyRoot(entropyRoot: BigInteger) = copy(entropyRoot = entropyRoot)
|
||||||
@ -102,7 +104,8 @@ data class MockNodeArgs(
|
|||||||
val config: NodeConfiguration,
|
val config: NodeConfiguration,
|
||||||
val network: MockNetwork,
|
val network: MockNetwork,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val entropyRoot: BigInteger
|
val entropyRoot: BigInteger,
|
||||||
|
val version: VersionInfo = MOCK_VERSION_INFO
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -241,7 +244,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
|||||||
open class MockNode(args: MockNodeArgs) : AbstractNode(
|
open class MockNode(args: MockNodeArgs) : AbstractNode(
|
||||||
args.config,
|
args.config,
|
||||||
TestClock(Clock.systemUTC()),
|
TestClock(Clock.systemUTC()),
|
||||||
MOCK_VERSION_INFO,
|
args.version,
|
||||||
CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages),
|
CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages),
|
||||||
args.network.busyLatch
|
args.network.busyLatch
|
||||||
) {
|
) {
|
||||||
@ -392,7 +395,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
|
|||||||
doReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties
|
doReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties
|
||||||
parameters.configOverrides(it)
|
parameters.configOverrides(it)
|
||||||
}
|
}
|
||||||
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot))
|
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version))
|
||||||
_nodes += node
|
_nodes += node
|
||||||
if (start) {
|
if (start) {
|
||||||
node.start()
|
node.start()
|
||||||
|
@ -245,6 +245,7 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun startCordformNodes(cordforms: List<CordformNode>): CordaFuture<*> {
|
internal fun startCordformNodes(cordforms: List<CordformNode>): CordaFuture<*> {
|
||||||
|
check(compatibilityZone == null) { "Cordform nodes should be run without compatibilityZone configuration" }
|
||||||
val clusterNodes = HashMultimap.create<ClusterType, CordaX500Name>()
|
val clusterNodes = HashMultimap.create<ClusterType, CordaX500Name>()
|
||||||
val notaryInfos = ArrayList<NotaryInfo>()
|
val notaryInfos = ArrayList<NotaryInfo>()
|
||||||
|
|
||||||
@ -354,7 +355,7 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
val notaryInfos = generateNotaryIdentities()
|
val notaryInfos = generateNotaryIdentities()
|
||||||
// The network parameters must be serialised before starting any of the nodes
|
// The network parameters must be serialised before starting any of the nodes
|
||||||
networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos))
|
if (compatibilityZone == null) networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos))
|
||||||
val nodeHandles = startNotaries()
|
val nodeHandles = startNotaries()
|
||||||
_notaries = notaryInfos.zip(nodeHandles) { (identity, validating), nodes -> NotaryHandle(identity, validating, nodes) }
|
_notaries = notaryInfos.zip(nodeHandles) { (identity, validating), nodes -> NotaryHandle(identity, validating, nodes) }
|
||||||
}
|
}
|
||||||
@ -519,7 +520,7 @@ class DriverDSLImpl(
|
|||||||
val configuration = config.parseAsNodeConfiguration()
|
val configuration = config.parseAsNodeConfiguration()
|
||||||
val baseDirectory = configuration.baseDirectory.createDirectories()
|
val baseDirectory = configuration.baseDirectory.createDirectories()
|
||||||
nodeInfoFilesCopier?.addConfig(baseDirectory)
|
nodeInfoFilesCopier?.addConfig(baseDirectory)
|
||||||
networkParameters!!.install(baseDirectory)
|
networkParameters?.install(baseDirectory)
|
||||||
val onNodeExit: () -> Unit = {
|
val onNodeExit: () -> Unit = {
|
||||||
nodeInfoFilesCopier?.removeConfig(baseDirectory)
|
nodeInfoFilesCopier?.removeConfig(baseDirectory)
|
||||||
countObservables.remove(configuration.myLegalName)
|
countObservables.remove(configuration.myLegalName)
|
||||||
|
@ -25,6 +25,7 @@ import net.corda.nodeapi.ConnectionDirection
|
|||||||
import net.corda.nodeapi.RPCApi
|
import net.corda.nodeapi.RPCApi
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
|
import net.corda.nodeapi.internal.serialization.KRYO_RPC_CLIENT_CONTEXT
|
||||||
|
import net.corda.testing.MAX_MESSAGE_SIZE
|
||||||
import net.corda.testing.driver.JmxPolicy
|
import net.corda.testing.driver.JmxPolicy
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
@ -227,8 +228,8 @@ data class RPCDriverDSL(
|
|||||||
fun <I : RPCOps> startInVmRpcServer(
|
fun <I : RPCOps> startInVmRpcServer(
|
||||||
rpcUser: User = rpcTestUser,
|
rpcUser: User = rpcTestUser,
|
||||||
nodeLegalName: CordaX500Name = fakeNodeLegalName,
|
nodeLegalName: CordaX500Name = fakeNodeLegalName,
|
||||||
maxFileSize: Int = ArtemisMessagingServer.MAX_FILE_SIZE,
|
maxFileSize: Int = MAX_MESSAGE_SIZE,
|
||||||
maxBufferedBytesPerClient: Long = 10L * ArtemisMessagingServer.MAX_FILE_SIZE,
|
maxBufferedBytesPerClient: Long = 10L * MAX_MESSAGE_SIZE,
|
||||||
configuration: RPCServerConfiguration = RPCServerConfiguration.default,
|
configuration: RPCServerConfiguration = RPCServerConfiguration.default,
|
||||||
ops: I
|
ops: I
|
||||||
): CordaFuture<RpcServerHandle> {
|
): CordaFuture<RpcServerHandle> {
|
||||||
@ -295,8 +296,8 @@ data class RPCDriverDSL(
|
|||||||
serverName: String = "driver-rpc-server-${random63BitValue()}",
|
serverName: String = "driver-rpc-server-${random63BitValue()}",
|
||||||
rpcUser: User = rpcTestUser,
|
rpcUser: User = rpcTestUser,
|
||||||
nodeLegalName: CordaX500Name = fakeNodeLegalName,
|
nodeLegalName: CordaX500Name = fakeNodeLegalName,
|
||||||
maxFileSize: Int = ArtemisMessagingServer.MAX_FILE_SIZE,
|
maxFileSize: Int = MAX_MESSAGE_SIZE,
|
||||||
maxBufferedBytesPerClient: Long = 10L * ArtemisMessagingServer.MAX_FILE_SIZE,
|
maxBufferedBytesPerClient: Long = 10L * MAX_MESSAGE_SIZE,
|
||||||
configuration: RPCServerConfiguration = RPCServerConfiguration.default,
|
configuration: RPCServerConfiguration = RPCServerConfiguration.default,
|
||||||
customPort: NetworkHostAndPort? = null,
|
customPort: NetworkHostAndPort? = null,
|
||||||
ops: I
|
ops: I
|
||||||
@ -378,8 +379,8 @@ data class RPCDriverDSL(
|
|||||||
fun startRpcBroker(
|
fun startRpcBroker(
|
||||||
serverName: String = "driver-rpc-server-${random63BitValue()}",
|
serverName: String = "driver-rpc-server-${random63BitValue()}",
|
||||||
rpcUser: User = rpcTestUser,
|
rpcUser: User = rpcTestUser,
|
||||||
maxFileSize: Int = ArtemisMessagingServer.MAX_FILE_SIZE,
|
maxFileSize: Int = MAX_MESSAGE_SIZE,
|
||||||
maxBufferedBytesPerClient: Long = 10L * ArtemisMessagingServer.MAX_FILE_SIZE,
|
maxBufferedBytesPerClient: Long = 10L * MAX_MESSAGE_SIZE,
|
||||||
customPort: NetworkHostAndPort? = null
|
customPort: NetworkHostAndPort? = null
|
||||||
): CordaFuture<RpcBrokerHandle> {
|
): CordaFuture<RpcBrokerHandle> {
|
||||||
val hostAndPort = customPort ?: driverDSL.portAllocation.nextHostAndPort()
|
val hostAndPort = customPort ?: driverDSL.portAllocation.nextHostAndPort()
|
||||||
@ -402,8 +403,8 @@ data class RPCDriverDSL(
|
|||||||
|
|
||||||
fun startInVmRpcBroker(
|
fun startInVmRpcBroker(
|
||||||
rpcUser: User = rpcTestUser,
|
rpcUser: User = rpcTestUser,
|
||||||
maxFileSize: Int = ArtemisMessagingServer.MAX_FILE_SIZE,
|
maxFileSize: Int = MAX_MESSAGE_SIZE,
|
||||||
maxBufferedBytesPerClient: Long = 10L * ArtemisMessagingServer.MAX_FILE_SIZE
|
maxBufferedBytesPerClient: Long = 10L * MAX_MESSAGE_SIZE
|
||||||
): CordaFuture<RpcBrokerHandle> {
|
): CordaFuture<RpcBrokerHandle> {
|
||||||
return driverDSL.executorService.fork {
|
return driverDSL.executorService.fork {
|
||||||
val artemisConfig = createInVmRpcServerArtemisConfig(maxFileSize, maxBufferedBytesPerClient)
|
val artemisConfig = createInVmRpcServerArtemisConfig(maxFileSize, maxBufferedBytesPerClient)
|
||||||
@ -431,7 +432,7 @@ data class RPCDriverDSL(
|
|||||||
brokerHandle: RpcBrokerHandle
|
brokerHandle: RpcBrokerHandle
|
||||||
): RpcServerHandle {
|
): RpcServerHandle {
|
||||||
val locator = ActiveMQClient.createServerLocatorWithoutHA(brokerHandle.clientTransportConfiguration).apply {
|
val locator = ActiveMQClient.createServerLocatorWithoutHA(brokerHandle.clientTransportConfiguration).apply {
|
||||||
minLargeMessageSize = ArtemisMessagingServer.MAX_FILE_SIZE
|
minLargeMessageSize = MAX_MESSAGE_SIZE
|
||||||
isUseGlobalPools = false
|
isUseGlobalPools = false
|
||||||
}
|
}
|
||||||
val rpcSecurityManager = RPCSecurityManagerImpl.fromUserList(users = listOf(rpcUser), id = AuthServiceId("TEST_SECURITY_MANAGER"))
|
val rpcSecurityManager = RPCSecurityManagerImpl.fromUserList(users = listOf(rpcUser), id = AuthServiceId("TEST_SECURITY_MANAGER"))
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
package net.corda.testing.node.network
|
package net.corda.testing.node.network
|
||||||
|
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.crypto.SignedData
|
|
||||||
import net.corda.core.crypto.sha256
|
|
||||||
import net.corda.core.internal.cert
|
import net.corda.core.internal.cert
|
||||||
import net.corda.core.internal.toX509CertHolder
|
import net.corda.core.internal.toX509CertHolder
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.hours
|
|
||||||
import net.corda.nodeapi.internal.DigitalSignatureWithCert
|
import net.corda.nodeapi.internal.DigitalSignatureWithCert
|
||||||
import net.corda.nodeapi.internal.NetworkMap
|
import net.corda.nodeapi.internal.NetworkMap
|
||||||
import net.corda.nodeapi.internal.NetworkParameters
|
import net.corda.nodeapi.internal.NetworkParameters
|
||||||
@ -39,9 +35,11 @@ import javax.ws.rs.core.Response.ok
|
|||||||
|
|
||||||
class NetworkMapServer(cacheTimeout: Duration,
|
class NetworkMapServer(cacheTimeout: Duration,
|
||||||
hostAndPort: NetworkHostAndPort,
|
hostAndPort: NetworkHostAndPort,
|
||||||
|
root_ca: CertificateAndKeyPair = ROOT_CA, // Default to ROOT_CA for testing.
|
||||||
vararg additionalServices: Any) : Closeable {
|
vararg additionalServices: Any) : Closeable {
|
||||||
companion object {
|
companion object {
|
||||||
val stubNetworkParameter = NetworkParameters(1, emptyList(), 1.hours, 10, 10, Instant.now(), 10)
|
val stubNetworkParameter = NetworkParameters(1, emptyList(), 40000, 40000, Instant.now(), 10)
|
||||||
|
private val serializedParameters = stubNetworkParameter.serialize()
|
||||||
|
|
||||||
private fun networkMapKeyAndCert(rootCAKeyAndCert: CertificateAndKeyPair): CertificateAndKeyPair {
|
private fun networkMapKeyAndCert(rootCAKeyAndCert: CertificateAndKeyPair): CertificateAndKeyPair {
|
||||||
val networkMapKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val networkMapKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
@ -56,9 +54,7 @@ class NetworkMapServer(cacheTimeout: Duration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val server: Server
|
private val server: Server
|
||||||
// Default to ROOT_CA for testing.
|
private val service = InMemoryNetworkMapService(cacheTimeout, networkMapKeyAndCert(root_ca))
|
||||||
// TODO: make this configurable?
|
|
||||||
private val service = InMemoryNetworkMapService(cacheTimeout, networkMapKeyAndCert(ROOT_CA))
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
server = Server(InetSocketAddress(hostAndPort.host, hostAndPort.port)).apply {
|
server = Server(InetSocketAddress(hostAndPort.host, hostAndPort.port)).apply {
|
||||||
@ -100,6 +96,11 @@ class NetworkMapServer(cacheTimeout: Duration,
|
|||||||
@Path("network-map")
|
@Path("network-map")
|
||||||
class InMemoryNetworkMapService(private val cacheTimeout: Duration, private val networkMapKeyAndCert: CertificateAndKeyPair) {
|
class InMemoryNetworkMapService(private val cacheTimeout: Duration, private val networkMapKeyAndCert: CertificateAndKeyPair) {
|
||||||
private val nodeInfoMap = mutableMapOf<SecureHash, SignedData<NodeInfo>>()
|
private val nodeInfoMap = mutableMapOf<SecureHash, SignedData<NodeInfo>>()
|
||||||
|
private val parametersHash = serializedParameters.hash
|
||||||
|
private val signedParameters = SignedData(
|
||||||
|
serializedParameters,
|
||||||
|
DigitalSignature.WithKey(networkMapKeyAndCert.keyPair.public, Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedParameters.bytes))
|
||||||
|
)
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("publish")
|
@Path("publish")
|
||||||
@ -115,7 +116,7 @@ class NetworkMapServer(cacheTimeout: Duration,
|
|||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
fun getNetworkMap(): Response {
|
fun getNetworkMap(): Response {
|
||||||
val networkMap = NetworkMap(nodeInfoMap.keys.map { it }, SecureHash.randomSHA256())
|
val networkMap = NetworkMap(nodeInfoMap.keys.map { it }, parametersHash)
|
||||||
val serializedNetworkMap = networkMap.serialize()
|
val serializedNetworkMap = networkMap.serialize()
|
||||||
val signature = Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedNetworkMap.bytes)
|
val signature = Crypto.doSign(networkMapKeyAndCert.keyPair.private, serializedNetworkMap.bytes)
|
||||||
val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate.cert, signature))
|
val signedNetworkMap = SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(networkMapKeyAndCert.certificate.cert, signature))
|
||||||
@ -143,7 +144,7 @@ class NetworkMapServer(cacheTimeout: Duration,
|
|||||||
@Path("network-parameter/{var}")
|
@Path("network-parameter/{var}")
|
||||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
fun getNetworkParameter(@PathParam("var") networkParameterHash: String): Response {
|
fun getNetworkParameter(@PathParam("var") networkParameterHash: String): Response {
|
||||||
return Response.ok(stubNetworkParameter.serialize().bytes).build()
|
return Response.ok(signedParameters.serialize().bytes).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
package net.corda.testing.common.internal
|
package net.corda.testing.common.internal
|
||||||
|
|
||||||
import net.corda.core.utilities.days
|
|
||||||
import net.corda.nodeapi.internal.NetworkParameters
|
import net.corda.nodeapi.internal.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.NotaryInfo
|
import net.corda.nodeapi.internal.NotaryInfo
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
fun testNetworkParameters(notaries: List<NotaryInfo>): NetworkParameters {
|
fun testNetworkParameters(
|
||||||
|
notaries: List<NotaryInfo>,
|
||||||
|
minimumPlatformVersion: Int = 1,
|
||||||
|
modifiedTime: Instant = Instant.now(),
|
||||||
|
maxMessageSize: Int = 1048576,
|
||||||
|
maxTransactionSize: Int = 40000,
|
||||||
|
epoch: Int = 1
|
||||||
|
): NetworkParameters {
|
||||||
return NetworkParameters(
|
return NetworkParameters(
|
||||||
minimumPlatformVersion = 1,
|
minimumPlatformVersion = minimumPlatformVersion,
|
||||||
notaries = notaries,
|
notaries = notaries,
|
||||||
modifiedTime = Instant.now(),
|
modifiedTime = modifiedTime,
|
||||||
eventHorizon = 10000.days,
|
maxMessageSize = maxMessageSize,
|
||||||
maxMessageSize = 40000,
|
maxTransactionSize = maxTransactionSize,
|
||||||
maxTransactionSize = 40000,
|
epoch = epoch
|
||||||
epoch = 1
|
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -51,3 +51,6 @@ val DEV_TRUST_ROOT: X509CertificateHolder by lazy {
|
|||||||
fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public)) = Command<TypeOnlyCommandData>(DummyCommandData, signers.toList())
|
fun dummyCommand(vararg signers: PublicKey = arrayOf(generateKeyPair().public)) = Command<TypeOnlyCommandData>(DummyCommandData, signers.toList())
|
||||||
|
|
||||||
object DummyCommandData : TypeOnlyCommandData()
|
object DummyCommandData : TypeOnlyCommandData()
|
||||||
|
|
||||||
|
/** Maximum artemis message size. 10 MiB maximum allowed file size for attachments, including message headers. */
|
||||||
|
const val MAX_MESSAGE_SIZE: Int = 1048576
|
||||||
|
@ -143,7 +143,6 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
|
|||||||
minimumPlatformVersion = 1,
|
minimumPlatformVersion = 1,
|
||||||
notaries = listOf(NotaryInfo(identity, config.nodeConfig.notary!!.validating)),
|
notaries = listOf(NotaryInfo(identity, config.nodeConfig.notary!!.validating)),
|
||||||
modifiedTime = Instant.now(),
|
modifiedTime = Instant.now(),
|
||||||
eventHorizon = 10000.days,
|
|
||||||
maxMessageSize = 40000,
|
maxMessageSize = 40000,
|
||||||
maxTransactionSize = 40000,
|
maxTransactionSize = 40000,
|
||||||
epoch = 1
|
epoch = 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user