mirror of
https://github.com/corda/corda.git
synced 2025-03-14 00:06:45 +00:00
ENT-1608: Check that notary identities are registered (#799)
Check that notary identities are registered On loading new parameteres check that provieded notary identities were registered by doorman. Refactor of NetworkParameters loading code in network-management.
This commit is contained in:
parent
c3429cc621
commit
2e1cee00f7
@ -61,17 +61,22 @@ It is ready to be included into the certificate revocation list.
|
||||
* The signed list is serialised and stored in the networking service database ready to be served. Also, all approved requests become revoked now.
|
||||
|
||||
|
||||
Signing Network Map
|
||||
-------------------
|
||||
Signing Network Map and Network Parameters
|
||||
------------------------------------------
|
||||
|
||||
* The networking service receives a new (or updated) node info.
|
||||
|
||||
* Periodically (the time interval is pre-configured at the deployment time), the signing service fetches from the database current network map, all node info objects with valid certificates and current network parameters.
|
||||
* Periodically (the time interval is pre-configured at the deployment time), the signing service fetches from the database all information needed to construct new network map, which includes:
|
||||
- current network map,
|
||||
- all node info objects with valid certificates,
|
||||
- current network parameters
|
||||
- information on parameters update (if exists).
|
||||
|
||||
* A new network map object is created out of the fetched data.
|
||||
|
||||
* If the new network map hash does not differ from the current network map hash, then nothing happens and current network map remains unchanged.
|
||||
|
||||
* If they are different, then the newly created network map object is serialized using the Corda AMQP serialisation format and signed by the dedicated intermediate certificate stored in HSM.
|
||||
The same process applies to network parameters which need to be signed separately.
|
||||
|
||||
* Once signed, the new network map data is stored in the networking service database and available for nodes to retrieve when they next poll for the network map.
|
@ -175,7 +175,7 @@ class NetworkParametersUpdateTest : IntegrationTest() {
|
||||
DatabaseConfig(runMigration = true),
|
||||
DoormanConfig(approveAll = true, jira = null, approveInterval = timeoutMillis),
|
||||
null).use {
|
||||
it.processNetworkParameters(networkParametersCmd)
|
||||
it.netParamsUpdateHandler.processNetworkParameters(networkParametersCmd)
|
||||
}
|
||||
server = startServer(startNetworkMap = true)
|
||||
// Wait for server to process the parameters update and for the nodes to poll again
|
||||
|
@ -191,7 +191,7 @@ class NodeRegistrationTest : IntegrationTest() {
|
||||
private fun applyNetworkParametersAndStart(networkParametersCmd: NetworkParametersCmd) {
|
||||
server?.close()
|
||||
NetworkManagementServer(makeTestDataSourceProperties(DOORMAN_DB_NAME, dbNamePostfix, fallBackConfigSupplier = ::networkMapInMemoryH2DataSourceConfig), makeTestDatabaseProperties(DOORMAN_DB_NAME), doormanConfig, revocationConfig).use {
|
||||
it.processNetworkParameters(networkParametersCmd)
|
||||
it.netParamsUpdateHandler.processNetworkParameters(networkParametersCmd)
|
||||
}
|
||||
server = startServer(startNetworkMap = true)
|
||||
// Wait for server to process the parameters update and for the nodes to poll again
|
||||
|
@ -2,15 +2,12 @@ package com.r3.corda.networkmanage.doorman
|
||||
|
||||
import com.google.common.primitives.Booleans
|
||||
import com.r3.corda.networkmanage.common.utils.ArgsParser
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import joptsimple.OptionSet
|
||||
import joptsimple.util.EnumConverter
|
||||
import joptsimple.util.PathConverter
|
||||
import joptsimple.util.PathProperties
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import java.nio.file.Path
|
||||
import java.time.Instant
|
||||
|
||||
@ -44,21 +41,18 @@ class DoormanArgsParser : ArgsParser<DoormanCmdLineOptions>() {
|
||||
require(Booleans.countTrue(setNetworkParametersFile != null, flagDay, cancelUpdate) <= 1) {
|
||||
"Only one of $setNetworkParametersArg, $flagDay and $cancelUpdate can be specified"
|
||||
}
|
||||
val networkParametersOption = when {
|
||||
setNetworkParametersFile != null -> NetworkParametersCmd.Set.fromFile(setNetworkParametersFile)
|
||||
flagDay -> NetworkParametersCmd.FlagDay
|
||||
cancelUpdate -> NetworkParametersCmd.CancelUpdate
|
||||
else -> null
|
||||
}
|
||||
val trustStorePassword = optionSet.valueOf(trustStorePasswordArg)
|
||||
return DoormanCmdLineOptions(configFile, mode, networkParametersOption, trustStorePassword)
|
||||
return DoormanCmdLineOptions(configFile, mode, trustStorePassword, setNetworkParametersFile, flagDay, cancelUpdate)
|
||||
}
|
||||
}
|
||||
|
||||
data class DoormanCmdLineOptions(val configFile: Path,
|
||||
val mode: Mode,
|
||||
val networkParametersCmd: NetworkParametersCmd?,
|
||||
val trustStorePassword: String?) {
|
||||
val trustStorePassword: String?,
|
||||
val setNetworkParametersFile: Path?,
|
||||
val flagDay: Boolean,
|
||||
val cancelUpdate: Boolean
|
||||
) {
|
||||
init {
|
||||
// Make sure trust store password is only specified in root keygen mode.
|
||||
if (mode != Mode.ROOT_KEYGEN) {
|
||||
@ -76,20 +70,17 @@ sealed class NetworkParametersCmd {
|
||||
val notaries: List<NotaryInfo>,
|
||||
val maxMessageSize: Int,
|
||||
val maxTransactionSize: Int,
|
||||
val parametersUpdate: ParametersUpdateConfig?) : NetworkParametersCmd() {
|
||||
val parametersUpdate: ParametersUpdateConfig?
|
||||
) : NetworkParametersCmd() {
|
||||
companion object {
|
||||
fun fromFile(file: Path): Set {
|
||||
return ConfigFactory.parseFile(file.toFile(), ConfigParseOptions.defaults())
|
||||
.parseAs<NetworkParametersConfig>()
|
||||
.let {
|
||||
Set(
|
||||
it.minimumPlatformVersion,
|
||||
it.notaries.map { it.toNotaryInfo() },
|
||||
it.maxMessageSize,
|
||||
it.maxTransactionSize,
|
||||
it.parametersUpdate
|
||||
)
|
||||
}
|
||||
fun fromConfig(config: NetworkParametersConfig): Set {
|
||||
return Set(
|
||||
config.minimumPlatformVersion,
|
||||
config.notaries.map { it.toNotaryInfo() },
|
||||
config.maxMessageSize,
|
||||
config.maxTransactionSize,
|
||||
config.parametersUpdate
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@ package com.r3.corda.networkmanage.doorman
|
||||
import com.jcabi.manifests.Manifests
|
||||
import com.r3.corda.networkmanage.common.utils.*
|
||||
import com.r3.corda.networkmanage.doorman.signer.LocalSigner
|
||||
import net.corda.core.crypto.CordaSecurityProvider
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.nodeapi.internal.crypto.X509KeyStore
|
||||
@ -85,7 +84,14 @@ private fun caKeyGenMode(config: NetworkManagementServerConfig) {
|
||||
private fun doormanMode(cmdLineOptions: DoormanCmdLineOptions, config: NetworkManagementServerConfig) {
|
||||
val networkManagementServer = NetworkManagementServer(config.dataSourceProperties, config.database, config.doorman, config.revocation)
|
||||
|
||||
if (cmdLineOptions.networkParametersCmd == null) {
|
||||
val networkParametersCmd = when {
|
||||
cmdLineOptions.setNetworkParametersFile != null ->
|
||||
networkManagementServer.netParamsUpdateHandler.loadParametersFromFile(cmdLineOptions.setNetworkParametersFile)
|
||||
cmdLineOptions.flagDay -> NetworkParametersCmd.FlagDay
|
||||
cmdLineOptions.cancelUpdate -> NetworkParametersCmd.CancelUpdate
|
||||
else -> null
|
||||
}
|
||||
if (networkParametersCmd == null) {
|
||||
// TODO: move signing to signing server.
|
||||
val csrAndNetworkMap = processKeyStore(config)
|
||||
if (csrAndNetworkMap != null) {
|
||||
@ -108,7 +114,7 @@ private fun doormanMode(cmdLineOptions: DoormanCmdLineOptions, config: NetworkMa
|
||||
})
|
||||
} else {
|
||||
networkManagementServer.use {
|
||||
it.processNetworkParameters(cmdLineOptions.networkParametersCmd)
|
||||
it.netParamsUpdateHandler.processNetworkParameters(networkParametersCmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,12 +12,10 @@ package com.r3.corda.networkmanage.doorman
|
||||
|
||||
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory
|
||||
import com.r3.corda.networkmanage.common.persistence.*
|
||||
import com.r3.corda.networkmanage.common.persistence.entity.UpdateStatus
|
||||
import com.r3.corda.networkmanage.common.signer.NetworkMapSigner
|
||||
import com.r3.corda.networkmanage.common.utils.CertPathAndKey
|
||||
import com.r3.corda.networkmanage.doorman.signer.*
|
||||
import com.r3.corda.networkmanage.doorman.webservice.*
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
@ -42,6 +40,14 @@ class NetworkManagementServer(dataSourceProperties: Properties,
|
||||
private val database = configureDatabase(dataSourceProperties, databaseConfig).also { closeActions += it::close }
|
||||
private val networkMapStorage = PersistentNetworkMapStorage(database)
|
||||
private val nodeInfoStorage = PersistentNodeInfoStorage(database)
|
||||
private val csrStorage = PersistentCertificateSigningRequestStorage(database).let {
|
||||
if (doormanConfig?.approveAll ?: false) {
|
||||
ApproveAllCertificateSigningRequestStorage(it)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
val netParamsUpdateHandler = ParametersUpdateHandler(csrStorage, networkMapStorage)
|
||||
|
||||
lateinit var hostAndPort: NetworkHostAndPort
|
||||
|
||||
@ -84,14 +90,6 @@ class NetworkManagementServer(dataSourceProperties: Properties,
|
||||
serverStatus: NetworkManagementServerStatus): RegistrationWebService {
|
||||
logger.info("Starting Doorman server.")
|
||||
|
||||
val csrStorage = PersistentCertificateSigningRequestStorage(database).let {
|
||||
if (config.approveAll) {
|
||||
ApproveAllCertificateSigningRequestStorage(it)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
val jiraConfig = config.jira
|
||||
val requestProcessor = if (jiraConfig != null) {
|
||||
val jiraWebAPI = AsynchronousJiraRestClientFactory().createWithBasicHttpAuthentication(URI(jiraConfig.address), jiraConfig.username, jiraConfig.password)
|
||||
@ -189,86 +187,4 @@ class NetworkManagementServer(dataSourceProperties: Properties,
|
||||
closeActions += webServer::close
|
||||
this.hostAndPort = webServer.hostAndPort
|
||||
}
|
||||
|
||||
fun processNetworkParameters(networkParametersCmd: NetworkParametersCmd) {
|
||||
when (networkParametersCmd) {
|
||||
is NetworkParametersCmd.Set -> handleSetNetworkParameters(networkParametersCmd)
|
||||
NetworkParametersCmd.FlagDay -> handleFlagDay()
|
||||
NetworkParametersCmd.CancelUpdate -> handleCancelUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSetNetworkParameters(setNetParams: NetworkParametersCmd.Set) {
|
||||
logger.info("maxMessageSize is not currently wired in the nodes")
|
||||
val activeNetParams = networkMapStorage.getNetworkMaps().publicNetworkMap?.networkParameters?.networkParameters
|
||||
if (activeNetParams == null) {
|
||||
require(setNetParams.parametersUpdate == null) {
|
||||
"'parametersUpdate' specified in network parameters file but there are no network parameters to update"
|
||||
}
|
||||
val initialNetParams = setNetParams.toNetworkParameters(modifiedTime = Instant.now(), epoch = 1)
|
||||
logger.info("Saving initial network parameters to be signed:\n$initialNetParams")
|
||||
networkMapStorage.saveNetworkParameters(initialNetParams, null)
|
||||
println("Saved initial network parameters to be signed:\n$initialNetParams")
|
||||
} else {
|
||||
val parametersUpdate = requireNotNull(setNetParams.parametersUpdate) {
|
||||
"'parametersUpdate' not specified in network parameters file but there is already an active set of network parameters"
|
||||
}
|
||||
|
||||
setNetParams.checkCompatibility(activeNetParams)
|
||||
|
||||
val latestNetParams = checkNotNull(networkMapStorage.getLatestNetworkParameters()?.networkParameters) {
|
||||
"Something has gone wrong! We have an active set of network parameters ($activeNetParams) but apparently no latest network parameters!"
|
||||
}
|
||||
|
||||
// It's not necessary that latestNetParams is the current active network parameters. It can be the network
|
||||
// parameters from a previous update attempt which has't activated yet. We still take the epoch value for this
|
||||
// new set from latestNetParams to make sure the advertised update attempts have incrementing epochs.
|
||||
// This has the implication that *active* network parameters may have gaps in their epochs.
|
||||
val newNetParams = setNetParams.toNetworkParameters(modifiedTime = Instant.now(), epoch = latestNetParams.epoch + 1)
|
||||
|
||||
logger.info("Enabling update to network parameters:\n$newNetParams\n$parametersUpdate")
|
||||
|
||||
require(!sameNetworkParameters(latestNetParams, newNetParams)) { "New network parameters are the same as the latest ones" }
|
||||
|
||||
networkMapStorage.saveNewParametersUpdate(newNetParams, parametersUpdate.description, parametersUpdate.updateDeadline)
|
||||
|
||||
logger.info("Update enabled")
|
||||
println("Enabled update to network parameters:\n$newNetParams\n$parametersUpdate")
|
||||
}
|
||||
}
|
||||
|
||||
private fun sameNetworkParameters(params1: NetworkParameters, params2: NetworkParameters): Boolean {
|
||||
return params1.copy(epoch = 1, modifiedTime = Instant.MAX) == params2.copy(epoch = 1, modifiedTime = Instant.MAX)
|
||||
}
|
||||
|
||||
private fun handleFlagDay() {
|
||||
val parametersUpdate = checkNotNull(networkMapStorage.getCurrentParametersUpdate()) {
|
||||
"No network parameters updates are scheduled"
|
||||
}
|
||||
check(Instant.now() >= parametersUpdate.updateDeadline) {
|
||||
"Update deadline of ${parametersUpdate.updateDeadline} hasn't passed yet"
|
||||
}
|
||||
val latestNetParamsEntity = networkMapStorage.getLatestNetworkParameters()
|
||||
check(parametersUpdate.networkParameters.hash == networkMapStorage.getLatestNetworkParameters()?.hash) {
|
||||
"The latest network parameters is not the scheduled one:\n${latestNetParamsEntity?.networkParameters}\n${parametersUpdate.toParametersUpdate()}"
|
||||
}
|
||||
val activeNetParams = networkMapStorage.getNetworkMaps().publicNetworkMap?.networkParameters
|
||||
check(parametersUpdate.networkParameters.isSigned) {
|
||||
"Parameters we are trying to switch to haven't been signed yet"
|
||||
}
|
||||
logger.info("""Flag day has occurred, however the new network parameters won't be active until the new network map is signed.
|
||||
From: ${activeNetParams?.networkParameters}
|
||||
To: ${parametersUpdate.networkParameters.networkParameters}""")
|
||||
networkMapStorage.setParametersUpdateStatus(parametersUpdate, UpdateStatus.FLAG_DAY)
|
||||
}
|
||||
|
||||
private fun handleCancelUpdate() {
|
||||
val parametersUpdate = checkNotNull(networkMapStorage.getCurrentParametersUpdate()) {
|
||||
"No network parameters updates are scheduled"
|
||||
}
|
||||
logger.info("""Cancelling parameters update: ${parametersUpdate.toParametersUpdate()}.
|
||||
However, the network map will continue to advertise this update until the new one is signed.""")
|
||||
networkMapStorage.setParametersUpdateStatus(parametersUpdate, UpdateStatus.CANCELLED)
|
||||
println("Done with cancel update")
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ package com.r3.corda.networkmanage.doorman
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import net.corda.core.internal.readObject
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||
import java.nio.file.Path
|
||||
@ -25,14 +26,15 @@ import java.time.Instant
|
||||
*/
|
||||
data class NotaryConfig(private val notaryNodeInfoFile: Path,
|
||||
private val validating: Boolean) {
|
||||
// TODO ENT-1608 - Check that the identity belongs to us
|
||||
val nodeInfo by lazy { toNodeInfo() }
|
||||
fun toNotaryInfo(): NotaryInfo {
|
||||
val nodeInfo = notaryNodeInfoFile.readObject<SignedNodeInfo>().verified()
|
||||
// It is always the last identity (in the list of identities) that corresponds to the notary identity.
|
||||
// In case of a single notary, the list has only one element. In case of distributed notaries the list has
|
||||
// two items and the second one corresponds to the notary identity.
|
||||
return NotaryInfo(nodeInfo.legalIdentities.last(), validating)
|
||||
}
|
||||
|
||||
private fun toNodeInfo(): NodeInfo = notaryNodeInfoFile.readObject<SignedNodeInfo>().verified()
|
||||
}
|
||||
|
||||
data class ParametersUpdateConfig(val description: String, val updateDeadline: Instant) {
|
||||
|
@ -0,0 +1,122 @@
|
||||
package com.r3.corda.networkmanage.doorman
|
||||
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequestStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.entity.UpdateStatus
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.config.parseAs
|
||||
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||
import java.nio.file.Path
|
||||
import java.time.Instant
|
||||
|
||||
class ParametersUpdateHandler(val csrStorage: CertificateSigningRequestStorage, val networkMapStorage: NetworkMapStorage) {
|
||||
companion object {
|
||||
private val logger = contextLogger()
|
||||
}
|
||||
|
||||
fun loadParametersFromFile(file: Path): NetworkParametersCmd.Set {
|
||||
val netParamsConfig = ConfigFactory.parseFile(file.toFile(), ConfigParseOptions.defaults())
|
||||
.parseAs<NetworkParametersConfig>()
|
||||
checkNotaryCertificates(netParamsConfig.notaries.map { it.nodeInfo })
|
||||
return NetworkParametersCmd.Set.fromConfig(netParamsConfig)
|
||||
}
|
||||
|
||||
fun processNetworkParameters(networkParametersCmd: NetworkParametersCmd) {
|
||||
when (networkParametersCmd) {
|
||||
is NetworkParametersCmd.Set -> handleSetNetworkParameters(networkParametersCmd)
|
||||
NetworkParametersCmd.FlagDay -> handleFlagDay()
|
||||
NetworkParametersCmd.CancelUpdate -> handleCancelUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkNotaryCertificates(notaryNodeInfos: List<NodeInfo>) {
|
||||
notaryNodeInfos.forEach { notaryInfo ->
|
||||
val cert = notaryInfo.legalIdentitiesAndCerts.last().certPath.x509Certificates.find {
|
||||
val certRole = CertRole.extract(it)
|
||||
certRole == CertRole.SERVICE_IDENTITY || certRole == CertRole.NODE_CA
|
||||
}
|
||||
cert ?: throw IllegalArgumentException("The notary certificate path does not contain SERVICE_IDENTITY or NODE_CA role in it")
|
||||
csrStorage.getValidCertificatePath(cert.publicKey)
|
||||
?: throw IllegalArgumentException("Notary with node info: $notaryInfo is not registered with the doorman")
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSetNetworkParameters(setNetParams: NetworkParametersCmd.Set) {
|
||||
logger.info("maxMessageSize is not currently wired in the nodes")
|
||||
val activeNetParams = networkMapStorage.getNetworkMaps().publicNetworkMap?.networkParameters?.networkParameters
|
||||
if (activeNetParams == null) {
|
||||
require(setNetParams.parametersUpdate == null) {
|
||||
"'parametersUpdate' specified in network parameters file but there are no network parameters to update"
|
||||
}
|
||||
val initialNetParams = setNetParams.toNetworkParameters(modifiedTime = Instant.now(), epoch = 1)
|
||||
logger.info("Saving initial network parameters to be signed:\n$initialNetParams")
|
||||
networkMapStorage.saveNetworkParameters(initialNetParams, null)
|
||||
println("Saved initial network parameters to be signed:\n$initialNetParams")
|
||||
} else {
|
||||
val parametersUpdate = requireNotNull(setNetParams.parametersUpdate) {
|
||||
"'parametersUpdate' not specified in network parameters file but there is already an active set of network parameters"
|
||||
}
|
||||
|
||||
setNetParams.checkCompatibility(activeNetParams)
|
||||
|
||||
val latestNetParams = checkNotNull(networkMapStorage.getLatestNetworkParameters()?.networkParameters) {
|
||||
"Something has gone wrong! We have an active set of network parameters ($activeNetParams) but apparently no latest network parameters!"
|
||||
}
|
||||
|
||||
// It's not necessary that latestNetParams is the current active network parameters. It can be the network
|
||||
// parameters from a previous update attempt which has't activated yet. We still take the epoch value for this
|
||||
// new set from latestNetParams to make sure the advertised update attempts have incrementing epochs.
|
||||
// This has the implication that *active* network parameters may have gaps in their epochs.
|
||||
val newNetParams = setNetParams.toNetworkParameters(modifiedTime = Instant.now(), epoch = latestNetParams.epoch + 1)
|
||||
|
||||
logger.info("Enabling update to network parameters:\n$newNetParams\n$parametersUpdate")
|
||||
|
||||
require(!sameNetworkParameters(latestNetParams, newNetParams)) { "New network parameters are the same as the latest ones" }
|
||||
|
||||
networkMapStorage.saveNewParametersUpdate(newNetParams, parametersUpdate.description, parametersUpdate.updateDeadline)
|
||||
|
||||
logger.info("Update enabled")
|
||||
println("Enabled update to network parameters:\n$newNetParams\n$parametersUpdate")
|
||||
}
|
||||
}
|
||||
|
||||
private fun sameNetworkParameters(params1: NetworkParameters, params2: NetworkParameters): Boolean {
|
||||
return params1.copy(epoch = 1, modifiedTime = Instant.MAX) == params2.copy(epoch = 1, modifiedTime = Instant.MAX)
|
||||
}
|
||||
|
||||
private fun handleFlagDay() {
|
||||
val parametersUpdate = checkNotNull(networkMapStorage.getCurrentParametersUpdate()) {
|
||||
"No network parameters updates are scheduled"
|
||||
}
|
||||
check(Instant.now() >= parametersUpdate.updateDeadline) {
|
||||
"Update deadline of ${parametersUpdate.updateDeadline} hasn't passed yet"
|
||||
}
|
||||
val latestNetParamsEntity = networkMapStorage.getLatestNetworkParameters()
|
||||
check(parametersUpdate.networkParameters.hash == networkMapStorage.getLatestNetworkParameters()?.hash) {
|
||||
"The latest network parameters is not the scheduled one:\n${latestNetParamsEntity?.networkParameters}\n${parametersUpdate.toParametersUpdate()}"
|
||||
}
|
||||
val activeNetParams = networkMapStorage.getNetworkMaps().publicNetworkMap?.networkParameters
|
||||
check(parametersUpdate.networkParameters.isSigned) {
|
||||
"Parameters we are trying to switch to haven't been signed yet"
|
||||
}
|
||||
logger.info("""Flag day has occurred, however the new network parameters won't be active until the new network map is signed.
|
||||
From: ${activeNetParams?.networkParameters}
|
||||
To: ${parametersUpdate.networkParameters.networkParameters}""")
|
||||
networkMapStorage.setParametersUpdateStatus(parametersUpdate, UpdateStatus.FLAG_DAY)
|
||||
}
|
||||
|
||||
private fun handleCancelUpdate() {
|
||||
val parametersUpdate = checkNotNull(networkMapStorage.getCurrentParametersUpdate()) {
|
||||
"No network parameters updates are scheduled"
|
||||
}
|
||||
logger.info("""Cancelling parameters update: ${parametersUpdate.toParametersUpdate()}.
|
||||
However, the network map will continue to advertise this update until the new one is signed.""")
|
||||
networkMapStorage.setParametersUpdateStatus(parametersUpdate, UpdateStatus.CANCELLED)
|
||||
println("Done with cancel update")
|
||||
}
|
||||
}
|
@ -254,12 +254,12 @@ class PersistentNodeInfoStorageTest : TestBase() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun createValidNodeInfo(organisation: String, storage: CertificateSigningRequestStorage): Pair<NodeInfo, PrivateKey> {
|
||||
internal fun createValidNodeInfo(organisation: String, storage: CertificateSigningRequestStorage): Pair<NodeInfo, PrivateKey> {
|
||||
val (nodeInfo, keys) = createValidNodeInfo(storage, CertRole.NODE_CA to organisation)
|
||||
return Pair(nodeInfo, keys.single())
|
||||
}
|
||||
|
||||
private fun createValidNodeInfo(storage: CertificateSigningRequestStorage, vararg identities: Pair<CertRole, String>): Pair<NodeInfo, List<PrivateKey>> {
|
||||
internal fun createValidNodeInfo(storage: CertificateSigningRequestStorage, vararg identities: Pair<CertRole, String>): Pair<NodeInfo, List<PrivateKey>> {
|
||||
val nodeInfoBuilder = TestNodeInfoBuilder()
|
||||
val keys = identities.map { (certRole, name) ->
|
||||
val (csr, nodeKeyPair) = createRequest(name, certRole = certRole)
|
||||
|
@ -41,7 +41,10 @@ class NotaryConfigTest {
|
||||
val file = fs.getPath(UUID.randomUUID().toString())
|
||||
signedNodeInfo.serialize().open().copyTo(file)
|
||||
|
||||
val notaryInfo = NotaryConfig(file, true).toNotaryInfo()
|
||||
val notaryConfig = NotaryConfig(file, true)
|
||||
val notaryInfo = notaryConfig.toNotaryInfo()
|
||||
val nodeInfoFromFile = notaryConfig.nodeInfo
|
||||
assertThat(nodeInfoFromFile).isEqualTo(nodeInfo)
|
||||
assertThat(notaryInfo.identity).isEqualTo(nodeInfo.legalIdentities[0])
|
||||
assertThat(notaryInfo.validating).isTrue()
|
||||
}
|
||||
|
@ -0,0 +1,90 @@
|
||||
package com.r3.corda.networkmanage.doorman
|
||||
|
||||
import com.nhaarman.mockito_kotlin.mock
|
||||
import com.r3.corda.networkmanage.common.persistence.*
|
||||
import com.typesafe.config.*
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.internal.signWith
|
||||
import net.corda.testing.node.MockServices
|
||||
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 org.junit.rules.TemporaryFolder
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
|
||||
class ParametersUpdateHandlerTest {
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
private lateinit var persistence: CordaPersistence
|
||||
private lateinit var csrStorage: CertificateSigningRequestStorage
|
||||
private lateinit var netParamsUpdateHandler: ParametersUpdateHandler
|
||||
|
||||
@Before
|
||||
fun init() {
|
||||
persistence = configureDatabase(MockServices.makeTestDataSourceProperties(), DatabaseConfig(runMigration = true))
|
||||
csrStorage = PersistentCertificateSigningRequestStorage(persistence)
|
||||
netParamsUpdateHandler = ParametersUpdateHandler(csrStorage, mock())
|
||||
}
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
persistence.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `load parameters from file and check notaries registered`() {
|
||||
// Create identities and put them into CertificateSigningStorage
|
||||
val (nodeInfo1, keys1) = createValidNodeInfo(csrStorage, CertRole.NODE_CA to "Alice", CertRole.SERVICE_IDENTITY to "Alice Notary")
|
||||
val (nodeInfo2, keys2) = createValidNodeInfo(csrStorage, CertRole.NODE_CA to "Bob Notary")
|
||||
val signedNodeInfo1 = nodeInfo1.signWith(keys1)
|
||||
val signedNodeInfo2 = nodeInfo2.signWith(keys2)
|
||||
val notaryFile1 = tempFolder.root.toPath() / UUID.randomUUID().toString()
|
||||
val notaryFile2 = tempFolder.root.toPath() / UUID.randomUUID().toString()
|
||||
signedNodeInfo1.serialize().open().copyTo(notaryFile1)
|
||||
signedNodeInfo2.serialize().open().copyTo(notaryFile2)
|
||||
val configFile = tempFolder.root.toPath() / UUID.randomUUID().toString()
|
||||
saveConfig(configFile, listOf(notaryFile1, notaryFile2))
|
||||
val cmd = netParamsUpdateHandler.loadParametersFromFile(configFile)
|
||||
assertThat(cmd.notaries.map { it.identity }).containsExactly(nodeInfo1.legalIdentities.last(), nodeInfo2.legalIdentities.last())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `notaries not registered`() {
|
||||
// Create notary NodeInfo with SERVICE_IDENTITY role but don't put in CertificateSigningStorage
|
||||
val (nodeInfo, keys) = createValidNodeInfo(mock(), CertRole.NODE_CA to "Alice", CertRole.SERVICE_IDENTITY to "Alice Notary")
|
||||
val signedNodeInfo = nodeInfo.signWith(keys)
|
||||
val notaryFile = tempFolder.root.toPath() / UUID.randomUUID().toString()
|
||||
signedNodeInfo.serialize().open().copyTo(notaryFile)
|
||||
val configFile = tempFolder.root.toPath() / UUID.randomUUID().toString()
|
||||
saveConfig(configFile, listOf(notaryFile))
|
||||
Assertions.assertThatThrownBy {netParamsUpdateHandler.loadParametersFromFile(configFile)}
|
||||
.isInstanceOf(IllegalArgumentException::class.java)
|
||||
.hasMessageContaining("is not registered with the doorman")
|
||||
}
|
||||
|
||||
private fun saveConfig(configFile: Path, notaryFiles: List<Path>) {
|
||||
val config = ConfigValueFactory.fromMap(
|
||||
mapOf("minimumPlatformVersion" to 1,
|
||||
"maxMessageSize" to 10485760,
|
||||
"maxTransactionSize" to 10485760,
|
||||
"notaries" to notaryFiles.map { mapOf("notaryNodeInfoFile" to it.toString(), "validating" to true) }
|
||||
)
|
||||
|
||||
).toConfig()
|
||||
val configString = config.root().render(ConfigRenderOptions.defaults())
|
||||
configFile.writeText(configString)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user