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",
updateDeadline = updateDeadline
)
)
)
))
updates.expectEvents(isStrict = true) {
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]
*/
// TODO "Active" is the wrong word here
fun getActiveNodeInfoHashes(): List<SecureHash>
/**

View File

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

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

View File

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

View File

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

View File

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

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

View File

@ -9,73 +9,52 @@ import java.time.Instant
import javax.persistence.*
@Entity
@Table(name = "certificate_revocation_request")
class CertificateRevocationRequestEntity(
@Table(name = "cert_revocation_request")
data class CertificateRevocationRequestEntity(
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
val id: Long? = null,
@Column(name = "request_id", length = 256, nullable = false, unique = true)
@Column(name = "request_id", length = 64, nullable = false, unique = true)
val requestId: String,
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "certificate_data")
@JoinColumn(name = "cert_data", nullable = false)
val certificateData: CertificateDataEntity,
@Column(name = "certificate_serial_number", nullable = false)
@Column(name = "cert_serial_number", nullable = false, columnDefinition = "NUMERIC(28)")
val certificateSerialNumber: BigInteger,
@Column(name = "legal_name", length = 256, nullable = false)
@Convert(converter = CordaX500NameAttributeConverter::class)
val legalName: CordaX500Name,
// Setting [columnDefinition] is a work around for a hibernate problem when using SQL database.
// TODO: Remove this when we find out the cause of the problem.
@Audited
@Column(name = "status", nullable = false)
@Column(name = "status", length = 16, nullable = false, columnDefinition = "NVARCHAR(16)")
@Enumerated(EnumType.STRING)
val status: RequestStatus = RequestStatus.NEW,
@Column(name = "reporter", nullable = false, length = 512)
@Column(name = "reporter", nullable = false, length = 64)
val reporter: String,
@Audited
@Column(name = "modified_by", nullable = false, length = 512)
@Column(name = "modified_by", nullable = false, length = 64)
val modifiedBy: String,
@Audited
@Column(name = "modified_at", nullable = false)
val modifiedAt: Instant = Instant.now(),
// Setting [columnDefinition] is a work around for a hibernate problem when using SQL database.
// TODO: Remove this when we find out the cause of the problem.
@Audited
@Column(name = "revocation_reason", nullable = false)
@Column(name = "revocation_reason", length = 32, nullable = false, columnDefinition = "NVARCHAR(32)")
@Enumerated(EnumType.STRING)
val revocationReason: CRLReason,
@Audited
@Column(name = "remark", length = 256)
val remark: String? = null
) {
fun copy(requestId: String = this.requestId,
certificateData: CertificateDataEntity = this.certificateData,
certificateSerialNumber: BigInteger = this.certificateSerialNumber,
status: RequestStatus = this.status,
legalName: CordaX500Name = this.legalName,
reporter: String = this.reporter,
modifiedBy: String = this.modifiedBy,
modifiedAt: Instant = this.modifiedAt,
revocationReason: CRLReason = this.revocationReason,
remark: String? = this.remark): CertificateRevocationRequestEntity {
return CertificateRevocationRequestEntity(
id = this.id,
requestId = requestId,
certificateData = certificateData,
certificateSerialNumber = certificateSerialNumber,
status = status,
legalName = legalName,
reporter = reporter,
modifiedBy = modifiedBy,
modifiedAt = modifiedAt,
revocationReason = revocationReason,
remark = remark
)
}
}
)

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.CertificateStatus
import com.r3.corda.networkmanage.common.persistence.RequestStatus
import com.r3.corda.networkmanage.common.utils.buildCertPath
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name
import net.corda.nodeapi.internal.crypto.x509Certificates
@ -26,10 +25,10 @@ import java.time.Instant
import javax.persistence.*
@Entity
@Table(name = "certificate_signing_request", indexes = arrayOf(Index(name = "IDX_PUB_KEY_HASH", columnList = "public_key_hash")))
@Table(name = "cert_signing_request", indexes = arrayOf(Index(name = "IDX__CSR__PKH", columnList = "public_key_hash")))
data class CertificateSigningRequestEntity(
@Id
@Column(name = "request_id", length = 64)
@Column(name = "request_id", length = 64, nullable = false)
val requestId: String,
// TODO: Store X500Name with a proper schema.
@ -37,19 +36,21 @@ data class CertificateSigningRequestEntity(
@Convert(converter = CordaX500NameAttributeConverter::class)
val legalName: CordaX500Name?,
@Column(name = "public_key_hash", length = 64)
@Column(name = "public_key_hash", length = 64, nullable = false)
val publicKeyHash: String,
// Setting [columnDefinition] is a work around for a hibernate problem when using SQL database.
// TODO: Remove this when we find out the cause of the problem.
@Audited
@Column(name = "status", nullable = false)
@Column(name = "status", length = 16, nullable = false, columnDefinition = "NVARCHAR(16)")
@Enumerated(EnumType.STRING)
val status: RequestStatus = RequestStatus.NEW,
@Audited
@Column(name = "modified_by", length = 512)
@Column(name = "modified_by", length = 64, nullable = false)
val modifiedBy: String,
@Audited
// TODO: Use audit framework instead.
@Column(name = "modified_at", nullable = false)
val modifiedAt: Instant = Instant.now(),
@ -62,10 +63,11 @@ data class CertificateSigningRequestEntity(
@Lob
@Column(name = "request_bytes", nullable = false)
val requestBytes: ByteArray,
@Convert(converter = PKCS10CertificationRequestConverter::class)
val request: PKCS10CertificationRequest,
@ManyToOne
@JoinColumn(name = "private_network", foreignKey = ForeignKey(name = "FK_CSR_PN"))
@JoinColumn(name = "private_network", foreignKey = ForeignKey(name = "FK__CSR__PN"))
val privateNetwork: PrivateNetworkEntity? = null
) {
fun toCertificateSigningRequest(): CertificateSigningRequest {
@ -74,70 +76,53 @@ data class CertificateSigningRequestEntity(
legalName = legalName,
publicKeyHash = SecureHash.parse(publicKeyHash),
status = status,
request = request(),
request = request,
remark = remark,
modifiedBy = modifiedBy,
certData = certificateData?.toCertificateData()
)
}
private fun request() = PKCS10CertificationRequest(requestBytes)
}
@Entity
@Table(name = "certificate_data")
@Table(name = "cert_data")
data class CertificateDataEntity(
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
val id: Long? = null,
@Column(name = "certificate_status")
// Setting [columnDefinition] is a work around for a hibernate problem when using SQL database.
// TODO: Remove this when we find out the cause of the problem.
@Column(name = "cert_status", length = 16, nullable = false, columnDefinition = "NVARCHAR(16)")
@Enumerated(EnumType.STRING)
val certificateStatus: CertificateStatus,
@Lob
@Column(name = "certificate_path_bytes")
val certificatePathBytes: ByteArray,
@Column(name = "cert_path_bytes", nullable = false)
@Convert(converter = CertPathConverter::class)
val certPath: CertPath,
@OneToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "certificate_signing_request", foreignKey = ForeignKey(name = "FK__cert_data__cert_sign_req"))
@JoinColumn(name = "cert_signing_request", foreignKey = ForeignKey(name = "FK__CD__CSR"), nullable = false)
val certificateSigningRequest: CertificateSigningRequestEntity,
@Column(name = "certificate_serial_number", unique = true)
@Column(name = "cert_serial_number", unique = true, nullable = false, columnDefinition = "NUMERIC(28)")
val certificateSerialNumber: BigInteger
) {
fun toCertificateData(): CertificateData {
return CertificateData(
certStatus = certificateStatus,
certPath = toCertificatePath()
)
}
fun toCertificateData(): CertificateData = CertificateData(certificateStatus, certPath)
val legalName: CordaX500Name get() {
return CordaX500Name.build(toCertificatePath().x509Certificates[0].subjectX500Principal)
return CordaX500Name.build(certPath.x509Certificates[0].subjectX500Principal)
}
fun copy(certificateStatus: CertificateStatus = this.certificateStatus,
certificatePathBytes: ByteArray = this.certificatePathBytes,
certificateSigningRequest: CertificateSigningRequestEntity = this.certificateSigningRequest,
certificateSerialNumber: BigInteger = this.certificateSerialNumber): CertificateDataEntity {
return CertificateDataEntity(
id = this.id,
certificateStatus = certificateStatus,
certificatePathBytes = certificatePathBytes,
certificateSigningRequest = certificateSigningRequest,
certificateSerialNumber = certificateSerialNumber)
}
private fun toCertificatePath(): CertPath = buildCertPath(certificatePathBytes)
}
@Entity
@Table(name = "private_network")
data class PrivateNetworkEntity(
@Id
@Column(name = "id")
@Column(name = "id", length = 64)
val networkId: String,
@Column(name = "name")
@Column(name = "name", length = 255, nullable = false)
val networkName: String
)

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

View File

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

View File

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

View File

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

View File

@ -12,15 +12,12 @@ package com.r3.corda.networkmanage.common.signer
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
import net.corda.core.crypto.SecureHash
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.serialize
import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.debug
import net.corda.nodeapi.internal.network.NetworkMap
import net.corda.nodeapi.internal.network.NetworkMapAndSigned
class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private val signer: Signer) {
private companion object {
val logger = contextLogger()
@ -30,41 +27,56 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private
* Signs the network map and latest network parameters if they haven't been signed yet.
*/
fun signNetworkMap() {
logger.debug("Fetching current network map...")
val currentNetworkMap = networkMapStorage.getActiveNetworkMap()
if (currentNetworkMap == null) {
logger.info("There is currently no network map")
} else {
logger.debug { "Current network map: $currentNetworkMap" }
}
val currentNetworkParameters = currentNetworkMap?.networkParameters
logger.debug("Current network map parameters: ${currentNetworkParameters?.toNetworkParameters()}")
logger.debug("Fetching node info hashes with VALID certificates...")
val nodeInfoHashes = networkMapStorage.getActiveNodeInfoHashes()
logger.debug("Retrieved node info hashes: $nodeInfoHashes")
val latestNetworkParameters = networkMapStorage.getLatestNetworkParameters()
if (latestNetworkParameters == null) {
logger.debug("No network parameters present")
return
}
logger.debug { "Retrieved latest network parameters: ${latestNetworkParameters.toNetworkParameters()}" }
logger.debug { "Retrieved latest network parameters: ${latestNetworkParameters.networkParameters}" }
val parametersUpdate = networkMapStorage.getParametersUpdate()
logger.debug { "Retrieved parameters update: $parametersUpdate" }
// We persist signed parameters only if they were not persisted before (they are not in currentSignedNetworkMap as normal parameters or as an update)
check(parametersUpdate == null || parametersUpdate.networkParameters.hash == latestNetworkParameters.hash) {
"The latest network parameters are not the scheduled updated ones"
}
val activeNetworkMap = networkMapStorage.getActiveNetworkMap()
if (activeNetworkMap == null) {
logger.info("There is currently no network map")
} else {
logger.debug { "Current network map: ${activeNetworkMap.networkMap}" }
}
val activeNetworkParameters = activeNetworkMap?.networkParameters
logger.debug { "Current network map parameters: ${activeNetworkParameters?.networkParameters}" }
val nodeInfoHashes = networkMapStorage.getActiveNodeInfoHashes()
logger.debug { "Retrieved node info hashes:\n${nodeInfoHashes.joinToString("\n")}" }
// We persist signed parameters only if they were not persisted before (they are not in currentSignedNetworkMap as
// normal parameters or as an update)
if (!latestNetworkParameters.isSigned) {
signAndPersistNetworkParameters(latestNetworkParameters.toNetworkParameters())
signAndPersistNetworkParameters(latestNetworkParameters.networkParameters)
} else {
logger.debug { "No need to sign any network parameters as they're up-to-date" }
}
val parametersToNetworkMap = if (parametersUpdate?.flagDay == true || currentNetworkParameters == null) {
val parametersToNetworkMap = if (parametersUpdate?.flagDay == true || activeNetworkParameters == null) {
networkMapStorage.clearParametersUpdates()
latestNetworkParameters
} else currentNetworkParameters
val newNetworkMap = NetworkMap(nodeInfoHashes, SecureHash.parse(parametersToNetworkMap.parametersHash), parametersUpdate?.toNetMapUpdate())
} else {
activeNetworkParameters
}
val newNetworkMap = NetworkMap(
nodeInfoHashes,
SecureHash.parse(parametersToNetworkMap.hash),
parametersUpdate?.let { if (!it.flagDay) it.toParametersUpdate() else null })
logger.debug { "Potential new network map: $newNetworkMap" }
if (currentNetworkMap?.toNetworkMap() != newNetworkMap) {
val netNetworkMapAndSigned = NetworkMapAndSigned(newNetworkMap) { signer.signBytes(it.bytes) }
networkMapStorage.saveNewActiveNetworkMap(netNetworkMapAndSigned)
if (activeNetworkMap?.networkMap != newNetworkMap) {
val newNetworkMapAndSigned = NetworkMapAndSigned(newNetworkMap) { signer.signBytes(it.bytes) }
networkMapStorage.saveNewActiveNetworkMap(newNetworkMapAndSigned)
logger.info("Signed new network map: $newNetworkMap")
} else {
logger.debug("Current network map is up-to-date")

View File

@ -22,7 +22,6 @@ import com.r3.corda.networkmanage.doorman.webservice.NetworkMapWebService
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
import net.corda.core.crypto.SecureHash
import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.persistence.CordaPersistence
@ -60,7 +59,7 @@ class NetworkManagementServer(dataSourceProperties: Properties, databaseConfig:
private fun getNetworkMapService(config: NetworkMapConfig, signer: LocalSigner?): NetworkMapWebService {
val localNetworkMapSigner = signer?.let { NetworkMapSigner(networkMapStorage, it) }
val latestParameters = networkMapStorage.getLatestNetworkParameters()?.toNetworkParameters() ?:
val latestParameters = networkMapStorage.getLatestNetworkParameters()?.networkParameters ?:
throw IllegalStateException("No network parameters were found. Please upload new network parameters before starting network map service")
logger.info("Starting network map service with network parameters: $latestParameters")
localNetworkMapSigner?.signAndPersistNetworkParameters(latestParameters)
@ -153,7 +152,7 @@ class NetworkManagementServer(dataSourceProperties: Properties, databaseConfig:
private fun handleSetNetworkParameters(setNetParams: NetworkParametersCmd.Set) {
logger.info("maxMessageSize is not currently wired in the nodes")
val activeNetParams = networkMapStorage.getActiveNetworkMap()?.networkParameters?.toNetworkParameters()
val activeNetParams = networkMapStorage.getActiveNetworkMap()?.networkParameters?.networkParameters
if (activeNetParams == null) {
require(setNetParams.parametersUpdate == null) {
"'parametersUpdate' specified in network parameters file but there are no network parameters to update"
@ -163,19 +162,19 @@ class NetworkManagementServer(dataSourceProperties: Properties, databaseConfig:
networkMapStorage.saveNetworkParameters(initialNetParams, null)
} else {
val parametersUpdate = requireNotNull(setNetParams.parametersUpdate) {
"'parametersUpdate' not specified in network parameters file but there is already an active set of network parameters."
"'parametersUpdate' not specified in network parameters file but there is already an active set of network parameters"
}
setNetParams.checkCompatibility(activeNetParams)
val latestNetParams = checkNotNull(networkMapStorage.getLatestNetworkParameters()?.toNetworkParameters()) {
val latestNetParams = checkNotNull(networkMapStorage.getLatestNetworkParameters()?.networkParameters) {
"Something has gone wrong! We have an active set of network parameters ($activeNetParams) but apparently no latest network parameters!"
}
// It's not necessary that latestNetParams is the current active network parameters. It can be the network
// parameters from a previous update attempt which has't activated yet. We still take the epoch value for this
// new set from latestNetParams to make sure the advertised update attempts have incrementing epochs.
// This has the implication that active network parameters may have gaps in their epochs.
// This has the implication that *active* network parameters may have gaps in their epochs.
val newNetParams = setNetParams.toNetworkParameters(modifiedTime = Instant.now(), epoch = latestNetParams.epoch + 1)
logger.info("Enabling update to network parameters:\n$newNetParams\n$parametersUpdate")
@ -205,12 +204,12 @@ class NetworkManagementServer(dataSourceProperties: Properties, databaseConfig:
"Parameters we are trying to switch to haven't been signed yet"
}
// TODO This check is stil not good enough as when it comes to signing, the NetworkMapSigner will just accept
check(latestNetParamsEntity.parametersHash == parametersUpdate.networkParameters.parametersHash) {
"The latest network parameters is not the scheduled one:\n${latestNetParamsEntity.toNetworkParameters()}\n${parametersUpdate.toParametersUpdate()}"
check(latestNetParamsEntity.hash == parametersUpdate.networkParameters.hash) {
"The latest network parameters is not the scheduled one:\n${latestNetParamsEntity.networkParameters}\n${parametersUpdate.toParametersUpdate()}"
}
logger.info("Flag day has occurred, however the new network parameters won't be active until the new network map is signed.\n" +
"Switching from: $activeNetParams\nTo: ${latestNetParamsEntity.toNetworkParameters()}")
networkMapStorage.setFlagDay(SecureHash.parse(parametersUpdate.networkParameters.parametersHash))
"Switching from: $activeNetParams\nTo: ${latestNetParamsEntity.networkParameters}")
networkMapStorage.setFlagDay(SecureHash.parse(parametersUpdate.networkParameters.hash))
}
private fun handleCancelUpdate() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -119,7 +119,7 @@ class NetworkMapWebServiceTest {
it.start()
val signedNetworkMapResponse = it.doGet<SignedNetworkMap>("")
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 networkParameterHash: SecureHash,
val parametersUpdate: ParametersUpdate?
)
) {
override fun toString(): String {
return """${NetworkMap::class.java.simpleName}(nodeInfoHashes=
${nodeInfoHashes.joinToString("\n")}
networkParameterHash=$networkParameterHash
parametersUpdate=$parametersUpdate)"""
}
}
/**
* Data class representing scheduled network parameters update.

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.
if (signedParametersFromFile == null) { // Node joins for the first time.
downloadParameters(trustRoot, advertisedParametersHash)
}
else if (signedParametersFromFile.raw.hash == advertisedParametersHash) { // Restarted with the same parameters.
} else if (signedParametersFromFile.raw.hash == advertisedParametersHash) { // Restarted with the same parameters.
signedParametersFromFile.verifiedNetworkMapCert(trustRoot)
} else { // Update case.
readParametersUpdate(advertisedParametersHash, signedParametersFromFile.raw.hash).verifiedNetworkMapCert(trustRoot)
@ -74,6 +73,7 @@ class NetworkParametersReader(private val trustRoot: X509Certificate,
"Please update node to use correct network parameters file.")
}
parametersUpdateFile.moveTo(networkParamsFile, StandardCopyOption.REPLACE_EXISTING)
logger.info("Scheduled update to network parameters has occurred - node now updated to these new parameters.")
return signedUpdatedParameters
}

View File

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

View File

@ -157,12 +157,13 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
// This update was handled already.
return
}
val newParameters = networkMapClient.getNetworkParameters(update.newParametersHash)
logger.info("Downloaded new network parameters: $newParameters from the update: $update")
newNetworkParameters = Pair(update, newParameters)
val newSignedNetParams = networkMapClient.getNetworkParameters(update.newParametersHash)
val newNetParams = newSignedNetParams.verifiedNetworkMapCert(networkMapClient.trustedRoot)
logger.info("Downloaded new network parameters: $newNetParams from the update: $update")
newNetworkParameters = Pair(update, newSignedNetParams)
val updateInfo = ParametersUpdateInfo(
update.newParametersHash,
newParameters.verifiedNetworkMapCert(networkMapClient.trustedRoot),
newNetParams,
update.description,
update.updateDeadline)
parametersUpdatesTrack.onNext(updateInfo)
@ -172,15 +173,17 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal,
networkMapClient ?: throw IllegalStateException("Network parameters updates are not support without compatibility zone configured")
// TODO This scenario will happen if node was restarted and didn't download parameters yet, but we accepted them.
// Add persisting of newest parameters from update.
val (_, newParams) = requireNotNull(newNetworkParameters) { "Couldn't find parameters update for the hash: $parametersHash" }
val (update, signedNewNetParams) = requireNotNull(newNetworkParameters) { "Couldn't find parameters update for the hash: $parametersHash" }
// We should check that we sign the right data structure hash.
val newParametersHash = newParams.verifiedNetworkMapCert(networkMapClient.trustedRoot).serialize().hash
val newNetParams = signedNewNetParams.verifiedNetworkMapCert(networkMapClient.trustedRoot)
val newParametersHash = newNetParams.serialize().hash
if (parametersHash == newParametersHash) {
// The latest parameters have priority.
newParams.serialize()
signedNewNetParams.serialize()
.open()
.copyTo(baseDirectory / NETWORK_PARAMS_UPDATE_FILE_NAME, StandardCopyOption.REPLACE_EXISTING)
networkMapClient.ackNetworkParametersUpdate(sign(parametersHash))
logger.info("Accepted network parameter update $update: $newNetParams")
} else {
throw IllegalArgumentException("Refused to accept parameters with hash $parametersHash because network map " +
"advertises update with hash $newParametersHash. Please check newest version")