mirror of
https://github.com/corda/corda.git
synced 2024-12-28 16:58:55 +00:00
ENT-1557 - Network Map returns 404 not found for current node info advertised in the network map (#511)
* * added is current and timestamp to the node info table * getNodeInfoHashes returns all "current" node info hashes * TODO: network map should return 404 if receive old node info request * TODO: database migration integration test * fix compilation error * * removed unnecessary unique constraint * rebase and tidy up liquid base xml * address PR issues * address PR issues * address PR issues
This commit is contained in:
parent
341e060424
commit
a435c23e19
@ -48,6 +48,14 @@ The built file will appear in
|
|||||||
network-management/capsule-hsm-cert-generator/build/libs/hsm-cert-generator-<VERSION>.jar
|
network-management/capsule-hsm-cert-generator/build/libs/hsm-cert-generator-<VERSION>.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
In order to set the desired logging level the system properties need to be used.
|
||||||
|
Appropriate system properties can be set at the execution time.
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
java -DdefaultLogLevel=TRACE -DconsoleLogLevel=TRACE -jar doorman-<version>.jar --config-file <config file>
|
||||||
|
```
|
||||||
|
|
||||||
#Configuring network management service
|
#Configuring network management service
|
||||||
### Local signing
|
### Local signing
|
||||||
|
|
||||||
@ -98,7 +106,8 @@ The doorman service can use JIRA to manage the certificate signing request appro
|
|||||||
signInterval = 10000
|
signInterval = 10000
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
`cacheTimeout`(ms) indicates how often the network map should poll the database for a newly signed network map. This is also added to the HTTP response header to set the node's network map update frequency.
|
`cacheTimeout`(ms) determines how often the server should poll the database for a newly signed network map and also how often nodes should poll for a new network map (by including this value in the HTTP response header). **This is not how often changes to the network map are signed, which is a different process.**
|
||||||
|
|
||||||
`signInterval`(ms) this is only relevant when local signer is enabled. The signer poll the database according to the `signInterval`, and create a new network map if the collection of node info hashes is different from the current network map.
|
`signInterval`(ms) this is only relevant when local signer is enabled. The signer poll the database according to the `signInterval`, and create a new network map if the collection of node info hashes is different from the current network map.
|
||||||
|
|
||||||
##Example config file
|
##Example config file
|
||||||
@ -212,10 +221,12 @@ We can now restart the network management server with both doorman and network m
|
|||||||
java -jar doorman-<version>.jar --config-file <config file> --update-network-parameters network-parameters.conf
|
java -jar doorman-<version>.jar --config-file <config file> --update-network-parameters network-parameters.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7. Logs
|
### 7. Archive policy
|
||||||
In order to set the desired logging level the system properties need to be used.
|
The ``node_info`` and ``network_map`` table are designed to retain all historical data for auditing purposes and will grow over time.
|
||||||
Appropriate system properties can be set at the execution time.
|
**It is recommended to monitor the space usage and archive these tables according to the data retention policy.**
|
||||||
Example:
|
|
||||||
|
Run the following SQL script to archive the node info table (change the timestamp according to the archive policy):
|
||||||
```
|
```
|
||||||
java -DdefaultLogLevel=TRACE -DconsoleLogLevel=TRACE -jar doorman-<version>.jar --config-file <config file>
|
delect from node_info where is_current = false and published_at < '2018-03-12'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -27,13 +27,11 @@ interface NetworkMapStorage {
|
|||||||
fun getCurrentNetworkMap(): SignedNetworkMap?
|
fun getCurrentNetworkMap(): SignedNetworkMap?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves node info hashes where the certificate status matches [certificateStatus].
|
* Retrieves node info hashes where [isCurrent] is true and the certificate status is [CertificateStatus.VALID]
|
||||||
*
|
*
|
||||||
* @param certificateStatus certificate status to be used in the node info filtering. Node info hash is returned
|
* @return list of current and valid node info hashes.
|
||||||
* in the result collection if its certificate status matches [certificateStatus].
|
|
||||||
* @return list of node info hashes satisfying the filtering criteria given by [certificateStatus].
|
|
||||||
*/
|
*/
|
||||||
fun getNodeInfoHashes(certificateStatus: CertificateStatus): List<SecureHash>
|
fun getActiveNodeInfoHashes(): List<SecureHash>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persists a new instance of the signed network map.
|
* Persists a new instance of the signed network map.
|
||||||
@ -51,6 +49,7 @@ interface NetworkMapStorage {
|
|||||||
/**
|
/**
|
||||||
* Retrieve the network parameters of the current network map, or null if there's no network map.
|
* Retrieve the network parameters of the current network map, or null if there's no network map.
|
||||||
*/
|
*/
|
||||||
|
// TODO: Remove this method. We should get the "current" network parameter by using the the hash in the network map and use the [getSignedNetworkParameters] method.
|
||||||
fun getNetworkParametersOfNetworkMap(): SignedNetworkParameters?
|
fun getNetworkParametersOfNetworkMap(): SignedNetworkParameters?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,19 +24,23 @@ import javax.persistence.criteria.CriteriaBuilder
|
|||||||
import javax.persistence.criteria.Path
|
import javax.persistence.criteria.Path
|
||||||
import javax.persistence.criteria.Predicate
|
import javax.persistence.criteria.Predicate
|
||||||
|
|
||||||
fun <T> DatabaseTransaction.singleRequestWhere(clazz: Class<T>, predicate: (CriteriaBuilder, Path<T>) -> Predicate): T? {
|
inline fun <reified T> DatabaseTransaction.singleEntityWhere(predicate: (CriteriaBuilder, Path<T>) -> Predicate): T? {
|
||||||
val builder = session.criteriaBuilder
|
return getEntitiesWhere(predicate).firstOrNull()
|
||||||
val criteriaQuery = builder.createQuery(clazz)
|
|
||||||
val query = criteriaQuery.from(clazz).run {
|
|
||||||
criteriaQuery.where(predicate(builder, this))
|
|
||||||
}
|
|
||||||
return session.createQuery(query).setLockMode(LockModeType.PESSIMISTIC_WRITE).resultList.firstOrNull()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> DatabaseTransaction.deleteRequest(clazz: Class<T>, predicate: (CriteriaBuilder, Path<T>) -> Predicate): Int {
|
inline fun <reified T> DatabaseTransaction.getEntitiesWhere(predicate: (CriteriaBuilder, Path<T>) -> Predicate): List<T> {
|
||||||
val builder = session.criteriaBuilder
|
val builder = session.criteriaBuilder
|
||||||
val criteriaDelete = builder.createCriteriaDelete(clazz)
|
val criteriaQuery = builder.createQuery(T::class.java)
|
||||||
val delete = criteriaDelete.from(clazz).run {
|
val query = criteriaQuery.from(T::class.java).run {
|
||||||
|
criteriaQuery.where(predicate(builder, this))
|
||||||
|
}
|
||||||
|
return session.createQuery(query).setLockMode(LockModeType.PESSIMISTIC_WRITE).resultList
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> DatabaseTransaction.deleteEntity(predicate: (CriteriaBuilder, Path<T>) -> Predicate): Int {
|
||||||
|
val builder = session.criteriaBuilder
|
||||||
|
val criteriaDelete = builder.createCriteriaDelete(T::class.java)
|
||||||
|
val delete = criteriaDelete.from(T::class.java).run {
|
||||||
criteriaDelete.where(predicate(builder, this))
|
criteriaDelete.where(predicate(builder, this))
|
||||||
}
|
}
|
||||||
return session.createQuery(delete).executeUpdate()
|
return session.createQuery(delete).executeUpdate()
|
||||||
|
@ -40,7 +40,7 @@ class PersistentCertificateRevocationListStorage(private val database: CordaPers
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun revokeCertificate(certificateSerialNumber: BigInteger, time: Instant, transaction: DatabaseTransaction) {
|
private fun revokeCertificate(certificateSerialNumber: BigInteger, time: Instant, transaction: DatabaseTransaction) {
|
||||||
val revocation = transaction.singleRequestWhere(CertificateRevocationRequestEntity::class.java) { builder, path ->
|
val revocation = transaction.singleEntityWhere<CertificateRevocationRequestEntity> { builder, path ->
|
||||||
builder.equal(path.get<BigInteger>(CertificateRevocationRequestEntity::certificateSerialNumber.name), certificateSerialNumber)
|
builder.equal(path.get<BigInteger>(CertificateRevocationRequestEntity::certificateSerialNumber.name), certificateSerialNumber)
|
||||||
}
|
}
|
||||||
revocation ?: throw IllegalStateException("The certificate revocation request for $certificateSerialNumber does not exist")
|
revocation ?: throw IllegalStateException("The certificate revocation request for $certificateSerialNumber does not exist")
|
||||||
|
@ -13,7 +13,7 @@ class PersistentCertificateRevocationRequestStorage(private val database: CordaP
|
|||||||
override fun saveRevocationRequest(certificateSerialNumber: BigInteger, reason: CRLReason, reporter: String): String {
|
override fun saveRevocationRequest(certificateSerialNumber: BigInteger, reason: CRLReason, reporter: String): String {
|
||||||
return database.transaction(TransactionIsolationLevel.SERIALIZABLE) {
|
return database.transaction(TransactionIsolationLevel.SERIALIZABLE) {
|
||||||
// Check if there is an entry for the given certificate serial number
|
// Check if there is an entry for the given certificate serial number
|
||||||
val revocation = singleRequestWhere(CertificateRevocationRequestEntity::class.java) { builder, path ->
|
val revocation = singleEntityWhere<CertificateRevocationRequestEntity> { builder, path ->
|
||||||
val serialNumberEqual = builder.equal(path.get<BigInteger>(CertificateRevocationRequestEntity::certificateSerialNumber.name), certificateSerialNumber)
|
val serialNumberEqual = builder.equal(path.get<BigInteger>(CertificateRevocationRequestEntity::certificateSerialNumber.name), certificateSerialNumber)
|
||||||
val statusNotEqualRejected = builder.notEqual(path.get<RequestStatus>(CertificateRevocationRequestEntity::status.name), RequestStatus.REJECTED)
|
val statusNotEqualRejected = builder.notEqual(path.get<RequestStatus>(CertificateRevocationRequestEntity::status.name), RequestStatus.REJECTED)
|
||||||
builder.and(serialNumberEqual, statusNotEqualRejected)
|
builder.and(serialNumberEqual, statusNotEqualRejected)
|
||||||
@ -21,7 +21,7 @@ class PersistentCertificateRevocationRequestStorage(private val database: CordaP
|
|||||||
if (revocation != null) {
|
if (revocation != null) {
|
||||||
revocation.requestId
|
revocation.requestId
|
||||||
} else {
|
} else {
|
||||||
val certificateData = singleRequestWhere(CertificateDataEntity::class.java) { builder, path ->
|
val certificateData = singleEntityWhere<CertificateDataEntity> { builder, path ->
|
||||||
val serialNumberEqual = builder.equal(path.get<BigInteger>(CertificateDataEntity::certificateSerialNumber.name), certificateSerialNumber)
|
val serialNumberEqual = builder.equal(path.get<BigInteger>(CertificateDataEntity::certificateSerialNumber.name), certificateSerialNumber)
|
||||||
val statusEqualValid = builder.equal(path.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name), CertificateStatus.VALID)
|
val statusEqualValid = builder.equal(path.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name), CertificateStatus.VALID)
|
||||||
builder.and(serialNumberEqual, statusEqualValid)
|
builder.and(serialNumberEqual, statusEqualValid)
|
||||||
@ -90,7 +90,7 @@ class PersistentCertificateRevocationRequestStorage(private val database: CordaP
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getRevocationRequestEntity(requestId: String): CertificateRevocationRequestEntity? = database.transaction {
|
private fun getRevocationRequestEntity(requestId: String): CertificateRevocationRequestEntity? = database.transaction {
|
||||||
singleRequestWhere(CertificateRevocationRequestEntity::class.java) { builder, path ->
|
singleEntityWhere { builder, path ->
|
||||||
builder.equal(path.get<String>(CertificateRevocationRequestEntity::requestId.name), requestId)
|
builder.equal(path.get<String>(CertificateRevocationRequestEntity::requestId.name), requestId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ class PersistentCertificateSigningRequestStorage(private val database: CordaPers
|
|||||||
|
|
||||||
override fun putCertificatePath(requestId: String, certPath: CertPath, signedBy: String) {
|
override fun putCertificatePath(requestId: String, certPath: CertPath, signedBy: String) {
|
||||||
return database.transaction(TransactionIsolationLevel.SERIALIZABLE) {
|
return database.transaction(TransactionIsolationLevel.SERIALIZABLE) {
|
||||||
val request = singleRequestWhere(CertificateSigningRequestEntity::class.java) { builder, path ->
|
val request = singleEntityWhere<CertificateSigningRequestEntity> { builder, path ->
|
||||||
val requestIdEq = builder.equal(path.get<String>(CertificateSigningRequestEntity::requestId.name), requestId)
|
val requestIdEq = builder.equal(path.get<String>(CertificateSigningRequestEntity::requestId.name), requestId)
|
||||||
val statusEq = builder.equal(path.get<String>(CertificateSigningRequestEntity::status.name), RequestStatus.APPROVED)
|
val statusEq = builder.equal(path.get<String>(CertificateSigningRequestEntity::status.name), RequestStatus.APPROVED)
|
||||||
builder.and(requestIdEq, statusEq)
|
builder.and(requestIdEq, statusEq)
|
||||||
@ -89,7 +89,7 @@ class PersistentCertificateSigningRequestStorage(private val database: CordaPers
|
|||||||
|
|
||||||
private fun DatabaseTransaction.findRequest(requestId: String,
|
private fun DatabaseTransaction.findRequest(requestId: String,
|
||||||
requestStatus: RequestStatus? = null): CertificateSigningRequestEntity? {
|
requestStatus: RequestStatus? = null): CertificateSigningRequestEntity? {
|
||||||
return singleRequestWhere(CertificateSigningRequestEntity::class.java) { builder, path ->
|
return singleEntityWhere { builder, path ->
|
||||||
val idClause = builder.equal(path.get<String>(CertificateSigningRequestEntity::requestId.name), requestId)
|
val idClause = builder.equal(path.get<String>(CertificateSigningRequestEntity::requestId.name), requestId)
|
||||||
if (requestStatus == null) {
|
if (requestStatus == null) {
|
||||||
idClause
|
idClause
|
||||||
|
@ -57,15 +57,17 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNodeInfoHashes(certificateStatus: CertificateStatus): List<SecureHash> {
|
override fun getActiveNodeInfoHashes(): List<SecureHash> {
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
val builder = session.criteriaBuilder
|
val builder = session.criteriaBuilder
|
||||||
val query = builder.createQuery(String::class.java).run {
|
val query = builder.createQuery(String::class.java).run {
|
||||||
from(NodeInfoEntity::class.java).run {
|
from(NodeInfoEntity::class.java).run {
|
||||||
select(get<String>(NodeInfoEntity::nodeInfoHash.name))
|
val certStatusExpression = get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name)
|
||||||
.where(builder.equal(get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name)
|
.get<CertificateDataEntity>(CertificateSigningRequestEntity::certificateData.name)
|
||||||
.get<CertificateDataEntity>(CertificateSigningRequestEntity::certificateData.name)
|
.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name)
|
||||||
.get<CertificateStatus>(CertificateDataEntity::certificateStatus.name), certificateStatus))
|
val certStatusEq = builder.equal(certStatusExpression, CertificateStatus.VALID)
|
||||||
|
val isCurrentNodeInfo = builder.isTrue(get<Boolean>(NodeInfoEntity::isCurrent.name))
|
||||||
|
select(get<String>(NodeInfoEntity::nodeInfoHash.name)).where(builder.and(certStatusEq, isCurrentNodeInfo))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
session.createQuery(query).resultList.map { SecureHash.parse(it) }
|
session.createQuery(query).resultList.map { SecureHash.parse(it) }
|
||||||
@ -117,7 +119,7 @@ class PersistentNetworkMapStorage(private val database: CordaPersistence) : Netw
|
|||||||
|
|
||||||
private fun getNetworkParametersEntity(parameterHash: String): NetworkParametersEntity? {
|
private fun getNetworkParametersEntity(parameterHash: String): NetworkParametersEntity? {
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
singleRequestWhere(NetworkParametersEntity::class.java) { builder, path ->
|
singleEntityWhere { builder, path ->
|
||||||
builder.equal(path.get<String>(NetworkParametersEntity::parametersHash.name), parameterHash)
|
builder.equal(path.get<String>(NetworkParametersEntity::parametersHash.name), parameterHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,29 +35,27 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
|||||||
nodeCaCert ?: throw IllegalArgumentException("Missing Node CA")
|
nodeCaCert ?: throw IllegalArgumentException("Missing Node CA")
|
||||||
return database.transaction {
|
return 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(), this)) {
|
val request = requireNotNull(getSignedRequestByPublicHash(nodeCaCert.publicKey.encoded.sha256())) {
|
||||||
"Node-info not registered with us"
|
"Node-info not registered with us"
|
||||||
}
|
}
|
||||||
request.certificateData?.certificateStatus.let {
|
request.certificateData?.certificateStatus.let {
|
||||||
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.
|
||||||
* Delete any previous [NodeInfoEntity] instance for this CSR
|
val existingNodeInfo = getEntitiesWhere<NodeInfoEntity> { builder, path ->
|
||||||
* Possibly it should be moved at the network signing process at the network signing process
|
val requestEq = builder.equal(path.get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name), request)
|
||||||
* as for a while the network map will have invalid entries (i.e. hashes for node info which have been
|
val isCurrent = builder.isTrue(path.get<Boolean>(NodeInfoEntity::isCurrent.name))
|
||||||
* removed). Either way, there will be a period of time when the network map data will be invalid
|
builder.and(requestEq, isCurrent)
|
||||||
* but it has been confirmed that this fact has been acknowledged at the design time and we are fine with it.
|
|
||||||
*/
|
|
||||||
deleteRequest(NodeInfoEntity::class.java) { builder, path ->
|
|
||||||
builder.equal(path.get<CertificateSigningRequestEntity>(NodeInfoEntity::certificateSigningRequest.name), request)
|
|
||||||
}
|
}
|
||||||
val hash = signedNodeInfo.raw.hash
|
existingNodeInfo.forEach { session.merge(it.copy(isCurrent = false)) }
|
||||||
|
|
||||||
|
val hash = signedNodeInfo.raw.hash
|
||||||
val hashedNodeInfo = NodeInfoEntity(
|
val hashedNodeInfo = NodeInfoEntity(
|
||||||
nodeInfoHash = hash.toString(),
|
nodeInfoHash = hash.toString(),
|
||||||
certificateSigningRequest = request,
|
certificateSigningRequest = request,
|
||||||
signedNodeInfoBytes = signedNodeInfo.serialize().bytes)
|
signedNodeInfoBytes = signedNodeInfo.serialize().bytes,
|
||||||
|
isCurrent = true)
|
||||||
session.save(hashedNodeInfo)
|
session.save(hashedNodeInfo)
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
@ -71,13 +69,13 @@ class PersistentNodeInfoStorage(private val database: CordaPersistence) : NodeIn
|
|||||||
|
|
||||||
override fun getCertificatePath(publicKeyHash: SecureHash): CertPath? {
|
override fun getCertificatePath(publicKeyHash: SecureHash): CertPath? {
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
val request = getSignedRequestByPublicHash(publicKeyHash, this)
|
val request = getSignedRequestByPublicHash(publicKeyHash)
|
||||||
request?.let { buildCertPath(it.certificateData!!.certificatePathBytes) }
|
request?.let { buildCertPath(it.certificateData!!.certificatePathBytes) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSignedRequestByPublicHash(publicKeyHash: SecureHash, transaction: DatabaseTransaction): CertificateSigningRequestEntity? {
|
private fun DatabaseTransaction.getSignedRequestByPublicHash(publicKeyHash: SecureHash): CertificateSigningRequestEntity? {
|
||||||
return transaction.singleRequestWhere(CertificateSigningRequestEntity::class.java) { builder, path ->
|
return singleEntityWhere { builder, path ->
|
||||||
val publicKeyEq = builder.equal(path.get<String>(CertificateSigningRequestEntity::publicKeyHash.name), publicKeyHash.toString())
|
val publicKeyEq = builder.equal(path.get<String>(CertificateSigningRequestEntity::publicKeyHash.name), publicKeyHash.toString())
|
||||||
val statusEq = builder.equal(path.get<RequestStatus>(CertificateSigningRequestEntity::status.name), RequestStatus.DONE)
|
val statusEq = builder.equal(path.get<RequestStatus>(CertificateSigningRequestEntity::status.name), RequestStatus.DONE)
|
||||||
builder.and(publicKeyEq, statusEq)
|
builder.and(publicKeyEq, statusEq)
|
||||||
|
@ -12,6 +12,8 @@ package com.r3.corda.networkmanage.common.persistence.entity
|
|||||||
|
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
|
import org.hibernate.annotations.CreationTimestamp
|
||||||
|
import java.time.Instant
|
||||||
import javax.persistence.*
|
import javax.persistence.*
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -22,13 +24,19 @@ class NodeInfoEntity(
|
|||||||
@Column(name = "node_info_hash", length = 64)
|
@Column(name = "node_info_hash", length = 64)
|
||||||
val nodeInfoHash: String = "",
|
val nodeInfoHash: String = "",
|
||||||
|
|
||||||
@OneToOne(optional = false, fetch = FetchType.LAZY)
|
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "certificate_signing_request", nullable = true)
|
@JoinColumn(name = "certificate_signing_request")
|
||||||
val certificateSigningRequest: CertificateSigningRequestEntity? = null,
|
val certificateSigningRequest: CertificateSigningRequestEntity,
|
||||||
|
|
||||||
@Lob
|
@Lob
|
||||||
@Column(name = "signed_node_info_bytes")
|
@Column(name = "signed_node_info_bytes")
|
||||||
val signedNodeInfoBytes: ByteArray
|
val signedNodeInfoBytes: ByteArray,
|
||||||
|
|
||||||
|
@Column(name="is_current")
|
||||||
|
val isCurrent: Boolean,
|
||||||
|
|
||||||
|
@Column(name = "published_at")
|
||||||
|
val publishedAt: Instant = Instant.now()
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Deserializes NodeInfoEntity.soignedNodeInfoBytes into the [SignedNodeInfo] instance
|
* Deserializes NodeInfoEntity.soignedNodeInfoBytes into the [SignedNodeInfo] instance
|
||||||
@ -36,13 +44,17 @@ class NodeInfoEntity(
|
|||||||
fun signedNodeInfo() = signedNodeInfoBytes.deserialize<SignedNodeInfo>()
|
fun signedNodeInfo() = signedNodeInfoBytes.deserialize<SignedNodeInfo>()
|
||||||
|
|
||||||
fun copy(nodeInfoHash: String = this.nodeInfoHash,
|
fun copy(nodeInfoHash: String = this.nodeInfoHash,
|
||||||
certificateSigningRequest: CertificateSigningRequestEntity? = this.certificateSigningRequest,
|
certificateSigningRequest: CertificateSigningRequestEntity = this.certificateSigningRequest,
|
||||||
signedNodeInfoBytes: ByteArray = this.signedNodeInfoBytes
|
signedNodeInfoBytes: ByteArray = this.signedNodeInfoBytes,
|
||||||
|
isCurrent: Boolean = this.isCurrent,
|
||||||
|
publishedAt: Instant = this.publishedAt
|
||||||
): NodeInfoEntity {
|
): NodeInfoEntity {
|
||||||
return NodeInfoEntity(
|
return NodeInfoEntity(
|
||||||
nodeInfoHash = nodeInfoHash,
|
nodeInfoHash = nodeInfoHash,
|
||||||
certificateSigningRequest = certificateSigningRequest,
|
certificateSigningRequest = certificateSigningRequest,
|
||||||
signedNodeInfoBytes = signedNodeInfoBytes
|
signedNodeInfoBytes = signedNodeInfoBytes,
|
||||||
|
isCurrent = isCurrent,
|
||||||
|
publishedAt = publishedAt
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
package com.r3.corda.networkmanage.common.signer
|
package com.r3.corda.networkmanage.common.signer
|
||||||
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.CertificateStatus
|
|
||||||
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
import com.r3.corda.networkmanage.common.persistence.NetworkMapStorage
|
||||||
import net.corda.core.internal.SignedDataWithCert
|
import net.corda.core.internal.SignedDataWithCert
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
@ -45,7 +44,7 @@ class NetworkMapSigner(private val networkMapStorage: NetworkMapStorage, private
|
|||||||
logger.debug("Fetching current network map...")
|
logger.debug("Fetching current network map...")
|
||||||
val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
val currentSignedNetworkMap = networkMapStorage.getCurrentNetworkMap()
|
||||||
logger.debug("Fetching node info hashes with VALID certificates...")
|
logger.debug("Fetching node info hashes with VALID certificates...")
|
||||||
val nodeInfoHashes = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID)
|
val nodeInfoHashes = networkMapStorage.getActiveNodeInfoHashes()
|
||||||
logger.debug("Retrieved node info hashes: $nodeInfoHashes")
|
logger.debug("Retrieved node info hashes: $nodeInfoHashes")
|
||||||
val newNetworkMap = NetworkMap(nodeInfoHashes, latestNetworkParameters.serialize().hash, null)
|
val newNetworkMap = NetworkMap(nodeInfoHashes, latestNetworkParameters.serialize().hash, null)
|
||||||
val serialisedNetworkMap = newNetworkMap.serialize()
|
val serialisedNetworkMap = newNetworkMap.serialize()
|
||||||
|
@ -51,11 +51,23 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
|
|||||||
const val NETWORK_MAP_PATH = "network-map"
|
const val NETWORK_MAP_PATH = "network-map"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val networkMapCache: LoadingCache<Boolean, Pair<SignedNetworkMap?, NetworkParameters?>> = CacheBuilder.newBuilder()
|
private val networkMapCache: LoadingCache<Boolean, CachedData> = CacheBuilder.newBuilder()
|
||||||
.expireAfterWrite(config.cacheTimeout, TimeUnit.MILLISECONDS)
|
.expireAfterWrite(config.cacheTimeout, TimeUnit.MILLISECONDS)
|
||||||
.build(CacheLoader.from { _ ->
|
.build(CacheLoader.from { _ -> networkMapStorage.getCurrentNetworkMap()?.let {
|
||||||
Pair(networkMapStorage.getCurrentNetworkMap(), networkMapStorage.getNetworkParametersOfNetworkMap()?.verified()) }
|
val networkMap = it.verified()
|
||||||
)
|
CachedData(it, networkMap.nodeInfoHashes.toSet(), networkMapStorage.getSignedNetworkParameters(networkMap.networkParameterHash)?.verified()) }
|
||||||
|
})
|
||||||
|
|
||||||
|
private val nodeInfoCache: LoadingCache<SecureHash, SignedNodeInfo> = CacheBuilder.newBuilder()
|
||||||
|
// TODO: Define cache retention policy.
|
||||||
|
.softValues()
|
||||||
|
.build(CacheLoader.from { key ->
|
||||||
|
key?.let { nodeInfoStorage.getNodeInfo(it) }
|
||||||
|
})
|
||||||
|
|
||||||
|
private val currentSignedNetworkMap: SignedNetworkMap? get() = networkMapCache.getOrNull(true)?.signedNetworkMap
|
||||||
|
private val currentNodeInfoHashes: Set<SecureHash> get() = networkMapCache.getOrNull(true)?.nodeInfoHashes ?: emptySet()
|
||||||
|
private val currentNetworkParameters: NetworkParameters? get() = networkMapCache.getOrNull(true)?.currentNetworkParameter
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("publish")
|
@Path("publish")
|
||||||
@ -84,13 +96,19 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
fun getNetworkMap(): Response = createResponse(networkMapCache.get(true).first, addCacheTimeout = true)
|
fun getNetworkMap(): Response = createResponse(currentSignedNetworkMap, addCacheTimeout = true)
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("node-info/{nodeInfoHash}")
|
@Path("node-info/{nodeInfoHash}")
|
||||||
fun getNodeInfo(@PathParam("nodeInfoHash") nodeInfoHash: String): Response {
|
fun getNodeInfo(@PathParam("nodeInfoHash") nodeInfoHash: String): Response {
|
||||||
val signedNodeInfo = nodeInfoStorage.getNodeInfo(SecureHash.parse(nodeInfoHash))
|
// Only serve node info if its in the current network map, otherwise return 404.
|
||||||
logger.trace { "Precessed node info request for hash: '$nodeInfoHash'" }
|
logger.trace { "Processing node info request for hash: '$nodeInfoHash'" }
|
||||||
|
val signedNodeInfo = if (SecureHash.parse(nodeInfoHash) in currentNodeInfoHashes) {
|
||||||
|
nodeInfoCache.getOrNull(SecureHash.parse(nodeInfoHash))
|
||||||
|
} else {
|
||||||
|
logger.trace { "Requested node info is not current, returning null." }
|
||||||
|
null
|
||||||
|
}
|
||||||
logger.trace { "Node Info: ${signedNodeInfo?.verified()}" }
|
logger.trace { "Node Info: ${signedNodeInfo?.verified()}" }
|
||||||
return createResponse(signedNodeInfo)
|
return createResponse(signedNodeInfo)
|
||||||
}
|
}
|
||||||
@ -113,7 +131,7 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun verifyNodeInfo(nodeInfo: NodeInfo) {
|
private fun verifyNodeInfo(nodeInfo: NodeInfo) {
|
||||||
val minimumPlatformVersion = networkMapCache.get(true).second?.minimumPlatformVersion
|
val minimumPlatformVersion = currentNetworkParameters?.minimumPlatformVersion
|
||||||
?: throw NetworkMapNotInitialisedException("Network parameters have not been initialised")
|
?: throw NetworkMapNotInitialisedException("Network parameters have not been initialised")
|
||||||
if (nodeInfo.platformVersion < minimumPlatformVersion) {
|
if (nodeInfo.platformVersion < minimumPlatformVersion) {
|
||||||
throw InvalidPlatformVersionException("Minimum platform version is $minimumPlatformVersion")
|
throw InvalidPlatformVersionException("Minimum platform version is $minimumPlatformVersion")
|
||||||
@ -134,4 +152,16 @@ class NetworkMapWebService(private val nodeInfoStorage: NodeInfoStorage,
|
|||||||
|
|
||||||
class NetworkMapNotInitialisedException(message: String?) : Exception(message)
|
class NetworkMapNotInitialisedException(message: String?) : Exception(message)
|
||||||
class InvalidPlatformVersionException(message: String?) : Exception(message)
|
class InvalidPlatformVersionException(message: String?) : Exception(message)
|
||||||
|
|
||||||
|
private data class CachedData(val signedNetworkMap: SignedNetworkMap, val nodeInfoHashes: Set<SecureHash>, val currentNetworkParameter: NetworkParameters?)
|
||||||
|
|
||||||
|
// Guava loading cache will throw if value is null, this helper method returns null instead.
|
||||||
|
// The loading cache will load the data from persistence again ignoring timeout if previous value was null.
|
||||||
|
private fun <K : Any, V : Any> LoadingCache<K, V>.getOrNull(key: K): V? {
|
||||||
|
return try {
|
||||||
|
get(key)
|
||||||
|
} catch (e: CacheLoader.InvalidCacheLoadException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,10 +100,18 @@
|
|||||||
<column name="node_info_hash" type="NVARCHAR(64)">
|
<column name="node_info_hash" type="NVARCHAR(64)">
|
||||||
<constraints nullable="false"/>
|
<constraints nullable="false"/>
|
||||||
</column>
|
</column>
|
||||||
<column name="signed_node_info_bytes" type="BLOB"/>
|
<column name="signed_node_info_bytes" type="BLOB">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
<column name="certificate_signing_request" type="NVARCHAR(64)">
|
<column name="certificate_signing_request" type="NVARCHAR(64)">
|
||||||
<constraints nullable="false"/>
|
<constraints nullable="false"/>
|
||||||
</column>
|
</column>
|
||||||
|
<column name="is_current" type="BOOLEAN">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="published_at" type="TIMESTAMP">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
</createTable>
|
</createTable>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
<changeSet author="R3.Corda" id="1520338500424-8">
|
<changeSet author="R3.Corda" id="1520338500424-8">
|
||||||
@ -135,9 +143,6 @@
|
|||||||
<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="certificate_signing_request" constraintName="UK_CD_CSR" tableName="certificate_data"/>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
<changeSet author="R3.Corda" id="1520338500424-16">
|
|
||||||
<addUniqueConstraint columnNames="certificate_signing_request" constraintName="UK_NI_CSR" tableName="node_info"/>
|
|
||||||
</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="certificate_signing_request_AUD">
|
||||||
<column name="REV"/>
|
<column name="REV"/>
|
||||||
|
@ -141,7 +141,7 @@ class PersistentNetworkMapStorageTest : TestBase() {
|
|||||||
networkMapStorage.saveNetworkMap(signedNetworkMap)
|
networkMapStorage.saveNetworkMap(signedNetworkMap)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val validNodeInfoHash = networkMapStorage.getNodeInfoHashes(CertificateStatus.VALID)
|
val validNodeInfoHash = networkMapStorage.getActiveNodeInfoHashes()
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(validNodeInfoHash).containsOnly(nodeInfoHashA, nodeInfoHashB)
|
assertThat(validNodeInfoHash).containsOnly(nodeInfoHashA, nodeInfoHashB)
|
||||||
|
@ -37,10 +37,12 @@ import javax.security.auth.x500.X500Principal
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotNull
|
import kotlin.test.assertNotNull
|
||||||
import kotlin.test.assertNull
|
import kotlin.test.assertNull
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class PersistentNodeInfoStorageTest : TestBase() {
|
class PersistentNodeInfoStorageTest : TestBase() {
|
||||||
private lateinit var requestStorage: CertificateSigningRequestStorage
|
private lateinit var requestStorage: CertificateSigningRequestStorage
|
||||||
private lateinit var nodeInfoStorage: PersistentNodeInfoStorage
|
private lateinit var nodeInfoStorage: PersistentNodeInfoStorage
|
||||||
|
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 intermediateCa: CertificateAndKeyPair
|
||||||
@ -53,6 +55,7 @@ class PersistentNodeInfoStorageTest : TestBase() {
|
|||||||
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)
|
||||||
|
networkMapStorage = PersistentNetworkMapStorage(persistence)
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -119,12 +122,15 @@ class PersistentNodeInfoStorageTest : TestBase() {
|
|||||||
|
|
||||||
val nodeInfo1Hash = nodeInfoStorage.putNodeInfo(node1)
|
val nodeInfo1Hash = nodeInfoStorage.putNodeInfo(node1)
|
||||||
assertEquals(node1.nodeInfo, nodeInfoStorage.getNodeInfo(nodeInfo1Hash)?.verified())
|
assertEquals(node1.nodeInfo, nodeInfoStorage.getNodeInfo(nodeInfo1Hash)?.verified())
|
||||||
|
assertTrue(networkMapStorage.getActiveNodeInfoHashes().contains(nodeInfo1Hash))
|
||||||
|
|
||||||
// This should replace the node info.
|
// This should replace the node info.
|
||||||
nodeInfoStorage.putNodeInfo(node2)
|
val nodeInfo2Hash = nodeInfoStorage.putNodeInfo(node2)
|
||||||
|
|
||||||
// Old node info should be removed.
|
// Old node info should be removed from list of current node info hashes, but still accessible if required.
|
||||||
assertNull(nodeInfoStorage.getNodeInfo(nodeInfo1Hash))
|
assertThat(networkMapStorage.getActiveNodeInfoHashes()).doesNotContain(nodeInfo1Hash)
|
||||||
|
assertThat(networkMapStorage.getActiveNodeInfoHashes()).contains(nodeInfo2Hash)
|
||||||
|
assertNotNull(nodeInfoStorage.getNodeInfo(nodeInfo1Hash))
|
||||||
assertEquals(nodeInfo2, nodeInfoStorage.getNodeInfo(nodeInfo2.serialize().hash)?.verified())
|
assertEquals(nodeInfo2, nodeInfoStorage.getNodeInfo(nodeInfo2.serialize().hash)?.verified())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,10 +27,10 @@ import net.corda.nodeapi.internal.network.NetworkMap
|
|||||||
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
|
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import java.security.cert.X509Certificate
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class NetworkMapSignerTest : TestBase() {
|
class NetworkMapSignerTest : TestBase() {
|
||||||
@ -60,7 +60,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
val networkMap = NetworkMap(signedNodeInfoHashes, currentParameters.serialize().hash, null)
|
val networkMap = NetworkMap(signedNodeInfoHashes, currentParameters.serialize().hash, null)
|
||||||
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
||||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
||||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(signedNodeInfoHashes)
|
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(signedNodeInfoHashes)
|
||||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(latestNetworkParameters)
|
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(latestNetworkParameters)
|
||||||
whenever(networkMapStorage.getNetworkParametersOfNetworkMap()).thenReturn(currentParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
whenever(networkMapStorage.getNetworkParametersOfNetworkMap()).thenReturn(currentParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
||||||
whenever(signer.signBytes(any())).then {
|
whenever(signer.signBytes(any())).then {
|
||||||
@ -76,7 +76,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
// Verify networkMapStorage calls
|
// Verify networkMapStorage calls
|
||||||
verify(networkMapStorage).getNodeInfoHashes(any())
|
verify(networkMapStorage).getActiveNodeInfoHashes()
|
||||||
verify(networkMapStorage).getLatestNetworkParameters()
|
verify(networkMapStorage).getLatestNetworkParameters()
|
||||||
verify(networkMapStorage).getNetworkParametersOfNetworkMap()
|
verify(networkMapStorage).getNetworkParametersOfNetworkMap()
|
||||||
argumentCaptor<SignedNetworkMap>().apply {
|
argumentCaptor<SignedNetworkMap>().apply {
|
||||||
@ -96,7 +96,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
val networkMap = NetworkMap(emptyList(), networkMapParametersHash, null)
|
val networkMap = NetworkMap(emptyList(), networkMapParametersHash, null)
|
||||||
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
||||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(signedNetworkMap)
|
||||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(emptyList())
|
||||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
||||||
whenever(networkMapStorage.getNetworkParametersOfNetworkMap()).thenReturn(networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
whenever(networkMapStorage.getNetworkParametersOfNetworkMap()).thenReturn(networkParameters.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
// given
|
// given
|
||||||
val networkParameters = testNetworkParameters(emptyList())
|
val networkParameters = testNetworkParameters(emptyList())
|
||||||
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null)
|
whenever(networkMapStorage.getCurrentNetworkMap()).thenReturn(null)
|
||||||
whenever(networkMapStorage.getNodeInfoHashes(any())).thenReturn(emptyList())
|
whenever(networkMapStorage.getActiveNodeInfoHashes()).thenReturn(emptyList())
|
||||||
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
whenever(networkMapStorage.getLatestNetworkParameters()).thenReturn(networkParameters)
|
||||||
whenever(signer.signBytes(any())).then {
|
whenever(signer.signBytes(any())).then {
|
||||||
DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
|
DigitalSignatureWithCert(networkMapCa.certificate, Crypto.doSign(networkMapCa.keyPair.private, it.arguments[0] as ByteArray))
|
||||||
@ -127,7 +127,7 @@ class NetworkMapSignerTest : TestBase() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
// Verify networkMapStorage calls
|
// Verify networkMapStorage calls
|
||||||
verify(networkMapStorage).getNodeInfoHashes(any())
|
verify(networkMapStorage).getActiveNodeInfoHashes()
|
||||||
verify(networkMapStorage).getLatestNetworkParameters()
|
verify(networkMapStorage).getLatestNetworkParameters()
|
||||||
argumentCaptor<SignedNetworkMap>().apply {
|
argumentCaptor<SignedNetworkMap>().apply {
|
||||||
verify(networkMapStorage).saveNetworkMap(capture())
|
verify(networkMapStorage).saveNetworkMap(capture())
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
package com.r3.corda.networkmanage.doorman.webservice
|
package com.r3.corda.networkmanage.doorman.webservice
|
||||||
|
|
||||||
|
import com.nhaarman.mockito_kotlin.any
|
||||||
import com.nhaarman.mockito_kotlin.mock
|
import com.nhaarman.mockito_kotlin.mock
|
||||||
import com.nhaarman.mockito_kotlin.times
|
import com.nhaarman.mockito_kotlin.times
|
||||||
import com.nhaarman.mockito_kotlin.verify
|
import com.nhaarman.mockito_kotlin.verify
|
||||||
@ -67,7 +68,9 @@ class NetworkMapWebServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `submit nodeInfo`() {
|
fun `submit nodeInfo`() {
|
||||||
val networkMapStorage: NetworkMapStorage = mock {
|
val networkMapStorage: NetworkMapStorage = mock {
|
||||||
on { getNetworkParametersOfNetworkMap() }.thenReturn(testNetworkParameters(emptyList()).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
val networkParameter = testNetworkParameters(emptyList()).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
||||||
|
on { getSignedNetworkParameters(any()) }.thenReturn(networkParameter)
|
||||||
|
on { getCurrentNetworkMap() }.thenReturn(NetworkMap(emptyList(), networkParameter.raw.hash, null).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
||||||
}
|
}
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"))
|
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"))
|
||||||
@ -83,7 +86,9 @@ class NetworkMapWebServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `submit old nodeInfo`() {
|
fun `submit old nodeInfo`() {
|
||||||
val networkMapStorage: NetworkMapStorage = mock {
|
val networkMapStorage: NetworkMapStorage = mock {
|
||||||
on { getNetworkParametersOfNetworkMap() }.thenReturn(testNetworkParameters(emptyList(), minimumPlatformVersion = 2).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
val networkParameter = testNetworkParameters(emptyList(), minimumPlatformVersion = 2).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
||||||
|
on { getSignedNetworkParameters(any()) }.thenReturn(networkParameter)
|
||||||
|
on { getCurrentNetworkMap() }.thenReturn(NetworkMap(emptyList(), networkParameter.raw.hash, null).signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate))
|
||||||
}
|
}
|
||||||
// Create node info.
|
// Create node info.
|
||||||
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1)
|
val (_, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"), platformVersion = 1)
|
||||||
@ -131,14 +136,21 @@ class NetworkMapWebServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `get node info`() {
|
fun `get node info`() {
|
||||||
|
// Mock node info storage
|
||||||
val (nodeInfo, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"))
|
val (nodeInfo, signedNodeInfo) = createNodeInfoAndSigned(CordaX500Name("Test", "London", "GB"))
|
||||||
val nodeInfoHash = nodeInfo.serialize().hash
|
val nodeInfoHash = nodeInfo.serialize().hash
|
||||||
|
|
||||||
val nodeInfoStorage: NodeInfoStorage = mock {
|
val nodeInfoStorage: NodeInfoStorage = mock {
|
||||||
on { getNodeInfo(nodeInfoHash) }.thenReturn(signedNodeInfo)
|
on { getNodeInfo(nodeInfoHash) }.thenReturn(signedNodeInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(nodeInfoStorage, mock(), testNetworkMapConfig)).use {
|
// Mock network map storage
|
||||||
|
val networkMap = NetworkMap(listOf(nodeInfoHash), randomSHA256(), null)
|
||||||
|
val signedNetworkMap = networkMap.signWithCert(networkMapCa.keyPair.private, networkMapCa.certificate)
|
||||||
|
val networkMapStorage: NetworkMapStorage = mock {
|
||||||
|
on { getCurrentNetworkMap() }.thenReturn(signedNetworkMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkManagementWebServer(NetworkHostAndPort("localhost", 0), NetworkMapWebService(nodeInfoStorage, networkMapStorage, testNetworkMapConfig)).use {
|
||||||
it.start()
|
it.start()
|
||||||
val nodeInfoResponse = it.doGet<SignedNodeInfo>("node-info/$nodeInfoHash")
|
val nodeInfoResponse = it.doGet<SignedNodeInfo>("node-info/$nodeInfoHash")
|
||||||
verify(nodeInfoStorage, times(1)).getNodeInfo(nodeInfoHash)
|
verify(nodeInfoStorage, times(1)).getNodeInfo(nodeInfoHash)
|
||||||
|
BIN
network-management/src/test/resources/oldNodeInfoDB.mv.db
Normal file
BIN
network-management/src/test/resources/oldNodeInfoDB.mv.db
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user