mirror of
https://github.com/corda/corda.git
synced 2024-12-28 16:58:55 +00:00
network_map table references the network_parameters table (ENT-1524). (#567)
This commit is contained in:
parent
2a898658c2
commit
78b2bc7549
@ -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> {
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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?
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
@ -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.")
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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")
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user