mirror of
https://github.com/corda/corda.git
synced 2025-01-30 08:04:16 +00:00
ENT-1586 Update entire service stack to support private network maps (#691)
* private network map
This commit is contained in:
parent
05ec885afd
commit
7f8c36faa0
@ -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()
|
||||
}
|
||||
|
@ -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>>)
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user