network_map table references the network_parameters table (ENT-1524). (#567)

This commit is contained in:
Shams Asari 2018-03-16 10:57:30 +00:00 committed by GitHub
parent 2a898658c2
commit 78b2bc7549
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 304 additions and 278 deletions

View File

@ -388,10 +388,16 @@ val CordaX500Name.x500Name: X500Name
val CordaX500Name.Companion.unspecifiedCountry
get() = "ZZ"
fun <T : Any> T.signWithCert(privateKey: PrivateKey, certificate: X509Certificate): SignedDataWithCert<T> {
inline fun <T : Any> T.signWithCert(signer: (SerializedBytes<T>) -> DigitalSignatureWithCert): SignedDataWithCert<T> {
val serialised = serialize()
val signature = Crypto.doSign(privateKey, serialised.bytes)
return SignedDataWithCert(serialised, DigitalSignatureWithCert(certificate, signature))
return SignedDataWithCert(serialised, signer(serialised))
}
fun <T : Any> T.signWithCert(privateKey: PrivateKey, certificate: X509Certificate): SignedDataWithCert<T> {
return signWithCert {
val signature = Crypto.doSign(privateKey, it.bytes)
DigitalSignatureWithCert(certificate, signature)
}
}
inline fun <T : Any> SerializedBytes<T>.sign(signer: (SerializedBytes<T>) -> DigitalSignature.WithKey): SignedData<T> {

View File

@ -165,9 +165,7 @@ class HsmSigningServiceTest : HsmBaseTest() {
networkMapSigner.signNetworkMap()
// then
val signedNetworkMap = networkMapStorage.getCurrentNetworkMap()
assertNotNull(signedNetworkMap)
val persistedNetworkMap = signedNetworkMap!!.verified()
val persistedNetworkMap = networkMapStorage.getActiveNetworkMap()!!.toSignedNetworkMap().verified()
assertEquals(networkMapParameters.serialize().hash, persistedNetworkMap.networkParameterHash)
assertThat(persistedNetworkMap.nodeInfoHashes).isEmpty()
}

View File

@ -10,11 +10,13 @@
package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.common.persistence.entity.NetworkMapEntity
import com.r3.corda.networkmanage.common.persistence.entity.NetworkParametersEntity
import com.r3.corda.networkmanage.common.persistence.entity.NodeInfoEntity
import net.corda.core.crypto.SecureHash
import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.node.NetworkParameters
import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.nodeapi.internal.network.NetworkMapAndSigned
import net.corda.nodeapi.internal.network.SignedNetworkParameters
/**
@ -22,47 +24,37 @@ import net.corda.nodeapi.internal.network.SignedNetworkParameters
*/
interface NetworkMapStorage {
/**
* Retrieves current network map. Current in this context means the one that has been most recently signed.
* @return current network map
* Returns the active network map, or null
*/
fun getCurrentNetworkMap(): SignedNetworkMap?
fun getActiveNetworkMap(): NetworkMapEntity?
/**
* Persist the new active network map, replacing any existing network map.
*/
fun saveNewActiveNetworkMap(networkMapAndSigned: NetworkMapAndSigned)
/**
* Retrieves node info hashes where [NodeInfoEntity.isCurrent] is true and the certificate status is [CertificateStatus.VALID]
*
* @return list of current and valid node info hashes.
*/
fun getActiveNodeInfoHashes(): List<SecureHash>
/**
* Persists a new instance of the signed network map.
* @param signedNetworkMap encapsulates all the information needed for persisting current network map state.
*/
fun saveNetworkMap(signedNetworkMap: SignedNetworkMap)
/**
* Retrieve the signed with certificate network parameters by their hash. The hash is that of the underlying
* [NetworkParameters] object and not the `SignedWithCert<NetworkParameters>` object that's returned.
* [NetworkParameters] object and not the [SignedNetworkParameters] object that's returned.
* @return signed network parameters corresponding to the given hash or null if it does not exist (parameters don't exist or they haven't been signed yet)
*/
fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters?
/**
* Retrieve the network parameters of the current network map, or null if there's no network map.
*/
// TODO: Remove this method. We should get the "current" network parameter by using the the hash in the network map and use the [getSignedNetworkParameters] method.
fun getNetworkParametersOfNetworkMap(): SignedNetworkParameters?
/**
* Persists given network parameters with signature if provided.
* @return hash corresponding to newly created network parameters entry
*/
fun saveNetworkParameters(networkParameters: NetworkParameters, sig: DigitalSignatureWithCert?): SecureHash
fun saveNetworkParameters(networkParameters: NetworkParameters, signature: DigitalSignatureWithCert?): SecureHash
/**
* Retrieves the latest (i.e. most recently inserted) network parameters
* Note that they may not have been signed up yet.
* @return latest network parameters
*/
fun getLatestNetworkParameters(): NetworkParameters?
fun getLatestNetworkParameters(): NetworkParametersEntity?
}

View File

@ -18,23 +18,24 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.nodeapi.internal.persistence.DatabaseTransaction
import net.corda.nodeapi.internal.persistence.SchemaMigration
import org.hibernate.query.Query
import java.util.*
import javax.persistence.LockModeType
import javax.persistence.criteria.CriteriaBuilder
import javax.persistence.criteria.Path
import javax.persistence.criteria.Predicate
inline fun <reified T> DatabaseTransaction.singleEntityWhere(predicate: (CriteriaBuilder, Path<T>) -> Predicate): T? {
return getEntitiesWhere(predicate).firstOrNull()
inline fun <reified T> DatabaseTransaction.uniqueEntityWhere(predicate: (CriteriaBuilder, Path<T>) -> Predicate): T? {
return entitiesWhere(predicate).setMaxResults(1).uniqueResult()
}
inline fun <reified T> DatabaseTransaction.getEntitiesWhere(predicate: (CriteriaBuilder, Path<T>) -> Predicate): List<T> {
inline fun <reified T> DatabaseTransaction.entitiesWhere(predicate: (CriteriaBuilder, Path<T>) -> Predicate): Query<T> {
val builder = session.criteriaBuilder
val criteriaQuery = builder.createQuery(T::class.java)
val query = criteriaQuery.from(T::class.java).run {
criteriaQuery.where(predicate(builder, this))
}
return session.createQuery(query).setLockMode(LockModeType.PESSIMISTIC_WRITE).resultList
return session.createQuery(query).setLockMode(LockModeType.PESSIMISTIC_WRITE)
}
inline fun <reified T> DatabaseTransaction.deleteEntity(predicate: (CriteriaBuilder, Path<T>) -> Predicate): Int {

View File

@ -40,7 +40,7 @@ class PersistentCertificateRevocationListStorage(private val database: CordaPers
}
private fun revokeCertificate(certificateSerialNumber: BigInteger, time: Instant, transaction: DatabaseTransaction) {
val revocation = transaction.singleEntityWhere<CertificateRevocationRequestEntity> { builder, path ->
val revocation = transaction.uniqueEntityWhere<CertificateRevocationRequestEntity> { builder, path ->
builder.equal(path.get<BigInteger>(CertificateRevocationRequestEntity::certificateSerialNumber.name), certificateSerialNumber)
}
revocation ?: throw IllegalStateException("The certificate revocation request for $certificateSerialNumber does not exist")

View File

@ -13,7 +13,7 @@ class PersistentCertificateRevocationRequestStorage(private val database: CordaP
override fun saveRevocationRequest(certificateSerialNumber: BigInteger, reason: CRLReason, reporter: String): String {
return database.transaction(TransactionIsolationLevel.SERIALIZABLE) {
// Check if there is an entry for the given certificate serial number
val revocation = singleEntityWhere<CertificateRevocationRequestEntity> { builder, path ->
val revocation = uniqueEntityWhere<CertificateRevocationRequestEntity> { builder, path ->
val serialNumberEqual = builder.equal(path.get<BigInteger>(CertificateRevocationRequestEntity::certificateSerialNumber.name), certificateSerialNumber)
val statusNotEqualRejected = builder.notEqual(path.get<RequestStatus>(CertificateRevocationRequestEntity::status.name), RequestStatus.REJECTED)
builder.and(serialNumberEqual, statusNotEqualRejected)
@ -21,7 +21,7 @@ class PersistentCertificateRevocationRequestStorage(private val database: CordaP
if (revocation != null) {
revocation.requestId
} else {
val certificateData = singleEntityWhere<CertificateDataEntity> { builder, path ->
val certificateData = uniqueEntityWhere<CertificateDataEntity> { builder, path ->
val serialNumberEqual = builder.equal(path.get<BigInteger>(CertificateDataEntity::certificateSerialNumber.name), certificateSerialNumber)
val statusEqualValid = builder.equal(path.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name), CertificateStatus.VALID)
builder.and(serialNumberEqual, statusEqualValid)
@ -90,7 +90,7 @@ class PersistentCertificateRevocationRequestStorage(private val database: CordaP
}
private fun getRevocationRequestEntity(requestId: String): CertificateRevocationRequestEntity? = database.transaction {
singleEntityWhere { builder, path ->
uniqueEntityWhere { builder, path ->
builder.equal(path.get<String>(CertificateRevocationRequestEntity::requestId.name), requestId)
}
}

View File

@ -38,7 +38,7 @@ class PersistentCertificateSigningRequestStorage(private val database: CordaPers
override fun putCertificatePath(requestId: String, certPath: CertPath, signedBy: String) {
return database.transaction(TransactionIsolationLevel.SERIALIZABLE) {
val request = singleEntityWhere<CertificateSigningRequestEntity> { builder, path ->
val request = uniqueEntityWhere<CertificateSigningRequestEntity> { builder, path ->
val requestIdEq = builder.equal(path.get<String>(CertificateSigningRequestEntity::requestId.name), requestId)
val statusEq = builder.equal(path.get<String>(CertificateSigningRequestEntity::status.name), RequestStatus.APPROVED)
builder.and(requestIdEq, statusEq)
@ -89,7 +89,7 @@ class PersistentCertificateSigningRequestStorage(private val database: CordaPers
private fun DatabaseTransaction.findRequest(requestId: String,
requestStatus: RequestStatus? = null): CertificateSigningRequestEntity? {
return singleEntityWhere { builder, path ->
return uniqueEntityWhere { builder, path ->
val idClause = builder.equal(path.get<String>(CertificateSigningRequestEntity::requestId.name), requestId)
if (requestStatus == null) {
idClause

View File

@ -12,48 +12,54 @@ package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.common.persistence.entity.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.nodeapi.internal.network.NetworkMapAndSigned
import net.corda.nodeapi.internal.network.SignedNetworkParameters
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseTransaction
/**
* Database implementation of the [NetworkMapStorage] interface
*/
class PersistentNetworkMapStorage(private val database: CordaPersistence) : NetworkMapStorage {
override fun getCurrentNetworkMap(): SignedNetworkMap? {
override fun getActiveNetworkMap(): NetworkMapEntity? {
return database.transaction {
getCurrentNetworkMapEntity()?.toSignedNetworkMap()
}
}
override fun getNetworkParametersOfNetworkMap(): SignedNetworkParameters? {
return database.transaction {
getCurrentNetworkMapEntity()?.let {
val netParamsHash = it.toNetworkMap().networkParameterHash
getSignedNetworkParameters(netParamsHash) ?:
throw IllegalStateException("Current network map is pointing to network parameters that do not exist: $netParamsHash")
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()
}
}
override fun saveNetworkMap(signedNetworkMap: SignedNetworkMap) {
override fun saveNewActiveNetworkMap(networkMapAndSigned: NetworkMapAndSigned) {
val (networkMap, signedNetworkMap) = networkMapAndSigned
database.transaction {
val networkMapEntity = NetworkMapEntity(
networkMap = signedNetworkMap.raw.bytes,
val networkParametersEntity = checkNotNull(getNetworkParametersEntity(networkMap.networkParameterHash)) {
"Network parameters ${networkMap.networkParameterHash} must be first persisted"
}
check(networkParametersEntity.isSigned) {
"Network parameters ${networkMap.networkParameterHash} are not signed"
}
session.save(NetworkMapEntity(
networkMapBytes = signedNetworkMap.raw.bytes,
signature = signedNetworkMap.sig.bytes,
certificate = signedNetworkMap.sig.by.encoded
)
session.save(networkMapEntity)
certificate = signedNetworkMap.sig.by.encoded,
networkParameters = networkParametersEntity
))
}
}
override fun getSignedNetworkParameters(hash: SecureHash): SignedNetworkParameters? {
return getNetworkParametersEntity(hash.toString())?.let {
if (it.isSigned) it.toSignedNetworkParameters() else null
return database.transaction {
getNetworkParametersEntity(hash)?.let {
if (it.isSigned) it.toSignedNetworkParameters() else null
}
}
}
@ -65,6 +71,8 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
val certStatusExpression = get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name)
.get<CertificateDataEntity>(CertificateSigningRequestEntity::certificateData.name)
.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name)
// TODO When revoking certs, all node-infos that point to it must be made non-current. Then this check
// 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))
@ -74,21 +82,21 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
}
}
override fun saveNetworkParameters(networkParameters: NetworkParameters, sig: DigitalSignatureWithCert?): SecureHash {
return database.transaction {
val bytes = networkParameters.serialize().bytes
val hash = bytes.sha256()
override fun saveNetworkParameters(networkParameters: NetworkParameters, signature: DigitalSignatureWithCert?): SecureHash {
val serialised = networkParameters.serialize()
val hash = serialised.hash
database.transaction {
session.saveOrUpdate(NetworkParametersEntity(
parametersBytes = bytes,
parametersBytes = serialised.bytes,
parametersHash = hash.toString(),
signature = sig?.bytes,
certificate = sig?.by?.encoded
signature = signature?.bytes,
certificate = signature?.by?.encoded
))
hash
}
return hash
}
override fun getLatestNetworkParameters(): NetworkParameters? {
override fun getLatestNetworkParameters(): NetworkParametersEntity? {
return database.transaction {
val query = session.criteriaBuilder.run {
createQuery(NetworkParametersEntity::class.java).run {
@ -98,30 +106,13 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
}
}
// We just want the last entry
session.createQuery(query).setMaxResults(1).uniqueResult()?.toNetworkParameters()
session.createQuery(query).setMaxResults(1).uniqueResult()
}
}
private fun getCurrentNetworkMapEntity(): NetworkMapEntity? {
return database.transaction {
val builder = session.criteriaBuilder
val query = builder.createQuery(NetworkMapEntity::class.java).run {
from(NetworkMapEntity::class.java).run {
// TODO a limit of 1 since we only need the first result
where(builder.isNotNull(get<ByteArray?>(NetworkMapEntity::signature.name)))
orderBy(builder.desc(get<String>(NetworkMapEntity::version.name)))
}
}
// We just want the last signed entry
session.createQuery(query).resultList.firstOrNull()
}
}
private fun getNetworkParametersEntity(parameterHash: String): NetworkParametersEntity? {
return database.transaction {
singleEntityWhere { builder, path ->
builder.equal(path.get<String>(NetworkParametersEntity::parametersHash.name), parameterHash)
}
private fun DatabaseTransaction.getNetworkParametersEntity(hash: SecureHash): NetworkParametersEntity? {
return uniqueEntityWhere { builder, path ->
builder.equal(path.get<String>(NetworkParametersEntity::parametersHash.name), hash.toString())
}
}
}

View File

@ -43,12 +43,13 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
}
// Update any [NodeInfoEntity] instance for this CSR as not current.
val existingNodeInfo = getEntitiesWhere<NodeInfoEntity> { builder, path ->
entitiesWhere<NodeInfoEntity> { builder, path ->
val requestEq = builder.equal(path.get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name), request)
val isCurrent = builder.isTrue(path.get<Boolean>(NodeInfoEntity::isCurrent.name))
builder.and(requestEq, isCurrent)
}.resultStream.use { existingNodeInfos ->
existingNodeInfos.forEach { session.merge(it.copy(isCurrent = false)) }
}
existingNodeInfo.forEach { session.merge(it.copy(isCurrent = false)) }
val hash = signedNodeInfo.raw.hash
val hashedNodeInfo = NodeInfoEntity(
@ -75,7 +76,7 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
}
private fun DatabaseTransaction.getSignedRequestByPublicHash(publicKeyHash: SecureHash): CertificateSigningRequestEntity? {
return singleEntityWhere { builder, path ->
return uniqueEntityWhere { builder, path ->
val publicKeyEq = builder.equal(path.get<String>(CertificateSigningRequestEntity::publicKeyHash.name), publicKeyHash.toString())
val statusEq = builder.equal(path.get<RequestStatus>(CertificateSigningRequestEntity::status.name), RequestStatus.DONE)
builder.and(publicKeyEq, statusEq)

View File

@ -26,22 +26,26 @@ class NetworkMapEntity(
val version: Long? = null,
@Lob
@Column(name = "serialized_network_map")
val networkMap: ByteArray,
@Column(name = "serialized_network_map", nullable = false)
val networkMapBytes: ByteArray,
@Lob
@Column(name = "signature")
@Column(name = "signature", nullable = false)
val signature: ByteArray,
@Lob
@Column(name = "certificate")
val certificate: ByteArray
@Column(name = "certificate", nullable = false)
val certificate: ByteArray,
@ManyToOne(optional = false, fetch = FetchType.EAGER)
@JoinColumn(name = "network_parameters")
val networkParameters: NetworkParametersEntity
) {
fun toNetworkMap(): NetworkMap = networkMap.deserialize()
fun toNetworkMap(): NetworkMap = networkMapBytes.deserialize()
fun toSignedNetworkMap(): SignedNetworkMap {
return SignedNetworkMap(
SerializedBytes(networkMap),
SerializedBytes(networkMapBytes),
DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)
)
}

View File

@ -31,7 +31,7 @@ class NetworkParametersEntity(
val created: Instant = Instant.now(),
@Lob
@Column(name = "parameters_bytes")
@Column(name = "parameters_bytes", nullable = false)
val parametersBytes: ByteArray,
// Both of the fields below are nullable, because of the way we sign network map data. NetworkParameters can be

View File

@ -11,11 +11,12 @@
package com.r3.corda.networkmanage.common.signer
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import net.corda.core.crypto.SecureHash
import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.serialize
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.SignedNetworkMap
import net.corda.nodeapi.internal.network.NetworkMapAndSigned
class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) {
private companion object {
@ -28,41 +29,45 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private
fun signNetworkMap() {
// TODO There is no network parameters update process in place yet. We assume that latest parameters are to be used
// in current network map.
val latestNetworkParameters = networkMapStorage.getLatestNetworkParameters()
if (latestNetworkParameters == null) {
logger.debug("No network parameters present")
val latestNetParamsEntity = networkMapStorage.getLatestNetworkParameters()
if (latestNetParamsEntity == null) {
logger.warn("No network parameters present")
return
}
logger.debug("Fetching current network map parameters...")
val currentNetworkParameters = networkMapStorage.getNetworkParametersOfNetworkMap()
logger.debug("Retrieved network map parameters: $currentNetworkParameters")
if (currentNetworkParameters?.verified() != latestNetworkParameters) {
persistSignedNetworkParameters(latestNetworkParameters)
val latestNetParams = latestNetParamsEntity.toNetworkParameters()
logger.debug { "Latest network parameters: $latestNetParams" }
if (!latestNetParamsEntity.isSigned) {
signAndPersistNetworkParameters(latestNetParams)
} else {
logger.debug("Network map parameters up-to-date. Skipping signing.")
logger.debug("Network parameters are already signed")
}
logger.debug("Fetching current network map...")
val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
logger.debug("Fetching node info hashes with VALID certificates...")
val nodeInfoHashes = networkMapStorage.getActiveNodeInfoHashes()
logger.debug("Retrieved node info hashes: $nodeInfoHashes")
val newNetworkMap = NetworkMap(nodeInfoHashes, latestNetworkParameters.serialize().hash, null)
val serialisedNetworkMap = newNetworkMap.serialize()
if (serialisedNetworkMap != currentSignedNetworkMap?.raw) {
logger.info("Signing a new network map: $newNetworkMap")
logger.debug("Creating a new signed network map: ${serialisedNetworkMap.hash}")
val newSignedNetworkMap = SignedNetworkMap(serialisedNetworkMap, signer.signBytes(serialisedNetworkMap.bytes))
networkMapStorage.saveNetworkMap(newSignedNetworkMap)
logger.debug("Signed network map saved")
logger.debug { "Active node-info hashes:\n${nodeInfoHashes.joinToString("\n")}" }
val currentNetworkMap = networkMapStorage.getActiveNetworkMap()?.toNetworkMap()
if (currentNetworkMap != null) {
logger.debug { "Current network map: $currentNetworkMap" }
} else {
logger.debug("Current network map is up-to-date. Skipping signing.")
logger.info("There is currently no network map")
}
val newNetworkMap = NetworkMap(nodeInfoHashes, SecureHash.parse(latestNetParamsEntity.parametersHash), null)
logger.debug { "Potential new network map: $newNetworkMap" }
if (currentNetworkMap != newNetworkMap) {
val netNetworkMapAndSigned = NetworkMapAndSigned(newNetworkMap) { signer.signBytes(it.bytes) }
networkMapStorage.saveNewActiveNetworkMap(netNetworkMapAndSigned)
logger.info("Signed new network map: $newNetworkMap")
} else {
logger.debug("Current network map is up-to-date")
}
}
fun persistSignedNetworkParameters(networkParameters: NetworkParameters) {
logger.info("Signing and persisting network parameters: $networkParameters")
val digitalSignature = signer.signObject(networkParameters).sig
networkMapStorage.saveNetworkParameters(networkParameters, digitalSignature)
logger.info("Signed network map parameters saved.")
fun signAndPersistNetworkParameters(networkParameters: NetworkParameters) {
networkMapStorage.saveNetworkParameters(networkParameters, signer.signObject(networkParameters).sig)
logger.info("Signed network parameters: $networkParameters")
}
}
}

View File

@ -59,9 +59,9 @@ class NetworkManagementServer : Closeable {
val localNetworkMapSigner = signer?.let { NetworkMapSigner(networkMapStorage, it) }
newNetworkParameters?.let {
val netParamsOfNetworkMap = networkMapStorage.getNetworkParametersOfNetworkMap()
val netParamsOfNetworkMap = networkMapStorage.getActiveNetworkMap()?.networkParameters
if (netParamsOfNetworkMap == null) {
localNetworkMapSigner?.persistSignedNetworkParameters(it) ?: networkMapStorage.saveNetworkParameters(it, null)
localNetworkMapSigner?.signAndPersistNetworkParameters(it) ?: networkMapStorage.saveNetworkParameters(it, null)
} else {
throw UnsupportedOperationException("Network parameters already exist. Updating them is not supported yet.")
}

View File

@ -52,11 +52,12 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
private val networkMapCache: LoadingCache<Boolean, CachedData> = Caffeine.newBuilder()
.expireAfterWrite(config.cacheTimeout, TimeUnit.MILLISECONDS)
.build { _ ->
networkMapStorage.getCurrentNetworkMap()?.let {
val networkMap = it.verified()
val networkParameters = networkMapStorage.getSignedNetworkParameters(networkMap.networkParameterHash)?.verified()
CachedData(it, networkMap.nodeInfoHashes.toSet(), networkParameters)
.build {
networkMapStorage.getActiveNetworkMap()?.let {
logger.info("Re-publishing network map")
val networkMap = it.toNetworkMap()
val signedNetworkMap = it.toSignedNetworkMap()
CachedData(signedNetworkMap, networkMap.nodeInfoHashes.toSet(), it.networkParameters.toNetworkParameters())
}
}
@ -67,7 +68,7 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
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)?.currentNetworkParameter
private val currentNetworkParameters: NetworkParameters? get() = networkMapCache.get(true)?.networkParameters
@POST
@Path("publish")
@ -153,6 +154,7 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
class NetworkMapNotInitialisedException(message: String?) : Exception(message)
class InvalidPlatformVersionException(message: String?) : Exception(message)
private data class CachedData(val signedNetworkMap: SignedNetworkMap, val nodeInfoHashes: Set<SecureHash>, val currentNetworkParameter: NetworkParameters?)
private data class CachedData(val signedNetworkMap: SignedNetworkMap,
val nodeInfoHashes: Set<SecureHash>,
val networkParameters: NetworkParameters)
}

View File

@ -80,9 +80,18 @@
<column name="version" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="certificate" type="BLOB"/>
<column name="serialized_network_map" type="BLOB"/>
<column name="signature" type="BLOB"/>
<column name="certificate" 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>
</createTable>
</changeSet>
<changeSet author="R3.Corda" id="1520338500424-6">
@ -92,7 +101,9 @@
</column>
<column name="certificate" type="BLOB"/>
<column name="created" type="TIMESTAMP"/>
<column name="parameters_bytes" type="BLOB"/>
<column name="parameters_bytes" type="BLOB">
<constraints nullable="false"/>
</column>
<column name="signature" type="BLOB"/>
</createTable>
</changeSet>
@ -299,4 +310,9 @@
constraintName="FK_CSR_PN"
referencedColumnNames="id" referencedTableName="private_network"/>
</changeSet>
<changeSet author="R3.Corda" id="1520338500424-38">
<addForeignKeyConstraint baseColumnNames="network_parameters" baseTableName="network_map"
constraintName="FK_NM_NP"
referencedColumnNames="hash" referencedTableName="network_parameters"/>
</changeSet>
</databaseChangeLog>

View File

@ -0,0 +1,40 @@
package com.r3.corda.networkmanage
import com.r3.corda.networkmanage.common.persistence.entity.NetworkMapEntity
import com.r3.corda.networkmanage.common.persistence.entity.NetworkParametersEntity
import net.corda.core.crypto.SecureHash
import net.corda.core.node.NetworkParameters
import net.corda.nodeapi.internal.createDevNetworkMapCa
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.testing.common.internal.testNetworkParameters
fun createNetworkParametersEntity(signingCertAndKeyPair: CertificateAndKeyPair = createDevNetworkMapCa(),
networkParameters: NetworkParameters = testNetworkParameters()): NetworkParametersEntity {
val signedNetParams = signingCertAndKeyPair.sign(networkParameters)
return NetworkParametersEntity(
parametersHash = signedNetParams.raw.hash.toString(),
parametersBytes = signedNetParams.raw.bytes,
signature = signedNetParams.sig.bytes,
certificate = signedNetParams.sig.by.encoded
)
}
fun createNetworkMapEntity(signingCertAndKeyPair: CertificateAndKeyPair = createDevNetworkMapCa(),
netParamsEntity: NetworkParametersEntity,
nodeInfoHashes: List<SecureHash> = emptyList()): NetworkMapEntity {
val signedNetMap = signingCertAndKeyPair.sign(NetworkMap(nodeInfoHashes, SecureHash.parse(netParamsEntity.parametersHash), null))
return NetworkMapEntity(
networkMapBytes = signedNetMap.raw.bytes,
signature = signedNetMap.sig.bytes,
certificate = signedNetMap.sig.by.encoded,
networkParameters = netParamsEntity
)
}
fun createNetworkMapEntity(signingCertAndKeyPair: CertificateAndKeyPair = createDevNetworkMapCa(),
networkParameters: NetworkParameters = testNetworkParameters(),
nodeInfoHashes: List<SecureHash> = emptyList()): NetworkMapEntity {
val netParamsEntity = createNetworkParametersEntity(signingCertAndKeyPair, networkParameters)
return createNetworkMapEntity(signingCertAndKeyPair, netParamsEntity, nodeInfoHashes)
}

View File

@ -11,10 +11,12 @@
package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.TestBase
import net.corda.core.internal.signWithCert
import com.r3.corda.networkmanage.common.persistence.entity.NodeInfoEntity
import net.corda.core.crypto.SecureHash
import net.corda.nodeapi.internal.createDevNetworkMapCa
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.NetworkMapAndSigned
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig
@ -26,7 +28,6 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import java.security.cert.X509Certificate
import kotlin.test.assertEquals
class PersistentNetworkMapStorageTest : TestBase() {
private lateinit var persistence: CordaPersistence
@ -35,13 +36,13 @@ class PersistentNetworkMapStorageTest : TestBase() {
private lateinit var requestStorage: PersistentCertificateSigningRequestStorage
private lateinit var rootCaCert: X509Certificate
private lateinit var networkMapCa: CertificateAndKeyPair
private lateinit var networkMapCertAndKeyPair: CertificateAndKeyPair
@Before
fun startDb() {
val (rootCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate
networkMapCa = createDevNetworkMapCa(rootCa)
networkMapCertAndKeyPair = createDevNetworkMapCa(rootCa)
persistence = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true))
networkMapStorage = PersistentNetworkMapStorage(persistence)
nodeInfoStorage = PersistentNodeInfoStorage(persistence)
@ -54,74 +55,52 @@ class PersistentNetworkMapStorageTest : TestBase() {
}
@Test
fun `saveNetworkMap and saveNetworkParameters create current network map and parameters`() {
fun `saveNetworkParameters and then saveNewActiveNetworkMap creates the active network map`() {
// given
// Create node info.
val (signedNodeInfo) = createValidSignedNodeInfo("Test", requestStorage)
val nodeInfoHash = nodeInfoStorage.putNodeInfo(signedNodeInfo)
val networkParameters = testNetworkParameters(emptyList())
val parametersSignature = networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig
// Create network parameters
val networkParametersHash = networkMapStorage.saveNetworkParameters(networkParameters, parametersSignature)
val networkParameters = testNetworkParameters(maxTransactionSize = 1234567)
val networkParametersSig = networkMapCertAndKeyPair.sign(networkParameters).sig
val networkParametersHash = networkMapStorage.saveNetworkParameters(networkParameters, networkParametersSig)
val networkMap = NetworkMap(listOf(nodeInfoHash), networkParametersHash, null)
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
val networkMapAndSigned = NetworkMapAndSigned(networkMap) { networkMapCertAndKeyPair.sign(networkMap).sig }
// when
networkMapStorage.saveNetworkMap(signedNetworkMap)
networkMapStorage.saveNewActiveNetworkMap(networkMapAndSigned)
// then
val persistedSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
val persistedSignedParameters = networkMapStorage.getNetworkParametersOfNetworkMap()
val activeNetworkMapEntity = networkMapStorage.getActiveNetworkMap()!!
val activeSignedNetworkMap = activeNetworkMapEntity.toSignedNetworkMap()
val activeNetworkMap = activeSignedNetworkMap.verifiedNetworkMapCert(rootCaCert)
val activeNetworkParametersEntity = activeNetworkMapEntity.networkParameters
val activeSignedNetworkParameters = activeNetworkParametersEntity.toSignedNetworkParameters()
val activeNetworkParameters = activeSignedNetworkParameters.verifiedNetworkMapCert(rootCaCert)
assertEquals(networkParameters, persistedSignedParameters?.verifiedNetworkMapCert(rootCaCert))
assertEquals(parametersSignature, persistedSignedParameters?.sig)
assertEquals(signedNetworkMap.sig, persistedSignedNetworkMap?.sig)
assertEquals(signedNetworkMap.verifiedNetworkMapCert(rootCaCert), persistedSignedNetworkMap?.verifiedNetworkMapCert(rootCaCert))
assertEquals(signedNetworkMap.verifiedNetworkMapCert(rootCaCert).networkParameterHash, persistedSignedParameters?.raw?.hash)
assertThat(activeNetworkMap).isEqualTo(networkMap)
assertThat(activeSignedNetworkMap.sig).isEqualTo(networkMapAndSigned.signed.sig)
assertThat(activeNetworkParameters).isEqualTo(networkParameters)
assertThat(activeSignedNetworkParameters.sig).isEqualTo(networkParametersSig)
assertThat(SecureHash.parse(activeNetworkParametersEntity.parametersHash))
.isEqualTo(activeNetworkMap.networkParameterHash)
.isEqualTo(networkParametersHash)
}
@Test
fun `getLatestNetworkParameters returns last inserted`() {
val params1 = testNetworkParameters(emptyList(), minimumPlatformVersion = 1)
val params2 = testNetworkParameters(emptyList(), minimumPlatformVersion = 2)
// given
networkMapStorage.saveNetworkParameters(params1, params1.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig)
val params1 = testNetworkParameters(minimumPlatformVersion = 1)
val params2 = testNetworkParameters(minimumPlatformVersion = 2)
networkMapStorage.saveNetworkParameters(params1, networkMapCertAndKeyPair.sign(params1).sig)
// We may have not signed them yet.
networkMapStorage.saveNetworkParameters(params2, null)
// when
val latest = networkMapStorage.getLatestNetworkParameters()?.minimumPlatformVersion
// then
assertEquals(2, latest)
assertThat(networkMapStorage.getLatestNetworkParameters()?.toNetworkParameters()).isEqualTo(params2)
}
@Test
fun `getNetworkParametersOfNetworkMap returns current network map parameters`() {
// given
// Create network parameters
val testParameters1 = testNetworkParameters(emptyList())
val networkParametersHash = networkMapStorage.saveNetworkParameters(testParameters1, testParameters1.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig)
// Create empty network map
// Sign network map making it current network map
val networkMap = NetworkMap(emptyList(), networkParametersHash, null)
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
networkMapStorage.saveNetworkMap(signedNetworkMap)
// Create new network parameters
val testParameters2 = testNetworkParameters(emptyList(), minimumPlatformVersion = 2)
networkMapStorage.saveNetworkParameters(testParameters2, testParameters2.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig)
// when
val result = networkMapStorage.getNetworkParametersOfNetworkMap()?.verifiedNetworkMapCert(rootCaCert)
// then
assertEquals(1, result?.minimumPlatformVersion)
}
@Test
fun `getValidNodeInfoHashes returns only valid and signed node info hashes`() {
fun `getValidNodeInfoHashes returns only for current node-infos`() {
// given
// Create node infos.
val (signedNodeInfoA) = createValidSignedNodeInfo("TestA", requestStorage)
@ -131,19 +110,15 @@ class PersistentNetworkMapStorageTest : TestBase() {
val nodeInfoHashA = nodeInfoStorage.putNodeInfo(signedNodeInfoA)
val nodeInfoHashB = nodeInfoStorage.putNodeInfo(signedNodeInfoB)
// Create network parameters
val testParameters = testNetworkParameters(emptyList())
val networkParametersHash = networkMapStorage.saveNetworkParameters(testParameters, testParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate).sig)
val networkMap = NetworkMap(listOf(nodeInfoHashA), networkParametersHash, null)
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
// Sign network map
networkMapStorage.saveNetworkMap(signedNetworkMap)
persistence.transaction {
val entity = session.find(NodeInfoEntity::class.java, nodeInfoHashA.toString())
session.merge(entity.copy(isCurrent = false))
}
// when
val validNodeInfoHash = networkMapStorage.getActiveNodeInfoHashes()
val validNodeInfoHashes = networkMapStorage.getActiveNodeInfoHashes()
// then
assertThat(validNodeInfoHash).containsOnly(nodeInfoHashA, nodeInfoHashB)
assertThat(validNodeInfoHashes).containsOnly(nodeInfoHashB)
}
}
}

View File

@ -13,18 +13,16 @@ 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.createNetworkMapEntity
import com.r3.corda.networkmanage.createNetworkParametersEntity
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.internal.signWithCert
import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.createDevNetworkMapCa
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.nodeapi.internal.network.NetworkMapAndSigned
import net.corda.nodeapi.internal.network.SignedNetworkParameters
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.createDevIntermediateCaCertPath
import org.assertj.core.api.Assertions.assertThat
@ -39,13 +37,13 @@ class NetworkMapSignerTest : TestBase() {
private lateinit var networkMapSigner: NetworkMapSigner
private lateinit var rootCaCert: X509Certificate
private lateinit var networkMapCa: CertificateAndKeyPair
private lateinit var signingCertAndKeyPair: CertificateAndKeyPair
@Before
fun setUp() {
val (rootCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate
networkMapCa = createDevNetworkMapCa(rootCa)
signingCertAndKeyPair = createDevNetworkMapCa(rootCa)
signer = mock()
networkMapStorage = mock()
networkMapSigner = NetworkMapSigner(networkMapStorage, signer)
@ -54,20 +52,20 @@ class NetworkMapSignerTest : TestBase() {
@Test
fun `signNetworkMap builds and signs network map and network parameters`() {
// given
val signedNodeInfoHashes = listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256())
val currentParameters = testNetworkParameters(emptyList(), minimumPlatformVersion = 1)
val latestNetworkParameters = testNetworkParameters(emptyList(), minimumPlatformVersion = 2)
val networkMap = NetworkMap(signedNodeInfoHashes, currentParameters.serialize().hash, null)
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(signedNodeInfoHashes)
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(latestNetworkParameters)
whenever(networkMapStorage.getNetworkParametersOfNetworkMap()).thenReturn(currentParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
val nodeInfoHashes = listOf(SecureHash.randomSHA256(), SecureHash.randomSHA256())
val activeNetParams = testNetworkParameters(minimumPlatformVersion = 1)
val latestNetParams = testNetworkParameters(minimumPlatformVersion = 2)
val activeNetParamsEntity = createNetworkParametersEntity(signingCertAndKeyPair, activeNetParams)
val latestNetParamsEntity = createNetworkParametersEntity(signingCertAndKeyPair, latestNetParams)
val netMapEntity = createNetworkMapEntity(signingCertAndKeyPair, activeNetParamsEntity, nodeInfoHashes)
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(latestNetParamsEntity)
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(nodeInfoHashes)
whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(netMapEntity)
whenever(signer.signBytes(any())).then {
DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
DigitalSignatureWithCert(signingCertAndKeyPair.certificate, Crypto.doSign(signingCertAndKeyPair.keyPair.private, it.arguments[0] as ByteArray))
}
whenever(signer.signObject(latestNetworkParameters)).then {
val serialised = latestNetworkParameters.serialize()
whenever(signer.signObject(latestNetParams)).then {
val serialised = latestNetParams.serialize()
SignedNetworkParameters(serialised, signer.signBytes(serialised.bytes))
}
@ -78,48 +76,43 @@ class NetworkMapSignerTest : TestBase() {
// Verify networkMapStorage calls
verify(networkMapStorage).getActiveNodeInfoHashes()
verify(networkMapStorage).getLatestNetworkParameters()
verify(networkMapStorage).getNetworkParametersOfNetworkMap()
argumentCaptor<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture())
val capturedNetworkMap = firstValue.verifiedNetworkMapCert(rootCaCert)
assertEquals(latestNetworkParameters.serialize().hash, capturedNetworkMap.networkParameterHash)
assertEquals(signedNodeInfoHashes.size, capturedNetworkMap.nodeInfoHashes.size)
assertThat(capturedNetworkMap.nodeInfoHashes).containsAll(signedNodeInfoHashes)
argumentCaptor<NetworkMapAndSigned>().apply {
verify(networkMapStorage).saveNewActiveNetworkMap(capture())
val capturedNetworkMap = firstValue.networkMap
assertEquals(latestNetParams.serialize().hash, capturedNetworkMap.networkParameterHash)
assertThat(capturedNetworkMap.nodeInfoHashes).isEqualTo(nodeInfoHashes)
}
}
@Test
fun `signNetworkMap does NOT create a new network map if there are no changes`() {
// given
val networkParameters = testNetworkParameters(emptyList())
val networkMapParametersHash = networkParameters.serialize().bytes.sha256()
val networkMap = NetworkMap(emptyList(), networkMapParametersHash, null)
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
val netParamsEntity = createNetworkParametersEntity(signingCertAndKeyPair)
val netMapEntity = createNetworkMapEntity(signingCertAndKeyPair, netParamsEntity, emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(netParamsEntity)
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
whenever(networkMapStorage.getNetworkParametersOfNetworkMap()).thenReturn(networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(netMapEntity)
// when
networkMapSigner.signNetworkMap()
// then
// Verify networkMapStorage is not called
verify(networkMapStorage, never()).saveNetworkMap(any())
verify(networkMapStorage, never()).saveNewActiveNetworkMap(any())
}
@Test
fun `signNetworkMap creates a new network map if there is no current network map`() {
// given
val networkParameters = testNetworkParameters(emptyList())
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null)
val netParams = testNetworkParameters()
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(createNetworkParametersEntity(signingCertAndKeyPair, netParams))
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(emptyList())
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(null)
whenever(signer.signBytes(any())).then {
DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
DigitalSignatureWithCert(signingCertAndKeyPair.certificate, Crypto.doSign(signingCertAndKeyPair.keyPair.private, it.arguments[0] as ByteArray))
}
whenever(signer.signObject(networkParameters)).then {
val serialised = networkParameters.serialize()
whenever(signer.signObject(netParams)).then {
val serialised = netParams.serialize()
SignedNetworkParameters(serialised, signer.signBytes(serialised.bytes))
}
// when
@ -129,10 +122,9 @@ class NetworkMapSignerTest : TestBase() {
// Verify networkMapStorage calls
verify(networkMapStorage).getActiveNodeInfoHashes()
verify(networkMapStorage).getLatestNetworkParameters()
argumentCaptor<SignedNetworkMap>().apply {
verify(networkMapStorage).saveNetworkMap(capture())
val networkMap = firstValue.verifiedNetworkMapCert(rootCaCert)
assertEquals(networkParameters.serialize().hash, networkMap.networkParameterHash)
argumentCaptor<NetworkMapAndSigned>().apply {
verify(networkMapStorage).saveNewActiveNetworkMap(capture())
assertEquals(netParams.serialize().hash, firstValue.networkMap.networkParameterHash)
}
}
}
}

View File

@ -10,12 +10,12 @@
package com.r3.corda.networkmanage.doorman.webservice
import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.times
import com.nhaarman.mockito_kotlin.verify
import com.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.doorman.NetworkManagementWebServer
import com.r3.corda.networkmanage.doorman.NetworkMapConfig
import net.corda.core.crypto.SecureHash.Companion.randomSHA256
@ -23,14 +23,12 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.checkOkResponse
import net.corda.core.internal.openHttpConnection
import net.corda.core.internal.responseAs
import net.corda.core.internal.signWithCert
import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.seconds
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.createDevNetworkMapCa
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.SignedNetworkMap
import net.corda.nodeapi.internal.network.SignedNetworkParameters
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
@ -54,7 +52,7 @@ class NetworkMapWebServiceTest {
val testSerialization = SerializationEnvironmentRule(true)
private lateinit var rootCaCert: X509Certificate
private lateinit var networkMapCa: CertificateAndKeyPair
private lateinit var signingCertAndKeyPair: CertificateAndKeyPair
private val testNetworkMapConfig = NetworkMapConfig(10.seconds.toMillis(), 10.seconds.toMillis())
@ -62,16 +60,15 @@ class NetworkMapWebServiceTest {
fun init() {
val (rootCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate
networkMapCa = createDevNetworkMapCa(rootCa)
signingCertAndKeyPair = createDevNetworkMapCa(rootCa)
}
@Test
fun `submit nodeInfo`() {
val networkMapStorage: NetworkMapStorage = mock {
val networkParameter = testNetworkParameters(emptyList()).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
on { getSignedNetworkParameters(any()) }.thenReturn(networkParameter)
on { getCurrentNetworkMap() }.thenReturn(NetworkMap(emptyList(), networkParameter.raw.hash, null).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
on { getActiveNetworkMap() }.thenReturn(createNetworkMapEntity())
}
// Create node info.
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"))
@ -86,9 +83,7 @@ class NetworkMapWebServiceTest {
@Test
fun `submit old nodeInfo`() {
val networkMapStorage: NetworkMapStorage = mock {
val networkParameter = testNetworkParameters(emptyList(), minimumPlatformVersion = 2).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
on { getSignedNetworkParameters(any()) }.thenReturn(networkParameter)
on { getCurrentNetworkMap() }.thenReturn(NetworkMap(emptyList(), networkParameter.raw.hash, null).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
on { getActiveNetworkMap() }.thenReturn(createNetworkMapEntity(networkParameters = testNetworkParameters(minimumPlatformVersion = 2)))
}
// Create node info.
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1)
@ -102,9 +97,9 @@ class NetworkMapWebServiceTest {
}
@Test
fun `submit nodeInfo when no network parameters`() {
fun `submit nodeInfo when no network map`() {
val networkMapStorage: NetworkMapStorage = mock {
on { getNetworkParametersOfNetworkMap() }.thenReturn(null)
on { getActiveNetworkMap() }.thenReturn(null)
}
// Create node info.
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1)
@ -119,18 +114,19 @@ class NetworkMapWebServiceTest {
@Test
fun `get network map`() {
val networkMap = NetworkMap(listOf(randomSHA256(), randomSHA256()), randomSHA256(), null)
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
val networkMapEntity = createNetworkMapEntity(
signingCertAndKeyPair = signingCertAndKeyPair,
nodeInfoHashes = listOf(randomSHA256(), randomSHA256()))
val networkMapStorage: NetworkMapStorage = mock {
on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap)
on { getActiveNetworkMap() }.thenReturn(networkMapEntity)
}
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(mock(), networkMapStorage, testNetworkMapConfig)).use {
it.start()
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
verify(networkMapStorage, times(1)).getCurrentNetworkMap()
assertEquals(signedNetworkMapResponse.verifiedNetworkMapCert(rootCaCert), networkMap)
verify(networkMapStorage, times(1)).getActiveNetworkMap()
assertEquals(signedNetworkMapResponse.verifiedNetworkMapCert(rootCaCert), networkMapEntity.toNetworkMap())
}
}
@ -144,10 +140,8 @@ class NetworkMapWebServiceTest {
}
// Mock network map storage
val networkMap = NetworkMap(listOf(nodeInfoHash), randomSHA256(), null)
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
val networkMapStorage: NetworkMapStorage = mock {
on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap)
on { getActiveNetworkMap() }.thenReturn(createNetworkMapEntity(nodeInfoHashes = listOf(nodeInfoHash)))
}
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(nodeInfoStorage, networkMapStorage, testNetworkMapConfig)).use {
@ -165,7 +159,7 @@ class NetworkMapWebServiceTest {
@Test
fun `get network parameters`() {
val networkParameters = testNetworkParameters(emptyList())
val signedNetworkParameters = networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
val signedNetworkParameters = signingCertAndKeyPair.sign(networkParameters)
val networkParametersHash = signedNetworkParameters.raw.hash
val networkMapStorage: NetworkMapStorage = mock {
@ -177,7 +171,7 @@ class NetworkMapWebServiceTest {
val netParamsResponse = it.doGet<SignedNetworkParameters>("network-parameters/$networkParametersHash")
verify(networkMapStorage, times(1)).getSignedNetworkParameters(networkParametersHash)
assertThat(netParamsResponse.verified()).isEqualTo(networkParameters)
assertThat(netParamsResponse.sig.by).isEqualTo(networkMapCa.certificate)
assertThat(netParamsResponse.sig.by).isEqualTo(signingCertAndKeyPair.certificate)
assertThatExceptionOfType(IOException::class.java)
.isThrownBy { it.doGet<SignedNetworkParameters>("network-parameters/${randomSHA256()}") }
.withMessageContaining("404")
@ -198,4 +192,4 @@ class NetworkMapWebServiceTest {
private inline fun <reified T : Any> NetworkManagementWebServer.doGet(path: String): T {
return URL("http://$hostAndPort/network-map/$path").openHttpConnection().responseAs()
}
}
}

View File

@ -14,10 +14,7 @@ import net.corda.core.CordaOID
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignatureScheme
import net.corda.core.crypto.random63BitValue
import net.corda.core.internal.CertRole
import net.corda.core.internal.reader
import net.corda.core.internal.uncheckedCast
import net.corda.core.internal.writer
import net.corda.core.internal.*
import net.corda.core.utilities.days
import net.corda.core.utilities.millis
import org.bouncycastle.asn1.*
@ -425,4 +422,6 @@ enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurpo
)
}
data class CertificateAndKeyPair(val certificate: X509Certificate, val keyPair: KeyPair)
data class CertificateAndKeyPair(val certificate: X509Certificate, val keyPair: KeyPair) {
fun <T : Any> sign(obj: T): SignedDataWithCert<T> = obj.signWithCert(keyPair.private, certificate)
}

View File

@ -12,10 +12,13 @@ package net.corda.nodeapi.internal.network
import net.corda.core.crypto.SecureHash
import net.corda.core.internal.CertRole
import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.internal.SignedDataWithCert
import net.corda.core.internal.signWithCert
import net.corda.core.node.NetworkParameters
import net.corda.core.node.NodeInfo
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes
import net.corda.nodeapi.internal.crypto.X509Utilities
import java.security.cert.X509Certificate
import java.time.Instant
@ -63,3 +66,10 @@ fun <T : Any> SignedDataWithCert<T>.verifiedNetworkMapCert(rootCert: X509Certifi
X509Utilities.validateCertificateChain(rootCert, sig.by, rootCert)
return verified()
}
class NetworkMapAndSigned private constructor(val networkMap: NetworkMap, val signed: SignedNetworkMap) {
constructor(networkMap: NetworkMap, signer: (SerializedBytes<NetworkMap>) -> DigitalSignatureWithCert) : this(networkMap, networkMap.signWithCert(signer))
constructor(signed: SignedNetworkMap) : this(signed.verified(), signed)
operator fun component1(): NetworkMap = networkMap
operator fun component2(): SignedNetworkMap = signed
}