ENT-1586 Update entire service stack to support private network maps (#691)

* private network map
This commit is contained in:
Patrick Kuo 2018-04-13 15:54:53 +01:00 committed by GitHub
parent 05ec885afd
commit 7f8c36faa0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 394 additions and 188 deletions

View File

@ -164,10 +164,10 @@ class HsmSigningServiceTest : HsmBaseTest() {
// when
initialiseSerialization()
networkMapStorage.saveNetworkParameters(networkMapParameters, hsmDataSigner.signBytes(networkMapParameters.serialize().bytes))
networkMapSigner.signNetworkMap()
networkMapSigner.signNetworkMaps()
// then
val persistedNetworkMap = networkMapStorage.getActiveNetworkMap()!!.toSignedNetworkMap().verified()
val persistedNetworkMap = networkMapStorage.getNetworkMaps().publicNetworkMap!!.toSignedNetworkMap().verified()
assertEquals(networkMapParameters.serialize().hash, persistedNetworkMap.networkParameterHash)
assertThat(persistedNetworkMap.nodeInfoHashes).isEmpty()
}

View File

@ -22,24 +22,25 @@ import java.time.Instant
* Data access object interface for NetworkMap persistence layer
*/
// TODO This storage abstraction needs some thought. Some of the methods clearly don't make sense e.g. setParametersUpdateStatus.
// TODO: We should avoid exposing entity objects.
// The NetworkMapSignerTest uses a mock of this which means we need to provide methods for every trivial db operation.
interface NetworkMapStorage {
/**
* Returns the active network map, or null
* Returns the network maps containing public network map and private network maps.
*/
fun getActiveNetworkMap(): NetworkMapEntity?
fun getNetworkMaps(): NetworkMaps
/**
* Persist the new active network map, replacing any existing network map.
* Persist the new network map for provided network ID, replacing any existing network map.
* The map will be stored as public network map if [networkId] = null.
*/
fun saveNewActiveNetworkMap(networkMapAndSigned: NetworkMapAndSigned)
fun saveNewNetworkMap(networkId: String? = null, networkMapAndSigned: NetworkMapAndSigned)
/**
* Retrieves node info hashes where [NodeInfoEntity.isCurrent] is true and the certificate status is [CertificateStatus.VALID]
* Retrieves node info hashes for both public and private networks where [NodeInfoEntity.isCurrent] is true and the certificate status is [CertificateStatus.VALID]
* Nodes should have declared that they are using correct set of parameters.
*/
// TODO "Active" is the wrong word here
fun getActiveNodeInfoHashes(): List<SecureHash>
fun getNodeInfoHashes(): NodeInfoHashes
/**
* Retrieve the signed with certificate network parameters by their hash. The hash is that of the underlying
@ -79,3 +80,9 @@ interface NetworkMapStorage {
*/
fun switchFlagDay(update: ParametersUpdateEntity)
}
data class NetworkMaps(val publicNetworkMap: NetworkMapEntity?, val privateNetworkMap: Map<String, NetworkMapEntity>) {
val allNodeInfoHashes: Set<SecureHash> = privateNetworkMap.flatMap { it.value.networkMap.nodeInfoHashes }.toSet() + (publicNetworkMap?.networkMap?.nodeInfoHashes ?: emptySet())
}
data class NodeInfoHashes(val publicNodeInfoHashes: List<SecureHash>, val privateNodeInfoHashes: Map<String, List<SecureHash>>)

View File

@ -26,20 +26,21 @@ import java.time.Instant
* Database implementation of the [NetworkMapStorage] interface
*/
class PersistentNetworkMapStorage(private val database: CordaPersistence) : NetworkMapStorage {
override fun getActiveNetworkMap(): NetworkMapEntity? {
companion object {
// Used internally to identify global network map in database table.
private const val PUBLIC_NETWORK_ID = "PUBLIC_NETWORK"
}
override fun getNetworkMaps(): NetworkMaps {
return database.transaction {
val builder = session.criteriaBuilder
val query = builder.createQuery(NetworkMapEntity::class.java).run {
from(NetworkMapEntity::class.java).run {
orderBy(builder.desc(get<Long>(NetworkMapEntity::version.name)))
}
}
// We want the latest signed entry
session.createQuery(query).setMaxResults(1).uniqueResult()
val networkMapEntities = session.createQuery("from ${NetworkMapEntity::class.java.name}", NetworkMapEntity::class.java)
.resultList
.associateBy { it.id }
NetworkMaps(networkMapEntities[PUBLIC_NETWORK_ID], networkMapEntities.filterKeys { it != PUBLIC_NETWORK_ID })
}
}
override fun saveNewActiveNetworkMap(networkMapAndSigned: NetworkMapAndSigned) {
override fun saveNewNetworkMap(networkId: String?, networkMapAndSigned: NetworkMapAndSigned) {
val (networkMap, signedNetworkMap) = networkMapAndSigned
database.transaction {
val networkParametersEntity = checkNotNull(getNetworkParametersEntity(networkMap.networkParameterHash)) {
@ -48,7 +49,8 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
check(networkParametersEntity.isSigned) {
"Network parameters ${networkMap.networkParameterHash} are not signed"
}
session.save(NetworkMapEntity(
session.merge(NetworkMapEntity(
id = networkId ?: PUBLIC_NETWORK_ID,
networkMap = networkMap,
signature = signedNetworkMap.sig.bytes,
certificate = signedNetworkMap.sig.by,
@ -65,10 +67,10 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
}
}
override fun getActiveNodeInfoHashes(): List<SecureHash> {
override fun getNodeInfoHashes(): NodeInfoHashes {
return database.transaction {
val builder = session.criteriaBuilder
val query = builder.createQuery(String::class.java).run {
val query = builder.createTupleQuery().run {
from(NodeInfoEntity::class.java).run {
val certStatusExpression = get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name)
.get<CertificateDataEntity>(CertificateSigningRequestEntity::certificateData.name)
@ -77,10 +79,16 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
// isn't needed.
val certStatusEq = builder.equal(certStatusExpression, CertificateStatus.VALID)
val isCurrentNodeInfo = builder.isTrue(get<Boolean>(NodeInfoEntity::isCurrent.name))
select(get<String>(NodeInfoEntity::nodeInfoHash.name)).where(builder.and(certStatusEq, isCurrentNodeInfo))
val networkIdSelector = get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name)
.get<PrivateNetworkEntity>(CertificateSigningRequestEntity::privateNetwork.name)
.get<String>(PrivateNetworkEntity::networkId.name)
multiselect(networkIdSelector, get<String>(NodeInfoEntity::nodeInfoHash.name)).where(builder.and(certStatusEq, isCurrentNodeInfo))
}
}
session.createQuery(query).resultList.map { SecureHash.parse(it) }
val allNodeInfos = session.createQuery(query).resultList.groupBy { it[0]?.toString() ?: PUBLIC_NETWORK_ID }.mapValues { it.value.map { SecureHash.parse(it.get(1, String::class.java)) } }
NodeInfoHashes(allNodeInfos[PUBLIC_NETWORK_ID] ?: emptyList(), allNodeInfos.filterKeys { it != PUBLIC_NETWORK_ID })
}
}
@ -90,19 +98,15 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
val hash = serialized.hash
return database.transaction {
val entity = getNetworkParametersEntity(hash)
val newNetworkParamsEntity = if (entity != null) {
entity.copy(
val newNetworkParamsEntity = entity?.copy(
signature = signature?.bytes,
certificate = signature?.by
)
} else {
NetworkParametersEntity(
) ?: NetworkParametersEntity(
networkParameters = networkParameters,
hash = hash.toString(),
signature = signature?.bytes,
certificate = signature?.by
)
}
session.merge(newNetworkParamsEntity) as NetworkParametersEntity
}
}

View File

@ -14,16 +14,20 @@ import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.SignedNetworkMap
import org.hibernate.envers.Audited
import org.hibernate.envers.RelationTargetAuditMode
import java.io.Serializable
import java.security.cert.X509Certificate
import java.time.Instant
import javax.persistence.*
@Entity
@Audited
@Table(name = "network_map")
class NetworkMapEntity(
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
val version: Long? = null,
@Column(name = "id")
val id: String,
@Lob
@Column(name = "serialized_network_map", nullable = false)
@ -41,7 +45,11 @@ class NetworkMapEntity(
@ManyToOne(optional = false, fetch = FetchType.EAGER)
@JoinColumn(name = "network_parameters", nullable = false)
val networkParameters: NetworkParametersEntity
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
val networkParameters: NetworkParametersEntity,
@Column(nullable = false)
val timestamp: Instant = Instant.now()
) : Serializable {
fun toSignedNetworkMap(): SignedNetworkMap {
return SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(certificate, signature))

View File

@ -11,13 +11,17 @@
package com.r3.corda.networkmanage.common.signer
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import com.r3.corda.networkmanage.common.persistence.entity.UpdateStatus.*
import com.r3.corda.networkmanage.common.persistence.entity.NetworkMapEntity
import com.r3.corda.networkmanage.common.persistence.entity.UpdateStatus.FLAG_DAY
import com.r3.corda.networkmanage.common.persistence.entity.UpdateStatus.NEW
import com.r3.corda.networkmanage.common.utils.join
import net.corda.core.crypto.SecureHash
import net.corda.core.node.NetworkParameters
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.debug
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.NetworkMapAndSigned
import net.corda.nodeapi.internal.network.ParametersUpdate
class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) {
private companion object {
@ -27,12 +31,46 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private
/**
* Signs the network map and latest network parameters if they haven't been signed yet.
*/
fun signNetworkMap() {
val latestNetworkParameters = networkMapStorage.getLatestNetworkParameters()
if (latestNetworkParameters == null) {
logger.debug("No network parameters present")
return
fun signNetworkMaps() {
val (publicNetworkMap, privateNetworkMaps) = networkMapStorage.getNetworkMaps()
val (publicNodeInfoHashes, privateNodeInfoHashes) = networkMapStorage.getNodeInfoHashes()
val (networkParameterHash, parametersUpdate) = maybeUpdateNetworkParameters(publicNetworkMap?.networkMap?.networkParameterHash)
logger.debug { "Current network parameters: $networkParameterHash" }
// Process public network map.
maybeSignNetworkMap(publicNetworkMap, publicNodeInfoHashes, parametersUpdate, networkParameterHash)
// Process each private network map.
privateNetworkMaps.join(privateNodeInfoHashes).forEach { networkId, (currentNetworkMap, nodeInfoHashes) ->
maybeSignNetworkMap(currentNetworkMap, nodeInfoHashes, parametersUpdate, networkParameterHash, networkId)
}
}
private fun maybeSignNetworkMap(currentNetworkMap: NetworkMapEntity?, nodeInfoHashes: List<SecureHash>?, parametersUpdate: ParametersUpdate?, networkParameterHash: SecureHash, networkId: String? = null) {
val printableNetworkId = networkId ?: "Public Network"
if (currentNetworkMap == null) {
logger.info("There is currently no network map for network '$printableNetworkId'")
} else {
logger.debug { "Current network map for network '$printableNetworkId': ${currentNetworkMap.networkMap}" }
}
logger.debug { "Retrieved node info hashes for network '$printableNetworkId' :\n${nodeInfoHashes?.joinToString("\n")}" }
val newNetworkMap = NetworkMap(nodeInfoHashes ?: emptyList(), networkParameterHash, parametersUpdate)
logger.debug { "Potential new network map for network '$printableNetworkId': $newNetworkMap" }
if (currentNetworkMap?.networkMap != newNetworkMap) {
val newNetworkMapAndSigned = NetworkMapAndSigned(newNetworkMap) { signer.signBytes(it.bytes) }
networkMapStorage.saveNewNetworkMap(networkId, newNetworkMapAndSigned)
logger.info("Signed new network map for network '$printableNetworkId' : $newNetworkMap")
} else {
logger.debug("Current network map for network '$printableNetworkId' is up-to-date")
}
}
private fun maybeUpdateNetworkParameters(currentNetworkParametersHash: SecureHash?): Pair<SecureHash, ParametersUpdate?> {
val latestNetworkParameters = requireNotNull(networkMapStorage.getLatestNetworkParameters()) { "No network parameters present" }
logger.debug { "Retrieved latest network parameters: ${latestNetworkParameters.networkParameters}" }
val parametersUpdate = networkMapStorage.getCurrentParametersUpdate()
@ -41,16 +79,6 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private
"The latest network parameters are not the scheduled updated ones"
}
val activeNetworkMap = networkMapStorage.getActiveNetworkMap()
if (activeNetworkMap == null) {
logger.info("There is currently no network map")
} else {
logger.debug { "Current network map: ${activeNetworkMap.networkMap}" }
}
val activeNetworkParameters = activeNetworkMap?.networkParameters
logger.debug { "Current network map parameters: ${activeNetworkParameters?.networkParameters}" }
// We persist signed parameters only if they were not persisted before (they are not in currentSignedNetworkMap as
// normal parameters or as an update)
if (!latestNetworkParameters.isSigned) {
@ -59,29 +87,14 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private
logger.debug { "No need to sign any network parameters as they're up-to-date" }
}
val parametersToNetworkMap = if (parametersUpdate?.status == FLAG_DAY || activeNetworkParameters == null) {
val parametersToNetworkMap = if (parametersUpdate?.status == FLAG_DAY || currentNetworkParametersHash == null) {
parametersUpdate?.let { networkMapStorage.switchFlagDay(it) }
latestNetworkParameters
SecureHash.parse(latestNetworkParameters.hash)
} else {
activeNetworkParameters
currentNetworkParametersHash
}
val nodeInfoHashes = networkMapStorage.getActiveNodeInfoHashes()
logger.debug { "Retrieved node info hashes:\n${nodeInfoHashes.joinToString("\n")}" }
val newNetworkMap = NetworkMap(
nodeInfoHashes,
SecureHash.parse(parametersToNetworkMap.hash),
parametersUpdate?.let { if (it.status == NEW) it.toParametersUpdate() else null })
logger.debug { "Potential new network map: $newNetworkMap" }
if (activeNetworkMap?.networkMap != newNetworkMap) {
val newNetworkMapAndSigned = NetworkMapAndSigned(newNetworkMap) { signer.signBytes(it.bytes) }
networkMapStorage.saveNewActiveNetworkMap(newNetworkMapAndSigned)
logger.info("Signed new network map: $newNetworkMap")
} else {
logger.debug("Current network map is up-to-date")
}
return Pair(parametersToNetworkMap, parametersUpdate?.let { if (it.status == NEW) it.toParametersUpdate() else null })
}
fun signAndPersistNetworkParameters(networkParameters: NetworkParameters) {

View File

@ -86,3 +86,5 @@ fun PKCS10CertificationRequest.getCertRole(): CertRole {
* Helper method to extract email from certificate signing request.
*/
fun PKCS10CertificationRequest.getEmail(): String = firstAttributeValue(BCStyle.E).toString()
fun <K, V, U> Map<K, V>.join(otherMap: Map<K, U>): Map<K, Pair<V?, U?>> = (keys + otherMap.keys).map { it to Pair(get(it), otherMap[it]) }.toMap()

View File

@ -84,7 +84,7 @@ class NetworkManagementServer(dataSourceProperties: Properties,
val scheduledExecutor = Executors.newScheduledThreadPool(1)
scheduledExecutor.scheduleAtFixedRate({
try {
localNetworkMapSigner.signNetworkMap()
localNetworkMapSigner.signNetworkMaps()
} catch (e: Exception) {
// Log the error and carry on.
logger.error("Unable to sign network map", e)
@ -201,7 +201,7 @@ class NetworkManagementServer(dataSourceProperties: Properties,
private fun handleSetNetworkParameters(setNetParams: NetworkParametersCmd.Set) {
logger.info("maxMessageSize is not currently wired in the nodes")
val activeNetParams = networkMapStorage.getActiveNetworkMap()?.networkParameters?.networkParameters
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"
@ -253,7 +253,7 @@ class NetworkManagementServer(dataSourceProperties: Properties,
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.getActiveNetworkMap()?.networkParameters
val activeNetParams = networkMapStorage.getNetworkMaps().publicNetworkMap?.networkParameters
check(parametersUpdate.networkParameters.isSigned) {
"Parameters we are trying to switch to haven't been signed yet"
}

View File

@ -14,13 +14,14 @@ import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.LoadingCache
import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequestStorage
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import com.r3.corda.networkmanage.common.persistence.NetworkMaps
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
import com.r3.corda.networkmanage.common.persistence.entity.NetworkMapEntity
import com.r3.corda.networkmanage.doorman.NetworkMapConfig
import com.r3.corda.networkmanage.doorman.webservice.NetworkMapWebService.Companion.NETWORK_MAP_PATH
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.sha256
import net.corda.core.internal.CertRole
import net.corda.core.internal.readObject
import net.corda.core.node.NetworkParameters
@ -34,12 +35,14 @@ import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.crypto.X509Utilities.validateCertPath
import net.corda.nodeapi.internal.crypto.x509
import net.corda.nodeapi.internal.crypto.x509Certificates
import net.corda.nodeapi.internal.network.SignedNetworkMap
import java.io.InputStream
import java.security.InvalidKeyException
import java.security.SignatureException
import java.security.cert.CertPathValidatorException
import java.time.Duration
import java.time.Instant
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.concurrent.TimeUnit
import javax.servlet.http.HttpServletRequest
import javax.ws.rs.*
@ -60,15 +63,11 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
const val NETWORK_MAP_PATH = "network-map"
}
private val networkMapCache: LoadingCache<Boolean, CachedData> = Caffeine.newBuilder()
private val networkMapCache: LoadingCache<Boolean, NetworkMaps> = Caffeine.newBuilder()
.expireAfterWrite(config.cacheTimeout, TimeUnit.MILLISECONDS)
.build {
networkMapStorage.getActiveNetworkMap()?.let {
logger.info("Re-publishing network map")
val networkMap = it.networkMap
val signedNetworkMap = it.toSignedNetworkMap()
CachedData(signedNetworkMap, networkMap.nodeInfoHashes.toSet(), it.networkParameters.networkParameters)
}
networkMapStorage.getNetworkMaps()
}
private val nodeInfoCache: LoadingCache<SecureHash, SignedNodeInfo> = Caffeine.newBuilder()
@ -76,9 +75,9 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
.softValues()
.build(nodeInfoStorage::getNodeInfo)
private val currentSignedNetworkMap: SignedNetworkMap? get() = networkMapCache.get(true)?.signedNetworkMap
private val currentNodeInfoHashes: Set<SecureHash> get() = networkMapCache.get(true)?.nodeInfoHashes ?: emptySet()
private val currentNetworkParameters: NetworkParameters? get() = networkMapCache.get(true)?.networkParameters
private val networkMaps: NetworkMaps? get() = networkMapCache[true]
private val currentNodeInfoHashes: Set<SecureHash> get() = networkMaps?.allNodeInfoHashes ?: emptySet()
private val currentNetworkParameters: NetworkParameters? get() = networkMaps?.publicNetworkMap?.networkParameters?.networkParameters
@POST
@Path("publish")
@ -124,7 +123,14 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
@GET
@Produces(MediaType.APPLICATION_OCTET_STREAM)
fun getNetworkMap(): Response = createResponse(currentSignedNetworkMap, addCacheTimeout = true)
fun getNetworkMap(): Response = createNetworkMapResponse(networkMaps?.publicNetworkMap)
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
fun getNetworkMap(@PathParam("id") privateNetworkID: String?): Response = createNetworkMapResponse(networkMaps?.privateNetworkMap?.get(privateNetworkID))
private fun createNetworkMapResponse(networkMap: NetworkMapEntity?) = createResponse(networkMap?.toSignedNetworkMap(), addCacheTimeout = true, timestamp = networkMap?.timestamp)
@GET
@Path("node-info/{nodeInfoHash}")
@ -182,12 +188,15 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
}
}
private fun createResponse(payload: Any?, addCacheTimeout: Boolean = false): Response {
private fun createResponse(payload: Any?, addCacheTimeout: Boolean = false, timestamp: Instant? = null): Response {
return if (payload != null) {
val ok = Response.ok(payload.serialize().bytes)
if (addCacheTimeout) {
ok.header("Cache-Control", "max-age=${Duration.ofMillis(config.cacheTimeout).seconds}")
}
timestamp?.let {
ok.header("Last-Modified", DateTimeFormatter.RFC_1123_DATE_TIME.withZone(ZoneId.of("GMT")).format(it))
}
ok
} else {
status(Response.Status.NOT_FOUND)
@ -211,8 +220,4 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
class NetworkMapNotInitialisedException(message: String?) : Exception(message)
class RequestException(message: String) : Exception(message)
private data class CachedData(val signedNetworkMap: SignedNetworkMap,
val nodeInfoHashes: Set<SecureHash>,
val networkParameters: NetworkParameters)
}

View File

@ -55,7 +55,7 @@ class NetworkMapProcessor(private val config: NetworkMapCertificateConfig,
val networkMapSigner = NetworkMapSigner(networkMapStorage, signer)
try {
logger.info("Executing network map signing...")
networkMapSigner.signNetworkMap()
networkMapSigner.signNetworkMaps()
} catch (e: Exception) {
logger.error("Exception thrown while signing network map", e)
}

View File

@ -13,4 +13,5 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
<include file="migration/network-manager.changelog-init.xml"/>
<include file="migration/network-manager.changelog-network-map-table-change.xml"/>
</databaseChangeLog>

View File

@ -0,0 +1,69 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<!--
~ R3 Proprietary and Confidential
~
~ Copyright (c) 2018 R3 Limited. All rights reserved.
~
~ The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
~
~ Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
<changeSet author="R3.Corda" id="1522853587063-1">
<dropTable tableName="network_map"/>
<createTable tableName="network_map">
<column name="id" type="NVARCHAR(64)">
<constraints nullable="false" primaryKey="true" primaryKeyName="PK__NM_ID"/>
</column>
<column name="cert" type="BLOB">
<constraints nullable="false"/>
</column>
<column name="serialized_network_map" type="BLOB">
<constraints nullable="false"/>
</column>
<column name="signature" type="BLOB">
<constraints nullable="false"/>
</column>
<column name="network_parameters" type="NVARCHAR(64)">
<constraints nullable="false"/>
</column>
<column name="timestamp" type="TIMESTAMP">
<constraints nullable="false"/>
</column>
</createTable>
<addForeignKeyConstraint baseColumnNames="network_parameters" baseTableName="network_map"
constraintName="FK__NM__NP"
referencedColumnNames="hash" referencedTableName="network_parameters"/>
<createTable tableName="network_map_AUD">
<column name="id" type="NVARCHAR(64)">
<constraints nullable="false"/>
</column>
<column name="REV" type="INT">
<constraints nullable="false"/>
</column>
<column name="REVTYPE" type="TINYINT"/>
<column name="cert" type="BLOB"/>
<column name="serialized_network_map" type="BLOB"/>
<column name="signature" type="BLOB"/>
<column name="network_parameters" type="NVARCHAR(64)"/>
<column name="timestamp" type="TIMESTAMP"/>
</createTable>
<addPrimaryKey columnNames="id, rev" constraintName="PK__NMA__RID"
tableName="network_map_AUD"/>
<createIndex indexName="IDX__NMA__REV" tableName="network_map_AUD">
<column name="REV"/>
</createIndex>
<addForeignKeyConstraint baseColumnNames="REV" baseTableName="network_map_AUD"
constraintName="FK__NMA__REV"
referencedColumnNames="REV" referencedTableName="REVINFO"/>
</changeSet>
</databaseChangeLog>

View File

@ -1,5 +1,6 @@
package com.r3.corda.networkmanage
import com.r3.corda.networkmanage.common.persistence.NetworkMaps
import com.r3.corda.networkmanage.common.persistence.entity.NetworkMapEntity
import com.r3.corda.networkmanage.common.persistence.entity.NetworkParametersEntity
import net.corda.core.crypto.SecureHash
@ -10,6 +11,7 @@ import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.ParametersUpdate
import net.corda.testing.common.internal.testNetworkParameters
import java.time.Instant
fun createNetworkParametersEntity(signingCertAndKeyPair: CertificateAndKeyPair = createDevNetworkMapCa(),
networkParameters: NetworkParameters = testNetworkParameters()): NetworkParametersEntity {
@ -31,23 +33,41 @@ fun createNetworkParametersEntityUnsigned(networkParameters: NetworkParameters =
)
}
fun createNetworkMapEntity(signingCertAndKeyPair: CertificateAndKeyPair = createDevNetworkMapCa(),
fun createNetworkMaps(signingCertAndKeyPair: CertificateAndKeyPair = createDevNetworkMapCa(),
netParamsEntity: NetworkParametersEntity,
nodeInfoHashes: List<SecureHash> = emptyList(),
parametersUpdate: ParametersUpdate? = null): NetworkMapEntity {
privateNodeInfoHashes: Map<String, List<SecureHash>> = emptyMap(),
parametersUpdate: ParametersUpdate? = null,
timestamp: Instant = Instant.now()): NetworkMaps {
val publicMapEntity = createNetworkMapEntity("PUBLIC_NETWORK", nodeInfoHashes, netParamsEntity, parametersUpdate, signingCertAndKeyPair, timestamp)
val privateNetworkMaps = privateNodeInfoHashes.mapValues {
createNetworkMapEntity(it.key, it.value, netParamsEntity, parametersUpdate, signingCertAndKeyPair, timestamp)
}
return NetworkMaps(publicMapEntity, privateNetworkMaps)
}
private fun createNetworkMapEntity(id: String,
nodeInfoHashes: List<SecureHash>,
netParamsEntity: NetworkParametersEntity,
parametersUpdate: ParametersUpdate?,
signingCertAndKeyPair: CertificateAndKeyPair,
timestamp: Instant): NetworkMapEntity {
val networkMap = NetworkMap(nodeInfoHashes, SecureHash.parse(netParamsEntity.hash), parametersUpdate)
val signedNetworkMap = signingCertAndKeyPair.sign(networkMap)
return NetworkMapEntity(
id = id,
networkMap = networkMap,
signature = signedNetworkMap.sig.bytes,
certificate = signedNetworkMap.sig.by,
networkParameters = netParamsEntity
)
networkParameters = netParamsEntity,
timestamp = timestamp)
}
fun createNetworkMapEntity(signingCertAndKeyPair: CertificateAndKeyPair = createDevNetworkMapCa(),
fun createNetworkMaps(signingCertAndKeyPair: CertificateAndKeyPair = createDevNetworkMapCa(),
networkParameters: NetworkParameters = testNetworkParameters(),
nodeInfoHashes: List<SecureHash> = emptyList()): NetworkMapEntity {
nodeInfoHashes: List<SecureHash> = emptyList(),
privateNodeInfoHashes: Map<String, List<SecureHash>> = emptyMap(),
timestamp: Instant = Instant.now()): NetworkMaps {
val netParamsEntity = createNetworkParametersEntity(signingCertAndKeyPair, networkParameters)
return createNetworkMapEntity(signingCertAndKeyPair, netParamsEntity, nodeInfoHashes)
return createNetworkMaps(signingCertAndKeyPair, netParamsEntity, nodeInfoHashes, privateNodeInfoHashes, timestamp = timestamp)
}

View File

@ -13,6 +13,7 @@ package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.common.persistence.entity.NodeInfoEntity
import com.r3.corda.networkmanage.common.persistence.entity.ParametersUpdateEntity
import com.r3.corda.networkmanage.common.persistence.entity.PrivateNetworkEntity
import com.r3.corda.networkmanage.common.persistence.entity.UpdateStatus
import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.serialize
@ -33,6 +34,7 @@ import org.junit.Before
import org.junit.Test
import java.security.cert.X509Certificate
import java.time.Instant
import java.util.*
class PersistentNetworkMapStorageTest : TestBase() {
private lateinit var persistence: CordaPersistence
@ -74,23 +76,15 @@ class PersistentNetworkMapStorageTest : TestBase() {
val networkMapAndSigned = NetworkMapAndSigned(networkMap) { networkMapCertAndKeyPair.sign(networkMap).sig }
// when
networkMapStorage.saveNewActiveNetworkMap(networkMapAndSigned)
networkMapStorage.saveNewNetworkMap(networkMapAndSigned = networkMapAndSigned)
// then
val activeNetworkMapEntity = networkMapStorage.getActiveNetworkMap()!!
val activeSignedNetworkMap = activeNetworkMapEntity.toSignedNetworkMap()
val networkMaps = networkMapStorage.getNetworkMaps()
val activeSignedNetworkMap = networkMaps.publicNetworkMap!!.toSignedNetworkMap()
val activeNetworkMap = activeSignedNetworkMap.verifiedNetworkMapCert(rootCaCert)
val activeNetworkParametersEntity = activeNetworkMapEntity.networkParameters
val activeSignedNetworkParameters = activeNetworkParametersEntity.toSignedNetworkParameters()
val activeNetworkParameters = activeSignedNetworkParameters.verifiedNetworkMapCert(rootCaCert)
assertThat(activeNetworkMap).isEqualTo(networkMap)
assertThat(activeSignedNetworkMap.sig).isEqualTo(networkMapAndSigned.signed.sig)
assertThat(activeNetworkParameters).isEqualTo(networkParameters)
assertThat(activeSignedNetworkParameters.sig).isEqualTo(networkParametersSig)
assertThat(activeNetworkParametersEntity.hash)
.isEqualTo(activeNetworkMap.networkParameterHash.toString())
.isEqualTo(networkParametersHash)
}
@Test
@ -105,26 +99,40 @@ class PersistentNetworkMapStorageTest : TestBase() {
}
@Test
fun `getValidNodeInfoHashes returns only for current node-infos`() {
fun `getValidNodeInfoHashes returns node-infos for public and private networks`() {
// given
// Create node infos.
val (signedNodeInfoA) = createValidSignedNodeInfo("TestA", requestStorage)
val (signedNodeInfoB) = createValidSignedNodeInfo("TestB", requestStorage)
val nodes = listOf("TestA", "TestB", "TestC", "TestD", "TestE")
val nodeInfos = nodes.map { it to createValidSignedNodeInfo(it, requestStorage) }.toMap()
// Put signed node info data
val nodeInfoHashA = nodeInfoStorage.putNodeInfo(signedNodeInfoA)
val nodeInfoHashB = nodeInfoStorage.putNodeInfo(signedNodeInfoB)
persistence.transaction {
val entity = session.find(NodeInfoEntity::class.java, nodeInfoHashA.toString())
session.merge(entity.copy(isCurrent = false))
val storedNodeInfos = nodeInfos.mapValues { nodeInfoStorage.putNodeInfo(it.value.first) }
val (testNet1, testNet2) = persistence.transaction {
val testNet1 = PrivateNetworkEntity(UUID.randomUUID().toString(), "TestNet1").apply { session.save(this) }
val testNet2 = PrivateNetworkEntity(UUID.randomUUID().toString(), "TestNet2").apply { session.save(this) }
// set different private network for node info
session.find(NodeInfoEntity::class.java, storedNodeInfos["TestA"].toString()).apply {
session.merge(certificateSigningRequest.copy(privateNetwork = testNet1))
}
session.find(NodeInfoEntity::class.java, storedNodeInfos["TestC"].toString()).apply {
session.merge(certificateSigningRequest.copy(privateNetwork = testNet2))
}
Pair(testNet1, testNet2)
}
// when
val validNodeInfoHashes = networkMapStorage.getActiveNodeInfoHashes()
val nodeInfoHashes = networkMapStorage.getNodeInfoHashes()
// then
assertThat(validNodeInfoHashes).containsOnly(nodeInfoHashB)
assertThat(nodeInfoHashes.publicNodeInfoHashes).containsOnlyElementsOf(storedNodeInfos.filterKeys { it !in setOf("TestA", "TestC") }.values)
assertThat(nodeInfoHashes.privateNodeInfoHashes.keys).containsOnlyElementsOf(listOf(testNet1.networkId, testNet2.networkId))
assertThat(nodeInfoHashes.privateNodeInfoHashes[testNet1.networkId]).containsOnlyElementsOf(listOf(storedNodeInfos["TestA"]))
assertThat(nodeInfoHashes.privateNodeInfoHashes[testNet2.networkId]).containsOnlyElementsOf(listOf(storedNodeInfos["TestC"]))
}
@Test
@ -169,7 +177,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
val parameterUpdate = networkMapStorage.getCurrentParametersUpdate()!!
networkMapStorage.switchFlagDay(parameterUpdate)
// when
val validNodeInfoHashes = networkMapStorage.getActiveNodeInfoHashes()
val validNodeInfoHashes = networkMapStorage.getNodeInfoHashes().publicNodeInfoHashes
// then
assertThat(validNodeInfoHashes).containsOnly(nodeInfoHashB)
}
@ -196,7 +204,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
val parameterUpdate = networkMapStorage.getCurrentParametersUpdate()!!
networkMapStorage.switchFlagDay(parameterUpdate)
// when
val validNodeInfoHashes = networkMapStorage.getActiveNodeInfoHashes()
val validNodeInfoHashes = networkMapStorage.getNodeInfoHashes().publicNodeInfoHashes
// then
assertThat(validNodeInfoHashes).containsOnly(nodeInfoHashB)
}

View File

@ -11,6 +11,7 @@
package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.common.persistence.entity.NetworkMapEntity
import com.r3.corda.networkmanage.common.persistence.entity.NodeInfoEntity
import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name
@ -127,14 +128,14 @@ class PersistentNodeInfoStorageTest : TestBase() {
val nodeInfo1Hash = nodeInfoStorage.putNodeInfo(node1)
assertEquals(node1.nodeInfo, nodeInfoStorage.getNodeInfo(nodeInfo1Hash)?.verified())
assertTrue(networkMapStorage.getActiveNodeInfoHashes().contains(nodeInfo1Hash))
assertTrue(networkMapStorage.getNodeInfoHashes().publicNodeInfoHashes.contains(nodeInfo1Hash))
// This should replace the node info.
val nodeInfo2Hash = nodeInfoStorage.putNodeInfo(node2)
// Old node info should be removed from list of current node info hashes, but still accessible if required.
assertThat(networkMapStorage.getActiveNodeInfoHashes()).doesNotContain(nodeInfo1Hash)
assertThat(networkMapStorage.getActiveNodeInfoHashes()).contains(nodeInfo2Hash)
assertThat(networkMapStorage.getNodeInfoHashes().publicNodeInfoHashes).doesNotContain(nodeInfo1Hash)
assertThat(networkMapStorage.getNodeInfoHashes().publicNodeInfoHashes).contains(nodeInfo2Hash)
assertNotNull(nodeInfoStorage.getNodeInfo(nodeInfo1Hash))
assertEquals(nodeInfo2, nodeInfoStorage.getNodeInfo(nodeInfo2.serialize().hash)?.verified())
}

View File

@ -13,9 +13,11 @@ package com.r3.corda.networkmanage.common.signer
import com.nhaarman.mockito_kotlin.*
import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import com.r3.corda.networkmanage.common.persistence.NetworkMaps
import com.r3.corda.networkmanage.common.persistence.NodeInfoHashes
import com.r3.corda.networkmanage.common.persistence.entity.ParametersUpdateEntity
import com.r3.corda.networkmanage.common.persistence.entity.UpdateStatus
import com.r3.corda.networkmanage.createNetworkMapEntity
import com.r3.corda.networkmanage.createNetworkMaps
import com.r3.corda.networkmanage.createNetworkParametersEntity
import com.r3.corda.networkmanage.createNetworkParametersEntityUnsigned
import net.corda.core.crypto.Crypto
@ -67,29 +69,29 @@ class NetworkMapSignerTest : TestBase() {
@Test
fun `signNetworkMap builds and signs network map and network parameters`() {
// given
val nodeInfoHashes = listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256())
val nodeInfoHashes = NodeInfoHashes(listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256()), emptyMap())
val latestNetParams = testNetworkParameters(epoch = 3)
val latestNetParamsEntity = createNetworkParametersEntityUnsigned(latestNetParams)
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(latestNetParamsEntity)
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(nodeInfoHashes)
whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(null)
whenever(networkMapStorage.getNodeInfoHashes()).thenReturn(nodeInfoHashes)
whenever(networkMapStorage.getNetworkMaps()).thenReturn(NetworkMaps(null, emptyMap()))
whenever(networkMapStorage.getCurrentParametersUpdate()).thenReturn(null)
// when
networkMapSigner.signNetworkMap()
networkMapSigner.signNetworkMaps()
// then
// Verify networkMapStorage calls
verify(networkMapStorage).getActiveNodeInfoHashes()
verify(networkMapStorage).getActiveNetworkMap()
verify(networkMapStorage).getNodeInfoHashes()
verify(networkMapStorage).getNetworkMaps()
verify(networkMapStorage).getCurrentParametersUpdate()
verify(networkMapStorage).getLatestNetworkParameters()
argumentCaptor<NetworkMapAndSigned>().apply {
verify(networkMapStorage).saveNewActiveNetworkMap(capture())
verify(networkMapStorage).saveNewNetworkMap(anyOrNull(), capture())
val capturedNetworkMap = firstValue.networkMap
// Parameters in network map got swapped for latest ones.
assertEquals(latestNetParams.serialize().hash, capturedNetworkMap.networkParameterHash)
assertThat(capturedNetworkMap.nodeInfoHashes).isEqualTo(nodeInfoHashes)
assertThat(capturedNetworkMap.nodeInfoHashes).isEqualTo(nodeInfoHashes.publicNodeInfoHashes)
}
val paramsCaptor = argumentCaptor<NetworkParameters>()
val signatureCaptor = argumentCaptor<DigitalSignatureWithCert>()
@ -102,17 +104,17 @@ class NetworkMapSignerTest : TestBase() {
fun `signNetworkMap does NOT create a new network map if there are no changes`() {
// given
val netParamsEntity = createNetworkParametersEntity(signingCertAndKeyPair)
val netMapEntity = createNetworkMapEntity(signingCertAndKeyPair, netParamsEntity, emptyList())
val netMapEntity = createNetworkMaps(signingCertAndKeyPair, netParamsEntity, emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(netParamsEntity)
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(emptyList())
whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(netMapEntity)
whenever(networkMapStorage.getNodeInfoHashes()).thenReturn(NodeInfoHashes(emptyList(), emptyMap()))
whenever(networkMapStorage.getNetworkMaps()).thenReturn(netMapEntity)
// when
networkMapSigner.signNetworkMap()
networkMapSigner.signNetworkMaps()
// then
// Verify networkMapStorage is not called
verify(networkMapStorage, never()).saveNewActiveNetworkMap(any())
verify(networkMapStorage, never()).saveNewNetworkMap(any(), any())
verify(networkMapStorage, never()).saveNetworkParameters(any(), any())
}
@ -121,21 +123,21 @@ class NetworkMapSignerTest : TestBase() {
// given
val netParams = testNetworkParameters()
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(createNetworkParametersEntityUnsigned(netParams))
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(emptyList())
whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(null)
whenever(networkMapStorage.getNodeInfoHashes()).thenReturn(NodeInfoHashes(emptyList(), emptyMap()))
whenever(networkMapStorage.getNetworkMaps()).thenReturn(NetworkMaps(null, emptyMap()))
whenever(networkMapStorage.getCurrentParametersUpdate()).thenReturn(null)
// when
networkMapSigner.signNetworkMap()
networkMapSigner.signNetworkMaps()
// then
// Verify networkMapStorage calls
verify(networkMapStorage).getActiveNodeInfoHashes()
verify(networkMapStorage).getActiveNetworkMap()
verify(networkMapStorage).getNodeInfoHashes()
verify(networkMapStorage).getNetworkMaps()
verify(networkMapStorage).getLatestNetworkParameters()
verify(networkMapStorage).getCurrentParametersUpdate()
argumentCaptor<NetworkMapAndSigned>().apply {
verify(networkMapStorage).saveNewActiveNetworkMap(capture())
verify(networkMapStorage).saveNewNetworkMap(anyOrNull(), capture())
assertEquals(netParams.serialize().hash, firstValue.networkMap.networkParameterHash)
}
val paramsCaptor = argumentCaptor<NetworkParameters>()
@ -151,19 +153,19 @@ class NetworkMapSignerTest : TestBase() {
val currentNetworkParameters = createNetworkParametersEntity(signingCertAndKeyPair)
val updateNetworkParameters = createNetworkParametersEntityUnsigned(testNetworkParameters(epoch = 2))
val parametersUpdate = ParametersUpdateEntity(0, updateNetworkParameters, "Update time", Instant.ofEpochMilli(random63BitValue()))
val netMapEntity = createNetworkMapEntity(signingCertAndKeyPair, currentNetworkParameters, emptyList(), null)
whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(netMapEntity)
val netMapEntity = createNetworkMaps(signingCertAndKeyPair, currentNetworkParameters)
whenever(networkMapStorage.getNetworkMaps()).thenReturn(netMapEntity)
whenever(networkMapStorage.getCurrentParametersUpdate()).thenReturn(parametersUpdate)
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(updateNetworkParameters)
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(emptyList())
whenever(networkMapStorage.getNodeInfoHashes()).thenReturn(NodeInfoHashes(emptyList(), emptyMap()))
// when
networkMapSigner.signNetworkMap()
networkMapSigner.signNetworkMaps()
// then
// Verify networkMapStorage calls
verify(networkMapStorage).getActiveNetworkMap()
verify(networkMapStorage).getActiveNodeInfoHashes()
verify(networkMapStorage).getNetworkMaps()
verify(networkMapStorage).getNodeInfoHashes()
verify(networkMapStorage).getLatestNetworkParameters()
verify(networkMapStorage).getCurrentParametersUpdate()
@ -178,13 +180,13 @@ class NetworkMapSignerTest : TestBase() {
fun `signNetworkMap fails if there is parameter update without relevant parameters stored`() {
val updateNetworkParameters = createNetworkParametersEntityUnsigned(testNetworkParameters(epoch = 2))
val parametersUpdate = ParametersUpdateEntity(0, updateNetworkParameters, "Update time", Instant.ofEpochMilli(random63BitValue()))
whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(null)
whenever(networkMapStorage.getNetworkMaps()).thenReturn(NetworkMaps(null, emptyMap()))
whenever(networkMapStorage.getCurrentParametersUpdate()).thenReturn(parametersUpdate)
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(emptyList())
whenever(networkMapStorage.getNodeInfoHashes()).thenReturn(NodeInfoHashes(emptyList(), emptyMap()))
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(createNetworkParametersEntity())
verify(networkMapStorage, never()).saveNetworkParameters(any(), any())
verify(networkMapStorage, never()).saveNewActiveNetworkMap(any())
verify(networkMapStorage, never()).saveNewNetworkMap(any(), any())
}
@Test
@ -192,19 +194,19 @@ class NetworkMapSignerTest : TestBase() {
val activeNetworkParameters = createNetworkParametersEntity(signingCertAndKeyPair, testNetworkParameters(epoch = 1))
val updateNetworkParameters = createNetworkParametersEntity(signingCertAndKeyPair, testNetworkParameters(epoch = 2))
val parametersUpdate = ParametersUpdateEntity(0, updateNetworkParameters, "Update time", Instant.ofEpochMilli(random63BitValue()))
val activeNetworkMap = createNetworkMapEntity(signingCertAndKeyPair, activeNetworkParameters, emptyList(), parametersUpdate.toParametersUpdate())
val activeNetworkMaps = createNetworkMaps(signingCertAndKeyPair, activeNetworkParameters, parametersUpdate = parametersUpdate.toParametersUpdate())
whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(activeNetworkMap)
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(emptyList())
whenever(networkMapStorage.getNetworkMaps()).thenReturn(activeNetworkMaps)
whenever(networkMapStorage.getNodeInfoHashes()).thenReturn(NodeInfoHashes(emptyList(), emptyMap()))
whenever(networkMapStorage.getCurrentParametersUpdate()).thenReturn(parametersUpdate.copy(status = UpdateStatus.FLAG_DAY))
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(updateNetworkParameters)
// when
networkMapSigner.signNetworkMap()
networkMapSigner.signNetworkMaps()
//then
argumentCaptor<NetworkMapAndSigned>().apply {
verify(networkMapStorage).saveNewActiveNetworkMap(capture())
verify(networkMapStorage).saveNewNetworkMap(anyOrNull(), capture())
val netMap = firstValue.networkMap
assertEquals(SecureHash.parse(updateNetworkParameters.hash), netMap.networkParameterHash)
assertEquals(emptyList(), netMap.nodeInfoHashes)
@ -217,19 +219,19 @@ class NetworkMapSignerTest : TestBase() {
val activeNetworkParameters = createNetworkParametersEntity(signingCertAndKeyPair, testNetworkParameters(epoch = 1))
val updateNetworkParameters = createNetworkParametersEntityUnsigned(testNetworkParameters(epoch = 2))
val parametersUpdate = ParametersUpdateEntity(0, updateNetworkParameters, "Update time", Instant.ofEpochMilli(random63BitValue()))
val activeNetworkMap = createNetworkMapEntity(signingCertAndKeyPair, activeNetworkParameters, emptyList(), parametersUpdate.toParametersUpdate())
val activeNetworkMaps = createNetworkMaps(signingCertAndKeyPair, activeNetworkParameters, parametersUpdate = parametersUpdate.toParametersUpdate())
whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(activeNetworkMap)
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(emptyList())
whenever(networkMapStorage.getNetworkMaps()).thenReturn(activeNetworkMaps)
whenever(networkMapStorage.getNodeInfoHashes()).thenReturn(NodeInfoHashes(emptyList(), emptyMap()))
whenever(networkMapStorage.getCurrentParametersUpdate()).thenReturn(null)
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(createNetworkParametersEntity())
// when
networkMapSigner.signNetworkMap()
networkMapSigner.signNetworkMaps()
//then
argumentCaptor<NetworkMapAndSigned>().apply {
verify(networkMapStorage).saveNewActiveNetworkMap(capture())
verify(networkMapStorage).saveNewNetworkMap(anyOrNull(), capture())
val netMap = firstValue.networkMap
assertEquals(SecureHash.parse(activeNetworkParameters.hash), netMap.networkParameterHash)
assertEquals(emptyList(), netMap.nodeInfoHashes)

View File

@ -14,13 +14,19 @@ import com.nhaarman.mockito_kotlin.*
import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequestStorage
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
import com.r3.corda.networkmanage.createNetworkMapEntity
import com.r3.corda.networkmanage.createNetworkMaps
import com.r3.corda.networkmanage.doorman.NetworkManagementWebServer
import com.r3.corda.networkmanage.doorman.NetworkMapConfig
import net.corda.core.crypto.*
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SecureHash.Companion.randomSHA256
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.sign
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.*
import net.corda.core.internal.openHttpConnection
import net.corda.core.internal.post
import net.corda.core.internal.responseAs
import net.corda.core.internal.sign
import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.OpaqueBytes
@ -42,6 +48,16 @@ import org.junit.Test
import java.io.IOException
import java.net.URL
import java.security.cert.X509Certificate
import java.time.Duration
import java.time.Instant
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.first
import kotlin.collections.forEach
import kotlin.collections.listOf
import kotlin.collections.mapOf
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
@ -67,7 +83,7 @@ class NetworkMapWebServiceTest {
// Create node info.
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"))
val networkMapStorage: NetworkMapStorage = mock {
on { getActiveNetworkMap() }.thenReturn(createNetworkMapEntity())
on { getNetworkMaps() }.thenReturn(createNetworkMaps())
}
val csrStorage: CertificateSigningRequestStorage = mock {
on { getValidCertificatePath(any()) }.thenReturn(signedNodeInfo.verified().legalIdentitiesAndCerts.first().certPath)
@ -85,7 +101,7 @@ class NetworkMapWebServiceTest {
// Create node info.
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"))
val networkMapStorage: NetworkMapStorage = mock {
on { getActiveNetworkMap() }.thenReturn(createNetworkMapEntity())
on { getNetworkMaps() }.thenReturn(createNetworkMaps())
}
val csrStorage: CertificateSigningRequestStorage = mock {
on { getValidCertificatePath(any()) }.thenReturn(null)
@ -105,7 +121,7 @@ class NetworkMapWebServiceTest {
// Create node info.
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1)
val networkMapStorage: NetworkMapStorage = mock {
on { getActiveNetworkMap() }.thenReturn(createNetworkMapEntity(networkParameters = testNetworkParameters(minimumPlatformVersion = 2)))
on { getNetworkMaps() }.thenReturn(createNetworkMaps(networkParameters = testNetworkParameters(minimumPlatformVersion = 2)))
}
val csrStorage: CertificateSigningRequestStorage = mock {
on { getValidCertificatePath(any()) }.thenReturn(signedNodeInfo.verified().legalIdentitiesAndCerts.first().certPath)
@ -123,7 +139,7 @@ class NetworkMapWebServiceTest {
// Create node info.
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1)
val networkMapStorage: NetworkMapStorage = mock {
on { getActiveNetworkMap() }.thenReturn(null)
on { getNetworkMaps() }.thenReturn(null)
}
val csrStorage: CertificateSigningRequestStorage = mock {
on { getValidCertificatePath(any()) }.thenReturn(signedNodeInfo.verified().legalIdentitiesAndCerts.first().certPath)
@ -138,19 +154,69 @@ class NetworkMapWebServiceTest {
@Test
fun `get network map`() {
val networkMapEntity = createNetworkMapEntity(
val networkMaps = createNetworkMaps(
signingCertAndKeyPair = signingCertAndKeyPair,
nodeInfoHashes = listOf(randomSHA256(), randomSHA256()))
val networkMapStorage: NetworkMapStorage = mock {
on { getActiveNetworkMap() }.thenReturn(networkMapEntity)
on { getNetworkMaps() }.thenReturn(networkMaps)
}
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(mock(), networkMapStorage, mock(), testNetworkMapConfig)).use {
it.start()
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
verify(networkMapStorage, times(1)).getActiveNetworkMap()
assertEquals(signedNetworkMapResponse.verifiedNetworkMapCert(rootCaCert), networkMapEntity.networkMap)
verify(networkMapStorage, times(1)).getNetworkMaps()
assertEquals(signedNetworkMapResponse.verifiedNetworkMapCert(rootCaCert), networkMaps.publicNetworkMap!!.networkMap)
}
}
@Test
fun `network map response contains correct http header parameter`() {
val timestamp = Instant.now()
val networkMaps = createNetworkMaps(
signingCertAndKeyPair = signingCertAndKeyPair,
nodeInfoHashes = listOf(randomSHA256(), randomSHA256()),
timestamp = timestamp)
val networkMapStorage: NetworkMapStorage = mock {
on { getNetworkMaps() }.thenReturn(networkMaps)
}
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(mock(), networkMapStorage, mock(), testNetworkMapConfig)).use {
it.start()
val headers = URL("http://${it.hostAndPort}/network-map/").openHttpConnection().headerFields
assertEquals("max-age=${Duration.ofMillis(testNetworkMapConfig.cacheTimeout).seconds}", headers["Cache-Control"]?.first())
assertEquals(DateTimeFormatter.RFC_1123_DATE_TIME.withZone(ZoneId.of("GMT")).format(timestamp), headers["Last-Modified"]?.first())
}
}
@Test
fun `get private network map`() {
val publicNodeInfoHashes = listOf(randomSHA256(), randomSHA256())
val privateNodeInfoHashes = mapOf("PrivateNet1" to listOf(randomSHA256(), randomSHA256()), "PrivateNet2" to listOf(randomSHA256(), randomSHA256()))
val networkMaps = createNetworkMaps(
signingCertAndKeyPair = signingCertAndKeyPair,
nodeInfoHashes = publicNodeInfoHashes,
privateNodeInfoHashes = privateNodeInfoHashes
)
val networkMapStorage: NetworkMapStorage = mock {
on { getNetworkMaps() }.thenReturn(networkMaps)
}
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(mock(), networkMapStorage, mock(), testNetworkMapConfig)).use {
it.start()
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
verify(networkMapStorage, times(1)).getNetworkMaps()
assertEquals(signedNetworkMapResponse.verifiedNetworkMapCert(rootCaCert), networkMaps.publicNetworkMap!!.networkMap)
networkMaps.privateNetworkMap.forEach { (key, privateNetworkMapEntity) ->
val response = it.doGet<SignedNetworkMap>(key)
// Result cached.
verify(networkMapStorage, times(1)).getNetworkMaps()
assertEquals(response.verifiedNetworkMapCert(rootCaCert), privateNetworkMapEntity.networkMap)
}
}
}
@ -165,7 +231,7 @@ class NetworkMapWebServiceTest {
// Mock network map storage
val networkMapStorage: NetworkMapStorage = mock {
on { getActiveNetworkMap() }.thenReturn(createNetworkMapEntity(nodeInfoHashes = listOf(nodeInfoHash)))
on { getNetworkMaps() }.thenReturn(createNetworkMaps(nodeInfoHashes = listOf(nodeInfoHash)))
}
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(nodeInfoStorage, networkMapStorage, mock(), testNetworkMapConfig)).use {