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
This commit is contained in:
Patrick Kuo 2018-03-22 06:39:02 +00:00 committed by Shams Asari
parent 34d5f72f51
commit ff4bbaf63f
29 changed files with 458 additions and 419 deletions

View File

@ -107,8 +107,7 @@ class NetworkParametersUpdateTest {
description = "Very Important Update", description = "Very Important Update",
updateDeadline = updateDeadline updateDeadline = updateDeadline
) )
) ))
)
updates.expectEvents(isStrict = true) { updates.expectEvents(isStrict = true) {
sequence( sequence(

View File

@ -40,6 +40,7 @@ interface NetworkMapStorage {
/** /**
* Retrieves node info hashes where [NodeInfoEntity.isCurrent] is true and the certificate status is [CertificateStatus.VALID] * 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<SecureHash> fun getActiveNodeInfoHashes(): List<SecureHash>
/** /**

View File

@ -10,6 +10,7 @@
package com.r3.corda.networkmanage.common.persistence 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.crypto.SecureHash
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.NodeInfoAndSigned
@ -33,11 +34,10 @@ interface NodeInfoStorage {
fun getNodeInfo(nodeInfoHash: SecureHash): SignedNodeInfo? fun getNodeInfo(nodeInfoHash: SecureHash): SignedNodeInfo?
/** /**
* Retrieve latest accepted parameters hash for nodeInfo with given hash. * Returns the network parameters that the node has accepted or null if couldn't find node info with given hash or
* @return Hash of 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 * 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. * The [nodeInfoAndSigned] is keyed by the public key, old node info with the same public key will be replaced by the new node info.

View File

@ -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.CertificateDataEntity
import com.r3.corda.networkmanage.common.persistence.entity.CertificateRevocationListEntity import com.r3.corda.networkmanage.common.persistence.entity.CertificateRevocationListEntity
import com.r3.corda.networkmanage.common.persistence.entity.CertificateRevocationRequestEntity 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.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseTransaction import net.corda.nodeapi.internal.persistence.DatabaseTransaction
import java.math.BigInteger import java.math.BigInteger
@ -21,10 +20,7 @@ class PersistentCertificateRevocationListStorage(private val database: CordaPers
} }
} }
// We just want the last signed entry // We just want the last signed entry
val crlEntity = session.createQuery(query).setMaxResults(1).uniqueResult() session.createQuery(query).setMaxResults(1).uniqueResult()?.crl
crlEntity?.let {
X509CertificateFactory().delegate.generateCRL(crlEntity.crlBytes.inputStream()) as X509CRL
}
} }
} }
@ -34,7 +30,7 @@ class PersistentCertificateRevocationListStorage(private val database: CordaPers
revokeCertificate(it.serialNumber, revokedAt, this) revokeCertificate(it.serialNumber, revokedAt, this)
} }
session.save(CertificateRevocationListEntity( session.save(CertificateRevocationListEntity(
crlBytes = crl.encoded, crl = crl,
crlIssuer = crlIssuer, crlIssuer = crlIssuer,
signedBy = signedBy, signedBy = signedBy,
modifiedAt = Instant.now() modifiedAt = Instant.now()

View File

@ -51,7 +51,7 @@ class PersistentCertificateSigningRequestStorage(private val database: CordaPers
session.merge(certificateSigningRequest) session.merge(certificateSigningRequest)
val certificateDataEntity = CertificateDataEntity( val certificateDataEntity = CertificateDataEntity(
certificateStatus = CertificateStatus.VALID, certificateStatus = CertificateStatus.VALID,
certificatePathBytes = certPath.encoded, certPath = certPath,
certificateSigningRequest = certificateSigningRequest, certificateSigningRequest = certificateSigningRequest,
certificateSerialNumber = certPath.x509Certificates.first().serialNumber) certificateSerialNumber = certPath.x509Certificates.first().serialNumber)
session.persist(certificateDataEntity) session.persist(certificateDataEntity)
@ -71,7 +71,7 @@ class PersistentCertificateSigningRequestStorage(private val database: CordaPers
requestId = requestId, requestId = requestId,
legalName = legalNameOrRejectMessage as? CordaX500Name, legalName = legalNameOrRejectMessage as? CordaX500Name,
publicKeyHash = toSupportedPublicKey(request.subjectPublicKeyInfo).hashString(), publicKeyHash = toSupportedPublicKey(request.subjectPublicKeyInfo).hashString(),
requestBytes = request.encoded, request = request,
remark = legalNameOrRejectMessage as? String, remark = legalNameOrRejectMessage as? String,
modifiedBy = CertificateSigningRequestStorage.DOORMAN_SIGNATURE, modifiedBy = CertificateSigningRequestStorage.DOORMAN_SIGNATURE,
status = if (legalNameOrRejectMessage is CordaX500Name) RequestStatus.NEW else RequestStatus.REJECTED status = if (legalNameOrRejectMessage is CordaX500Name) RequestStatus.NEW else RequestStatus.REJECTED

View File

@ -15,12 +15,11 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.internal.DigitalSignatureWithCert import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.serialize 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.NetworkMapAndSigned
import net.corda.nodeapi.internal.network.SignedNetworkParameters import net.corda.nodeapi.internal.network.SignedNetworkParameters
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import java.time.Instant
import net.corda.nodeapi.internal.persistence.DatabaseTransaction import net.corda.nodeapi.internal.persistence.DatabaseTransaction
import java.time.Instant
/** /**
* Database implementation of the [NetworkMapStorage] interface * Database implementation of the [NetworkMapStorage] interface
@ -49,9 +48,9 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
"Network parameters ${networkMap.networkParameterHash} are not signed" "Network parameters ${networkMap.networkParameterHash} are not signed"
} }
session.save(NetworkMapEntity( session.save(NetworkMapEntity(
networkMapBytes = signedNetworkMap.raw.bytes, networkMap = networkMap,
signature = signedNetworkMap.sig.bytes, signature = signedNetworkMap.sig.bytes,
certificate = signedNetworkMap.sig.by.encoded, certificate = signedNetworkMap.sig.by,
networkParameters = networkParametersEntity networkParameters = networkParametersEntity
)) ))
} }
@ -85,21 +84,22 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
} }
override fun saveNetworkParameters(networkParameters: NetworkParameters, signature: DigitalSignatureWithCert?): SecureHash { override fun saveNetworkParameters(networkParameters: NetworkParameters, signature: DigitalSignatureWithCert?): SecureHash {
val serialised = networkParameters.serialize() val serialized = networkParameters.serialize()
val hash = serialised.hash signature?.verify(serialized)
val hash = serialized.hash
database.transaction { database.transaction {
val entity = getNetworkParametersEntity(hash) val entity = getNetworkParametersEntity(hash)
val newNetworkParamsEntity = if (entity != null) { val newNetworkParamsEntity = if (entity != null) {
entity.copy( entity.copy(
signature = signature?.bytes, signature = signature?.bytes,
certificate = signature?.by?.encoded certificate = signature?.by
) )
} else { } else {
NetworkParametersEntity( NetworkParametersEntity(
parametersBytes = serialised.bytes, networkParameters = networkParameters,
parametersHash = hash.toString(), hash = hash.toString(),
signature = signature?.bytes, signature = signature?.bytes,
certificate = signature?.by?.encoded certificate = signature?.by
) )
} }
session.merge(newNetworkParamsEntity) session.merge(newNetworkParamsEntity)
@ -139,7 +139,7 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
override fun getParametersUpdate(): ParametersUpdateEntity? { override fun getParametersUpdate(): ParametersUpdateEntity? {
return database.transaction { return database.transaction {
val currentParametersHash = getActiveNetworkMap()?.toNetworkMap()?.networkParameterHash val currentParametersHash = getActiveNetworkMap()?.networkParameters?.hash
val latestParameters = getLatestNetworkParameters() val latestParameters = getLatestNetworkParameters()
val criteria = session.criteriaBuilder.createQuery(ParametersUpdateEntity::class.java) val criteria = session.criteriaBuilder.createQuery(ParametersUpdateEntity::class.java)
val root = criteria.from(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" "ParametersUpdate doesn't correspond to latest network parameters"
} }
// Highly unlikely, but... // 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" "Having update for parameters that are already in network map"
} }
parametersUpdate parametersUpdate
@ -160,16 +160,16 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
override fun setFlagDay(parametersHash: SecureHash) { override fun setFlagDay(parametersHash: SecureHash) {
database.transaction { database.transaction {
val parametersUpdateEntity = getParametersUpdate() ?: throw IllegalArgumentException("Setting flag day but no parameters update to switch to") val parametersUpdateEntity = getParametersUpdate() ?: throw IllegalArgumentException("Setting flag day but no parameters update to switch to")
if (parametersHash.toString() != 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.parametersHash}") throw IllegalArgumentException("Setting flag day for parameters: $parametersHash, but in database we have update for: ${parametersUpdateEntity.networkParameters.hash}")
} }
session.merge(parametersUpdateEntity.copy(flagDay = true)) session.merge(parametersUpdateEntity.copy(flagDay = true))
} }
} }
}
private fun DatabaseTransaction.getNetworkParametersEntity(hash: SecureHash): NetworkParametersEntity? { internal fun DatabaseTransaction.getNetworkParametersEntity(hash: SecureHash): NetworkParametersEntity? {
return uniqueEntityWhere { builder, path -> return uniqueEntityWhere { builder, path ->
builder.equal(path.get<String>(NetworkParametersEntity::parametersHash.name), hash.toString()) builder.equal(path.get<String>(NetworkParametersEntity::hash.name), hash.toString())
}
} }
} }

View File

@ -11,14 +11,13 @@
package com.r3.corda.networkmanage.common.persistence package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.networkmanage.common.persistence.entity.CertificateSigningRequestEntity 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.persistence.entity.NodeInfoEntity
import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.networkmanage.common.utils.hashString import com.r3.corda.networkmanage.common.utils.hashString
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.internal.CertRole import net.corda.core.internal.CertRole
import net.corda.core.internal.CertRole.NODE_CA 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.NodeInfoAndSigned
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.crypto.x509Certificates import net.corda.nodeapi.internal.crypto.x509Certificates
@ -34,7 +33,7 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
val (nodeInfo, signedNodeInfo) = nodeInfoAndSigned val (nodeInfo, signedNodeInfo) = nodeInfoAndSigned
val nodeCaCert = nodeInfo.legalIdentitiesAndCerts[0].certPath.x509Certificates.find { CertRole.extract(it) == NODE_CA } val nodeCaCert = nodeInfo.legalIdentitiesAndCerts[0].certPath.x509Certificates.find { CertRole.extract(it) == NODE_CA }
nodeCaCert ?: throw IllegalArgumentException("Missing Node CA") nodeCaCert ?: throw IllegalArgumentException("Missing Node CA")
return database.transaction { database.transaction {
// TODO Move these checks out of data access layer // TODO Move these checks out of data access layer
val request = requireNotNull(getSignedRequestByPublicHash(nodeCaCert.publicKey.encoded.sha256())) { val request = requireNotNull(getSignedRequestByPublicHash(nodeCaCert.publicKey.encoded.sha256())) {
"Node-info not registered with us" "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" } require(it == CertificateStatus.VALID) { "Certificate is no longer valid: $it" }
} }
// Update any [NodeInfoEntity] instance for this CSR as not current. val existingNodeInfos = session.createQuery(
entitiesWhere<NodeInfoEntity> { builder, path -> "from ${NodeInfoEntity::class.java.name} n where n.certificateSigningRequest = :csr and n.isCurrent = true order by n.publishedAt desc",
val requestEq = builder.equal(path.get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name), request) NodeInfoEntity::class.java)
val isCurrent = builder.isTrue(path.get<Boolean>(NodeInfoEntity::isCurrent.name)) .setParameter("csr", request)
builder.and(requestEq, isCurrent) .resultList
}.resultStream.use { existingNodeInfos ->
existingNodeInfos.forEach { session.merge(it.copy(isCurrent = false)) }
}
val hash = signedNodeInfo.raw.hash // Update any [NodeInfoEntity] instance for this CSR as not current.
val hashedNodeInfo = NodeInfoEntity( existingNodeInfos.forEach { session.merge(it.copy(isCurrent = false)) }
nodeInfoHash = hash.toString(),
identityPkHash = nodeInfo.legalIdentities.first().owningKey.hashString(), session.save(NodeInfoEntity(
nodeInfoHash = signedNodeInfo.raw.hash.toString(),
publicKeyHash = nodeInfo.legalIdentities[0].owningKey.hashString(),
certificateSigningRequest = request, certificateSigningRequest = request,
signedNodeInfoBytes = signedNodeInfo.serialize().bytes, signedNodeInfo = signedNodeInfo,
isCurrent = true) isCurrent = true,
session.save(hashedNodeInfo) acceptedNetworkParameters = existingNodeInfos.firstOrNull()?.acceptedNetworkParameters
hash ))
} }
return signedNodeInfo.raw.hash
} }
override fun getNodeInfo(nodeInfoHash: SecureHash): SignedNodeInfo? { override fun getNodeInfo(nodeInfoHash: SecureHash): SignedNodeInfo? {
return database.transaction { 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 { return database.transaction {
val hashString = session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())?.acceptedParametersHash session.find(NodeInfoEntity::class.java, nodeInfoHash.toString())?.acceptedNetworkParameters
if (hashString == null || hashString.isEmpty()) null else SecureHash.parse(hashString)
} }
} }
override fun getCertificatePath(publicKeyHash: SecureHash): CertPath? { override fun getCertificatePath(publicKeyHash: SecureHash): CertPath? {
return database.transaction { return database.transaction {
val request = getSignedRequestByPublicHash(publicKeyHash) 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 builder = session.criteriaBuilder
val query = builder.createQuery(NodeInfoEntity::class.java).run { val query = builder.createQuery(NodeInfoEntity::class.java).run {
from(NodeInfoEntity::class.java).run { from(NodeInfoEntity::class.java).run {
where(builder.equal(get<NodeInfoEntity>(NodeInfoEntity::identityPkHash.name), publicKeyHash.toString())) where(builder.equal(get<NodeInfoEntity>(NodeInfoEntity::publicKeyHash.name), publicKeyHash.toString()))
} }
} }
val nodeInfo = session.createQuery(query).setMaxResults(1).uniqueResult() val nodeInfo = requireNotNull(session.createQuery(query).setMaxResults(1).uniqueResult()) {
val newInfo = nodeInfo.copy(acceptedParametersHash = acceptedParametersHash.toString()) "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) session.merge(newInfo)
} }
} }

View File

@ -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<PKCS10CertificationRequest, ByteArray> {
override fun convertToEntityAttribute(dbData: ByteArray?): PKCS10CertificationRequest? = dbData?.let(::PKCS10CertificationRequest)
override fun convertToDatabaseColumn(attribute: PKCS10CertificationRequest?): ByteArray? = attribute?.encoded
}
class CertPathConverter : AttributeConverter<CertPath, ByteArray> {
override fun convertToEntityAttribute(dbData: ByteArray?): CertPath? = dbData?.let(::buildCertPath)
override fun convertToDatabaseColumn(attribute: CertPath?): ByteArray? = attribute?.encoded
}
class X509CertificateConverter : AttributeConverter<X509Certificate, ByteArray> {
override fun convertToEntityAttribute(dbData: ByteArray?): X509Certificate? {
return dbData?.let { X509CertificateFactory().generateCertificate(it.inputStream()) }
}
override fun convertToDatabaseColumn(attribute: X509Certificate?): ByteArray? = attribute?.encoded
}
class X509CRLConverter : AttributeConverter<X509CRL, ByteArray> {
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>(NetworkParameters::class.java)
class NetworkMapConverter : CordaSerializationConverter<NetworkMap>(NetworkMap::class.java)
class SignedNodeInfoConverter : CordaSerializationConverter<SignedNodeInfo>(SignedNodeInfo::class.java)
sealed class CordaSerializationConverter<T : Any>(private val clazz: Class<T>) : AttributeConverter<T, ByteArray> {
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<CordaX500Name, String> {
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<SecureHash, String> {
// override fun convertToDatabaseColumn(attribute: SecureHash?): String? = attribute?.toString()
// override fun convertToEntityAttribute(dbData: String?): SecureHash? = dbData?.let { SecureHash.parse(it) }
//}

View File

@ -1,24 +1,27 @@
package com.r3.corda.networkmanage.common.persistence.entity package com.r3.corda.networkmanage.common.persistence.entity
import com.r3.corda.networkmanage.common.persistence.CrlIssuer import com.r3.corda.networkmanage.common.persistence.CrlIssuer
import java.security.cert.X509CRL
import java.time.Instant import java.time.Instant
import javax.persistence.* import javax.persistence.*
@Entity @Entity
@Table(name = "certificate_revocation_list") @Table(name = "cert_revocation_list")
class CertificateRevocationListEntity( class CertificateRevocationListEntity(
@Id @Id
@GeneratedValue(strategy = GenerationType.SEQUENCE) @GeneratedValue(strategy = GenerationType.SEQUENCE)
val id: Long? = null, val id: Long? = null,
@Column(name = "issuer") @Column(name = "issuer", length = 16, nullable = false, columnDefinition = "NVARCHAR(16)")
@Enumerated(EnumType.STRING)
val crlIssuer: CrlIssuer, val crlIssuer: CrlIssuer,
@Lob @Lob
@Column(name = "crl_bytes", nullable = false) @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, val signedBy: String,
@Column(name = "modified_at", nullable = false) @Column(name = "modified_at", nullable = false)

View File

@ -9,73 +9,52 @@ import java.time.Instant
import javax.persistence.* import javax.persistence.*
@Entity @Entity
@Table(name = "certificate_revocation_request") @Table(name = "cert_revocation_request")
class CertificateRevocationRequestEntity( data class CertificateRevocationRequestEntity(
@Id @Id
@GeneratedValue(strategy = GenerationType.SEQUENCE) @GeneratedValue(strategy = GenerationType.SEQUENCE)
val id: Long? = null, 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, val requestId: String,
@OneToOne(fetch = FetchType.EAGER) @OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "certificate_data") @JoinColumn(name = "cert_data", nullable = false)
val certificateData: CertificateDataEntity, val certificateData: CertificateDataEntity,
@Column(name = "certificate_serial_number", nullable = false) @Column(name = "cert_serial_number", nullable = false, columnDefinition = "NUMERIC(28)")
val certificateSerialNumber: BigInteger, val certificateSerialNumber: BigInteger,
@Column(name = "legal_name", length = 256, nullable = false) @Column(name = "legal_name", length = 256, nullable = false)
@Convert(converter = CordaX500NameAttributeConverter::class) @Convert(converter = CordaX500NameAttributeConverter::class)
val legalName: CordaX500Name, 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 @Audited
@Column(name = "status", nullable = false) @Column(name = "status", length = 16, nullable = false, columnDefinition = "NVARCHAR(16)")
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
val status: RequestStatus = RequestStatus.NEW, val status: RequestStatus = RequestStatus.NEW,
@Column(name = "reporter", nullable = false, length = 512) @Column(name = "reporter", nullable = false, length = 64)
val reporter: String, val reporter: String,
@Audited @Audited
@Column(name = "modified_by", nullable = false, length = 512) @Column(name = "modified_by", nullable = false, length = 64)
val modifiedBy: String, val modifiedBy: String,
@Audited @Audited
@Column(name = "modified_at", nullable = false) @Column(name = "modified_at", nullable = false)
val modifiedAt: Instant = Instant.now(), 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 @Audited
@Column(name = "revocation_reason", nullable = false) @Column(name = "revocation_reason", length = 32, nullable = false, columnDefinition = "NVARCHAR(32)")
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
val revocationReason: CRLReason, val revocationReason: CRLReason,
@Audited @Audited
@Column(name = "remark", length = 256) @Column(name = "remark", length = 256)
val remark: String? = null 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
)
}
}

View File

@ -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.CertificateSigningRequest
import com.r3.corda.networkmanage.common.persistence.CertificateStatus import com.r3.corda.networkmanage.common.persistence.CertificateStatus
import com.r3.corda.networkmanage.common.persistence.RequestStatus 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.crypto.SecureHash
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.nodeapi.internal.crypto.x509Certificates import net.corda.nodeapi.internal.crypto.x509Certificates
@ -26,10 +25,10 @@ import java.time.Instant
import javax.persistence.* import javax.persistence.*
@Entity @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( data class CertificateSigningRequestEntity(
@Id @Id
@Column(name = "request_id", length = 64) @Column(name = "request_id", length = 64, nullable = false)
val requestId: String, val requestId: String,
// TODO: Store X500Name with a proper schema. // TODO: Store X500Name with a proper schema.
@ -37,19 +36,21 @@ data class CertificateSigningRequestEntity(
@Convert(converter = CordaX500NameAttributeConverter::class) @Convert(converter = CordaX500NameAttributeConverter::class)
val legalName: CordaX500Name?, val legalName: CordaX500Name?,
@Column(name = "public_key_hash", length = 64) @Column(name = "public_key_hash", length = 64, nullable = false)
val publicKeyHash: String, 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 @Audited
@Column(name = "status", nullable = false) @Column(name = "status", length = 16, nullable = false, columnDefinition = "NVARCHAR(16)")
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
val status: RequestStatus = RequestStatus.NEW, val status: RequestStatus = RequestStatus.NEW,
@Audited @Audited
@Column(name = "modified_by", length = 512) @Column(name = "modified_by", length = 64, nullable = false)
val modifiedBy: String, val modifiedBy: String,
@Audited // TODO: Use audit framework instead.
@Column(name = "modified_at", nullable = false) @Column(name = "modified_at", nullable = false)
val modifiedAt: Instant = Instant.now(), val modifiedAt: Instant = Instant.now(),
@ -62,10 +63,11 @@ data class CertificateSigningRequestEntity(
@Lob @Lob
@Column(name = "request_bytes", nullable = false) @Column(name = "request_bytes", nullable = false)
val requestBytes: ByteArray, @Convert(converter = PKCS10CertificationRequestConverter::class)
val request: PKCS10CertificationRequest,
@ManyToOne @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 val privateNetwork: PrivateNetworkEntity? = null
) { ) {
fun toCertificateSigningRequest(): CertificateSigningRequest { fun toCertificateSigningRequest(): CertificateSigningRequest {
@ -74,70 +76,53 @@ data class CertificateSigningRequestEntity(
legalName = legalName, legalName = legalName,
publicKeyHash = SecureHash.parse(publicKeyHash), publicKeyHash = SecureHash.parse(publicKeyHash),
status = status, status = status,
request = request(), request = request,
remark = remark, remark = remark,
modifiedBy = modifiedBy, modifiedBy = modifiedBy,
certData = certificateData?.toCertificateData() certData = certificateData?.toCertificateData()
) )
} }
private fun request() = PKCS10CertificationRequest(requestBytes)
} }
@Entity @Entity
@Table(name = "certificate_data") @Table(name = "cert_data")
data class CertificateDataEntity( data class CertificateDataEntity(
@Id @Id
@GeneratedValue(strategy = GenerationType.SEQUENCE) @GeneratedValue(strategy = GenerationType.SEQUENCE)
val id: Long? = null, 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, val certificateStatus: CertificateStatus,
@Lob @Lob
@Column(name = "certificate_path_bytes") @Column(name = "cert_path_bytes", nullable = false)
val certificatePathBytes: ByteArray, @Convert(converter = CertPathConverter::class)
val certPath: CertPath,
@OneToOne(fetch = FetchType.EAGER, optional = false) @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, 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 val certificateSerialNumber: BigInteger
) { ) {
fun toCertificateData(): CertificateData { fun toCertificateData(): CertificateData = CertificateData(certificateStatus, certPath)
return CertificateData(
certStatus = certificateStatus,
certPath = toCertificatePath()
)
}
val legalName: CordaX500Name get() { 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 @Entity
@Table(name = "private_network") @Table(name = "private_network")
data class PrivateNetworkEntity( data class PrivateNetworkEntity(
@Id @Id
@Column(name = "id") @Column(name = "id", length = 64)
val networkId: String, val networkId: String,
@Column(name = "name") @Column(name = "name", length = 255, nullable = false)
val networkName: String val networkName: String
) )

View File

@ -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<CordaX500Name, String> {
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<SecureHash, String> {
// override fun convertToDatabaseColumn(attribute: SecureHash?): String? = attribute?.toString()
// override fun convertToEntityAttribute(dbData: String?): SecureHash? = dbData?.let { SecureHash.parse(it) }
//}

View File

@ -11,11 +11,10 @@
package com.r3.corda.networkmanage.common.persistence.entity package com.r3.corda.networkmanage.common.persistence.entity
import net.corda.core.internal.DigitalSignatureWithCert import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.serialize
import net.corda.core.serialization.deserialize
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.network.SignedNetworkMap
import java.security.cert.X509Certificate
import javax.persistence.* import javax.persistence.*
@Entity @Entity
@ -27,26 +26,23 @@ class NetworkMapEntity(
@Lob @Lob
@Column(name = "serialized_network_map", nullable = false) @Column(name = "serialized_network_map", nullable = false)
val networkMapBytes: ByteArray, @Convert(converter = NetworkMapConverter::class)
val networkMap: NetworkMap,
@Lob @Lob
@Column(name = "signature", nullable = false) @Column(name = "signature", nullable = false)
val signature: ByteArray, val signature: ByteArray,
@Lob @Lob
@Column(name = "certificate", nullable = false) @Column(name = "cert", nullable = false)
val certificate: ByteArray, @Convert(converter = X509CertificateConverter::class)
val certificate: X509Certificate,
@ManyToOne(optional = false, fetch = FetchType.EAGER) @ManyToOne(optional = false, fetch = FetchType.EAGER)
@JoinColumn(name = "network_parameters") @JoinColumn(name = "network_parameters", nullable = false)
val networkParameters: NetworkParametersEntity val networkParameters: NetworkParametersEntity
) { ) {
fun toNetworkMap(): NetworkMap = networkMapBytes.deserialize()
fun toSignedNetworkMap(): SignedNetworkMap { fun toSignedNetworkMap(): SignedNetworkMap {
return SignedNetworkMap( return SignedNetworkMap(networkMap.serialize(), DigitalSignatureWithCert(certificate, signature))
SerializedBytes(networkMapBytes),
DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)
)
} }
} }

View File

@ -12,27 +12,26 @@ package com.r3.corda.networkmanage.common.persistence.entity
import net.corda.core.internal.DigitalSignatureWithCert import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.serialize
import net.corda.core.serialization.deserialize
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.network.SignedNetworkParameters import net.corda.nodeapi.internal.network.SignedNetworkParameters
import org.hibernate.annotations.CreationTimestamp import java.security.cert.X509Certificate
import java.time.Instant import java.time.Instant
import javax.persistence.* import javax.persistence.*
@Entity @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( class NetworkParametersEntity(
@Id @Id
@Column(name = "hash", length = 64, unique = true) @Column(name = "hash", length = 64, nullable = false)
val parametersHash: String, val hash: String,
@CreationTimestamp @Column(nullable = false)
val created: Instant = Instant.now(), val created: Instant = Instant.now(),
@Lob @Lob
@Column(name = "parameters_bytes", nullable = false) @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 // 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. // inserted into database without signature. Then signing service will sign them.
@ -41,31 +40,27 @@ class NetworkParametersEntity(
val signature: ByteArray?, val signature: ByteArray?,
@Lob @Lob
@Column(name = "certificate") @Column(name = "cert")
val certificate: ByteArray? @Convert(converter = X509CertificateConverter::class)
val certificate: X509Certificate?
) { ) {
val isSigned: Boolean get() = certificate != null && signature != null val isSigned: Boolean get() = certificate != null && signature != null
fun toNetworkParameters(): NetworkParameters = parametersBytes.deserialize()
fun toSignedNetworkParameters(): SignedNetworkParameters { fun toSignedNetworkParameters(): SignedNetworkParameters {
if (certificate == null || signature == null) throw IllegalStateException("Network parameters entity is not signed: $parametersHash") if (certificate == null || signature == null) throw IllegalStateException("Network parameters entity is not signed: $hash")
return SignedNetworkParameters( return SignedNetworkParameters(networkParameters.serialize(), DigitalSignatureWithCert(certificate, signature))
SerializedBytes(parametersBytes),
DigitalSignatureWithCert(X509CertificateFactory().generateCertificate(certificate.inputStream()), signature)
)
} }
fun copy(parametersHash: String = this.parametersHash, fun copy(parametersHash: String = this.hash,
created: Instant = this.created, created: Instant = this.created,
parametersBytes: ByteArray = this.parametersBytes, networkParameters: NetworkParameters = this.networkParameters,
signature: ByteArray? = this.signature, signature: ByteArray? = this.signature,
certificate: ByteArray? = this.certificate certificate: X509Certificate? = this.certificate
): NetworkParametersEntity { ): NetworkParametersEntity {
return NetworkParametersEntity( return NetworkParametersEntity(
parametersHash = parametersHash, hash = parametersHash,
created = created, created = created,
parametersBytes = parametersBytes, networkParameters = networkParameters,
signature = signature, signature = signature,
certificate = certificate certificate = certificate
) )

View File

@ -10,7 +10,6 @@
package com.r3.corda.networkmanage.common.persistence.entity package com.r3.corda.networkmanage.common.persistence.entity
import net.corda.core.serialization.deserialize
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import java.time.Instant import java.time.Instant
import javax.persistence.* import javax.persistence.*
@ -21,47 +20,27 @@ data class NodeInfoEntity(
// Hash of serialized [NodeInfo] without signatures. // Hash of serialized [NodeInfo] without signatures.
@Id @Id
@Column(name = "node_info_hash", length = 64) @Column(name = "node_info_hash", length = 64)
val nodeInfoHash: String = "", val nodeInfoHash: String,
@Column(name = "public_key_hash", length = 64) @Column(name = "public_key_hash", length = 64)
val identityPkHash: String = "", val publicKeyHash: String,
@ManyToOne(optional = false, fetch = FetchType.LAZY) @ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "certificate_signing_request") @JoinColumn(name = "cert_signing_request", nullable = false)
val certificateSigningRequest: CertificateSigningRequestEntity, val certificateSigningRequest: CertificateSigningRequestEntity,
@Lob @Lob
@Column(name = "signed_node_info_bytes") @Column(name = "signed_node_info_bytes", nullable = false)
val signedNodeInfoBytes: ByteArray, @Convert(converter = SignedNodeInfoConverter::class)
val signedNodeInfo: SignedNodeInfo,
@Column(name = "is_current") @Column(name = "is_current", nullable = false)
val isCurrent: Boolean, val isCurrent: Boolean,
@Column(name = "published_at") @Column(name = "published_at", nullable = false)
val publishedAt: Instant = Instant.now(), val publishedAt: Instant = Instant.now(),
@Column(name = "accepted_parameters_hash", length = 64) @ManyToOne(fetch = FetchType.LAZY)
val acceptedParametersHash: String = "" @JoinColumn(name = "accepted_network_parameters")
) { val acceptedNetworkParameters: NetworkParametersEntity?
/** )
* Deserialize NodeInfoEntity.signedNodeInfoBytes into the [SignedNodeInfo] instance
*/
fun toSignedNodeInfo() = signedNodeInfoBytes.deserialize<SignedNodeInfo>()
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
)
}
}

View File

@ -7,7 +7,7 @@ import javax.persistence.*
@Entity @Entity
@Table(name = "parameters_update") @Table(name = "parameters_update")
class ParametersUpdateEntity( data class ParametersUpdateEntity(
@Id @Id
@GeneratedValue(strategy = GenerationType.SEQUENCE) @GeneratedValue(strategy = GenerationType.SEQUENCE)
val id: Long? = null, val id: Long? = null,
@ -17,31 +17,17 @@ class ParametersUpdateEntity(
@JoinColumn(name = "network_parameters", foreignKey = ForeignKey(name = "FK__param_up__net_param")) @JoinColumn(name = "network_parameters", foreignKey = ForeignKey(name = "FK__param_up__net_param"))
val networkParameters: NetworkParametersEntity, val networkParameters: NetworkParametersEntity,
@Column(name = "description") @Column(name = "description", nullable = false)
val description: String, val description: String,
@Column(name = "update_deadline") @Column(name = "update_deadline", nullable = false)
val updateDeadline: Instant, val updateDeadline: Instant,
// This boolean flag is used when we want to explicitly point that it's time to switch parameters in network map. // 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 val flagDay: Boolean = false
) { ) {
fun toParametersUpdate(): ParametersUpdate = ParametersUpdate(SecureHash.parse(networkParameters.parametersHash), description, updateDeadline) fun toParametersUpdate(): ParametersUpdate {
return ParametersUpdate(SecureHash.parse(networkParameters.hash), 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
)
} }
} }

View File

@ -12,15 +12,12 @@ package com.r3.corda.networkmanage.common.signer
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.core.node.NetworkParameters 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.contextLogger
import net.corda.core.utilities.debug import net.corda.core.utilities.debug
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.NetworkMapAndSigned import net.corda.nodeapi.internal.network.NetworkMapAndSigned
class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) { class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) {
private companion object { private companion object {
val logger = contextLogger() 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. * Signs the network map and latest network parameters if they haven't been signed yet.
*/ */
fun signNetworkMap() { 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() val latestNetworkParameters = networkMapStorage.getLatestNetworkParameters()
if (latestNetworkParameters == null) { if (latestNetworkParameters == null) {
logger.debug("No network parameters present") logger.debug("No network parameters present")
return return
} }
logger.debug { "Retrieved latest network parameters: ${latestNetworkParameters.toNetworkParameters()}" } logger.debug { "Retrieved latest network parameters: ${latestNetworkParameters.networkParameters}" }
val parametersUpdate = networkMapStorage.getParametersUpdate() val parametersUpdate = networkMapStorage.getParametersUpdate()
logger.debug { "Retrieved parameters update: $parametersUpdate" } 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) { if (!latestNetworkParameters.isSigned) {
signAndPersistNetworkParameters(latestNetworkParameters.toNetworkParameters()) signAndPersistNetworkParameters(latestNetworkParameters.networkParameters)
} else { } else {
logger.debug { "No need to sign any network parameters as they're up-to-date" } 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() networkMapStorage.clearParametersUpdates()
latestNetworkParameters latestNetworkParameters
} else currentNetworkParameters } else {
val newNetworkMap = NetworkMap(nodeInfoHashes, SecureHash.parse(parametersToNetworkMap.parametersHash), parametersUpdate?.toNetMapUpdate()) 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" } logger.debug { "Potential new network map: $newNetworkMap" }
if (currentNetworkMap?.toNetworkMap() != newNetworkMap) {
val netNetworkMapAndSigned = NetworkMapAndSigned(newNetworkMap) { signer.signBytes(it.bytes) } if (activeNetworkMap?.networkMap != newNetworkMap) {
networkMapStorage.saveNewActiveNetworkMap(netNetworkMapAndSigned) val newNetworkMapAndSigned = NetworkMapAndSigned(newNetworkMap) { signer.signBytes(it.bytes) }
networkMapStorage.saveNewActiveNetworkMap(newNetworkMapAndSigned)
logger.info("Signed new network map: $newNetworkMap") logger.info("Signed new network map: $newNetworkMap")
} else { } else {
logger.debug("Current network map is up-to-date") logger.debug("Current network map is up-to-date")

View File

@ -22,7 +22,6 @@ import com.r3.corda.networkmanage.doorman.webservice.NetworkMapWebService
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
@ -60,7 +59,7 @@ class NetworkManagementServer(dataSourceProperties: Properties, databaseConfig:
private fun getNetworkMapService(config: NetworkMapConfig, signer: LocalSigner?): NetworkMapWebService { private fun getNetworkMapService(config: NetworkMapConfig, signer: LocalSigner?): NetworkMapWebService {
val localNetworkMapSigner = signer?.let { NetworkMapSigner(networkMapStorage, it) } 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") 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") logger.info("Starting network map service with network parameters: $latestParameters")
localNetworkMapSigner?.signAndPersistNetworkParameters(latestParameters) localNetworkMapSigner?.signAndPersistNetworkParameters(latestParameters)
@ -153,7 +152,7 @@ class NetworkManagementServer(dataSourceProperties: Properties, databaseConfig:
private fun handleSetNetworkParameters(setNetParams: NetworkParametersCmd.Set) { private fun handleSetNetworkParameters(setNetParams: NetworkParametersCmd.Set) {
logger.info("maxMessageSize is not currently wired in the nodes") 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) { if (activeNetParams == null) {
require(setNetParams.parametersUpdate == null) { require(setNetParams.parametersUpdate == null) {
"'parametersUpdate' specified in network parameters file but there are no network parameters to update" "'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) networkMapStorage.saveNetworkParameters(initialNetParams, null)
} else { } else {
val parametersUpdate = requireNotNull(setNetParams.parametersUpdate) { 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) 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!" "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 // 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 // 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. // 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) val newNetParams = setNetParams.toNetworkParameters(modifiedTime = Instant.now(), epoch = latestNetParams.epoch + 1)
logger.info("Enabling update to network parameters:\n$newNetParams\n$parametersUpdate") 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" "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 // 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) { check(latestNetParamsEntity.hash == parametersUpdate.networkParameters.hash) {
"The latest network parameters is not the scheduled one:\n${latestNetParamsEntity.toNetworkParameters()}\n${parametersUpdate.toParametersUpdate()}" "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" + 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()}") "Switching from: $activeNetParams\nTo: ${latestNetParamsEntity.networkParameters}")
networkMapStorage.setFlagDay(SecureHash.parse(parametersUpdate.networkParameters.parametersHash)) networkMapStorage.setFlagDay(SecureHash.parse(parametersUpdate.networkParameters.hash))
} }
private fun handleCancelUpdate() { private fun handleCancelUpdate() {

View File

@ -57,9 +57,9 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
.build { .build {
networkMapStorage.getActiveNetworkMap()?.let { networkMapStorage.getActiveNetworkMap()?.let {
logger.info("Re-publishing network map") logger.info("Re-publishing network map")
val networkMap = it.toNetworkMap() val networkMap = it.networkMap
val signedNetworkMap = it.toSignedNetworkMap() 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}" } logger.debug { "Received ack-parameters with $hash from ${signedParametersHash.sig.by}" }
nodeInfoStorage.ackNodeInfoParametersUpdate(signedParametersHash.sig.by.encoded.sha256(), hash) nodeInfoStorage.ackNodeInfoParametersUpdate(signedParametersHash.sig.by.encoded.sha256(), hash)
ok() ok()
} catch (e: Exception) { } catch (e: SignatureException) {
when (e) { status(Response.Status.FORBIDDEN).entity(e.message)
is SignatureException -> status(Response.Status.FORBIDDEN).entity(e.message)
else -> status(Response.Status.INTERNAL_SERVER_ERROR)
}
}.build() }.build()
} }

View File

@ -15,30 +15,39 @@
<changeSet author="R3.Corda" id="1513267683777-1" dbms="mssql"> <changeSet author="R3.Corda" id="1513267683777-1" dbms="mssql">
<createSequence sequenceName="hibernate_sequence" minValue="1"/> <createSequence sequenceName="hibernate_sequence" minValue="1"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1513267683777-1.1" dbms="h2">
<createSequence sequenceName="hibernate_sequence"/>
</changeSet>
<changeSet author="R3.Corda" id="1513267683777-1.2" dbms="azure"> <changeSet author="R3.Corda" id="1513267683777-1.2" dbms="azure">
<createSequence sequenceName="hibernate_sequence" minValue="1"/> <createSequence sequenceName="hibernate_sequence" minValue="1"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1513267683777-1.3" dbms="postgres"> <changeSet author="R3.Corda" id="1513267683777-1.3" dbms="postgres">
<createSequence sequenceName="hibernate_sequence" minValue="1"/> <createSequence sequenceName="hibernate_sequence" minValue="1"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1513267683777-1.1" dbms="h2"> <changeSet author="R3.Corda" id="1513267683777-1.4" dbms="oracle">
<createSequence sequenceName="hibernate_sequence"/> <createSequence sequenceName="hibernate_sequence" minValue="1"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-2"> <changeSet author="R3.Corda" id="1520338500424-2">
<createTable tableName="certificate_data"> <createTable tableName="cert_data">
<column name="id" type="BIGINT"> <column name="id" type="BIGINT">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="certificate_path_bytes" type="BLOB"/> <column name="cert_path_bytes" type="BLOB">
<column name="certificate_status" type="INT"/> <constraints nullable="false"/>
<column name="certificate_signing_request" type="NVARCHAR(64)"> </column>
<column name="cert_status" type="NVARCHAR(16)">
<constraints nullable="false"/>
</column>
<column name="cert_signing_request" type="NVARCHAR(64)">
<constraints nullable="false"/>
</column>
<column name="cert_serial_number" type="NUMERIC(28)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="certificate_serial_number" type="NUMERIC(28)"/>
</createTable> </createTable>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-3"> <changeSet author="R3.Corda" id="1520338500424-3">
<createTable tableName="certificate_signing_request"> <createTable tableName="cert_signing_request">
<column name="request_id" type="NVARCHAR(64)"> <column name="request_id" type="NVARCHAR(64)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
@ -50,16 +59,20 @@
<column name="request_bytes" type="BLOB"> <column name="request_bytes" type="BLOB">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="status" type="NVARCHAR(255)"> <column name="status" type="NVARCHAR(16)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="public_key_hash" type="NVARCHAR(64)"/> <column name="public_key_hash" type="NVARCHAR(64)">
<column name="modified_by" type="NVARCHAR(512)"/> <constraints nullable="false"/>
<column name="private_network" type="NVARCHAR(255)"/> </column>
<column name="modified_by" type="NVARCHAR(64)">
<constraints nullable="false"/>
</column>
<column name="private_network" type="NVARCHAR(64)"/>
</createTable> </createTable>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-4"> <changeSet author="R3.Corda" id="1520338500424-4">
<createTable tableName="certificate_signing_request_AUD"> <createTable tableName="cert_signing_request_AUD">
<column name="request_id" type="NVARCHAR(64)"> <column name="request_id" type="NVARCHAR(64)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
@ -67,10 +80,9 @@
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="REVTYPE" type="TINYINT"/> <column name="REVTYPE" type="TINYINT"/>
<column name="modified_at" type="TIMESTAMP"/>
<column name="remark" type="NVARCHAR(256)"/> <column name="remark" type="NVARCHAR(256)"/>
<column name="status" type="NVARCHAR(255)"/> <column name="status" type="NVARCHAR(16)"/>
<column name="modified_by" type="NVARCHAR(512)"/> <column name="modified_by" type="NVARCHAR(64)"/>
</createTable> </createTable>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-5"> <changeSet author="R3.Corda" id="1520338500424-5">
@ -78,7 +90,7 @@
<column name="version" type="BIGINT"> <column name="version" type="BIGINT">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="certificate" type="BLOB"> <column name="cert" type="BLOB">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="serialized_network_map" type="BLOB"> <column name="serialized_network_map" type="BLOB">
@ -97,8 +109,10 @@
<column name="hash" type="NVARCHAR(64)"> <column name="hash" type="NVARCHAR(64)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="created" type="TIMESTAMP"/> <column name="cert" type="BLOB"/>
<column name="certificate" type="BLOB"/> <column name="created" type="TIMESTAMP">
<constraints nullable="false"/>
</column>
<column name="parameters_bytes" type="BLOB"> <column name="parameters_bytes" type="BLOB">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
@ -114,7 +128,7 @@
<column name="signed_node_info_bytes" type="BLOB"> <column name="signed_node_info_bytes" type="BLOB">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="certificate_signing_request" type="NVARCHAR(64)"> <column name="cert_signing_request" type="NVARCHAR(64)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="is_current" type="BOOLEAN"> <column name="is_current" type="BOOLEAN">
@ -123,62 +137,66 @@
<column name="published_at" type="TIMESTAMP"> <column name="published_at" type="TIMESTAMP">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="accepted_parameters_hash" type="NVARCHAR(64)"/> <column name="accepted_network_parameters" type="NVARCHAR(64)"/>
</createTable> </createTable>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-8"> <changeSet author="R3.Corda" id="1520338500424-8">
<createTable tableName="REVINFO"> <createTable tableName="REVINFO">
<column autoIncrement="true" name="REV" type="INT"> <column autoIncrement="true" name="REV" type="INT">
<constraints primaryKey="true" primaryKeyName="PK_REVINFO_REV"/> <constraints primaryKey="true" primaryKeyName="PK__REVINFO__REV"/>
</column> </column>
<column name="REVTSTMP" type="BIGINT"/> <column name="REVTSTMP" type="BIGINT"/>
</createTable> </createTable>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-9"> <changeSet author="R3.Corda" id="1520338500424-9">
<addPrimaryKey columnNames="hash" constraintName="PK_NP_H" tableName="network_parameters"/> <addPrimaryKey columnNames="hash" constraintName="PK__NP__H" tableName="network_parameters"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-10"> <changeSet author="R3.Corda" id="1520338500424-10">
<addPrimaryKey columnNames="id" constraintName="PK_CD_ID" tableName="certificate_data"/> <addPrimaryKey columnNames="id" constraintName="PK__CD__ID" tableName="cert_data"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-11"> <changeSet author="R3.Corda" id="1520338500424-11">
<addPrimaryKey columnNames="request_id, rev" constraintName="PK_CSRA_RID" tableName="certificate_signing_request_AUD"/> <addPrimaryKey columnNames="request_id, rev" constraintName="PK__CSRA__RID"
tableName="cert_signing_request_AUD"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-12"> <changeSet author="R3.Corda" id="1520338500424-12">
<addPrimaryKey columnNames="node_info_hash" constraintName="PK_NI_NIH" tableName="node_info"/> <addPrimaryKey columnNames="node_info_hash" constraintName="PK__NI__NIH" tableName="node_info"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-13"> <changeSet author="R3.Corda" id="1520338500424-13">
<addPrimaryKey columnNames="version" constraintName="PK_NM_V" tableName="network_map"/> <addPrimaryKey columnNames="version" constraintName="PK__NM__V" tableName="network_map"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-14"> <changeSet author="R3.Corda" id="1520338500424-14">
<addPrimaryKey columnNames="request_id" constraintName="PK_CSR_RID" tableName="certificate_signing_request"/> <addPrimaryKey columnNames="request_id" constraintName="PK__CSR__RID" tableName="cert_signing_request"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-15"> <changeSet author="R3.Corda" id="1520338500424-15">
<addUniqueConstraint columnNames="certificate_signing_request" constraintName="UK_CD_CSR" tableName="certificate_data"/> <addUniqueConstraint columnNames="cert_signing_request" constraintName="UK_CD_CSR" tableName="cert_data"/>
</changeSet>
<changeSet author="R3.Corda" id="1520338500424-16">
<addUniqueConstraint columnNames="cert_serial_number" constraintName="UK_CD_CSN" tableName="cert_data"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-19"> <changeSet author="R3.Corda" id="1520338500424-19">
<createIndex indexName="IDX_CSRA_REV" tableName="certificate_signing_request_AUD"> <createIndex indexName="IDX__CSRA__REV" tableName="cert_signing_request_AUD">
<column name="REV"/> <column name="REV"/>
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-20"> <changeSet author="R3.Corda" id="1520338500424-20">
<createIndex indexName="IDX_PUB_KEY_HASH" tableName="certificate_signing_request"> <createIndex indexName="IDX__CSR__PKH" tableName="cert_signing_request">
<column name="public_key_hash"/> <column name="public_key_hash"/>
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-21"> <changeSet author="R3.Corda" id="1520338500424-21">
<addForeignKeyConstraint baseColumnNames="REV" baseTableName="certificate_signing_request_AUD" <addForeignKeyConstraint baseColumnNames="REV" baseTableName="cert_signing_request_AUD"
constraintName="FK_CSRA_REV" constraintName="FK__CSRA__REV"
referencedColumnNames="REV" referencedTableName="REVINFO"/> referencedColumnNames="REV" referencedTableName="REVINFO"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-22"> <changeSet author="R3.Corda" id="1520338500424-22">
<addForeignKeyConstraint baseColumnNames="certificate_signing_request" baseTableName="node_info" <addForeignKeyConstraint baseColumnNames="cert_signing_request" baseTableName="node_info"
constraintName="FK_NI_CSR" constraintName="FK__NI__CSR"
referencedColumnNames="request_id" referencedTableName="certificate_signing_request"/> referencedColumnNames="request_id" referencedTableName="cert_signing_request"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-23"> <changeSet author="R3.Corda" id="1520338500424-23">
<addForeignKeyConstraint baseColumnNames="certificate_signing_request" baseTableName="certificate_data" <addForeignKeyConstraint baseColumnNames="cert_signing_request" baseTableName="cert_data"
constraintName="FK_CD_CSR" constraintName="FK__CD__CSR"
referencedColumnNames="request_id" referencedTableName="certificate_signing_request"/> referencedColumnNames="request_id" referencedTableName="cert_signing_request"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-24"> <changeSet author="R3.Corda" id="1520338500424-24">
<createIndex indexName="IDX_NP_HASH" tableName="network_parameters"> <createIndex indexName="IDX_NP_HASH" tableName="network_parameters">
@ -186,42 +204,42 @@
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-25"> <changeSet author="R3.Corda" id="1520338500424-25">
<createTable tableName="certificate_revocation_request"> <createTable tableName="cert_revocation_request">
<column name="id" type="BIGINT"> <column name="id" type="BIGINT">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="request_id" type="NVARCHAR(256)"> <column name="request_id" type="NVARCHAR(64)">
<constraints nullable="false" unique="true"/> <constraints nullable="false" unique="true"/>
</column> </column>
<column name="certificate_serial_number" type="NUMERIC(28)"> <column name="cert_serial_number" type="NUMERIC(28)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="legal_name" type="NVARCHAR(256)"> <column name="legal_name" type="NVARCHAR(256)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="status" type="NVARCHAR(256)"> <column name="status" type="NVARCHAR(16)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="reporter" type="NVARCHAR(512)"> <column name="reporter" type="NVARCHAR(64)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="modified_by" type="NVARCHAR(512)"> <column name="modified_by" type="NVARCHAR(64)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="modified_at" type="TIMESTAMP"> <column name="modified_at" type="TIMESTAMP">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="remark" type="NVARCHAR(256)"/> <column name="remark" type="NVARCHAR(256)"/>
<column name="revocation_reason" type="NVARCHAR(256)"> <column name="revocation_reason" type="NVARCHAR(32)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="certificate_data" type="BIGINT"> <column name="cert_data" type="BIGINT">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
</createTable> </createTable>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-26"> <changeSet author="R3.Corda" id="1520338500424-26">
<createTable tableName="certificate_revocation_request_AUD"> <createTable tableName="cert_revocation_request_AUD">
<column name="id" type="BIGINT"> <column name="id" type="BIGINT">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
@ -229,85 +247,73 @@
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="revtype" type="TINYINT"/> <column name="revtype" type="TINYINT"/>
<column name="revocation_reason" type="NVARCHAR(256)"/> <column name="revocation_reason" type="NVARCHAR(32)"/>
<column name="modified_at" type="TIMESTAMP"/> <column name="modified_at" type="TIMESTAMP"/>
<column name="modified_by" type="NVARCHAR(256)"/> <column name="modified_by" type="NVARCHAR(64)"/>
<column name="remark" type="NVARCHAR(256)"/> <column name="remark" type="NVARCHAR(256)"/>
<column name="status" type="NVARCHAR(256)"/> <column name="status" type="NVARCHAR(16)"/>
</createTable> </createTable>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-27"> <changeSet author="R3.Corda" id="1520338500424-27">
<createTable tableName="certificate_revocation_list"> <createTable tableName="cert_revocation_list">
<column name="id" type="BIGINT"> <column name="id" type="BIGINT">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="issuer" type="NVARCHAR(256)"> <column name="issuer" type="NVARCHAR(16)">
<constraints nullable="false"/>
</column>
<column name="crl_bytes" type="BLOB">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="crl_bytes" type="BLOB"/>
<column name="modified_at" type="TIMESTAMP"> <column name="modified_at" type="TIMESTAMP">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="signed_by" type="NVARCHAR(512)"> <column name="signed_by" type="NVARCHAR(64)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
</createTable> </createTable>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-28">
<createTable tableName="certificate_revocation_list_AUD">
<column name="id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="rev" type="INT">
<constraints nullable="false"/>
</column>
<column name="revtype" type="TINYINT"/>
<column name="modified_at" type="TIMESTAMP"/>
<column name="signed_by" type="NVARCHAR(512)"/>
</createTable>
</changeSet>
<changeSet author="R3.Corda" id="1520338500424-29"> <changeSet author="R3.Corda" id="1520338500424-29">
<addPrimaryKey columnNames="id" constraintName="certificate_revocation_request_pk" tableName="certificate_revocation_request"/> <addPrimaryKey columnNames="id" constraintName="PK__CRR" tableName="cert_revocation_request"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-30"> <changeSet author="R3.Corda" id="1520338500424-30">
<addPrimaryKey columnNames="id, rev" constraintName="certificate_revocation_request_AUD_pk" tableName="certificate_revocation_request_AUD"/> <addPrimaryKey columnNames="id, rev" constraintName="PK__CRR_AUD" tableName="cert_revocation_request_AUD"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-31"> <changeSet author="R3.Corda" id="1520338500424-31">
<addPrimaryKey columnNames="id" constraintName="certificate_revocation_list_pk" tableName="certificate_revocation_list"/> <addPrimaryKey columnNames="id" constraintName="PK__CRL" tableName="cert_revocation_list"/>
</changeSet>
<changeSet author="R3.Corda" id="1520338500424-32">
<addPrimaryKey columnNames="id, rev" constraintName="certificate_revocation_list_AUD_pk" tableName="certificate_revocation_list_AUD"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-33"> <changeSet author="R3.Corda" id="1520338500424-33">
<addForeignKeyConstraint baseColumnNames="certificate_data" <addForeignKeyConstraint baseColumnNames="cert_data"
baseTableName="certificate_revocation_request" baseTableName="cert_revocation_request"
constraintName="cert_data__cert_rev_req_fk" constraintName="FK__CRR__CD"
referencedColumnNames="id" referencedColumnNames="id"
referencedTableName="certificate_data"/> referencedTableName="cert_data"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-34"> <changeSet author="R3.Corda" id="1520338500424-34">
<addForeignKeyConstraint baseColumnNames="rev" <addForeignKeyConstraint baseColumnNames="rev"
baseTableName="certificate_revocation_request_AUD" baseTableName="cert_revocation_request_AUD"
constraintName="cert_rev_req__REVINFO_AUD_fk" constraintName="FK__CRR_AUD__REVINFO"
referencedColumnNames="rev" referencedColumnNames="rev"
referencedTableName="REVINFO"/> referencedTableName="REVINFO"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-35"> <changeSet author="R3.Corda" id="1520338500424-35">
<createIndex indexName="certificate_revocation_request_AUD_index" tableName="certificate_revocation_request_AUD"> <createIndex indexName="IDX__CRR_AUD" tableName="cert_revocation_request_AUD">
<column name="rev"/> <column name="rev"/>
</createIndex> </createIndex>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-36"> <changeSet author="R3.Corda" id="1520338500424-36">
<createTable tableName="private_network"> <createTable tableName="private_network">
<column name="id" type="NVARCHAR(255)"> <column name="id" type="NVARCHAR(64)">
<constraints primaryKey="true" primaryKeyName="PK_PRIV_NETWORK_ID"/> <constraints primaryKey="true" primaryKeyName="PK__PRIV_NETWORK__ID" nullable="false"/>
</column>
<column name="name" type="NVARCHAR(255)">
<constraints nullable="false"/>
</column> </column>
<column name="name" type="NVARCHAR(255)"/>
</createTable> </createTable>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-37"> <changeSet author="R3.Corda" id="1520338500424-37">
<addForeignKeyConstraint baseColumnNames="private_network" baseTableName="certificate_signing_request" <addForeignKeyConstraint baseColumnNames="private_network" baseTableName="cert_signing_request"
constraintName="FK_CSR_PN" constraintName="FK__CSR__PN"
referencedColumnNames="id" referencedTableName="private_network"/> referencedColumnNames="id" referencedTableName="private_network"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-38"> <changeSet author="R3.Corda" id="1520338500424-38">
@ -315,21 +321,32 @@
constraintName="FK_NM_NP" constraintName="FK_NM_NP"
referencedColumnNames="hash" referencedTableName="network_parameters"/> referencedColumnNames="hash" referencedTableName="network_parameters"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="update-net-params"> <changeSet author="R3.Corda" id="1520338500424-39">
<createTable tableName="parameters_update"> <createTable tableName="parameters_update">
<column name="description" type="VARCHAR(255)"/> <column name="description" type="VARCHAR(255)">
<column name="update_deadline" type="TIMESTAMP"/> <constraints nullable="false"/>
</column>
<column name="update_deadline" type="TIMESTAMP">
<constraints nullable="false"/>
</column>
<column name="id" type="BIGINT"> <column name="id" type="BIGINT">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="network_parameters" type="NVARCHAR(64)"> <column name="network_parameters" type="NVARCHAR(64)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="flag_day" type="BOOLEAN"/> <column name="flag_day" type="BOOLEAN">
<constraints nullable="false"/>
</column>
</createTable> </createTable>
<addPrimaryKey columnNames="id" constraintName="CONSTRAINT_PARAMUPKEY" tableName="parameters_update"/> <addPrimaryKey columnNames="id" constraintName="CONSTRAINT_PARAMUPKEY" tableName="parameters_update"/>
<addForeignKeyConstraint baseTableName="parameters_update" baseColumnNames="network_parameters" <addForeignKeyConstraint baseTableName="parameters_update" baseColumnNames="network_parameters"
constraintName="FK_PU_NP" constraintName="FK_PU_NP"
referencedTableName="network_parameters" referencedColumnNames="hash"/> referencedTableName="network_parameters" referencedColumnNames="hash"/>
</changeSet> </changeSet>
<changeSet author="R3.Corda" id="1520338500424-40">
<addForeignKeyConstraint baseColumnNames="accepted_network_parameters" baseTableName="node_info"
constraintName="FK__NI__NP"
referencedColumnNames="hash" referencedTableName="network_parameters"/>
</changeSet>
</databaseChangeLog> </databaseChangeLog>

View File

@ -15,18 +15,17 @@ fun createNetworkParametersEntity(signingCertAndKeyPair: CertificateAndKeyPair =
networkParameters: NetworkParameters = testNetworkParameters()): NetworkParametersEntity { networkParameters: NetworkParameters = testNetworkParameters()): NetworkParametersEntity {
val signedNetParams = signingCertAndKeyPair.sign(networkParameters) val signedNetParams = signingCertAndKeyPair.sign(networkParameters)
return NetworkParametersEntity( return NetworkParametersEntity(
parametersHash = signedNetParams.raw.hash.toString(), hash = signedNetParams.raw.hash.toString(),
parametersBytes = signedNetParams.raw.bytes, networkParameters = networkParameters,
signature = signedNetParams.sig.bytes, signature = signedNetParams.sig.bytes,
certificate = signedNetParams.sig.by.encoded certificate = signedNetParams.sig.by
) )
} }
fun createNetworkParametersEntityUnsigned(networkParameters: NetworkParameters = testNetworkParameters()): NetworkParametersEntity { fun createNetworkParametersEntityUnsigned(networkParameters: NetworkParameters = testNetworkParameters()): NetworkParametersEntity {
val serialised = networkParameters.serialize()
return NetworkParametersEntity( return NetworkParametersEntity(
parametersHash = serialised.hash.toString(), hash = networkParameters.serialize().hash.toString(),
parametersBytes = serialised.bytes, networkParameters = networkParameters,
signature = null, signature = null,
certificate = null certificate = null
) )
@ -36,11 +35,12 @@ fun createNetworkMapEntity(signingCertAndKeyPair: CertificateAndKeyPair = create
netParamsEntity: NetworkParametersEntity, netParamsEntity: NetworkParametersEntity,
nodeInfoHashes: List<SecureHash> = emptyList(), nodeInfoHashes: List<SecureHash> = emptyList(),
parametersUpdate: ParametersUpdate? = null): NetworkMapEntity { 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( return NetworkMapEntity(
networkMapBytes = signedNetMap.raw.bytes, networkMap = networkMap,
signature = signedNetMap.sig.bytes, signature = signedNetworkMap.sig.bytes,
certificate = signedNetMap.sig.by.encoded, certificate = signedNetworkMap.sig.by,
networkParameters = netParamsEntity networkParameters = netParamsEntity
) )
} }

View File

@ -87,7 +87,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
assertThat(activeSignedNetworkMap.sig).isEqualTo(networkMapAndSigned.signed.sig) assertThat(activeSignedNetworkMap.sig).isEqualTo(networkMapAndSigned.signed.sig)
assertThat(activeNetworkParameters).isEqualTo(networkParameters) assertThat(activeNetworkParameters).isEqualTo(networkParameters)
assertThat(activeSignedNetworkParameters.sig).isEqualTo(networkParametersSig) assertThat(activeSignedNetworkParameters.sig).isEqualTo(networkParametersSig)
assertThat(SecureHash.parse(activeNetworkParametersEntity.parametersHash)) assertThat(SecureHash.parse(activeNetworkParametersEntity.hash))
.isEqualTo(activeNetworkMap.networkParameterHash) .isEqualTo(activeNetworkMap.networkParameterHash)
.isEqualTo(networkParametersHash) .isEqualTo(networkParametersHash)
} }
@ -100,7 +100,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
// We may have not signed them yet. // We may have not signed them yet.
networkMapStorage.saveNetworkParameters(params2, null) networkMapStorage.saveNetworkParameters(params2, null)
assertThat(networkMapStorage.getLatestNetworkParameters()?.toNetworkParameters()).isEqualTo(params2) assertThat(networkMapStorage.getLatestNetworkParameters()?.networkParameters).isEqualTo(params2)
} }
@Test @Test

View File

@ -14,15 +14,18 @@ import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.common.utils.hashString import com.r3.corda.networkmanage.common.utils.hashString
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.CertRole import net.corda.core.internal.CertRole
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.NodeInfoAndSigned 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.CertificateAndKeyPair
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.DatabaseConfig 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.TestNodeInfoBuilder
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.corda.testing.internal.signWith import net.corda.testing.internal.signWith
@ -45,13 +48,15 @@ class PersistentNodeInfoStorageTest : TestBase() {
private lateinit var networkMapStorage: PersistentNetworkMapStorage private lateinit var networkMapStorage: PersistentNetworkMapStorage
private lateinit var persistence: CordaPersistence private lateinit var persistence: CordaPersistence
private lateinit var rootCaCert: X509Certificate private lateinit var rootCaCert: X509Certificate
private lateinit var intermediateCa: CertificateAndKeyPair private lateinit var doormanCertAndKeyPair: CertificateAndKeyPair
private lateinit var networkMapCertAndKeyPair: CertificateAndKeyPair
@Before @Before
fun startDb() { fun startDb() {
val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() val (rootCa, intermediateCa) = createDevIntermediateCaCertPath()
rootCaCert = rootCa.certificate rootCaCert = rootCa.certificate
this.intermediateCa = intermediateCa this.doormanCertAndKeyPair = intermediateCa
networkMapCertAndKeyPair = createDevNetworkMapCa(rootCa)
persistence = configureDatabase(MockServices.makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)) persistence = configureDatabase(MockServices.makeTestDataSourceProperties(), DatabaseConfig(runMigration = true))
nodeInfoStorage = PersistentNodeInfoStorage(persistence) nodeInfoStorage = PersistentNodeInfoStorage(persistence)
requestStorage = PersistentCertificateSigningRequestStorage(persistence) requestStorage = PersistentCertificateSigningRequestStorage(persistence)
@ -70,8 +75,8 @@ class PersistentNodeInfoStorageTest : TestBase() {
val name = CordaX500Name(organisation = "Test", locality = "London", country = "GB") val name = CordaX500Name(organisation = "Test", locality = "London", country = "GB")
val nodeCaCert = X509Utilities.createCertificate( val nodeCaCert = X509Utilities.createCertificate(
CertificateType.NODE_CA, CertificateType.NODE_CA,
intermediateCa.certificate, doormanCertAndKeyPair.certificate,
intermediateCa.keyPair, doormanCertAndKeyPair.keyPair,
name.x500Principal, name.x500Principal,
keyPair.public) keyPair.public)
@ -85,7 +90,7 @@ class PersistentNodeInfoStorageTest : TestBase() {
requestStorage.putCertificatePath( requestStorage.putCertificatePath(
requestId, requestId,
X509Utilities.buildCertPath(nodeCaCert, intermediateCa.certificate, rootCaCert), X509Utilities.buildCertPath(nodeCaCert, doormanCertAndKeyPair.certificate, rootCaCert),
CertificateSigningRequestStorage.DOORMAN_SIGNATURE) CertificateSigningRequestStorage.DOORMAN_SIGNATURE)
val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString())) val storedCertPath = nodeInfoStorage.getCertificatePath(SecureHash.parse(keyPair.public.hashString()))
@ -150,16 +155,37 @@ class PersistentNodeInfoStorageTest : TestBase() {
@Test @Test
fun `accept parameters updates node info correctly`() { fun `accept parameters updates node info correctly`() {
// given // given
val (nodeInfoWithSigned) = createValidSignedNodeInfo("Test", requestStorage) val (nodeInfoAndSigned) = createValidSignedNodeInfo("Test", requestStorage)
// when // when
val paramsHash = SecureHash.randomSHA256() val networkParameters = testNetworkParameters()
val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfoWithSigned) val sigWithCert = networkMapCertAndKeyPair.sign(networkParameters).sig
nodeInfoStorage.ackNodeInfoParametersUpdate(SecureHash.parse(nodeInfoWithSigned.nodeInfo.legalIdentities.first().owningKey.hashString()), paramsHash) val netParamsHash = networkMapStorage.saveNetworkParameters(networkParameters, sigWithCert)
val nodeInfoHash = nodeInfoStorage.putNodeInfo(nodeInfoAndSigned)
nodeInfoStorage.ackNodeInfoParametersUpdate(nodeInfoAndSigned.nodeInfo.legalIdentities[0].owningKey.encoded.sha256(), netParamsHash)
// then // then
val persistedParametersHash = nodeInfoStorage.getAcceptedParametersUpdateHash(nodeInfoHash) val acceptedNetworkParameters = nodeInfoStorage.getAcceptedNetworkParameters(nodeInfoHash)
assertThat(persistedParametersHash).isEqualTo(paramsHash) 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())
} }
} }

View File

@ -149,7 +149,6 @@ class NetworkMapSignerTest : TestBase() {
// given // given
val currentNetworkParameters = createNetworkParametersEntity(signingCertAndKeyPair) val currentNetworkParameters = createNetworkParametersEntity(signingCertAndKeyPair)
val updateNetworkParameters = createNetworkParametersEntityUnsigned(testNetworkParameters(epoch = 2)) val updateNetworkParameters = createNetworkParametersEntityUnsigned(testNetworkParameters(epoch = 2))
val updateParametersHash = SecureHash.parse(updateNetworkParameters.parametersHash)
val parametersUpdate = ParametersUpdateEntity(0, updateNetworkParameters,"Update time", Instant.ofEpochMilli(random63BitValue())) val parametersUpdate = ParametersUpdateEntity(0, updateNetworkParameters,"Update time", Instant.ofEpochMilli(random63BitValue()))
val netMapEntity = createNetworkMapEntity(signingCertAndKeyPair, currentNetworkParameters, emptyList(), null) val netMapEntity = createNetworkMapEntity(signingCertAndKeyPair, currentNetworkParameters, emptyList(), null)
whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(netMapEntity) whenever(networkMapStorage.getActiveNetworkMap()).thenReturn(netMapEntity)
@ -170,8 +169,8 @@ class NetworkMapSignerTest : TestBase() {
val paramsCaptor = argumentCaptor<NetworkParameters>() val paramsCaptor = argumentCaptor<NetworkParameters>()
val signatureCaptor = argumentCaptor<DigitalSignatureWithCert>() val signatureCaptor = argumentCaptor<DigitalSignatureWithCert>()
verify(networkMapStorage, times(1)).saveNetworkParameters(paramsCaptor.capture(), signatureCaptor.capture()) verify(networkMapStorage, times(1)).saveNetworkParameters(paramsCaptor.capture(), signatureCaptor.capture())
assertEquals(paramsCaptor.firstValue, updateNetworkParameters.toNetworkParameters()) assertEquals(paramsCaptor.firstValue, updateNetworkParameters.networkParameters)
assertThat(signatureCaptor.firstValue.verify(updateNetworkParameters.parametersBytes)) signatureCaptor.firstValue.verify(updateNetworkParameters.networkParameters.serialize())
} }
@Test @Test
@ -206,7 +205,7 @@ class NetworkMapSignerTest : TestBase() {
argumentCaptor<NetworkMapAndSigned>().apply { argumentCaptor<NetworkMapAndSigned>().apply {
verify(networkMapStorage).saveNewActiveNetworkMap(capture()) verify(networkMapStorage).saveNewActiveNetworkMap(capture())
val netMap = firstValue.networkMap val netMap = firstValue.networkMap
assertEquals(SecureHash.parse(updateNetworkParameters.parametersHash), netMap.networkParameterHash) assertEquals(SecureHash.parse(updateNetworkParameters.hash), netMap.networkParameterHash)
assertEquals(emptyList(), netMap.nodeInfoHashes) assertEquals(emptyList(), netMap.nodeInfoHashes)
assertEquals(null, netMap.parametersUpdate) assertEquals(null, netMap.parametersUpdate)
} }
@ -231,7 +230,7 @@ class NetworkMapSignerTest : TestBase() {
argumentCaptor<NetworkMapAndSigned>().apply { argumentCaptor<NetworkMapAndSigned>().apply {
verify(networkMapStorage).saveNewActiveNetworkMap(capture()) verify(networkMapStorage).saveNewActiveNetworkMap(capture())
val netMap = firstValue.networkMap val netMap = firstValue.networkMap
assertEquals(SecureHash.parse(activeNetworkParameters.parametersHash), netMap.networkParameterHash) assertEquals(SecureHash.parse(activeNetworkParameters.hash), netMap.networkParameterHash)
assertEquals(emptyList(), netMap.nodeInfoHashes) assertEquals(emptyList(), netMap.nodeInfoHashes)
assertEquals(null, netMap.parametersUpdate) assertEquals(null, netMap.parametersUpdate)
} }

View File

@ -119,7 +119,7 @@ class NetworkMapWebServiceTest {
it.start() it.start()
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("") val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
verify(networkMapStorage, times(1)).getActiveNetworkMap() verify(networkMapStorage, times(1)).getActiveNetworkMap()
assertEquals(signedNetworkMapResponse.verifiedNetworkMapCert(rootCaCert), networkMapEntity.toNetworkMap()) assertEquals(signedNetworkMapResponse.verifiedNetworkMapCert(rootCaCert), networkMapEntity.networkMap)
} }
} }

View File

@ -41,7 +41,14 @@ data class NetworkMap(
val nodeInfoHashes: List<SecureHash>, val nodeInfoHashes: List<SecureHash>,
val networkParameterHash: SecureHash, val networkParameterHash: SecureHash,
val parametersUpdate: ParametersUpdate? 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. * Data class representing scheduled network parameters update.

View File

@ -48,8 +48,7 @@ class NetworkParametersReader(private val trustRoot: X509Certificate,
// you get them from network map, but you have to run the approval step. // you get them from network map, but you have to run the approval step.
if (signedParametersFromFile == null) { // Node joins for the first time. if (signedParametersFromFile == null) { // Node joins for the first time.
downloadParameters(trustRoot, advertisedParametersHash) 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) signedParametersFromFile.verifiedNetworkMapCert(trustRoot)
} else { // Update case. } else { // Update case.
readParametersUpdate(advertisedParametersHash, signedParametersFromFile.raw.hash).verifiedNetworkMapCert(trustRoot) 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.") "Please update node to use correct network parameters file.")
} }
parametersUpdateFile.moveTo(networkParamsFile, StandardCopyOption.REPLACE_EXISTING) parametersUpdateFile.moveTo(networkParamsFile, StandardCopyOption.REPLACE_EXISTING)
logger.info("Scheduled update to network parameters has occurred - node now updated to these new parameters.")
return signedUpdatedParameters return signedUpdatedParameters
} }

View File

@ -12,7 +12,9 @@ package net.corda.node.services.network
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignedData 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.node.NodeInfo
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize 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.core.utilities.trace
import net.corda.node.utilities.registration.cacheControl import net.corda.node.utilities.registration.cacheControl
import net.corda.nodeapi.internal.SignedNodeInfo 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.io.BufferedReader
import java.net.URL import java.net.URL
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
@ -54,10 +59,7 @@ class NetworkMapClient(compatibilityZoneURL: URL, val trustedRoot: X509Certifica
val signedNetworkMap = connection.responseAs<SignedNetworkMap>() val signedNetworkMap = connection.responseAs<SignedNetworkMap>()
val networkMap = signedNetworkMap.verifiedNetworkMapCert(trustedRoot) val networkMap = signedNetworkMap.verifiedNetworkMapCert(trustedRoot)
val timeout = connection.cacheControl().maxAgeSeconds().seconds val timeout = connection.cacheControl().maxAgeSeconds().seconds
logger.trace { logger.trace { "Fetched network map update from $networkMapUrl successfully: $networkMap" }
"Fetched network map update from $networkMapUrl successfully, retrieved ${networkMap.nodeInfoHashes.size} " +
"node info hashes. Node Info hashes:\n${networkMap.nodeInfoHashes.joinToString("\n")}"
}
return NetworkMapResponse(networkMap, timeout) return NetworkMapResponse(networkMap, timeout)
} }

View File

@ -157,12 +157,13 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
// This update was handled already. // This update was handled already.
return return
} }
val newParameters = networkMapClient.getNetworkParameters(update.newParametersHash) val newSignedNetParams = networkMapClient.getNetworkParameters(update.newParametersHash)
logger.info("Downloaded new network parameters: $newParameters from the update: $update") val newNetParams = newSignedNetParams.verifiedNetworkMapCert(networkMapClient.trustedRoot)
newNetworkParameters = Pair(update, newParameters) logger.info("Downloaded new network parameters: $newNetParams from the update: $update")
newNetworkParameters = Pair(update, newSignedNetParams)
val updateInfo = ParametersUpdateInfo( val updateInfo = ParametersUpdateInfo(
update.newParametersHash, update.newParametersHash,
newParameters.verifiedNetworkMapCert(networkMapClient.trustedRoot), newNetParams,
update.description, update.description,
update.updateDeadline) update.updateDeadline)
parametersUpdatesTrack.onNext(updateInfo) 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") 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. // 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. // 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. // 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) { if (parametersHash == newParametersHash) {
// The latest parameters have priority. // The latest parameters have priority.
newParams.serialize() signedNewNetParams.serialize()
.open() .open()
.copyTo(baseDirectory / NETWORK_PARAMS_UPDATE_FILE_NAME, StandardCopyOption.REPLACE_EXISTING) .copyTo(baseDirectory / NETWORK_PARAMS_UPDATE_FILE_NAME, StandardCopyOption.REPLACE_EXISTING)
networkMapClient.ackNetworkParametersUpdate(sign(parametersHash)) networkMapClient.ackNetworkParametersUpdate(sign(parametersHash))
logger.info("Accepted network parameter update $update: $newNetParams")
} else { } else {
throw IllegalArgumentException("Refused to accept parameters with hash $parametersHash because network map " + throw IllegalArgumentException("Refused to accept parameters with hash $parametersHash because network map " +
"advertises update with hash $newParametersHash. Please check newest version") "advertises update with hash $newParametersHash. Please check newest version")