mirror of
https://github.com/corda/corda.git
synced 2025-01-28 15:14:48 +00:00
Removed all remaining special treatment of the X500 common name.
With network parameters the CN is no longer needed to identify notaries. This frees it up to be used in the node's name alongside the other attributes. Also, the identity generation logic has been simplified, removing the need to have magic string values for storing distributed identities in the keystore. Now there are just two alias prefixes: "identity" as it was previously, and "distributed-notary".
This commit is contained in:
parent
f7445a5e6a
commit
e1e715ee81
@ -1836,7 +1836,7 @@ public @interface net.corda.core.node.services.CordaService
|
|||||||
@org.jetbrains.annotations.NotNull public static final String ID_PREFIX = "corda.notary."
|
@org.jetbrains.annotations.NotNull public static final String ID_PREFIX = "corda.notary."
|
||||||
##
|
##
|
||||||
public static final class net.corda.core.node.services.NotaryService$Companion extends java.lang.Object
|
public static final class net.corda.core.node.services.NotaryService$Companion extends java.lang.Object
|
||||||
@org.jetbrains.annotations.NotNull public final String constructId(boolean, boolean, boolean, boolean)
|
@kotlin.Deprecated @org.jetbrains.annotations.NotNull public final String constructId(boolean, boolean, boolean, boolean)
|
||||||
##
|
##
|
||||||
public abstract class net.corda.core.node.services.PartyInfo extends java.lang.Object
|
public abstract class net.corda.core.node.services.PartyInfo extends java.lang.Object
|
||||||
@org.jetbrains.annotations.NotNull public abstract net.corda.core.identity.Party getParty()
|
@org.jetbrains.annotations.NotNull public abstract net.corda.core.identity.Party getParty()
|
||||||
|
@ -36,7 +36,9 @@ data class CordaX500Name(val commonName: String?,
|
|||||||
val locality: String,
|
val locality: String,
|
||||||
val state: String?,
|
val state: String?,
|
||||||
val country: String) {
|
val country: String) {
|
||||||
constructor(commonName: String, organisation: String, locality: String, country: String) : this(commonName = commonName, organisationUnit = null, organisation = organisation, locality = locality, state = null, country = country)
|
constructor(commonName: String, organisation: String, locality: String, country: String) :
|
||||||
|
this(commonName = commonName, organisationUnit = null, organisation = organisation, locality = locality, state = null, country = country)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param organisation name of the organisation.
|
* @param organisation name of the organisation.
|
||||||
* @param locality locality of the organisation, typically nearest major city.
|
* @param locality locality of the organisation, typically nearest major city.
|
||||||
|
@ -118,6 +118,8 @@ fun Path.isDirectory(vararg options: LinkOption): Boolean = Files.isDirectory(th
|
|||||||
inline val Path.size: Long get() = Files.size(this)
|
inline val Path.size: Long get() = Files.size(this)
|
||||||
inline fun <R> Path.list(block: (Stream<Path>) -> R): R = Files.list(this).use(block)
|
inline fun <R> Path.list(block: (Stream<Path>) -> R): R = Files.list(this).use(block)
|
||||||
fun Path.deleteIfExists(): Boolean = Files.deleteIfExists(this)
|
fun Path.deleteIfExists(): Boolean = Files.deleteIfExists(this)
|
||||||
|
fun Path.reader(charset: Charset = UTF_8): BufferedReader = Files.newBufferedReader(this, charset)
|
||||||
|
fun Path.writer(charset: Charset = UTF_8, vararg options: OpenOption): BufferedWriter = Files.newBufferedWriter(this, charset, *options)
|
||||||
fun Path.readAll(): ByteArray = Files.readAllBytes(this)
|
fun Path.readAll(): ByteArray = Files.readAllBytes(this)
|
||||||
inline fun <R> Path.read(vararg options: OpenOption, block: (InputStream) -> R): R = Files.newInputStream(this, *options).use(block)
|
inline fun <R> Path.read(vararg options: OpenOption, block: (InputStream) -> R): R = Files.newInputStream(this, *options).use(block)
|
||||||
inline fun Path.write(createDirs: Boolean = false, vararg options: OpenOption = emptyArray(), block: (OutputStream) -> Unit) {
|
inline fun Path.write(createDirs: Boolean = false, vararg options: OpenOption = emptyArray(), block: (OutputStream) -> Unit) {
|
||||||
|
@ -15,7 +15,9 @@ import java.security.PublicKey
|
|||||||
|
|
||||||
abstract class NotaryService : SingletonSerializeAsToken() {
|
abstract class NotaryService : SingletonSerializeAsToken() {
|
||||||
companion object {
|
companion object {
|
||||||
|
@Deprecated("No longer used")
|
||||||
const val ID_PREFIX = "corda.notary."
|
const val ID_PREFIX = "corda.notary."
|
||||||
|
@Deprecated("No longer used")
|
||||||
fun constructId(validating: Boolean, raft: Boolean = false, bft: Boolean = false, custom: Boolean = false): String {
|
fun constructId(validating: Boolean, raft: Boolean = false, bft: Boolean = false, custom: Boolean = false): String {
|
||||||
require(Booleans.countTrue(raft, bft, custom) <= 1) { "At most one of raft, bft or custom may be true" }
|
require(Booleans.countTrue(raft, bft, custom) <= 1) { "At most one of raft, bft or custom may be true" }
|
||||||
return StringBuffer(ID_PREFIX).apply {
|
return StringBuffer(ID_PREFIX).apply {
|
||||||
|
@ -9,8 +9,8 @@ import net.corda.core.serialization.serialize
|
|||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.toBase58String
|
import net.corda.core.utilities.toBase58String
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.testing.internal.kryoSpecific
|
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
|
import net.corda.testing.internal.kryoSpecific
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
import org.junit.rules.TemporaryFolder
|
||||||
@ -24,6 +24,7 @@ class CompositeKeyTests {
|
|||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val testSerialization = SerializationEnvironmentRule()
|
val testSerialization = SerializationEnvironmentRule()
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val tempFolder: TemporaryFolder = TemporaryFolder()
|
val tempFolder: TemporaryFolder = TemporaryFolder()
|
||||||
@ -40,9 +41,9 @@ class CompositeKeyTests {
|
|||||||
private val secureHash = message.sha256()
|
private val secureHash = message.sha256()
|
||||||
|
|
||||||
// By lazy is required so that the serialisers are configured before vals initialisation takes place (they internally invoke serialise).
|
// By lazy is required so that the serialisers are configured before vals initialisation takes place (they internally invoke serialise).
|
||||||
val aliceSignature by lazy { aliceKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(alicePublicKey).schemeNumberID))) }
|
private val aliceSignature by lazy { aliceKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(alicePublicKey).schemeNumberID))) }
|
||||||
val bobSignature by lazy { bobKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(bobPublicKey).schemeNumberID))) }
|
private val bobSignature by lazy { bobKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(bobPublicKey).schemeNumberID))) }
|
||||||
val charlieSignature by lazy { charlieKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(charliePublicKey).schemeNumberID))) }
|
private val charlieSignature by lazy { charlieKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(charliePublicKey).schemeNumberID))) }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `(Alice) fulfilled by Alice signature`() {
|
fun `(Alice) fulfilled by Alice signature`() {
|
||||||
@ -337,7 +338,7 @@ class CompositeKeyTests {
|
|||||||
val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair)
|
val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair)
|
||||||
|
|
||||||
// Sign the composite key with the self sign CA.
|
// Sign the composite key with the self sign CA.
|
||||||
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, ca, caKeyPair, caName.copy(commonName = "CompositeKey"), compositeKey)
|
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.LEGAL_IDENTITY, ca, caKeyPair, caName, compositeKey)
|
||||||
|
|
||||||
// Store certificate to keystore.
|
// Store certificate to keystore.
|
||||||
val keystorePath = tempFolder.root.toPath() / "keystore.jks"
|
val keystorePath = tempFolder.root.toPath() / "keystore.jks"
|
||||||
|
@ -159,7 +159,7 @@ class AttachmentSerializationTest {
|
|||||||
|
|
||||||
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
|
private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String {
|
||||||
client.dispose()
|
client.dispose()
|
||||||
client = mockNet.createNode(MockNodeParameters(client.internals.id), { args ->
|
client = mockNet.createNode(MockNodeParameters(client.internals.id, client.internals.configuration.myLegalName), { args ->
|
||||||
object : MockNetwork.MockNode(args) {
|
object : MockNetwork.MockNode(args) {
|
||||||
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad }
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,6 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=PartyA,L=London,C=GB"
|
name "O=PartyA,L=London,C=GB"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10005
|
p2pPort 10005
|
||||||
rpcPort 10006
|
rpcPort 10006
|
||||||
webPort 10007
|
webPort 10007
|
||||||
@ -103,7 +102,6 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=PartyB,L=New York,C=US"
|
name "O=PartyB,L=New York,C=US"
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10009
|
p2pPort 10009
|
||||||
rpcPort 10010
|
rpcPort 10010
|
||||||
webPort 10011
|
webPort 10011
|
||||||
|
@ -22,7 +22,7 @@ service.
|
|||||||
directory "./build/nodes"
|
directory "./build/nodes"
|
||||||
node {
|
node {
|
||||||
name "O=Controller,L=London,C=GB"
|
name "O=Controller,L=London,C=GB"
|
||||||
advertisedServices = ["corda.notary.validating"]
|
notary = [validating : true]
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcPort 10003
|
rpcPort 10003
|
||||||
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
cordapps = ["net.corda:corda-finance:$corda_release_version"]
|
||||||
|
@ -13,42 +13,54 @@ import org.slf4j.LoggerFactory
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
object ServiceIdentityGenerator {
|
object IdentityGenerator {
|
||||||
private val log = LoggerFactory.getLogger(javaClass)
|
private val log = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
|
const val NODE_IDENTITY_ALIAS_PREFIX = "identity"
|
||||||
|
const val DISTRIBUTED_NOTARY_ALIAS_PREFIX = "distributed-notary"
|
||||||
|
|
||||||
|
fun generateNodeIdentity(dir: Path, legalName: CordaX500Name, customRootCert: X509Certificate? = null): Party {
|
||||||
|
return generateToDisk(listOf(dir), legalName, NODE_IDENTITY_ALIAS_PREFIX, threshold = 1, customRootCert = customRootCert)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateDistributedNotaryIdentity(dirs: List<Path>, notaryName: CordaX500Name, threshold: Int = 1, customRootCert: X509Certificate? = null): Party {
|
||||||
|
return generateToDisk(dirs, notaryName, DISTRIBUTED_NOTARY_ALIAS_PREFIX, threshold, customRootCert)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates signing key pairs and a common distributed service identity for a set of nodes.
|
* Generates signing key pairs and a common distributed service identity for a set of nodes.
|
||||||
* The key pairs and the group identity get serialized to disk in the corresponding node directories.
|
* The key pairs and the group identity get serialized to disk in the corresponding node directories.
|
||||||
* This method should be called *before* any of the nodes are started.
|
* This method should be called *before* any of the nodes are started.
|
||||||
*
|
*
|
||||||
* @param dirs List of node directories to place the generated identity and key pairs in.
|
* @param dirs List of node directories to place the generated identity and key pairs in.
|
||||||
* @param serviceName The legal name of the distributed service.
|
* @param name The name of the identity.
|
||||||
* @param threshold The threshold for the generated group [CompositeKey].
|
* @param threshold The threshold for the generated group [CompositeKey].
|
||||||
* @param customRootCert the certificate to use a Corda root CA. If not specified the one in
|
* @param customRootCert the certificate to use as the Corda root CA. If not specified the one in
|
||||||
* certificates/cordadevcakeys.jks is used.
|
* internal/certificates/cordadevcakeys.jks is used.
|
||||||
*/
|
*/
|
||||||
fun generateToDisk(dirs: List<Path>,
|
private fun generateToDisk(dirs: List<Path>,
|
||||||
serviceName: CordaX500Name,
|
name: CordaX500Name,
|
||||||
serviceId: String,
|
aliasPrefix: String,
|
||||||
threshold: Int = 1,
|
threshold: Int,
|
||||||
customRootCert: X509Certificate? = null): Party {
|
customRootCert: X509Certificate?): Party {
|
||||||
log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" }
|
log.trace { "Generating identity \"$name\" for nodes: ${dirs.joinToString()}" }
|
||||||
val keyPairs = (1..dirs.size).map { generateKeyPair() }
|
val keyPairs = (1..dirs.size).map { generateKeyPair() }
|
||||||
val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
|
val key = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
|
||||||
|
|
||||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||||
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
val intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||||
val rootCert = customRootCert ?: caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
|
val rootCert = customRootCert ?: caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
|
|
||||||
keyPairs.zip(dirs) { keyPair, dir ->
|
keyPairs.zip(dirs) { keyPair, dir ->
|
||||||
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, serviceName, keyPair.public)
|
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, name, keyPair.public)
|
||||||
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, serviceName, notaryKey)
|
val compositeKeyCert = X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, name, key)
|
||||||
val certPath = (dir / "certificates").createDirectories() / "distributedService.jks"
|
val certPath = (dir / "certificates").createDirectories() / "distributedService.jks"
|
||||||
val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass")
|
val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass")
|
||||||
keystore.setCertificateEntry("$serviceId-composite-key", compositeKeyCert.cert)
|
keystore.setCertificateEntry("$aliasPrefix-composite-key", compositeKeyCert.cert)
|
||||||
keystore.setKeyEntry("$serviceId-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert))
|
keystore.setKeyEntry("$aliasPrefix-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert))
|
||||||
keystore.save(certPath, "cordacadevpass")
|
keystore.save(certPath, "cordacadevpass")
|
||||||
}
|
}
|
||||||
return Party(serviceName, notaryKey)
|
|
||||||
|
return Party(name, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,8 @@ import net.corda.core.crypto.random63BitValue
|
|||||||
import net.corda.core.internal.CertRole
|
import net.corda.core.internal.CertRole
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.cert
|
import net.corda.core.internal.cert
|
||||||
import net.corda.core.internal.read
|
import net.corda.core.internal.reader
|
||||||
|
import net.corda.core.internal.writer
|
||||||
import net.corda.core.internal.x500Name
|
import net.corda.core.internal.x500Name
|
||||||
import net.corda.core.utilities.days
|
import net.corda.core.utilities.days
|
||||||
import net.corda.core.utilities.millis
|
import net.corda.core.utilities.millis
|
||||||
@ -48,8 +49,6 @@ object X509Utilities {
|
|||||||
const val CORDA_CLIENT_TLS = "cordaclienttls"
|
const val CORDA_CLIENT_TLS = "cordaclienttls"
|
||||||
const val CORDA_CLIENT_CA = "cordaclientca"
|
const val CORDA_CLIENT_CA = "cordaclientca"
|
||||||
|
|
||||||
const val CORDA_CLIENT_CA_CN = "Corda Client CA Certificate"
|
|
||||||
|
|
||||||
private val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
|
private val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,7 +161,7 @@ object X509Utilities {
|
|||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) {
|
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) {
|
||||||
JcaPEMWriter(file.toFile().writer()).use {
|
JcaPEMWriter(file.writer()).use {
|
||||||
it.writeObject(x509Certificate)
|
it.writeObject(x509Certificate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,9 +173,8 @@ object X509Utilities {
|
|||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun loadCertificateFromPEMFile(file: Path): X509Certificate {
|
fun loadCertificateFromPEMFile(file: Path): X509Certificate {
|
||||||
return file.read {
|
return file.reader().use {
|
||||||
val reader = PemReader(it.reader())
|
val pemObject = PemReader(it).readPemObject()
|
||||||
val pemObject = reader.readPemObject()
|
|
||||||
val certHolder = X509CertificateHolder(pemObject.content)
|
val certHolder = X509CertificateHolder(pemObject.content)
|
||||||
certHolder.isValidOn(Date())
|
certHolder.isValidOn(Date())
|
||||||
certHolder.cert
|
certHolder.cert
|
||||||
|
@ -13,7 +13,6 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.deleteIfExists
|
import net.corda.core.internal.deleteIfExists
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.node.services.NotaryService
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
@ -24,10 +23,10 @@ import net.corda.node.services.config.BFTSMaRtConfiguration
|
|||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.node.services.transactions.minClusterSize
|
import net.corda.node.services.transactions.minClusterSize
|
||||||
import net.corda.node.services.transactions.minCorrectReplicas
|
import net.corda.node.services.transactions.minCorrectReplicas
|
||||||
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
import net.corda.nodeapi.internal.IdentityGenerator
|
||||||
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||||
import net.corda.testing.chooseIdentity
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.dummyCommand
|
import net.corda.testing.dummyCommand
|
||||||
@ -55,10 +54,9 @@ class BFTNotaryServiceTests {
|
|||||||
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
|
(Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists?
|
||||||
val replicaIds = (0 until clusterSize)
|
val replicaIds = (0 until clusterSize)
|
||||||
|
|
||||||
notary = ServiceIdentityGenerator.generateToDisk(
|
notary = IdentityGenerator.generateDistributedNotaryIdentity(
|
||||||
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
|
replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) },
|
||||||
CordaX500Name("BFT", "Zurich", "CH"),
|
CordaX500Name("BFT", "Zurich", "CH"))
|
||||||
NotaryService.constructId(validating = false, bft = true))
|
|
||||||
|
|
||||||
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notary, false))))
|
val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notary, false))))
|
||||||
|
|
||||||
|
@ -11,30 +11,30 @@ import net.corda.core.internal.concurrent.map
|
|||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
import net.corda.testing.DUMMY_BANK_A_NAME
|
||||||
import net.corda.testing.*
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.dummyCommand
|
import net.corda.testing.dummyCommand
|
||||||
import net.corda.testing.node.startFlow
|
|
||||||
import net.corda.testing.node.ClusterSpec
|
import net.corda.testing.node.ClusterSpec
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
|
import net.corda.testing.node.startFlow
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
class RaftNotaryServiceTests {
|
class RaftNotaryServiceTests {
|
||||||
private val notaryName = CordaX500Name(RaftValidatingNotaryService.id, "RAFT Notary Service", "London", "GB")
|
private val notaryName = CordaX500Name("RAFT Notary Service", "London", "GB")
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `detect double spend`() {
|
fun `detect double spend`() {
|
||||||
driver(
|
driver(
|
||||||
startNodesInProcess = true,
|
startNodesInProcess = true,
|
||||||
extraCordappPackagesToScan = listOf("net.corda.testing.contracts"),
|
extraCordappPackagesToScan = listOf("net.corda.testing.contracts"),
|
||||||
notarySpecs = listOf(NotarySpec(notaryName, cluster = ClusterSpec.Raft(clusterSize = 3))))
|
notarySpecs = listOf(NotarySpec(notaryName, cluster = ClusterSpec.Raft(clusterSize = 3)))
|
||||||
{
|
) {
|
||||||
val bankA = startNode(providedName = DUMMY_BANK_A_NAME).map { (it as NodeHandle.InProcess).node }.getOrThrow()
|
val bankA = startNode(providedName = DUMMY_BANK_A_NAME).map { (it as NodeHandle.InProcess).node }.getOrThrow()
|
||||||
val inputState = issueState(bankA, defaultNotaryIdentity)
|
val inputState = issueState(bankA, defaultNotaryIdentity)
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ import net.corda.core.utilities.seconds
|
|||||||
import net.corda.node.internal.Node
|
import net.corda.node.internal.Node
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.messaging.*
|
import net.corda.node.services.messaging.*
|
||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.chooseIdentity
|
import net.corda.testing.chooseIdentity
|
||||||
import net.corda.testing.driver.DriverDSL
|
import net.corda.testing.driver.DriverDSL
|
||||||
@ -32,7 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger
|
|||||||
|
|
||||||
class P2PMessagingTest {
|
class P2PMessagingTest {
|
||||||
private companion object {
|
private companion object {
|
||||||
val DISTRIBUTED_SERVICE_NAME = CordaX500Name(RaftValidatingNotaryService.id, "DistributedService", "London", "GB")
|
val DISTRIBUTED_SERVICE_NAME = CordaX500Name("DistributedService", "London", "GB")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -59,8 +59,12 @@ import net.corda.node.services.vault.NodeVaultService
|
|||||||
import net.corda.node.services.vault.VaultSoftLockManager
|
import net.corda.node.services.vault.VaultSoftLockManager
|
||||||
import net.corda.node.shell.InteractiveShell
|
import net.corda.node.shell.InteractiveShell
|
||||||
import net.corda.node.utilities.AffinityExecutor
|
import net.corda.node.utilities.AffinityExecutor
|
||||||
|
import net.corda.nodeapi.internal.IdentityGenerator
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.KeyStoreWrapper
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||||
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
|
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
|
||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
@ -133,25 +137,19 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
protected val services: ServiceHubInternal get() = _services
|
protected val services: ServiceHubInternal get() = _services
|
||||||
private lateinit var _services: ServiceHubInternalImpl
|
private lateinit var _services: ServiceHubInternalImpl
|
||||||
protected var myNotaryIdentity: PartyAndCertificate? = null
|
protected var myNotaryIdentity: PartyAndCertificate? = null
|
||||||
protected lateinit var checkpointStorage: CheckpointStorage
|
private lateinit var checkpointStorage: CheckpointStorage
|
||||||
private lateinit var tokenizableServices: List<Any>
|
private lateinit var tokenizableServices: List<Any>
|
||||||
protected lateinit var attachments: NodeAttachmentService
|
protected lateinit var attachments: NodeAttachmentService
|
||||||
protected lateinit var network: MessagingService
|
protected lateinit var network: MessagingService
|
||||||
protected val runOnStop = ArrayList<() -> Any?>()
|
protected val runOnStop = ArrayList<() -> Any?>()
|
||||||
protected val _nodeReadyFuture = openFuture<Unit>()
|
private val _nodeReadyFuture = openFuture<Unit>()
|
||||||
protected var networkMapClient: NetworkMapClient? = null
|
protected var networkMapClient: NetworkMapClient? = null
|
||||||
|
|
||||||
lateinit var securityManager: RPCSecurityManager get
|
lateinit var securityManager: RPCSecurityManager get
|
||||||
|
|
||||||
/** Completes once the node has successfully registered with the network map service
|
/** Completes once the node has successfully registered with the network map service
|
||||||
* or has loaded network map data from local database */
|
* or has loaded network map data from local database */
|
||||||
val nodeReadyFuture: CordaFuture<Unit>
|
val nodeReadyFuture: CordaFuture<Unit> get() = _nodeReadyFuture
|
||||||
get() = _nodeReadyFuture
|
|
||||||
/** A [CordaX500Name] with null common name. */
|
|
||||||
protected val myLegalName: CordaX500Name by lazy {
|
|
||||||
val cert = loadKeyStore(configuration.nodeKeystore, configuration.keyStorePassword).getX509Certificate(X509Utilities.CORDA_CLIENT_CA)
|
|
||||||
CordaX500Name.build(cert.subjectX500Principal).copy(commonName = null)
|
|
||||||
}
|
|
||||||
|
|
||||||
open val serializationWhitelists: List<SerializationWhitelist> by lazy {
|
open val serializationWhitelists: List<SerializationWhitelist> by lazy {
|
||||||
cordappLoader.cordapps.flatMap { it.serializationWhitelists }
|
cordappLoader.cordapps.flatMap { it.serializationWhitelists }
|
||||||
@ -308,7 +306,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
)
|
)
|
||||||
// Check if we have already stored a version of 'our own' NodeInfo, this is to avoid regenerating it with
|
// Check if we have already stored a version of 'our own' NodeInfo, this is to avoid regenerating it with
|
||||||
// a different timestamp.
|
// a different timestamp.
|
||||||
networkMapCache.getNodesByLegalName(myLegalName).firstOrNull()?.let {
|
networkMapCache.getNodesByLegalName(configuration.myLegalName).firstOrNull()?.let {
|
||||||
if (info.copy(serial = it.serial) == it) {
|
if (info.copy(serial = it.serial) == it) {
|
||||||
info = it
|
info = it
|
||||||
}
|
}
|
||||||
@ -728,13 +726,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
|
|
||||||
val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) {
|
val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) {
|
||||||
// Node's main identity or if it's a single node notary
|
// Node's main identity or if it's a single node notary
|
||||||
Pair("identity", myLegalName)
|
Pair(IdentityGenerator.NODE_IDENTITY_ALIAS_PREFIX, configuration.myLegalName)
|
||||||
} else {
|
} else {
|
||||||
val notaryId = notaryConfig.run {
|
|
||||||
NotaryService.constructId(validating, raft != null, bftSMaRt != null, custom)
|
|
||||||
}
|
|
||||||
// The node is part of a distributed notary whose identity must already be generated beforehand.
|
// The node is part of a distributed notary whose identity must already be generated beforehand.
|
||||||
Pair(notaryId, null)
|
Pair(IdentityGenerator.DISTRIBUTED_NOTARY_ALIAS_PREFIX, null)
|
||||||
}
|
}
|
||||||
// TODO: Integrate with Key management service?
|
// TODO: Integrate with Key management service?
|
||||||
val privateKeyAlias = "$id-private-key"
|
val privateKeyAlias = "$id-private-key"
|
||||||
@ -742,7 +737,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
if (!keyStore.containsAlias(privateKeyAlias)) {
|
if (!keyStore.containsAlias(privateKeyAlias)) {
|
||||||
singleName ?: throw IllegalArgumentException(
|
singleName ?: throw IllegalArgumentException(
|
||||||
"Unable to find in the key store the identity of the distributed notary ($id) the node is part of")
|
"Unable to find in the key store the identity of the distributed notary ($id) the node is part of")
|
||||||
// TODO: Remove use of [ServiceIdentityGenerator.generateToDisk].
|
// TODO: Remove use of [IdentityGenerator.generateToDisk].
|
||||||
log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!")
|
log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!")
|
||||||
keyStore.signAndSaveNewKeyPair(singleName, privateKeyAlias, generateKeyPair())
|
keyStore.signAndSaveNewKeyPair(singleName, privateKeyAlias, generateKeyPair())
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) {
|
|||||||
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass")
|
||||||
createKeystoreForCordaNode(sslKeystore, nodeKeystore, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass", myLegalName)
|
createKeystoreForCordaNode(sslKeystore, nodeKeystore, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass", myLegalName)
|
||||||
|
|
||||||
// Move distributed service composite key (generated by ServiceIdentityGenerator.generateToDisk) to keystore if exists.
|
// Move distributed service composite key (generated by IdentityGenerator.generateToDisk) to keystore if exists.
|
||||||
val distributedServiceKeystore = certificatesDirectory / "distributedService.jks"
|
val distributedServiceKeystore = certificatesDirectory / "distributedService.jks"
|
||||||
if (distributedServiceKeystore.exists()) {
|
if (distributedServiceKeystore.exists()) {
|
||||||
val serviceKeystore = loadKeyStore(distributedServiceKeystore, "cordacadevpass")
|
val serviceKeystore = loadKeyStore(distributedServiceKeystore, "cordacadevpass")
|
||||||
@ -98,18 +98,17 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path,
|
|||||||
val (intermediateCACert, intermediateCAKeyPair) = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caKeyPassword)
|
val (intermediateCACert, intermediateCAKeyPair) = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caKeyPassword)
|
||||||
|
|
||||||
val clientKey = Crypto.generateKeyPair(signatureScheme)
|
val clientKey = Crypto.generateKeyPair(signatureScheme)
|
||||||
val clientName = legalName.copy(commonName = null)
|
|
||||||
|
|
||||||
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, clientName.x500Name))), arrayOf())
|
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.x500Name))), arrayOf())
|
||||||
val clientCACert = X509Utilities.createCertificate(CertificateType.NODE_CA,
|
val clientCACert = X509Utilities.createCertificate(CertificateType.NODE_CA,
|
||||||
intermediateCACert,
|
intermediateCACert,
|
||||||
intermediateCAKeyPair,
|
intermediateCAKeyPair,
|
||||||
clientName.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN),
|
legalName,
|
||||||
clientKey.public,
|
clientKey.public,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
|
|
||||||
val tlsKey = Crypto.generateKeyPair(signatureScheme)
|
val tlsKey = Crypto.generateKeyPair(signatureScheme)
|
||||||
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, clientName, tlsKey.public)
|
val clientTLSCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientKey, legalName, tlsKey.public)
|
||||||
|
|
||||||
val keyPass = keyPassword.toCharArray()
|
val keyPass = keyPassword.toCharArray()
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import javax.annotation.concurrent.ThreadSafe
|
|||||||
*
|
*
|
||||||
* @param identities initial set of identities for the service, typically only used for unit tests.
|
* @param identities initial set of identities for the service, typically only used for unit tests.
|
||||||
*/
|
*/
|
||||||
|
// TODO There is duplicated logic between this and PersistentIdentityService
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
|
||||||
trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||||
|
@ -26,6 +26,7 @@ import javax.persistence.Entity
|
|||||||
import javax.persistence.Id
|
import javax.persistence.Id
|
||||||
import javax.persistence.Lob
|
import javax.persistence.Lob
|
||||||
|
|
||||||
|
// TODO There is duplicated logic between this and InMemoryIdentityService
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||||
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||||
|
@ -34,12 +34,13 @@ import kotlin.concurrent.thread
|
|||||||
*
|
*
|
||||||
* A transaction is notarised when the consensus is reached by the cluster on its uniqueness, and time-window validity.
|
* A transaction is notarised when the consensus is reached by the cluster on its uniqueness, and time-window validity.
|
||||||
*/
|
*/
|
||||||
class BFTNonValidatingNotaryService(override val services: ServiceHubInternal,
|
class BFTNonValidatingNotaryService(
|
||||||
|
override val services: ServiceHubInternal,
|
||||||
override val notaryIdentityKey: PublicKey,
|
override val notaryIdentityKey: PublicKey,
|
||||||
private val bftSMaRtConfig: BFTSMaRtConfiguration,
|
private val bftSMaRtConfig: BFTSMaRtConfiguration,
|
||||||
cluster: BFTSMaRt.Cluster) : NotaryService() {
|
cluster: BFTSMaRt.Cluster
|
||||||
|
) : NotaryService() {
|
||||||
companion object {
|
companion object {
|
||||||
val id = constructId(validating = false, bft = true)
|
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,14 +8,13 @@ import net.corda.core.node.services.TrustedAuthorityNotaryService
|
|||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
/** A non-validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
|
/** A non-validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
|
||||||
class RaftNonValidatingNotaryService(override val services: ServiceHub,
|
class RaftNonValidatingNotaryService(
|
||||||
|
override val services: ServiceHub,
|
||||||
override val notaryIdentityKey: PublicKey,
|
override val notaryIdentityKey: PublicKey,
|
||||||
override val uniquenessProvider: RaftUniquenessProvider) : TrustedAuthorityNotaryService() {
|
override val uniquenessProvider: RaftUniquenessProvider
|
||||||
companion object {
|
) : TrustedAuthorityNotaryService() {
|
||||||
val id = constructId(validating = false, raft = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
|
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
|
||||||
|
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
|
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
|
||||||
return NonValidatingNotaryFlow(otherPartySession, this)
|
return NonValidatingNotaryFlow(otherPartySession, this)
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,13 @@ import net.corda.core.node.services.TrustedAuthorityNotaryService
|
|||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
/** A validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
|
/** A validating notary service operated by a group of mutually trusting parties, uses the Raft algorithm to achieve consensus. */
|
||||||
class RaftValidatingNotaryService(override val services: ServiceHub,
|
class RaftValidatingNotaryService(
|
||||||
|
override val services: ServiceHub,
|
||||||
override val notaryIdentityKey: PublicKey,
|
override val notaryIdentityKey: PublicKey,
|
||||||
override val uniquenessProvider: RaftUniquenessProvider) : TrustedAuthorityNotaryService() {
|
override val uniquenessProvider: RaftUniquenessProvider
|
||||||
companion object {
|
) : TrustedAuthorityNotaryService() {
|
||||||
val id = constructId(validating = true, raft = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
|
override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock)
|
||||||
|
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
|
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service {
|
||||||
return ValidatingNotaryFlow(otherPartySession, this)
|
return ValidatingNotaryFlow(otherPartySession, this)
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,8 @@ import java.security.PublicKey
|
|||||||
|
|
||||||
/** A Notary service that validates the transaction chain of the submitted transaction before committing it */
|
/** A Notary service that validates the transaction chain of the submitted transaction before committing it */
|
||||||
class ValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
class ValidatingNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : TrustedAuthorityNotaryService() {
|
||||||
companion object {
|
|
||||||
val id = constructId(validating = true)
|
|
||||||
}
|
|
||||||
override val timeWindowChecker = TimeWindowChecker(services.clock)
|
override val timeWindowChecker = TimeWindowChecker(services.clock)
|
||||||
|
|
||||||
override val uniquenessProvider = PersistentUniquenessProvider()
|
override val uniquenessProvider = PersistentUniquenessProvider()
|
||||||
|
|
||||||
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = ValidatingNotaryFlow(otherPartySession, this)
|
override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = ValidatingNotaryFlow(otherPartySession, this)
|
||||||
|
@ -103,10 +103,9 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
|||||||
println("Generating SSL certificate for node messaging service.")
|
println("Generating SSL certificate for node messaging service.")
|
||||||
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder()
|
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA).toX509CertHolder()
|
||||||
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal).copy(commonName = null), sslKey.public)
|
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, CordaX500Name.build(caCert.cert.subjectX500Principal), sslKey.public)
|
||||||
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
|
val sslKeyStore = loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
|
||||||
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(),
|
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates))
|
||||||
arrayOf(sslCert.cert, *certificates))
|
|
||||||
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
|
sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
|
||||||
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
println("SSL private key and certificate stored in ${config.sslKeystore}.")
|
||||||
// All done, clean up temp files.
|
// All done, clean up temp files.
|
||||||
|
@ -659,7 +659,7 @@ class FlowFrameworkTests {
|
|||||||
private inline fun <reified P : FlowLogic<*>> StartedNode<MockNode>.restartAndGetRestoredFlow() = internals.run {
|
private inline fun <reified P : FlowLogic<*>> StartedNode<MockNode>.restartAndGetRestoredFlow() = internals.run {
|
||||||
disableDBCloseOnStop() // Handover DB to new node copy
|
disableDBCloseOnStop() // Handover DB to new node copy
|
||||||
stop()
|
stop()
|
||||||
val newNode = mockNet.createNode(MockNodeParameters(id))
|
val newNode = mockNet.createNode(MockNodeParameters(id, configuration.myLegalName))
|
||||||
newNode.internals.acceptableLiveFiberCountOnStop = 1
|
newNode.internals.acceptableLiveFiberCountOnStop = 1
|
||||||
manuallyCloseDB()
|
manuallyCloseDB()
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package net.corda.node.utilities.registration
|
package net.corda.node.utilities.registration
|
||||||
|
|
||||||
|
import com.google.common.jimfs.Configuration.unix
|
||||||
|
import com.google.common.jimfs.Jimfs
|
||||||
import com.nhaarman.mockito_kotlin.any
|
import com.nhaarman.mockito_kotlin.any
|
||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import com.nhaarman.mockito_kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.eq
|
import com.nhaarman.mockito_kotlin.eq
|
||||||
@ -7,68 +9,61 @@ import com.nhaarman.mockito_kotlin.whenever
|
|||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.cert
|
||||||
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
|
||||||
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
|
||||||
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.rules.TemporaryFolder
|
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
import kotlin.test.assertEquals
|
import java.security.cert.X509Certificate
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class NetworkRegistrationHelperTest {
|
class NetworkRegistrationHelperTest {
|
||||||
@Rule
|
private val fs = Jimfs.newFileSystem(unix())
|
||||||
@JvmField
|
|
||||||
val tempFolder = TemporaryFolder()
|
|
||||||
|
|
||||||
private val requestId = SecureHash.randomSHA256().toString()
|
private val requestId = SecureHash.randomSHA256().toString()
|
||||||
|
private val nodeLegalName = ALICE_NAME
|
||||||
|
private val intermediateCaName = CordaX500Name("CORDA_INTERMEDIATE_CA", "R3 Ltd", "London", "GB")
|
||||||
|
private val rootCaName = CordaX500Name("CORDA_ROOT_CA", "R3 Ltd", "London", "GB")
|
||||||
|
private val nodeCaCert = createCaCert(nodeLegalName)
|
||||||
|
private val intermediateCaCert = createCaCert(intermediateCaName)
|
||||||
|
private val rootCaCert = createCaCert(rootCaName)
|
||||||
|
|
||||||
private lateinit var config: NodeConfiguration
|
private lateinit var config: NodeConfiguration
|
||||||
|
|
||||||
private val identities = listOf("CORDA_CLIENT_CA",
|
|
||||||
"CORDA_INTERMEDIATE_CA",
|
|
||||||
"CORDA_ROOT_CA")
|
|
||||||
.map { CordaX500Name(commonName = it, organisation = "R3 Ltd", locality = "London", country = "GB") }
|
|
||||||
private val certs = identities.map { X509Utilities.createSelfSignedCACertificate(it, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) }
|
|
||||||
.map { it.cert }.toTypedArray()
|
|
||||||
|
|
||||||
private val certService = mockRegistrationResponse(*certs)
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun init() {
|
fun init() {
|
||||||
|
val baseDirectory = fs.getPath("/baseDir").createDirectories()
|
||||||
abstract class AbstractNodeConfiguration : NodeConfiguration
|
abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||||
config = rigorousMock<AbstractNodeConfiguration>().also {
|
config = rigorousMock<AbstractNodeConfiguration>().also {
|
||||||
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
|
doReturn(baseDirectory).whenever(it).baseDirectory
|
||||||
doReturn("trustpass").whenever(it).trustStorePassword
|
doReturn("trustpass").whenever(it).trustStorePassword
|
||||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||||
doReturn(ALICE_NAME).whenever(it).myLegalName
|
doReturn(nodeLegalName).whenever(it).myLegalName
|
||||||
doReturn("").whenever(it).emailAddress
|
doReturn("").whenever(it).emailAddress
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@After
|
||||||
fun `successful registration`() {
|
fun cleanUp() {
|
||||||
assertFalse(config.nodeKeystore.exists())
|
fs.close()
|
||||||
assertFalse(config.sslKeystore.exists())
|
|
||||||
config.trustStoreFile.parent.createDirectories()
|
|
||||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
|
||||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, certs.last())
|
|
||||||
it.save(config.trustStoreFile, config.trustStorePassword)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkRegistrationHelper(config, certService).buildKeystore()
|
@Test
|
||||||
|
fun `successful registration`() {
|
||||||
|
assertThat(config.nodeKeystore).doesNotExist()
|
||||||
|
assertThat(config.sslKeystore).doesNotExist()
|
||||||
|
assertThat(config.trustStoreFile).doesNotExist()
|
||||||
|
|
||||||
assertTrue(config.nodeKeystore.exists())
|
saveTrustStoreWithRootCa(rootCaCert)
|
||||||
assertTrue(config.sslKeystore.exists())
|
|
||||||
assertTrue(config.trustStoreFile.exists())
|
createRegistrationHelper().buildKeystore()
|
||||||
|
|
||||||
val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
val nodeKeystore = loadKeyStore(config.nodeKeystore, config.keyStorePassword)
|
||||||
val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
val sslKeystore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||||
@ -79,9 +74,8 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
val nodeCaCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||||
assertEquals(3, certificateChain.size)
|
assertThat(nodeCaCertChain).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
|
||||||
assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { it.toX509CertHolder().subject.commonName })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sslKeystore.run {
|
sslKeystore.run {
|
||||||
@ -89,46 +83,55 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)
|
val nodeTlsCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)
|
||||||
assertEquals(4, certificateChain.size)
|
assertThat(nodeTlsCertChain).hasSize(4)
|
||||||
assertEquals(listOf(CordaX500Name(organisation = "R3 Ltd", locality = "London", country = "GB").x500Name) + identities.map { it.x500Name },
|
// The TLS cert has the same subject as the node CA cert
|
||||||
certificateChain.map { it.toX509CertHolder().subject })
|
assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName)
|
||||||
assertEquals(CordaX500Name(organisation = "R3 Ltd", locality = "London", country = "GB").x500Principal,
|
assertThat(nodeTlsCertChain.drop(1)).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert)
|
||||||
getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trustStore.run {
|
trustStore.run {
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
|
||||||
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||||
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
|
val trustStoreRootCaCert = getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
|
assertThat(trustStoreRootCaCert).isEqualTo(rootCaCert)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `missing truststore`() {
|
fun `missing truststore`() {
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
NetworkRegistrationHelper(config, certService).buildKeystore()
|
createRegistrationHelper()
|
||||||
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
|
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
|
||||||
.isInstanceOf(IllegalArgumentException::class.java)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `wrong root cert in truststore`() {
|
fun `wrong root cert in truststore`() {
|
||||||
val someCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Foo", "MU", "GB"), Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)).cert
|
saveTrustStoreWithRootCa(createCaCert(CordaX500Name("Foo", "MU", "GB")))
|
||||||
config.trustStoreFile.parent.createDirectories()
|
val registrationHelper = createRegistrationHelper()
|
||||||
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
|
||||||
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, someCert)
|
|
||||||
it.save(config.trustStoreFile, config.trustStorePassword)
|
|
||||||
}
|
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
NetworkRegistrationHelper(config, certService).buildKeystore()
|
registrationHelper.buildKeystore()
|
||||||
}.isInstanceOf(WrongRootCertException::class.java)
|
}.isInstanceOf(WrongRootCertException::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mockRegistrationResponse(vararg response: Certificate): NetworkRegistrationService {
|
private fun createRegistrationHelper(): NetworkRegistrationHelper {
|
||||||
return rigorousMock<NetworkRegistrationService>().also {
|
val certService = rigorousMock<NetworkRegistrationService>().also {
|
||||||
doReturn(requestId).whenever(it).submitRequest(any())
|
doReturn(requestId).whenever(it).submitRequest(any())
|
||||||
doReturn(response).whenever(it).retrieveCertificates(eq(requestId))
|
doReturn(arrayOf<Certificate>(nodeCaCert, intermediateCaCert, rootCaCert)).whenever(it).retrieveCertificates(eq(requestId))
|
||||||
|
}
|
||||||
|
return NetworkRegistrationHelper(config, certService)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveTrustStoreWithRootCa(rootCa: X509Certificate) {
|
||||||
|
config.trustStoreFile.parent.createDirectories()
|
||||||
|
loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also {
|
||||||
|
it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCa)
|
||||||
|
it.save(config.trustStoreFile, config.trustStorePassword)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createCaCert(name: CordaX500Name): X509Certificate {
|
||||||
|
return X509Utilities.createSelfSignedCACertificate(name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)).cert
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,11 @@ import net.corda.cordform.CordformContext
|
|||||||
import net.corda.cordform.CordformDefinition
|
import net.corda.cordform.CordformDefinition
|
||||||
import net.corda.cordform.CordformNode
|
import net.corda.cordform.CordformNode
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.node.services.NotaryService
|
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.services.config.BFTSMaRtConfiguration
|
import net.corda.node.services.config.BFTSMaRtConfiguration
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
|
|
||||||
import net.corda.node.services.transactions.minCorrectReplicas
|
import net.corda.node.services.transactions.minCorrectReplicas
|
||||||
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
import net.corda.nodeapi.internal.IdentityGenerator
|
||||||
import net.corda.testing.node.internal.demorun.*
|
import net.corda.testing.node.internal.demorun.*
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.BOB_NAME
|
import net.corda.testing.BOB_NAME
|
||||||
@ -24,7 +22,7 @@ private val notaryNames = createNotaryNames(clusterSize)
|
|||||||
// This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO
|
// This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO
|
||||||
// NOT use this as a design to copy.
|
// NOT use this as a design to copy.
|
||||||
class BFTNotaryCordform : CordformDefinition() {
|
class BFTNotaryCordform : CordformDefinition() {
|
||||||
private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH")
|
private val clusterName = CordaX500Name("BFT", "Zurich", "CH")
|
||||||
|
|
||||||
init {
|
init {
|
||||||
nodesDirectory = Paths.get("build", "nodes", "nodesBFT")
|
nodesDirectory = Paths.get("build", "nodes", "nodesBFT")
|
||||||
@ -64,10 +62,10 @@ class BFTNotaryCordform : CordformDefinition() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun setup(context: CordformContext) {
|
override fun setup(context: CordformContext) {
|
||||||
ServiceIdentityGenerator.generateToDisk(
|
IdentityGenerator.generateDistributedNotaryIdentity(
|
||||||
notaryNames.map { context.baseDirectory(it.toString()) },
|
notaryNames.map { context.baseDirectory(it.toString()) },
|
||||||
clusterName,
|
clusterName,
|
||||||
NotaryService.constructId(validating = false, bft = true),
|
minCorrectReplicas(clusterSize)
|
||||||
minCorrectReplicas(clusterSize))
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,7 @@ import net.corda.core.node.services.NotaryService
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.node.services.config.RaftConfig
|
import net.corda.node.services.config.RaftConfig
|
||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
import net.corda.nodeapi.internal.IdentityGenerator
|
||||||
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
|
||||||
import net.corda.testing.node.internal.demorun.*
|
import net.corda.testing.node.internal.demorun.*
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.BOB_NAME
|
import net.corda.testing.BOB_NAME
|
||||||
@ -24,7 +23,7 @@ private val notaryNames = createNotaryNames(3)
|
|||||||
// This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO
|
// This is not the intended final design for how to use CordformDefinition, please treat this as experimental and DO
|
||||||
// NOT use this as a design to copy.
|
// NOT use this as a design to copy.
|
||||||
class RaftNotaryCordform : CordformDefinition() {
|
class RaftNotaryCordform : CordformDefinition() {
|
||||||
private val clusterName = CordaX500Name(RaftValidatingNotaryService.id, "Raft", "Zurich", "CH")
|
private val clusterName = CordaX500Name("Raft", "Zurich", "CH")
|
||||||
|
|
||||||
init {
|
init {
|
||||||
nodesDirectory = Paths.get("build", "nodes", "nodesRaft")
|
nodesDirectory = Paths.get("build", "nodes", "nodesRaft")
|
||||||
@ -60,9 +59,9 @@ class RaftNotaryCordform : CordformDefinition() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun setup(context: CordformContext) {
|
override fun setup(context: CordformContext) {
|
||||||
ServiceIdentityGenerator.generateToDisk(
|
IdentityGenerator.generateDistributedNotaryIdentity(
|
||||||
notaryNames.map { context.baseDirectory(it.toString()) },
|
notaryNames.map { context.baseDirectory(it.toString()) },
|
||||||
clusterName,
|
clusterName
|
||||||
NotaryService.constructId(validating = true, raft = true))
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ import net.corda.node.services.transactions.BFTSMaRt
|
|||||||
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
||||||
import net.corda.node.utilities.AffinityExecutor
|
import net.corda.node.utilities.AffinityExecutor
|
||||||
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
||||||
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
import net.corda.nodeapi.internal.IdentityGenerator
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||||
@ -159,6 +159,7 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
throw IllegalStateException("Using more than one MockNetwork simultaneously is not supported.", e)
|
throw IllegalStateException("Using more than one MockNetwork simultaneously is not supported.", e)
|
||||||
}
|
}
|
||||||
private val sharedUserCount = AtomicInteger(0)
|
private val sharedUserCount = AtomicInteger(0)
|
||||||
|
|
||||||
/** A read only view of the current set of nodes. */
|
/** A read only view of the current set of nodes. */
|
||||||
val nodes: List<MockNode> get() = _nodes
|
val nodes: List<MockNode> get() = _nodes
|
||||||
|
|
||||||
@ -172,8 +173,7 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
* Returns the single notary node on the network. Throws if there are none or more than one.
|
* Returns the single notary node on the network. Throws if there are none or more than one.
|
||||||
* @see notaryNodes
|
* @see notaryNodes
|
||||||
*/
|
*/
|
||||||
val defaultNotaryNode: StartedNode<MockNode>
|
val defaultNotaryNode: StartedNode<MockNode> get() {
|
||||||
get() {
|
|
||||||
return when (notaryNodes.size) {
|
return when (notaryNodes.size) {
|
||||||
0 -> throw IllegalStateException("There are no notaries defined on the network")
|
0 -> throw IllegalStateException("There are no notaries defined on the network")
|
||||||
1 -> notaryNodes[0]
|
1 -> notaryNodes[0]
|
||||||
@ -185,8 +185,7 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
* Return the identity of the default notary node.
|
* Return the identity of the default notary node.
|
||||||
* @see defaultNotaryNode
|
* @see defaultNotaryNode
|
||||||
*/
|
*/
|
||||||
val defaultNotaryIdentity: Party
|
val defaultNotaryIdentity: Party get() {
|
||||||
get() {
|
|
||||||
return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities")
|
return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,8 +193,7 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
* Return the identity of the default notary node.
|
* Return the identity of the default notary node.
|
||||||
* @see defaultNotaryNode
|
* @see defaultNotaryNode
|
||||||
*/
|
*/
|
||||||
val defaultNotaryIdentityAndCert: PartyAndCertificate
|
val defaultNotaryIdentityAndCert: PartyAndCertificate get() {
|
||||||
get() {
|
|
||||||
return defaultNotaryNode.info.legalIdentitiesAndCerts.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities")
|
return defaultNotaryNode.info.legalIdentitiesAndCerts.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,19 +235,16 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
|
|
||||||
private fun generateNotaryIdentities(): List<NotaryInfo> {
|
private fun generateNotaryIdentities(): List<NotaryInfo> {
|
||||||
return notarySpecs.mapIndexed { index, (name, validating) ->
|
return notarySpecs.mapIndexed { index, (name, validating) ->
|
||||||
val identity = ServiceIdentityGenerator.generateToDisk(
|
val identity = IdentityGenerator.generateNodeIdentity(baseDirectory(nextNodeId + index), name)
|
||||||
dirs = listOf(baseDirectory(nextNodeId + index)),
|
|
||||||
serviceName = name,
|
|
||||||
serviceId = "identity")
|
|
||||||
NotaryInfo(identity, validating)
|
NotaryInfo(identity, validating)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
internal open fun createNotaries(): List<StartedNode<MockNode>> {
|
internal open fun createNotaries(): List<StartedNode<MockNode>> {
|
||||||
return notarySpecs.map { spec ->
|
return notarySpecs.map { (name, validating) ->
|
||||||
createNode(MockNodeParameters(legalName = spec.name, configOverrides = {
|
createNode(MockNodeParameters(legalName = name, configOverrides = {
|
||||||
doReturn(NotaryConfig(spec.validating)).whenever(it).notary
|
doReturn(NotaryConfig(validating)).whenever(it).notary
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,7 +301,7 @@ open class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
id,
|
id,
|
||||||
serverThread,
|
serverThread,
|
||||||
myNotaryIdentity,
|
myNotaryIdentity,
|
||||||
myLegalName,
|
configuration.myLegalName,
|
||||||
database).also { runOnStop += it::stop }
|
database).also { runOnStop += it::stop }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ import net.corda.core.internal.createDirectories
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.node.services.NetworkMapCache
|
import net.corda.core.node.services.NetworkMapCache
|
||||||
import net.corda.core.node.services.NotaryService
|
|
||||||
import net.corda.core.toFuture
|
import net.corda.core.toFuture
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
@ -30,12 +29,9 @@ import net.corda.node.internal.NodeStartup
|
|||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.services.Permissions
|
import net.corda.node.services.Permissions
|
||||||
import net.corda.node.services.config.*
|
import net.corda.node.services.config.*
|
||||||
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
|
|
||||||
import net.corda.node.services.transactions.RaftNonValidatingNotaryService
|
|
||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
|
||||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||||
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
||||||
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
import net.corda.nodeapi.internal.IdentityGenerator
|
||||||
import net.corda.nodeapi.internal.addShutdownHook
|
import net.corda.nodeapi.internal.addShutdownHook
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.nodeapi.internal.config.parseAs
|
import net.corda.nodeapi.internal.config.parseAs
|
||||||
@ -245,9 +241,9 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private enum class ClusterType(val validating: Boolean, val clusterName: CordaX500Name) {
|
private enum class ClusterType(val validating: Boolean, val clusterName: CordaX500Name) {
|
||||||
VALIDATING_RAFT(true, CordaX500Name(RaftValidatingNotaryService.id, "Raft", "Zurich", "CH")),
|
VALIDATING_RAFT(true, CordaX500Name("Raft", "Zurich", "CH")),
|
||||||
NON_VALIDATING_RAFT(false, CordaX500Name(RaftNonValidatingNotaryService.id, "Raft", "Zurich", "CH")),
|
NON_VALIDATING_RAFT(false, CordaX500Name("Raft", "Zurich", "CH")),
|
||||||
NON_VALIDATING_BFT(false, CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH"))
|
NON_VALIDATING_BFT(false, CordaX500Name("BFT", "Zurich", "CH"))
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun startCordformNodes(cordforms: List<CordformNode>): CordaFuture<*> {
|
internal fun startCordformNodes(cordforms: List<CordformNode>): CordaFuture<*> {
|
||||||
@ -270,24 +266,15 @@ class DriverDSLImpl(
|
|||||||
clusterNodes.put(ClusterType.NON_VALIDATING_BFT, name)
|
clusterNodes.put(ClusterType.NON_VALIDATING_BFT, name)
|
||||||
} else {
|
} else {
|
||||||
// We have all we need here to generate the identity for single node notaries
|
// We have all we need here to generate the identity for single node notaries
|
||||||
val identity = ServiceIdentityGenerator.generateToDisk(
|
val identity = IdentityGenerator.generateNodeIdentity(baseDirectory(name), legalName = name)
|
||||||
dirs = listOf(baseDirectory(name)),
|
|
||||||
serviceName = name,
|
|
||||||
serviceId = "identity"
|
|
||||||
)
|
|
||||||
notaryInfos += NotaryInfo(identity, notaryConfig.validating)
|
notaryInfos += NotaryInfo(identity, notaryConfig.validating)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterNodes.asMap().forEach { type, nodeNames ->
|
clusterNodes.asMap().forEach { type, nodeNames ->
|
||||||
val identity = ServiceIdentityGenerator.generateToDisk(
|
val identity = IdentityGenerator.generateDistributedNotaryIdentity(
|
||||||
dirs = nodeNames.map { baseDirectory(it) },
|
dirs = nodeNames.map { baseDirectory(it) },
|
||||||
serviceName = type.clusterName,
|
notaryName = type.clusterName
|
||||||
serviceId = NotaryService.constructId(
|
|
||||||
validating = type.validating,
|
|
||||||
raft = type in setOf(VALIDATING_RAFT, NON_VALIDATING_RAFT),
|
|
||||||
bft = type == ClusterType.NON_VALIDATING_BFT
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
notaryInfos += NotaryInfo(identity, type.validating)
|
notaryInfos += NotaryInfo(identity, type.validating)
|
||||||
}
|
}
|
||||||
@ -369,20 +356,11 @@ class DriverDSLImpl(
|
|||||||
private fun generateNotaryIdentities(): List<NotaryInfo> {
|
private fun generateNotaryIdentities(): List<NotaryInfo> {
|
||||||
return notarySpecs.map { spec ->
|
return notarySpecs.map { spec ->
|
||||||
val identity = if (spec.cluster == null) {
|
val identity = if (spec.cluster == null) {
|
||||||
ServiceIdentityGenerator.generateToDisk(
|
IdentityGenerator.generateNodeIdentity(baseDirectory(spec.name), spec.name, compatibilityZone?.rootCert)
|
||||||
dirs = listOf(baseDirectory(spec.name)),
|
|
||||||
serviceName = spec.name,
|
|
||||||
serviceId = "identity",
|
|
||||||
customRootCert = compatibilityZone?.rootCert
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
ServiceIdentityGenerator.generateToDisk(
|
IdentityGenerator.generateDistributedNotaryIdentity(
|
||||||
dirs = generateNodeNames(spec).map { baseDirectory(it) },
|
dirs = generateNodeNames(spec).map { baseDirectory(it) },
|
||||||
serviceName = spec.name,
|
notaryName = spec.name,
|
||||||
serviceId = NotaryService.constructId(
|
|
||||||
validating = spec.validating,
|
|
||||||
raft = spec.cluster is ClusterSpec.Raft
|
|
||||||
),
|
|
||||||
customRootCert = compatibilityZone?.rootCert
|
customRootCert = compatibilityZone?.rootCert
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -91,13 +91,13 @@ fun getTestPartyAndCertificate(party: Party): PartyAndCertificate {
|
|||||||
val trustRoot: X509CertificateHolder = DEV_TRUST_ROOT
|
val trustRoot: X509CertificateHolder = DEV_TRUST_ROOT
|
||||||
val intermediate: CertificateAndKeyPair = DEV_CA
|
val intermediate: CertificateAndKeyPair = DEV_CA
|
||||||
|
|
||||||
val nodeCaName = party.name.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN)
|
|
||||||
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val nodeCaKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val nodeCaCert = X509Utilities.createCertificate(
|
val nodeCaCert = X509Utilities.createCertificate(
|
||||||
CertificateType.NODE_CA,
|
CertificateType.NODE_CA,
|
||||||
intermediate.certificate,
|
intermediate.certificate,
|
||||||
intermediate.keyPair,
|
intermediate.keyPair,
|
||||||
nodeCaName,
|
party.name,
|
||||||
nodeCaKeyPair.public,
|
nodeCaKeyPair.public,
|
||||||
nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, party.name.x500Name))), arrayOf()))
|
nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, party.name.x500Name))), arrayOf()))
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import net.corda.demobench.pty.R3Pty
|
|||||||
import net.corda.nodeapi.internal.network.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.nodeapi.internal.network.NotaryInfo
|
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||||
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
import net.corda.nodeapi.internal.IdentityGenerator
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.management.ManagementFactory
|
import java.lang.management.ManagementFactory
|
||||||
@ -153,10 +153,7 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
|
|||||||
|
|
||||||
// Generate notary identity and save it into node's directory. This identity will be used in network parameters.
|
// Generate notary identity and save it into node's directory. This identity will be used in network parameters.
|
||||||
private fun getNotaryIdentity(config: NodeConfigWrapper): Party {
|
private fun getNotaryIdentity(config: NodeConfigWrapper): Party {
|
||||||
return ServiceIdentityGenerator.generateToDisk(
|
return IdentityGenerator.generateNodeIdentity(config.nodeDir, config.nodeConfig.myLegalName)
|
||||||
dirs = listOf(config.nodeDir),
|
|
||||||
serviceName = config.nodeConfig.myLegalName,
|
|
||||||
serviceId = "identity")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reset() {
|
fun reset() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user