From ff4bbaf63f39f6e7d7ebc914ba26766d6121a796 Mon Sep 17 00:00:00 2001 From: Patrick Kuo Date: Thu, 22 Mar 2018 06:39:02 +0000 Subject: [PATCH] ENT-1524 & ENT-1358: Cleaned up doorman db schema (#565) * Renamed tables and constraints to have name < 30 characters * Using AttributeConverters were possible for custom types * NodeInfoEntity has relationship to accepted NetworkParametersEntity, not the hash * Improved logging in the node --- .../doorman/NetworkParametersUpdateTest.kt | 3 +- .../common/persistence/NetworkMapStorage.kt | 1 + .../common/persistence/NodeInfoStorage.kt | 6 +- ...sistentCertificateRevocationListStorage.kt | 8 +- ...sistentCertificateSigningRequestStorage.kt | 4 +- .../PersistentNetworkMapStorage.kt | 36 ++-- .../persistence/PersistentNodeInfoStorage.kt | 58 +++--- .../persistence/entity/AttributeConverters.kt | 69 ++++++ .../entity/CertificateRevocationListEntity.kt | 11 +- .../CertificateRevocationRequestEntity.kt | 49 ++--- .../entity/CertificateSigningRequestEntity.kt | 67 +++--- .../common/persistence/entity/Converters.kt | 15 -- .../persistence/entity/NetworkMapEntity.kt | 22 +- .../entity/NetworkParametersEntity.kt | 41 ++-- .../persistence/entity/NodeInfoEntity.kt | 45 ++-- .../entity/ParametersUpdateEntity.kt | 26 +-- .../common/signer/NetworkMapSigner.kt | 62 +++--- .../doorman/NetworkManagementServer.kt | 19 +- .../webservice/NetworkMapWebService.kt | 11 +- .../network-manager.changelog-init.xml | 197 ++++++++++-------- .../com/r3/corda/networkmanage/TestUtils.kt | 20 +- .../PersistentNetworkMapStorageTest.kt | 4 +- .../PersistentNodeInfoStorageTest.kt | 48 ++++- .../common/signer/NetworkMapSignerTest.kt | 9 +- .../webservice/NetworkMapWebServiceTest.kt | 2 +- .../nodeapi/internal/network/NetworkMap.kt | 9 +- .../node/internal/NetworkParametersReader.kt | 4 +- .../node/services/network/NetworkMapClient.kt | 14 +- .../services/network/NetworkMapUpdater.kt | 17 +- 29 files changed, 458 insertions(+), 419 deletions(-) create mode 100644 network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/AttributeConverters.kt delete mode 100644 network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/Converters.kt diff --git a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/NetworkParametersUpdateTest.kt b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/NetworkParametersUpdateTest.kt index 5b1827a42e..3c6e93186d 100644 --- a/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/NetworkParametersUpdateTest.kt +++ b/network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/NetworkParametersUpdateTest.kt @@ -107,8 +107,7 @@ class NetworkParametersUpdateTest { description = "Very Important Update", updateDeadline = updateDeadline ) - ) - ) + )) updates.expectEvents(isStrict = true) { sequence( diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/NetworkMapStorage.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/NetworkMapStorage.kt index de6098be80..8bbebe2a9d 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/NetworkMapStorage.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/NetworkMapStorage.kt @@ -40,6 +40,7 @@ interface NetworkMapStorage { /** * Retrieves node info hashes where [NodeInfoEntity.isCurrent] is true and the certificate status is [CertificateStatus.VALID] */ + // TODO "Active" is the wrong word here fun getActiveNodeInfoHashes(): List /** diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/NodeInfoStorage.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/NodeInfoStorage.kt index a573d05fbb..7fff43f8b8 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/NodeInfoStorage.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/NodeInfoStorage.kt @@ -10,6 +10,7 @@ package com.r3.corda.networkmanage.common.persistence +import com.r3.corda.networkmanage.common.persistence.entity.NetworkParametersEntity import net.corda.core.crypto.SecureHash import net.corda.core.node.NodeInfo import net.corda.nodeapi.internal.NodeInfoAndSigned @@ -33,11 +34,10 @@ interface NodeInfoStorage { fun getNodeInfo(nodeInfoHash: SecureHash): SignedNodeInfo? /** - * Retrieve latest accepted parameters hash for nodeInfo with given hash. - * @return Hash of network parameters that the node has accepted or null if couldn't find node info with given hash or + * Returns the network parameters that the node has accepted or null if couldn't find node info with given hash or * there is no information on accepted parameters hash stored for this entity */ - fun getAcceptedParametersUpdateHash(nodeInfoHash: SecureHash): SecureHash? + fun getAcceptedNetworkParameters(nodeInfoHash: SecureHash): NetworkParametersEntity? /** * The [nodeInfoAndSigned] is keyed by the public key, old node info with the same public key will be replaced by the new node info. diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRevocationListStorage.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRevocationListStorage.kt index db5f35bba0..16a5ee5ab3 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRevocationListStorage.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRevocationListStorage.kt @@ -3,7 +3,6 @@ package com.r3.corda.networkmanage.common.persistence import com.r3.corda.networkmanage.common.persistence.entity.CertificateDataEntity import com.r3.corda.networkmanage.common.persistence.entity.CertificateRevocationListEntity import com.r3.corda.networkmanage.common.persistence.entity.CertificateRevocationRequestEntity -import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseTransaction import java.math.BigInteger @@ -21,10 +20,7 @@ class PersistentCertificateRevocationListStorage(private val database: CordaPers } } // We just want the last signed entry - val crlEntity = session.createQuery(query).setMaxResults(1).uniqueResult() - crlEntity?.let { - X509CertificateFactory().delegate.generateCRL(crlEntity.crlBytes.inputStream()) as X509CRL - } + session.createQuery(query).setMaxResults(1).uniqueResult()?.crl } } @@ -34,7 +30,7 @@ class PersistentCertificateRevocationListStorage(private val database: CordaPers revokeCertificate(it.serialNumber, revokedAt, this) } session.save(CertificateRevocationListEntity( - crlBytes = crl.encoded, + crl = crl, crlIssuer = crlIssuer, signedBy = signedBy, modifiedAt = Instant.now() diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateSigningRequestStorage.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateSigningRequestStorage.kt index 9d8ffae345..fc7c902d33 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateSigningRequestStorage.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateSigningRequestStorage.kt @@ -51,7 +51,7 @@ class PersistentCertificateSigningRequestStorage(private val database: CordaPers session.merge(certificateSigningRequest) val certificateDataEntity = CertificateDataEntity( certificateStatus = CertificateStatus.VALID, - certificatePathBytes = certPath.encoded, + certPath = certPath, certificateSigningRequest = certificateSigningRequest, certificateSerialNumber = certPath.x509Certificates.first().serialNumber) session.persist(certificateDataEntity) @@ -71,7 +71,7 @@ class PersistentCertificateSigningRequestStorage(private val database: CordaPers requestId = requestId, legalName = legalNameOrRejectMessage as? CordaX500Name, publicKeyHash = toSupportedPublicKey(request.subjectPublicKeyInfo).hashString(), - requestBytes = request.encoded, + request = request, remark = legalNameOrRejectMessage as? String, modifiedBy = CertificateSigningRequestStorage.DOORMAN_SIGNATURE, status = if (legalNameOrRejectMessage is CordaX500Name) RequestStatus.NEW else RequestStatus.REJECTED diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorage.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorage.kt index 4e1775caae..2f9d0c42ef 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorage.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorage.kt @@ -15,12 +15,11 @@ import net.corda.core.crypto.SecureHash import net.corda.core.internal.DigitalSignatureWithCert import net.corda.core.node.NetworkParameters import net.corda.core.serialization.serialize -import net.corda.nodeapi.internal.network.ParametersUpdate import net.corda.nodeapi.internal.network.NetworkMapAndSigned import net.corda.nodeapi.internal.network.SignedNetworkParameters import net.corda.nodeapi.internal.persistence.CordaPersistence -import java.time.Instant import net.corda.nodeapi.internal.persistence.DatabaseTransaction +import java.time.Instant /** * Database implementation of the [NetworkMapStorage] interface @@ -49,9 +48,9 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw "Network parameters ${networkMap.networkParameterHash} are not signed" } session.save(NetworkMapEntity( - networkMapBytes = signedNetworkMap.raw.bytes, + networkMap = networkMap, signature = signedNetworkMap.sig.bytes, - certificate = signedNetworkMap.sig.by.encoded, + certificate = signedNetworkMap.sig.by, networkParameters = networkParametersEntity )) } @@ -85,21 +84,22 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw } override fun saveNetworkParameters(networkParameters: NetworkParameters, signature: DigitalSignatureWithCert?): SecureHash { - val serialised = networkParameters.serialize() - val hash = serialised.hash + val serialized = networkParameters.serialize() + signature?.verify(serialized) + val hash = serialized.hash database.transaction { val entity = getNetworkParametersEntity(hash) val newNetworkParamsEntity = if (entity != null) { entity.copy( signature = signature?.bytes, - certificate = signature?.by?.encoded + certificate = signature?.by ) } else { NetworkParametersEntity( - parametersBytes = serialised.bytes, - parametersHash = hash.toString(), + networkParameters = networkParameters, + hash = hash.toString(), signature = signature?.bytes, - certificate = signature?.by?.encoded + certificate = signature?.by ) } session.merge(newNetworkParamsEntity) @@ -139,7 +139,7 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw override fun getParametersUpdate(): ParametersUpdateEntity? { return database.transaction { - val currentParametersHash = getActiveNetworkMap()?.toNetworkMap()?.networkParameterHash + val currentParametersHash = getActiveNetworkMap()?.networkParameters?.hash val latestParameters = getLatestNetworkParameters() val criteria = session.criteriaBuilder.createQuery(ParametersUpdateEntity::class.java) val root = criteria.from(ParametersUpdateEntity::class.java) @@ -150,7 +150,7 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw "ParametersUpdate doesn't correspond to latest network parameters" } // Highly unlikely, but... - check (parametersUpdate == null || latestParameters?.parametersHash != currentParametersHash?.toString()) { + check(parametersUpdate == null || latestParameters?.hash != currentParametersHash) { "Having update for parameters that are already in network map" } parametersUpdate @@ -160,16 +160,16 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw override fun setFlagDay(parametersHash: SecureHash) { database.transaction { val parametersUpdateEntity = getParametersUpdate() ?: throw IllegalArgumentException("Setting flag day but no parameters update to switch to") - if (parametersHash.toString() != parametersUpdateEntity.networkParameters.parametersHash) { - throw IllegalArgumentException("Setting flag day for parameters: $parametersHash, but in database we have update for: ${parametersUpdateEntity.networkParameters.parametersHash}") + if (parametersHash.toString() != parametersUpdateEntity.networkParameters.hash) { + throw IllegalArgumentException("Setting flag day for parameters: $parametersHash, but in database we have update for: ${parametersUpdateEntity.networkParameters.hash}") } session.merge(parametersUpdateEntity.copy(flagDay = true)) } } +} - private fun DatabaseTransaction.getNetworkParametersEntity(hash: SecureHash): NetworkParametersEntity? { - return uniqueEntityWhere { builder, path -> - builder.equal(path.get(NetworkParametersEntity::parametersHash.name), hash.toString()) - } +internal fun DatabaseTransaction.getNetworkParametersEntity(hash: SecureHash): NetworkParametersEntity? { + return uniqueEntityWhere { builder, path -> + builder.equal(path.get(NetworkParametersEntity::hash.name), hash.toString()) } } diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorage.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorage.kt index 0409472884..3c101ee494 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorage.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorage.kt @@ -11,14 +11,13 @@ package com.r3.corda.networkmanage.common.persistence import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity +import com.r3.corda.networkmanage.common.persistence.entity.NetworkParametersEntity import com.r3.corda.networkmanage.common.persistence.entity.NodeInfoEntity -import com.r3.corda.networkmanage.common.utils.buildCertPath import com.r3.corda.networkmanage.common.utils.hashString import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 import net.corda.core.internal.CertRole import net.corda.core.internal.CertRole.NODE_CA -import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.crypto.x509Certificates @@ -34,7 +33,7 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn val (nodeInfo, signedNodeInfo) = nodeInfoAndSigned val nodeCaCert = nodeInfo.legalIdentitiesAndCerts[0].certPath.x509Certificates.find { CertRole.extract(it) == NODE_CA } nodeCaCert ?: throw IllegalArgumentException("Missing Node CA") - return database.transaction { + database.transaction { // TODO Move these checks out of data access layer val request = requireNotNull(getSignedRequestByPublicHash(nodeCaCert.publicKey.encoded.sha256())) { "Node-info not registered with us" @@ -43,44 +42,43 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn require(it == CertificateStatus.VALID) { "Certificate is no longer valid: $it" } } - // Update any [NodeInfoEntity] instance for this CSR as not current. - entitiesWhere { builder, path -> - val requestEq = builder.equal(path.get(NodeInfoEntity::certificateSigningRequest.name), request) - val isCurrent = builder.isTrue(path.get(NodeInfoEntity::isCurrent.name)) - builder.and(requestEq, isCurrent) - }.resultStream.use { existingNodeInfos -> - existingNodeInfos.forEach { session.merge(it.copy(isCurrent = false)) } - } + val existingNodeInfos = session.createQuery( + "from ${NodeInfoEntity::class.java.name} n where n.certificateSigningRequest = :csr and n.isCurrent = true order by n.publishedAt desc", + NodeInfoEntity::class.java) + .setParameter("csr", request) + .resultList - val hash = signedNodeInfo.raw.hash - val hashedNodeInfo = NodeInfoEntity( - nodeInfoHash = hash.toString(), - identityPkHash = nodeInfo.legalIdentities.first().owningKey.hashString(), + // Update any [NodeInfoEntity] instance for this CSR as not current. + existingNodeInfos.forEach { session.merge(it.copy(isCurrent = false)) } + + session.save(NodeInfoEntity( + nodeInfoHash = signedNodeInfo.raw.hash.toString(), + publicKeyHash = nodeInfo.legalIdentities[0].owningKey.hashString(), certificateSigningRequest = request, - signedNodeInfoBytes = signedNodeInfo.serialize().bytes, - isCurrent = true) - session.save(hashedNodeInfo) - hash + signedNodeInfo = signedNodeInfo, + isCurrent = true, + acceptedNetworkParameters = existingNodeInfos.firstOrNull()?.acceptedNetworkParameters + )) } + return signedNodeInfo.raw.hash } override fun getNodeInfo(nodeInfoHash: SecureHash): SignedNodeInfo? { return database.transaction { - session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())?.toSignedNodeInfo() + session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())?.signedNodeInfo } } - override fun getAcceptedParametersUpdateHash(nodeInfoHash: SecureHash): SecureHash? { + override fun getAcceptedNetworkParameters(nodeInfoHash: SecureHash): NetworkParametersEntity? { return database.transaction { - val hashString = session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())?.acceptedParametersHash - if (hashString == null || hashString.isEmpty()) null else SecureHash.parse(hashString) + session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())?.acceptedNetworkParameters } } override fun getCertificatePath(publicKeyHash: SecureHash): CertPath? { return database.transaction { val request = getSignedRequestByPublicHash(publicKeyHash) - request?.let { buildCertPath(it.certificateData!!.certificatePathBytes) } + request?.let { it.certificateData!!.certPath } } } @@ -89,11 +87,17 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn val builder = session.criteriaBuilder val query = builder.createQuery(NodeInfoEntity::class.java).run { from(NodeInfoEntity::class.java).run { - where(builder.equal(get(NodeInfoEntity::identityPkHash.name), publicKeyHash.toString())) + where(builder.equal(get(NodeInfoEntity::publicKeyHash.name), publicKeyHash.toString())) } } - val nodeInfo = session.createQuery(query).setMaxResults(1).uniqueResult() - val newInfo = nodeInfo.copy(acceptedParametersHash = acceptedParametersHash.toString()) + val nodeInfo = requireNotNull(session.createQuery(query).setMaxResults(1).uniqueResult()) { + "NodeInfo with public key hash $publicKeyHash doesn't exist" + } + val networkParameters = requireNotNull(getNetworkParametersEntity(acceptedParametersHash)) { + "Network parameters $acceptedParametersHash doesn't exist" + } + require(networkParameters.isSigned) { "Network parameters $acceptedParametersHash is not signed" } + val newInfo = nodeInfo.copy(acceptedNetworkParameters = networkParameters) session.merge(newInfo) } } diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/AttributeConverters.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/AttributeConverters.kt new file mode 100644 index 0000000000..3902113fcf --- /dev/null +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/AttributeConverters.kt @@ -0,0 +1,69 @@ +package com.r3.corda.networkmanage.common.persistence.entity + +import com.r3.corda.networkmanage.common.utils.buildCertPath +import net.corda.core.node.NetworkParameters +import net.corda.core.serialization.SerializationFactory +import net.corda.core.serialization.serialize +import net.corda.core.utilities.sequence +import net.corda.nodeapi.internal.SignedNodeInfo +import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import net.corda.nodeapi.internal.network.NetworkMap +import org.bouncycastle.pkcs.PKCS10CertificationRequest +import java.security.cert.CertPath +import java.security.cert.X509CRL +import java.security.cert.X509Certificate +import javax.persistence.AttributeConverter +import net.corda.core.identity.CordaX500Name + +class PKCS10CertificationRequestConverter : AttributeConverter { + override fun convertToEntityAttribute(dbData: ByteArray?): PKCS10CertificationRequest? = dbData?.let(::PKCS10CertificationRequest) + override fun convertToDatabaseColumn(attribute: PKCS10CertificationRequest?): ByteArray? = attribute?.encoded +} + +class CertPathConverter : AttributeConverter { + override fun convertToEntityAttribute(dbData: ByteArray?): CertPath? = dbData?.let(::buildCertPath) + override fun convertToDatabaseColumn(attribute: CertPath?): ByteArray? = attribute?.encoded +} + +class X509CertificateConverter : AttributeConverter { + override fun convertToEntityAttribute(dbData: ByteArray?): X509Certificate? { + return dbData?.let { X509CertificateFactory().generateCertificate(it.inputStream()) } + } + override fun convertToDatabaseColumn(attribute: X509Certificate?): ByteArray? = attribute?.encoded +} + +class X509CRLConverter : AttributeConverter { + override fun convertToEntityAttribute(dbData: ByteArray?): X509CRL? { + return dbData?.let { X509CertificateFactory().delegate.generateCRL(it.inputStream()) as X509CRL } + } + override fun convertToDatabaseColumn(attribute: X509CRL?): ByteArray? = attribute?.encoded +} + +class NetworkParametersConverter : CordaSerializationConverter(NetworkParameters::class.java) + +class NetworkMapConverter : CordaSerializationConverter(NetworkMap::class.java) + +class SignedNodeInfoConverter : CordaSerializationConverter(SignedNodeInfo::class.java) + +sealed class CordaSerializationConverter(private val clazz: Class) : AttributeConverter { + override fun convertToEntityAttribute(dbData: ByteArray?): T? { + return dbData?.let { + val serializationFactory = SerializationFactory.defaultFactory + serializationFactory.deserialize(it.sequence(), clazz, serializationFactory.defaultContext) + } + } + + override fun convertToDatabaseColumn(attribute: T?): ByteArray? = attribute?.serialize()?.bytes +} + +class CordaX500NameAttributeConverter : AttributeConverter { + override fun convertToDatabaseColumn(attribute: CordaX500Name?): String? = attribute?.toString() + override fun convertToEntityAttribute(dbData: String?): CordaX500Name? = dbData?.let { CordaX500Name.parse(it) } +} + +// TODO Use SecureHash in entities +//class SecureHashAttributeConverter : AttributeConverter { +// override fun convertToDatabaseColumn(attribute: SecureHash?): String? = attribute?.toString() +// override fun convertToEntityAttribute(dbData: String?): SecureHash? = dbData?.let { SecureHash.parse(it) } +//} + diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/CertificateRevocationListEntity.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/CertificateRevocationListEntity.kt index b541e05179..ef84d3fcf7 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/CertificateRevocationListEntity.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/CertificateRevocationListEntity.kt @@ -1,24 +1,27 @@ package com.r3.corda.networkmanage.common.persistence.entity import com.r3.corda.networkmanage.common.persistence.CrlIssuer +import java.security.cert.X509CRL import java.time.Instant import javax.persistence.* @Entity -@Table(name = "certificate_revocation_list") +@Table(name = "cert_revocation_list") class CertificateRevocationListEntity( @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) val id: Long? = null, - @Column(name = "issuer") + @Column(name = "issuer", length = 16, nullable = false, columnDefinition = "NVARCHAR(16)") + @Enumerated(EnumType.STRING) val crlIssuer: CrlIssuer, @Lob @Column(name = "crl_bytes", nullable = false) - val crlBytes: ByteArray, + @Convert(converter = X509CRLConverter::class) + val crl: X509CRL, - @Column(name = "signed_by", length = 512) + @Column(name = "signed_by", length = 64, nullable = false) val signedBy: String, @Column(name = "modified_at", nullable = false) diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/CertificateRevocationRequestEntity.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/CertificateRevocationRequestEntity.kt index 3319d1f7a0..c28d10c767 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/CertificateRevocationRequestEntity.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/CertificateRevocationRequestEntity.kt @@ -9,73 +9,52 @@ import java.time.Instant import javax.persistence.* @Entity -@Table(name = "certificate_revocation_request") -class CertificateRevocationRequestEntity( +@Table(name = "cert_revocation_request") +data class CertificateRevocationRequestEntity( @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) val id: Long? = null, - @Column(name = "request_id", length = 256, nullable = false, unique = true) + @Column(name = "request_id", length = 64, nullable = false, unique = true) val requestId: String, @OneToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "certificate_data") + @JoinColumn(name = "cert_data", nullable = false) val certificateData: CertificateDataEntity, - @Column(name = "certificate_serial_number", nullable = false) + @Column(name = "cert_serial_number", nullable = false, columnDefinition = "NUMERIC(28)") val certificateSerialNumber: BigInteger, @Column(name = "legal_name", length = 256, nullable = false) @Convert(converter = CordaX500NameAttributeConverter::class) val legalName: CordaX500Name, + // Setting [columnDefinition] is a work around for a hibernate problem when using SQL database. + // TODO: Remove this when we find out the cause of the problem. @Audited - @Column(name = "status", nullable = false) + @Column(name = "status", length = 16, nullable = false, columnDefinition = "NVARCHAR(16)") @Enumerated(EnumType.STRING) val status: RequestStatus = RequestStatus.NEW, - @Column(name = "reporter", nullable = false, length = 512) + @Column(name = "reporter", nullable = false, length = 64) val reporter: String, @Audited - @Column(name = "modified_by", nullable = false, length = 512) + @Column(name = "modified_by", nullable = false, length = 64) val modifiedBy: String, @Audited @Column(name = "modified_at", nullable = false) val modifiedAt: Instant = Instant.now(), + // Setting [columnDefinition] is a work around for a hibernate problem when using SQL database. + // TODO: Remove this when we find out the cause of the problem. @Audited - @Column(name = "revocation_reason", nullable = false) + @Column(name = "revocation_reason", length = 32, nullable = false, columnDefinition = "NVARCHAR(32)") @Enumerated(EnumType.STRING) val revocationReason: CRLReason, @Audited @Column(name = "remark", length = 256) val remark: String? = null -) { - fun copy(requestId: String = this.requestId, - certificateData: CertificateDataEntity = this.certificateData, - certificateSerialNumber: BigInteger = this.certificateSerialNumber, - status: RequestStatus = this.status, - legalName: CordaX500Name = this.legalName, - reporter: String = this.reporter, - modifiedBy: String = this.modifiedBy, - modifiedAt: Instant = this.modifiedAt, - revocationReason: CRLReason = this.revocationReason, - remark: String? = this.remark): CertificateRevocationRequestEntity { - return CertificateRevocationRequestEntity( - id = this.id, - requestId = requestId, - certificateData = certificateData, - certificateSerialNumber = certificateSerialNumber, - status = status, - legalName = legalName, - reporter = reporter, - modifiedBy = modifiedBy, - modifiedAt = modifiedAt, - revocationReason = revocationReason, - remark = remark - ) - } -} \ No newline at end of file +) \ No newline at end of file diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/CertificateSigningRequestEntity.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/CertificateSigningRequestEntity.kt index 2a4a41b477..7ba09df012 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/CertificateSigningRequestEntity.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/CertificateSigningRequestEntity.kt @@ -14,7 +14,6 @@ import com.r3.corda.networkmanage.common.persistence.CertificateData import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequest import com.r3.corda.networkmanage.common.persistence.CertificateStatus import com.r3.corda.networkmanage.common.persistence.RequestStatus -import com.r3.corda.networkmanage.common.utils.buildCertPath import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name import net.corda.nodeapi.internal.crypto.x509Certificates @@ -26,10 +25,10 @@ import java.time.Instant import javax.persistence.* @Entity -@Table(name = "certificate_signing_request", indexes = arrayOf(Index(name = "IDX_PUB_KEY_HASH", columnList = "public_key_hash"))) +@Table(name = "cert_signing_request", indexes = arrayOf(Index(name = "IDX__CSR__PKH", columnList = "public_key_hash"))) data class CertificateSigningRequestEntity( @Id - @Column(name = "request_id", length = 64) + @Column(name = "request_id", length = 64, nullable = false) val requestId: String, // TODO: Store X500Name with a proper schema. @@ -37,19 +36,21 @@ data class CertificateSigningRequestEntity( @Convert(converter = CordaX500NameAttributeConverter::class) val legalName: CordaX500Name?, - @Column(name = "public_key_hash", length = 64) + @Column(name = "public_key_hash", length = 64, nullable = false) val publicKeyHash: String, + // Setting [columnDefinition] is a work around for a hibernate problem when using SQL database. + // TODO: Remove this when we find out the cause of the problem. @Audited - @Column(name = "status", nullable = false) + @Column(name = "status", length = 16, nullable = false, columnDefinition = "NVARCHAR(16)") @Enumerated(EnumType.STRING) val status: RequestStatus = RequestStatus.NEW, @Audited - @Column(name = "modified_by", length = 512) + @Column(name = "modified_by", length = 64, nullable = false) val modifiedBy: String, - @Audited + // TODO: Use audit framework instead. @Column(name = "modified_at", nullable = false) val modifiedAt: Instant = Instant.now(), @@ -62,10 +63,11 @@ data class CertificateSigningRequestEntity( @Lob @Column(name = "request_bytes", nullable = false) - val requestBytes: ByteArray, + @Convert(converter = PKCS10CertificationRequestConverter::class) + val request: PKCS10CertificationRequest, @ManyToOne - @JoinColumn(name = "private_network", foreignKey = ForeignKey(name = "FK_CSR_PN")) + @JoinColumn(name = "private_network", foreignKey = ForeignKey(name = "FK__CSR__PN")) val privateNetwork: PrivateNetworkEntity? = null ) { fun toCertificateSigningRequest(): CertificateSigningRequest { @@ -74,70 +76,53 @@ data class CertificateSigningRequestEntity( legalName = legalName, publicKeyHash = SecureHash.parse(publicKeyHash), status = status, - request = request(), + request = request, remark = remark, modifiedBy = modifiedBy, certData = certificateData?.toCertificateData() ) } - - private fun request() = PKCS10CertificationRequest(requestBytes) } @Entity -@Table(name = "certificate_data") +@Table(name = "cert_data") data class CertificateDataEntity( @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) val id: Long? = null, - @Column(name = "certificate_status") + // Setting [columnDefinition] is a work around for a hibernate problem when using SQL database. + // TODO: Remove this when we find out the cause of the problem. + @Column(name = "cert_status", length = 16, nullable = false, columnDefinition = "NVARCHAR(16)") + @Enumerated(EnumType.STRING) val certificateStatus: CertificateStatus, @Lob - @Column(name = "certificate_path_bytes") - val certificatePathBytes: ByteArray, + @Column(name = "cert_path_bytes", nullable = false) + @Convert(converter = CertPathConverter::class) + val certPath: CertPath, @OneToOne(fetch = FetchType.EAGER, optional = false) - @JoinColumn(name = "certificate_signing_request", foreignKey = ForeignKey(name = "FK__cert_data__cert_sign_req")) + @JoinColumn(name = "cert_signing_request", foreignKey = ForeignKey(name = "FK__CD__CSR"), nullable = false) val certificateSigningRequest: CertificateSigningRequestEntity, - @Column(name = "certificate_serial_number", unique = true) + @Column(name = "cert_serial_number", unique = true, nullable = false, columnDefinition = "NUMERIC(28)") val certificateSerialNumber: BigInteger ) { - fun toCertificateData(): CertificateData { - return CertificateData( - certStatus = certificateStatus, - certPath = toCertificatePath() - ) - } + fun toCertificateData(): CertificateData = CertificateData(certificateStatus, certPath) val legalName: CordaX500Name get() { - return CordaX500Name.build(toCertificatePath().x509Certificates[0].subjectX500Principal) + return CordaX500Name.build(certPath.x509Certificates[0].subjectX500Principal) } - - fun copy(certificateStatus: CertificateStatus = this.certificateStatus, - certificatePathBytes: ByteArray = this.certificatePathBytes, - certificateSigningRequest: CertificateSigningRequestEntity = this.certificateSigningRequest, - certificateSerialNumber: BigInteger = this.certificateSerialNumber): CertificateDataEntity { - return CertificateDataEntity( - id = this.id, - certificateStatus = certificateStatus, - certificatePathBytes = certificatePathBytes, - certificateSigningRequest = certificateSigningRequest, - certificateSerialNumber = certificateSerialNumber) - } - - private fun toCertificatePath(): CertPath = buildCertPath(certificatePathBytes) } @Entity @Table(name = "private_network") data class PrivateNetworkEntity( @Id - @Column(name = "id") + @Column(name = "id", length = 64) val networkId: String, - @Column(name = "name") + @Column(name = "name", length = 255, nullable = false) val networkName: String ) \ No newline at end of file diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/Converters.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/Converters.kt deleted file mode 100644 index a315c632b3..0000000000 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/Converters.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.r3.corda.networkmanage.common.persistence.entity - -import net.corda.core.identity.CordaX500Name -import javax.persistence.AttributeConverter - -class CordaX500NameAttributeConverter : AttributeConverter { - override fun convertToDatabaseColumn(attribute: CordaX500Name?): String? = attribute?.toString() - override fun convertToEntityAttribute(dbData: String?): CordaX500Name? = dbData?.let { CordaX500Name.parse(it) } -} - -// TODO Use SecureHash in entities -//class SecureHashAttributeConverter : AttributeConverter { -// override fun convertToDatabaseColumn(attribute: SecureHash?): String? = attribute?.toString() -// override fun convertToEntityAttribute(dbData: String?): SecureHash? = dbData?.let { SecureHash.parse(it) } -//} diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NetworkMapEntity.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NetworkMapEntity.kt index 343102efc4..c080d33e69 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NetworkMapEntity.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NetworkMapEntity.kt @@ -11,11 +11,10 @@ package com.r3.corda.networkmanage.common.persistence.entity import net.corda.core.internal.DigitalSignatureWithCert -import net.corda.core.serialization.SerializedBytes -import net.corda.core.serialization.deserialize -import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.SignedNetworkMap +import java.security.cert.X509Certificate import javax.persistence.* @Entity @@ -27,26 +26,23 @@ class NetworkMapEntity( @Lob @Column(name = "serialized_network_map", nullable = false) - val networkMapBytes: ByteArray, + @Convert(converter = NetworkMapConverter::class) + val networkMap: NetworkMap, @Lob @Column(name = "signature", nullable = false) val signature: ByteArray, @Lob - @Column(name = "certificate", nullable = false) - val certificate: ByteArray, + @Column(name = "cert", nullable = false) + @Convert(converter = X509CertificateConverter::class) + val certificate: X509Certificate, @ManyToOne(optional = false, fetch = FetchType.EAGER) - @JoinColumn(name = "network_parameters") + @JoinColumn(name = "network_parameters", nullable = false) val networkParameters: NetworkParametersEntity ) { - fun toNetworkMap(): NetworkMap = networkMapBytes.deserialize() - fun toSignedNetworkMap(): SignedNetworkMap { - return SignedNetworkMap( - SerializedBytes(networkMapBytes), - DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature) - ) + return SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(certificate, signature)) } } diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NetworkParametersEntity.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NetworkParametersEntity.kt index 83d3f22237..4fe42149c1 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NetworkParametersEntity.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NetworkParametersEntity.kt @@ -12,27 +12,26 @@ package com.r3.corda.networkmanage.common.persistence.entity import net.corda.core.internal.DigitalSignatureWithCert import net.corda.core.node.NetworkParameters -import net.corda.core.serialization.SerializedBytes -import net.corda.core.serialization.deserialize -import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.network.SignedNetworkParameters -import org.hibernate.annotations.CreationTimestamp +import java.security.cert.X509Certificate import java.time.Instant import javax.persistence.* @Entity -@Table(name = "network_parameters", indexes = arrayOf(Index(name = "IDX_NET_PARAMS_HASH", columnList = "hash"))) +@Table(name = "network_parameters", indexes = arrayOf(Index(name = "IDX_NP_HASH", columnList = "hash"))) class NetworkParametersEntity( @Id - @Column(name = "hash", length = 64, unique = true) - val parametersHash: String, + @Column(name = "hash", length = 64, nullable = false) + val hash: String, - @CreationTimestamp + @Column(nullable = false) val created: Instant = Instant.now(), @Lob @Column(name = "parameters_bytes", nullable = false) - val parametersBytes: ByteArray, + @Convert(converter = NetworkParametersConverter::class) + val networkParameters: NetworkParameters, // Both of the fields below are nullable, because of the way we sign network map data. NetworkParameters can be // inserted into database without signature. Then signing service will sign them. @@ -41,31 +40,27 @@ class NetworkParametersEntity( val signature: ByteArray?, @Lob - @Column(name = "certificate") - val certificate: ByteArray? + @Column(name = "cert") + @Convert(converter = X509CertificateConverter::class) + val certificate: X509Certificate? ) { val isSigned: Boolean get() = certificate != null && signature != null - fun toNetworkParameters(): NetworkParameters = parametersBytes.deserialize() - fun toSignedNetworkParameters(): SignedNetworkParameters { - if (certificate == null || signature == null) throw IllegalStateException("Network parameters entity is not signed: $parametersHash") - return SignedNetworkParameters( - SerializedBytes(parametersBytes), - DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature) - ) + if (certificate == null || signature == null) throw IllegalStateException("Network parameters entity is not signed: $hash") + return SignedNetworkParameters(networkParameters.serialize(), DigitalSignatureWithCert(certificate, signature)) } - fun copy(parametersHash: String = this.parametersHash, + fun copy(parametersHash: String = this.hash, created: Instant = this.created, - parametersBytes: ByteArray = this.parametersBytes, + networkParameters: NetworkParameters = this.networkParameters, signature: ByteArray? = this.signature, - certificate: ByteArray? = this.certificate + certificate: X509Certificate? = this.certificate ): NetworkParametersEntity { return NetworkParametersEntity( - parametersHash = parametersHash, + hash = parametersHash, created = created, - parametersBytes = parametersBytes, + networkParameters = networkParameters, signature = signature, certificate = certificate ) diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NodeInfoEntity.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NodeInfoEntity.kt index 24b88bf876..f08e9d2387 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NodeInfoEntity.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/NodeInfoEntity.kt @@ -10,7 +10,6 @@ package com.r3.corda.networkmanage.common.persistence.entity -import net.corda.core.serialization.deserialize import net.corda.nodeapi.internal.SignedNodeInfo import java.time.Instant import javax.persistence.* @@ -21,47 +20,27 @@ data class NodeInfoEntity( // Hash of serialized [NodeInfo] without signatures. @Id @Column(name = "node_info_hash", length = 64) - val nodeInfoHash: String = "", + val nodeInfoHash: String, @Column(name = "public_key_hash", length = 64) - val identityPkHash: String = "", + val publicKeyHash: String, @ManyToOne(optional = false, fetch = FetchType.LAZY) - @JoinColumn(name = "certificate_signing_request") + @JoinColumn(name = "cert_signing_request", nullable = false) val certificateSigningRequest: CertificateSigningRequestEntity, @Lob - @Column(name = "signed_node_info_bytes") - val signedNodeInfoBytes: ByteArray, + @Column(name = "signed_node_info_bytes", nullable = false) + @Convert(converter = SignedNodeInfoConverter::class) + val signedNodeInfo: SignedNodeInfo, - @Column(name = "is_current") + @Column(name = "is_current", nullable = false) val isCurrent: Boolean, - @Column(name = "published_at") + @Column(name = "published_at", nullable = false) val publishedAt: Instant = Instant.now(), - @Column(name = "accepted_parameters_hash", length = 64) - val acceptedParametersHash: String = "" -) { - /** - * Deserialize NodeInfoEntity.signedNodeInfoBytes into the [SignedNodeInfo] instance - */ - fun toSignedNodeInfo() = signedNodeInfoBytes.deserialize() - - fun copy(nodeInfoHash: String = this.nodeInfoHash, - certificateSigningRequest: CertificateSigningRequestEntity = this.certificateSigningRequest, - signedNodeInfoBytes: ByteArray = this.signedNodeInfoBytes, - isCurrent: Boolean = this.isCurrent, - publishedAt: Instant = this.publishedAt, - acceptedParametersHash: String = this.acceptedParametersHash - ): NodeInfoEntity { - return NodeInfoEntity( - nodeInfoHash = nodeInfoHash, - certificateSigningRequest = certificateSigningRequest, - signedNodeInfoBytes = signedNodeInfoBytes, - isCurrent = isCurrent, - publishedAt = publishedAt, - acceptedParametersHash = acceptedParametersHash - ) - } -} + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "accepted_network_parameters") + val acceptedNetworkParameters: NetworkParametersEntity? +) diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/ParametersUpdateEntity.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/ParametersUpdateEntity.kt index 1be5cfd86f..cc099e03dd 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/ParametersUpdateEntity.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/entity/ParametersUpdateEntity.kt @@ -7,7 +7,7 @@ import javax.persistence.* @Entity @Table(name = "parameters_update") -class ParametersUpdateEntity( +data class ParametersUpdateEntity( @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) val id: Long? = null, @@ -17,31 +17,17 @@ class ParametersUpdateEntity( @JoinColumn(name = "network_parameters", foreignKey = ForeignKey(name = "FK__param_up__net_param")) val networkParameters: NetworkParametersEntity, - @Column(name = "description") + @Column(name = "description", nullable = false) val description: String, - @Column(name = "update_deadline") + @Column(name = "update_deadline", nullable = false) val updateDeadline: Instant, // This boolean flag is used when we want to explicitly point that it's time to switch parameters in network map. - @Column(name = "flag_day") + @Column(name = "flag_day", nullable = false) val flagDay: Boolean = false ) { - fun toParametersUpdate(): ParametersUpdate = ParametersUpdate(SecureHash.parse(networkParameters.parametersHash), description, updateDeadline) - - fun toNetMapUpdate(): ParametersUpdate? = if (!flagDay) ParametersUpdate(SecureHash.parse(networkParameters.parametersHash), description, updateDeadline) else null - - fun copy(id: Long? = this.id, - networkParameters: NetworkParametersEntity = this.networkParameters, - description: String = this.description, - updateDeadline: Instant = this.updateDeadline, - flagDay: Boolean = this.flagDay): ParametersUpdateEntity { - return ParametersUpdateEntity( - id = id, - networkParameters = networkParameters, - description = description, - updateDeadline = updateDeadline, - flagDay = flagDay - ) + fun toParametersUpdate(): ParametersUpdate { + return ParametersUpdate(SecureHash.parse(networkParameters.hash), description, updateDeadline) } } \ No newline at end of file diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSigner.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSigner.kt index cb93637983..e7beb52f35 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSigner.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSigner.kt @@ -12,15 +12,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.nodeapi.internal.network.NetworkMap import net.corda.core.node.NetworkParameters -import net.corda.core.serialization.SerializedBytes -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.NetworkMapAndSigned - class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) { private companion object { val logger = contextLogger() @@ -30,41 +27,56 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private * Signs the network map and latest network parameters if they haven't been signed yet. */ fun signNetworkMap() { - logger.debug("Fetching current network map...") - val currentNetworkMap = networkMapStorage.getActiveNetworkMap() - if (currentNetworkMap == null) { - logger.info("There is currently no network map") - } else { - logger.debug { "Current network map: $currentNetworkMap" } - } - val currentNetworkParameters = currentNetworkMap?.networkParameters - logger.debug("Current network map parameters: ${currentNetworkParameters?.toNetworkParameters()}") - logger.debug("Fetching node info hashes with VALID certificates...") - val nodeInfoHashes = networkMapStorage.getActiveNodeInfoHashes() - logger.debug("Retrieved node info hashes: $nodeInfoHashes") val latestNetworkParameters = networkMapStorage.getLatestNetworkParameters() if (latestNetworkParameters == null) { logger.debug("No network parameters present") return } - logger.debug { "Retrieved latest network parameters: ${latestNetworkParameters.toNetworkParameters()}" } + logger.debug { "Retrieved latest network parameters: ${latestNetworkParameters.networkParameters}" } + val parametersUpdate = networkMapStorage.getParametersUpdate() logger.debug { "Retrieved parameters update: $parametersUpdate" } - // We persist signed parameters only if they were not persisted before (they are not in currentSignedNetworkMap as normal parameters or as an update) + check(parametersUpdate == null || parametersUpdate.networkParameters.hash == latestNetworkParameters.hash) { + "The latest network parameters are not the scheduled updated ones" + } + + val activeNetworkMap = networkMapStorage.getActiveNetworkMap() + if (activeNetworkMap == null) { + logger.info("There is currently no network map") + } else { + logger.debug { "Current network map: ${activeNetworkMap.networkMap}" } + } + + val activeNetworkParameters = activeNetworkMap?.networkParameters + logger.debug { "Current network map parameters: ${activeNetworkParameters?.networkParameters}" } + + val nodeInfoHashes = networkMapStorage.getActiveNodeInfoHashes() + logger.debug { "Retrieved node info hashes:\n${nodeInfoHashes.joinToString("\n")}" } + + // We persist signed parameters only if they were not persisted before (they are not in currentSignedNetworkMap as + // normal parameters or as an update) if (!latestNetworkParameters.isSigned) { - signAndPersistNetworkParameters(latestNetworkParameters.toNetworkParameters()) + signAndPersistNetworkParameters(latestNetworkParameters.networkParameters) } else { logger.debug { "No need to sign any network parameters as they're up-to-date" } } - val parametersToNetworkMap = if (parametersUpdate?.flagDay == true || currentNetworkParameters == null) { + + val parametersToNetworkMap = if (parametersUpdate?.flagDay == true || activeNetworkParameters == null) { networkMapStorage.clearParametersUpdates() latestNetworkParameters - } else currentNetworkParameters - val newNetworkMap = NetworkMap(nodeInfoHashes, SecureHash.parse(parametersToNetworkMap.parametersHash), parametersUpdate?.toNetMapUpdate()) + } else { + activeNetworkParameters + } + + val newNetworkMap = NetworkMap( + nodeInfoHashes, + SecureHash.parse(parametersToNetworkMap.hash), + parametersUpdate?.let { if (!it.flagDay) it.toParametersUpdate() else null }) logger.debug { "Potential new network map: $newNetworkMap" } - if (currentNetworkMap?.toNetworkMap() != newNetworkMap) { - val netNetworkMapAndSigned = NetworkMapAndSigned(newNetworkMap) { signer.signBytes(it.bytes) } - networkMapStorage.saveNewActiveNetworkMap(netNetworkMapAndSigned) + + if (activeNetworkMap?.networkMap != newNetworkMap) { + val newNetworkMapAndSigned = NetworkMapAndSigned(newNetworkMap) { signer.signBytes(it.bytes) } + networkMapStorage.saveNewActiveNetworkMap(newNetworkMapAndSigned) logger.info("Signed new network map: $newNetworkMap") } else { logger.debug("Current network map is up-to-date") diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/NetworkManagementServer.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/NetworkManagementServer.kt index 4c063f0d09..1d8a96db7c 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/NetworkManagementServer.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/NetworkManagementServer.kt @@ -22,7 +22,6 @@ import com.r3.corda.networkmanage.doorman.webservice.NetworkMapWebService import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService import net.corda.core.crypto.SecureHash import net.corda.core.node.NetworkParameters -import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger import net.corda.nodeapi.internal.persistence.CordaPersistence @@ -60,7 +59,7 @@ class NetworkManagementServer(dataSourceProperties: Properties, databaseConfig: private fun getNetworkMapService(config: NetworkMapConfig, signer: LocalSigner?): NetworkMapWebService { val localNetworkMapSigner = signer?.let { NetworkMapSigner(networkMapStorage, it) } - val latestParameters = networkMapStorage.getLatestNetworkParameters()?.toNetworkParameters() ?: + val latestParameters = networkMapStorage.getLatestNetworkParameters()?.networkParameters ?: throw IllegalStateException("No network parameters were found. Please upload new network parameters before starting network map service") logger.info("Starting network map service with network parameters: $latestParameters") localNetworkMapSigner?.signAndPersistNetworkParameters(latestParameters) @@ -153,7 +152,7 @@ class NetworkManagementServer(dataSourceProperties: Properties, databaseConfig: private fun handleSetNetworkParameters(setNetParams: NetworkParametersCmd.Set) { logger.info("maxMessageSize is not currently wired in the nodes") - val activeNetParams = networkMapStorage.getActiveNetworkMap()?.networkParameters?.toNetworkParameters() + val activeNetParams = networkMapStorage.getActiveNetworkMap()?.networkParameters?.networkParameters if (activeNetParams == null) { require(setNetParams.parametersUpdate == null) { "'parametersUpdate' specified in network parameters file but there are no network parameters to update" @@ -163,19 +162,19 @@ class NetworkManagementServer(dataSourceProperties: Properties, databaseConfig: networkMapStorage.saveNetworkParameters(initialNetParams, null) } else { val parametersUpdate = requireNotNull(setNetParams.parametersUpdate) { - "'parametersUpdate' not specified in network parameters file but there is already an active set of network parameters." + "'parametersUpdate' not specified in network parameters file but there is already an active set of network parameters" } setNetParams.checkCompatibility(activeNetParams) - val latestNetParams = checkNotNull(networkMapStorage.getLatestNetworkParameters()?.toNetworkParameters()) { + val latestNetParams = checkNotNull(networkMapStorage.getLatestNetworkParameters()?.networkParameters) { "Something has gone wrong! We have an active set of network parameters ($activeNetParams) but apparently no latest network parameters!" } // It's not necessary that latestNetParams is the current active network parameters. It can be the network // parameters from a previous update attempt which has't activated yet. We still take the epoch value for this // new set from latestNetParams to make sure the advertised update attempts have incrementing epochs. - // This has the implication that active network parameters may have gaps in their epochs. + // This has the implication that *active* network parameters may have gaps in their epochs. val newNetParams = setNetParams.toNetworkParameters(modifiedTime = Instant.now(), epoch = latestNetParams.epoch + 1) logger.info("Enabling update to network parameters:\n$newNetParams\n$parametersUpdate") @@ -205,12 +204,12 @@ class NetworkManagementServer(dataSourceProperties: Properties, databaseConfig: "Parameters we are trying to switch to haven't been signed yet" } // TODO This check is stil not good enough as when it comes to signing, the NetworkMapSigner will just accept - check(latestNetParamsEntity.parametersHash == parametersUpdate.networkParameters.parametersHash) { - "The latest network parameters is not the scheduled one:\n${latestNetParamsEntity.toNetworkParameters()}\n${parametersUpdate.toParametersUpdate()}" + check(latestNetParamsEntity.hash == parametersUpdate.networkParameters.hash) { + "The latest network parameters is not the scheduled one:\n${latestNetParamsEntity.networkParameters}\n${parametersUpdate.toParametersUpdate()}" } logger.info("Flag day has occurred, however the new network parameters won't be active until the new network map is signed.\n" + - "Switching from: $activeNetParams\nTo: ${latestNetParamsEntity.toNetworkParameters()}") - networkMapStorage.setFlagDay(SecureHash.parse(parametersUpdate.networkParameters.parametersHash)) + "Switching from: $activeNetParams\nTo: ${latestNetParamsEntity.networkParameters}") + networkMapStorage.setFlagDay(SecureHash.parse(parametersUpdate.networkParameters.hash)) } private fun handleCancelUpdate() { diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/webservice/NetworkMapWebService.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/webservice/NetworkMapWebService.kt index 489d8aab4f..41ab0f43a2 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/webservice/NetworkMapWebService.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/doorman/webservice/NetworkMapWebService.kt @@ -57,9 +57,9 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage, .build { networkMapStorage.getActiveNetworkMap()?.let { logger.info("Re-publishing network map") - val networkMap = it.toNetworkMap() + val networkMap = it.networkMap val signedNetworkMap = it.toSignedNetworkMap() - CachedData(signedNetworkMap, networkMap.nodeInfoHashes.toSet(), it.networkParameters.toNetworkParameters()) + CachedData(signedNetworkMap, networkMap.nodeInfoHashes.toSet(), it.networkParameters.networkParameters) } } @@ -109,11 +109,8 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage, logger.debug { "Received ack-parameters with $hash from ${signedParametersHash.sig.by}" } nodeInfoStorage.ackNodeInfoParametersUpdate(signedParametersHash.sig.by.encoded.sha256(), hash) ok() - } catch (e: Exception) { - when (e) { - is SignatureException -> status(Response.Status.FORBIDDEN).entity(e.message) - else -> status(Response.Status.INTERNAL_SERVER_ERROR) - } + } catch (e: SignatureException) { + status(Response.Status.FORBIDDEN).entity(e.message) }.build() } diff --git a/network-management/src/main/resources/migration/network-manager.changelog-init.xml b/network-management/src/main/resources/migration/network-manager.changelog-init.xml index 91ab80bef6..d00daac373 100644 --- a/network-management/src/main/resources/migration/network-manager.changelog-init.xml +++ b/network-management/src/main/resources/migration/network-manager.changelog-init.xml @@ -15,30 +15,39 @@ + + + - - + + - + - - - + + + + + + + + + + - - + @@ -50,16 +59,20 @@ - + - - - + + + + + + + - + @@ -67,10 +80,9 @@ - - - + + @@ -78,7 +90,7 @@ - + @@ -97,8 +109,10 @@ - - + + + + @@ -114,7 +128,7 @@ - + @@ -123,62 +137,66 @@ - + - + - + - + - + - + - + - + - + + + + - + - + - - + - + @@ -186,42 +204,42 @@ - + - + - + - + - + - + - + - + - + @@ -229,85 +247,73 @@ - + - + - + - + - + + + + - - + - - - - - - - - - - - - - - + - + - - - - + - + referencedTableName="cert_data"/> - + - - + + + + + - - @@ -315,21 +321,32 @@ constraintName="FK_NM_NP" referencedColumnNames="hash" referencedTableName="network_parameters"/> - + - - + + + + + + - + + + + + + diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/TestUtils.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/TestUtils.kt index f9d19a5cef..ac2e9c78f7 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/TestUtils.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/TestUtils.kt @@ -15,18 +15,17 @@ fun createNetworkParametersEntity(signingCertAndKeyPair: CertificateAndKeyPair = networkParameters: NetworkParameters = testNetworkParameters()): NetworkParametersEntity { val signedNetParams = signingCertAndKeyPair.sign(networkParameters) return NetworkParametersEntity( - parametersHash = signedNetParams.raw.hash.toString(), - parametersBytes = signedNetParams.raw.bytes, + hash = signedNetParams.raw.hash.toString(), + networkParameters = networkParameters, signature = signedNetParams.sig.bytes, - certificate = signedNetParams.sig.by.encoded + certificate = signedNetParams.sig.by ) } fun createNetworkParametersEntityUnsigned(networkParameters: NetworkParameters = testNetworkParameters()): NetworkParametersEntity { - val serialised = networkParameters.serialize() return NetworkParametersEntity( - parametersHash = serialised.hash.toString(), - parametersBytes = serialised.bytes, + hash = networkParameters.serialize().hash.toString(), + networkParameters = networkParameters, signature = null, certificate = null ) @@ -36,11 +35,12 @@ fun createNetworkMapEntity(signingCertAndKeyPair: CertificateAndKeyPair = create netParamsEntity: NetworkParametersEntity, nodeInfoHashes: List = emptyList(), parametersUpdate: ParametersUpdate? = null): NetworkMapEntity { - val signedNetMap = signingCertAndKeyPair.sign(NetworkMap(nodeInfoHashes, SecureHash.parse(netParamsEntity.parametersHash), parametersUpdate)) + val networkMap = NetworkMap(nodeInfoHashes, SecureHash.parse(netParamsEntity.hash), parametersUpdate) + val signedNetworkMap = signingCertAndKeyPair.sign(networkMap) return NetworkMapEntity( - networkMapBytes = signedNetMap.raw.bytes, - signature = signedNetMap.sig.bytes, - certificate = signedNetMap.sig.by.encoded, + networkMap = networkMap, + signature = signedNetworkMap.sig.bytes, + certificate = signedNetworkMap.sig.by, networkParameters = netParamsEntity ) } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt index 9add80ec2b..45be8c2160 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt @@ -87,7 +87,7 @@ class PersistentNetworkMapStorageTest : TestBase() { assertThat(activeSignedNetworkMap.sig).isEqualTo(networkMapAndSigned.signed.sig) assertThat(activeNetworkParameters).isEqualTo(networkParameters) assertThat(activeSignedNetworkParameters.sig).isEqualTo(networkParametersSig) - assertThat(SecureHash.parse(activeNetworkParametersEntity.parametersHash)) + assertThat(SecureHash.parse(activeNetworkParametersEntity.hash)) .isEqualTo(activeNetworkMap.networkParameterHash) .isEqualTo(networkParametersHash) } @@ -100,7 +100,7 @@ class PersistentNetworkMapStorageTest : TestBase() { // We may have not signed them yet. networkMapStorage.saveNetworkParameters(params2, null) - assertThat(networkMapStorage.getLatestNetworkParameters()?.toNetworkParameters()).isEqualTo(params2) + assertThat(networkMapStorage.getLatestNetworkParameters()?.networkParameters).isEqualTo(params2) } @Test diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorageTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorageTest.kt index e96e7384f3..cbd8176d8b 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorageTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorageTest.kt @@ -14,15 +14,18 @@ import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.common.utils.hashString import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.sha256 import net.corda.core.identity.CordaX500Name import net.corda.core.internal.CertRole import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.NodeInfoAndSigned +import net.corda.nodeapi.internal.createDevNetworkMapCa import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig +import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.internal.TestNodeInfoBuilder import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.signWith @@ -45,13 +48,15 @@ class PersistentNodeInfoStorageTest : TestBase() { private lateinit var networkMapStorage: PersistentNetworkMapStorage private lateinit var persistence: CordaPersistence private lateinit var rootCaCert: X509Certificate - private lateinit var intermediateCa: CertificateAndKeyPair + private lateinit var doormanCertAndKeyPair: CertificateAndKeyPair + private lateinit var networkMapCertAndKeyPair: CertificateAndKeyPair @Before fun startDb() { val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() rootCaCert = rootCa.certificate - this.intermediateCa = intermediateCa + this.doormanCertAndKeyPair = intermediateCa + networkMapCertAndKeyPair = createDevNetworkMapCa(rootCa) persistence = configureDatabase(MockServices.makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)) nodeInfoStorage = PersistentNodeInfoStorage(persistence) requestStorage = PersistentCertificateSigningRequestStorage(persistence) @@ -70,8 +75,8 @@ class PersistentNodeInfoStorageTest : TestBase() { val name = CordaX500Name(organisation = "Test", locality = "London", country = "GB") val nodeCaCert = X509Utilities.createCertificate( CertificateType.NODE_CA, - intermediateCa.certificate, - intermediateCa.keyPair, + doormanCertAndKeyPair.certificate, + doormanCertAndKeyPair.keyPair, name.x500Principal, keyPair.public) @@ -85,7 +90,7 @@ class PersistentNodeInfoStorageTest : TestBase() { requestStorage.putCertificatePath( requestId, - X509Utilities.buildCertPath(nodeCaCert, intermediateCa.certificate, rootCaCert), + X509Utilities.buildCertPath(nodeCaCert, doormanCertAndKeyPair.certificate, rootCaCert), CertificateSigningRequestStorage.DOORMAN_SIGNATURE) val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString())) @@ -150,16 +155,37 @@ class PersistentNodeInfoStorageTest : TestBase() { @Test fun `accept parameters updates node info correctly`() { // given - val (nodeInfoWithSigned) = createValidSignedNodeInfo("Test", requestStorage) + val (nodeInfoAndSigned) = createValidSignedNodeInfo("Test", requestStorage) // when - val paramsHash = SecureHash.randomSHA256() - val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfoWithSigned) - nodeInfoStorage.ackNodeInfoParametersUpdate(SecureHash.parse(nodeInfoWithSigned.nodeInfo.legalIdentities.first().owningKey.hashString()), paramsHash) + val networkParameters = testNetworkParameters() + val sigWithCert = networkMapCertAndKeyPair.sign(networkParameters).sig + val netParamsHash = networkMapStorage.saveNetworkParameters(networkParameters, sigWithCert) + val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfoAndSigned) + nodeInfoStorage.ackNodeInfoParametersUpdate(nodeInfoAndSigned.nodeInfo.legalIdentities[0].owningKey.encoded.sha256(), netParamsHash) // then - val persistedParametersHash = nodeInfoStorage.getAcceptedParametersUpdateHash(nodeInfoHash) - assertThat(persistedParametersHash).isEqualTo(paramsHash) + val acceptedNetworkParameters = nodeInfoStorage.getAcceptedNetworkParameters(nodeInfoHash) + assertThat(acceptedNetworkParameters?.hash).isEqualTo(netParamsHash.toString()) + } + + @Test + fun `updating node info after it's accepted network parameters`() { + val networkParameters = testNetworkParameters() + val sigWithCert = networkMapCertAndKeyPair.sign(networkParameters).sig + val netParamsHash = networkMapStorage.saveNetworkParameters(networkParameters, sigWithCert) + + val (nodeInfoAndSigned, privateKey) = createValidSignedNodeInfo("Test", requestStorage) + nodeInfoStorage.putNodeInfo(nodeInfoAndSigned) + + nodeInfoStorage.ackNodeInfoParametersUpdate(nodeInfoAndSigned.nodeInfo.legalIdentities[0].owningKey.encoded.sha256(), netParamsHash) + + val nodeInfo2 = nodeInfoAndSigned.nodeInfo.copy(serial = 2) + val nodeInfoAndSigned2 = NodeInfoAndSigned(nodeInfo2.signWith(listOf(privateKey))) + val nodeInfoHash2 = nodeInfoStorage.putNodeInfo(nodeInfoAndSigned2) + + val acceptedNetworkParameters = nodeInfoStorage.getAcceptedNetworkParameters(nodeInfoHash2) + assertThat(acceptedNetworkParameters?.hash).isEqualTo(netParamsHash.toString()) } } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSignerTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSignerTest.kt index 849aaf6cff..63319faa03 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSignerTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/signer/NetworkMapSignerTest.kt @@ -149,7 +149,6 @@ class NetworkMapSignerTest : TestBase() { // given val currentNetworkParameters = createNetworkParametersEntity(signingCertAndKeyPair) val updateNetworkParameters = createNetworkParametersEntityUnsigned(testNetworkParameters(epoch = 2)) - val updateParametersHash = SecureHash.parse(updateNetworkParameters.parametersHash) val parametersUpdate = ParametersUpdateEntity(0, updateNetworkParameters,"Update time", Instant.ofEpochMilli(random63BitValue())) val netMapEntity = createNetworkMapEntity(signingCertAndKeyPair, currentNetworkParameters, emptyList(), null) whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(netMapEntity) @@ -170,8 +169,8 @@ class NetworkMapSignerTest : TestBase() { val paramsCaptor = argumentCaptor() val signatureCaptor = argumentCaptor() verify(networkMapStorage, times(1)).saveNetworkParameters(paramsCaptor.capture(), signatureCaptor.capture()) - assertEquals(paramsCaptor.firstValue, updateNetworkParameters.toNetworkParameters()) - assertThat(signatureCaptor.firstValue.verify(updateNetworkParameters.parametersBytes)) + assertEquals(paramsCaptor.firstValue, updateNetworkParameters.networkParameters) + signatureCaptor.firstValue.verify(updateNetworkParameters.networkParameters.serialize()) } @Test @@ -206,7 +205,7 @@ class NetworkMapSignerTest : TestBase() { argumentCaptor().apply { verify(networkMapStorage).saveNewActiveNetworkMap(capture()) val netMap = firstValue.networkMap - assertEquals(SecureHash.parse(updateNetworkParameters.parametersHash), netMap.networkParameterHash) + assertEquals(SecureHash.parse(updateNetworkParameters.hash), netMap.networkParameterHash) assertEquals(emptyList(), netMap.nodeInfoHashes) assertEquals(null, netMap.parametersUpdate) } @@ -231,7 +230,7 @@ class NetworkMapSignerTest : TestBase() { argumentCaptor().apply { verify(networkMapStorage).saveNewActiveNetworkMap(capture()) val netMap = firstValue.networkMap - assertEquals(SecureHash.parse(activeNetworkParameters.parametersHash), netMap.networkParameterHash) + assertEquals(SecureHash.parse(activeNetworkParameters.hash), netMap.networkParameterHash) assertEquals(emptyList(), netMap.nodeInfoHashes) assertEquals(null, netMap.parametersUpdate) } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/webservice/NetworkMapWebServiceTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/webservice/NetworkMapWebServiceTest.kt index c9476ec879..06ee6f373f 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/webservice/NetworkMapWebServiceTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/doorman/webservice/NetworkMapWebServiceTest.kt @@ -119,7 +119,7 @@ class NetworkMapWebServiceTest { it.start() val signedNetworkMapResponse = it.doGet("") verify(networkMapStorage, times(1)).getActiveNetworkMap() - assertEquals(signedNetworkMapResponse.verifiedNetworkMapCert(rootCaCert), networkMapEntity.toNetworkMap()) + assertEquals(signedNetworkMapResponse.verifiedNetworkMapCert(rootCaCert), networkMapEntity.networkMap) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkMap.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkMap.kt index a32f034d5d..fe2cc1ad34 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkMap.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkMap.kt @@ -41,7 +41,14 @@ data class NetworkMap( val nodeInfoHashes: List, val networkParameterHash: SecureHash, val parametersUpdate: ParametersUpdate? -) +) { + override fun toString(): String { + return """${NetworkMap::class.java.simpleName}(nodeInfoHashes= +${nodeInfoHashes.joinToString("\n")} +networkParameterHash=$networkParameterHash +parametersUpdate=$parametersUpdate)""" + } +} /** * Data class representing scheduled network parameters update. diff --git a/node/src/main/kotlin/net/corda/node/internal/NetworkParametersReader.kt b/node/src/main/kotlin/net/corda/node/internal/NetworkParametersReader.kt index 54f98c6777..a578781cff 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NetworkParametersReader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NetworkParametersReader.kt @@ -48,8 +48,7 @@ class NetworkParametersReader(private val trustRoot: X509Certificate, // you get them from network map, but you have to run the approval step. if (signedParametersFromFile == null) { // Node joins for the first time. downloadParameters(trustRoot, advertisedParametersHash) - } - else if (signedParametersFromFile.raw.hash == advertisedParametersHash) { // Restarted with the same parameters. + } else if (signedParametersFromFile.raw.hash == advertisedParametersHash) { // Restarted with the same parameters. signedParametersFromFile.verifiedNetworkMapCert(trustRoot) } else { // Update case. readParametersUpdate(advertisedParametersHash, signedParametersFromFile.raw.hash).verifiedNetworkMapCert(trustRoot) @@ -74,6 +73,7 @@ class NetworkParametersReader(private val trustRoot: X509Certificate, "Please update node to use correct network parameters file.") } parametersUpdateFile.moveTo(networkParamsFile, StandardCopyOption.REPLACE_EXISTING) + logger.info("Scheduled update to network parameters has occurred - node now updated to these new parameters.") return signedUpdatedParameters } diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapClient.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapClient.kt index 9f228d671f..0561be6c75 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapClient.kt @@ -12,7 +12,9 @@ package net.corda.node.services.network import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SignedData -import net.corda.core.internal.* +import net.corda.core.internal.openHttpConnection +import net.corda.core.internal.post +import net.corda.core.internal.responseAs import net.corda.core.node.NodeInfo import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize @@ -21,7 +23,10 @@ import net.corda.core.utilities.seconds import net.corda.core.utilities.trace import net.corda.node.utilities.registration.cacheControl import net.corda.nodeapi.internal.SignedNodeInfo -import net.corda.nodeapi.internal.network.* +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 import java.io.BufferedReader import java.net.URL import java.security.cert.X509Certificate @@ -54,10 +59,7 @@ class NetworkMapClient(compatibilityZoneURL: URL, val trustedRoot: X509Certifica val signedNetworkMap = connection.responseAs() val networkMap = signedNetworkMap.verifiedNetworkMapCert(trustedRoot) val timeout = connection.cacheControl().maxAgeSeconds().seconds - logger.trace { - "Fetched network map update from $networkMapUrl successfully, retrieved ${networkMap.nodeInfoHashes.size} " + - "node info hashes. Node Info hashes:\n${networkMap.nodeInfoHashes.joinToString("\n")}" - } + logger.trace { "Fetched network map update from $networkMapUrl successfully: $networkMap" } return NetworkMapResponse(networkMap, timeout) } diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt index 28509323cd..b44bfe5270 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt @@ -157,12 +157,13 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, // This update was handled already. return } - val newParameters = networkMapClient.getNetworkParameters(update.newParametersHash) - logger.info("Downloaded new network parameters: $newParameters from the update: $update") - newNetworkParameters = Pair(update, newParameters) + val newSignedNetParams = networkMapClient.getNetworkParameters(update.newParametersHash) + val newNetParams = newSignedNetParams.verifiedNetworkMapCert(networkMapClient.trustedRoot) + logger.info("Downloaded new network parameters: $newNetParams from the update: $update") + newNetworkParameters = Pair(update, newSignedNetParams) val updateInfo = ParametersUpdateInfo( update.newParametersHash, - newParameters.verifiedNetworkMapCert(networkMapClient.trustedRoot), + newNetParams, update.description, update.updateDeadline) parametersUpdatesTrack.onNext(updateInfo) @@ -172,15 +173,17 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, networkMapClient ?: throw IllegalStateException("Network parameters updates are not support without compatibility zone configured") // TODO This scenario will happen if node was restarted and didn't download parameters yet, but we accepted them. // Add persisting of newest parameters from update. - val (_, newParams) = requireNotNull(newNetworkParameters) { "Couldn't find parameters update for the hash: $parametersHash" } + val (update, signedNewNetParams) = requireNotNull(newNetworkParameters) { "Couldn't find parameters update for the hash: $parametersHash" } // We should check that we sign the right data structure hash. - val newParametersHash = newParams.verifiedNetworkMapCert(networkMapClient.trustedRoot).serialize().hash + val newNetParams = signedNewNetParams.verifiedNetworkMapCert(networkMapClient.trustedRoot) + val newParametersHash = newNetParams.serialize().hash if (parametersHash == newParametersHash) { // The latest parameters have priority. - newParams.serialize() + signedNewNetParams.serialize() .open() .copyTo(baseDirectory / NETWORK_PARAMS_UPDATE_FILE_NAME, StandardCopyOption.REPLACE_EXISTING) networkMapClient.ackNetworkParametersUpdate(sign(parametersHash)) + logger.info("Accepted network parameter update $update: $newNetParams") } else { throw IllegalArgumentException("Refused to accept parameters with hash $parametersHash because network map " + "advertises update with hash $newParametersHash. Please check newest version")