diff --git a/.ci/api-current.txt b/.ci/api-current.txt index 5fd0e2262b..09b766e46d 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -1862,7 +1862,7 @@ public @interface net.corda.core.node.services.CordaService @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 - @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 @org.jetbrains.annotations.NotNull public abstract net.corda.core.identity.Party getParty() diff --git a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt index 0b6a3ddeb7..ad315c065e 100644 --- a/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt +++ b/core/src/main/kotlin/net/corda/core/identity/CordaX500Name.kt @@ -2,7 +2,7 @@ package net.corda.core.identity import com.google.common.collect.ImmutableSet import net.corda.core.internal.LegalNameValidator -import net.corda.core.internal.VisibleForTesting +import net.corda.core.internal.unspecifiedCountry import net.corda.core.internal.x500Name import net.corda.core.serialization.CordaSerializable import org.bouncycastle.asn1.ASN1Encodable @@ -36,7 +36,9 @@ data class CordaX500Name(val commonName: String?, val locality: String, val state: 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 locality locality of the organisation, typically nearest major city. @@ -79,8 +81,6 @@ data class CordaX500Name(val commonName: String?, const val MAX_LENGTH_ORGANISATION_UNIT = 64 const val MAX_LENGTH_COMMON_NAME = 64 private val supportedAttributes = setOf(BCStyle.O, BCStyle.C, BCStyle.L, BCStyle.CN, BCStyle.ST, BCStyle.OU) - @VisibleForTesting - val unspecifiedCountry = "ZZ" private val countryCodes: Set = ImmutableSet.copyOf(Locale.getISOCountries() + unspecifiedCountry) @JvmStatic fun build(principal: X500Principal): CordaX500Name { diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index 36022463ff..42145a2f26 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -5,6 +5,7 @@ package net.corda.core.internal import net.corda.core.cordapp.CordappProvider import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 +import net.corda.core.identity.CordaX500Name import net.corda.core.node.ServicesForResolution import net.corda.core.serialization.SerializationContext import net.corda.core.transactions.TransactionBuilder @@ -118,6 +119,8 @@ fun Path.isDirectory(vararg options: LinkOption): Boolean = Files.isDirectory(th inline val Path.size: Long get() = Files.size(this) inline fun Path.list(block: (Stream) -> R): R = Files.list(this).use(block) 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) inline fun 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) { @@ -316,3 +319,8 @@ fun ExecutorService.join() { // Try forever. Do not give up, tests use this method to assert the executor has no more tasks. } } + +@Suppress("unused") +@VisibleForTesting +val CordaX500Name.Companion.unspecifiedCountry + get() = "ZZ" diff --git a/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt b/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt index 0c928a7b59..417916e752 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NotaryService.kt @@ -15,16 +15,11 @@ import java.security.PublicKey abstract class NotaryService : SingletonSerializeAsToken() { companion object { + @Deprecated("No longer used") const val ID_PREFIX = "corda.notary." - @JvmOverloads - fun constructId( - validating: Boolean, - raft: Boolean = false, - bft: Boolean = false, - custom: Boolean = false, - mysql: Boolean = false - ): String { - require(Booleans.countTrue(raft, bft, custom, mysql) <= 1) { "At most one of raft, bft, mysql or custom may be true" } + @Deprecated("No longer used") + 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" } return StringBuffer(ID_PREFIX).apply { append(if (validating) "validating" else "simple") if (raft) append(".raft") diff --git a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt index 734505498d..751037b1ee 100644 --- a/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt +++ b/core/src/test/kotlin/net/corda/core/crypto/CompositeKeyTests.kt @@ -9,8 +9,8 @@ import net.corda.core.serialization.serialize import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.toBase58String import net.corda.nodeapi.internal.crypto.* -import net.corda.testing.internal.kryoSpecific import net.corda.testing.SerializationEnvironmentRule +import net.corda.testing.internal.kryoSpecific import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder @@ -24,6 +24,7 @@ class CompositeKeyTests { @Rule @JvmField val testSerialization = SerializationEnvironmentRule() + @Rule @JvmField val tempFolder: TemporaryFolder = TemporaryFolder() @@ -40,9 +41,9 @@ class CompositeKeyTests { private val secureHash = message.sha256() // 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))) } - 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 aliceSignature by lazy { aliceKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(alicePublicKey).schemeNumberID))) } + private val bobSignature by lazy { bobKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(bobPublicKey).schemeNumberID))) } + private val charlieSignature by lazy { charlieKey.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(charliePublicKey).schemeNumberID))) } @Test fun `(Alice) fulfilled by Alice signature`() { @@ -337,7 +338,7 @@ class CompositeKeyTests { val ca = X509Utilities.createSelfSignedCACertificate(caName, caKeyPair) // 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. val keystorePath = tempFolder.root.toPath() / "keystore.jks" diff --git a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt index 940d959860..d72d7068d4 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/AttachmentSerializationTest.kt @@ -159,7 +159,7 @@ class AttachmentSerializationTest { private fun rebootClientAndGetAttachmentContent(checkAttachmentsOnLoad: Boolean = true): String { 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) { override fun start() = super.start().apply { attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad } } diff --git a/docs/source/generating-a-node.rst b/docs/source/generating-a-node.rst index 4a721e304d..eee15b3d5d 100644 --- a/docs/source/generating-a-node.rst +++ b/docs/source/generating-a-node.rst @@ -92,7 +92,6 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates } node { name "O=PartyA,L=London,C=GB" - advertisedServices = [] p2pPort 10005 rpcPort 10006 webPort 10007 @@ -103,7 +102,6 @@ nodes. Here is an example ``Cordform`` task called ``deployNodes`` that creates } node { name "O=PartyB,L=New York,C=US" - advertisedServices = [] p2pPort 10009 rpcPort 10010 webPort 10011 diff --git a/docs/source/hello-world-running.rst b/docs/source/hello-world-running.rst index 592cfee44c..4ac394d26c 100644 --- a/docs/source/hello-world-running.rst +++ b/docs/source/hello-world-running.rst @@ -22,7 +22,7 @@ service. directory "./build/nodes" node { name "O=Controller,L=London,C=GB" - advertisedServices = ["corda.notary.validating"] + notary = [validating : true] p2pPort 10002 rpcPort 10003 cordapps = ["net.corda:corda-finance:$corda_release_version"] diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceIdentityGenerator.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/IdentityGenerator.kt similarity index 53% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceIdentityGenerator.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/IdentityGenerator.kt index 65b60433c7..97927e54db 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ServiceIdentityGenerator.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/IdentityGenerator.kt @@ -13,42 +13,54 @@ import org.slf4j.LoggerFactory import java.nio.file.Path import java.security.cert.X509Certificate -object ServiceIdentityGenerator { +object IdentityGenerator { 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, 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. * 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. * * @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 customRootCert the certificate to use a Corda root CA. If not specified the one in - * certificates/cordadevcakeys.jks is used. + * @param customRootCert the certificate to use as the Corda root CA. If not specified the one in + * internal/certificates/cordadevcakeys.jks is used. */ - fun generateToDisk(dirs: List, - serviceName: CordaX500Name, - serviceId: String, - threshold: Int = 1, - customRootCert: X509Certificate? = null): Party { - log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" } + private fun generateToDisk(dirs: List, + name: CordaX500Name, + aliasPrefix: String, + threshold: Int, + customRootCert: X509Certificate?): Party { + log.trace { "Generating identity \"$name\" for nodes: ${dirs.joinToString()}" } 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 intermediateCa = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass") val rootCert = customRootCert ?: caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA) keyPairs.zip(dirs) { keyPair, dir -> - val serviceKeyCert = X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, serviceName, keyPair.public) - val compositeKeyCert = X509Utilities.createCertificate(CertificateType.SERVICE_IDENTITY, intermediateCa.certificate, intermediateCa.keyPair, serviceName, notaryKey) + 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, name, key) val certPath = (dir / "certificates").createDirectories() / "distributedService.jks" val keystore = loadOrCreateKeyStore(certPath, "cordacadevpass") - keystore.setCertificateEntry("$serviceId-composite-key", compositeKeyCert.cert) - keystore.setKeyEntry("$serviceId-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert)) + keystore.setCertificateEntry("$aliasPrefix-composite-key", compositeKeyCert.cert) + keystore.setKeyEntry("$aliasPrefix-private-key", keyPair.private, "cordacadevkeypass".toCharArray(), arrayOf(serviceKeyCert.cert, intermediateCa.certificate.cert, rootCert)) keystore.save(certPath, "cordacadevpass") } - return Party(serviceName, notaryKey) + + return Party(name, key) } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt index 70617daf76..563ddaa9f0 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt @@ -7,7 +7,8 @@ import net.corda.core.crypto.random63BitValue import net.corda.core.internal.CertRole import net.corda.core.identity.CordaX500Name 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.utilities.days import net.corda.core.utilities.millis @@ -48,8 +49,6 @@ object X509Utilities { const val CORDA_CLIENT_TLS = "cordaclienttls" 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) /** @@ -162,7 +161,7 @@ object X509Utilities { */ @JvmStatic fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) { - JcaPEMWriter(file.toFile().writer()).use { + JcaPEMWriter(file.writer()).use { it.writeObject(x509Certificate) } } @@ -174,9 +173,8 @@ object X509Utilities { */ @JvmStatic fun loadCertificateFromPEMFile(file: Path): X509Certificate { - return file.read { - val reader = PemReader(it.reader()) - val pemObject = reader.readPemObject() + return file.reader().use { + val pemObject = PemReader(it).readPemObject() val certHolder = X509CertificateHolder(pemObject.content) certHolder.isValidOn(Date()) certHolder.cert diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index aaf2cb5e04..7f45d28a15 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -13,7 +13,6 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.internal.deleteIfExists import net.corda.core.internal.div -import net.corda.core.node.services.NotaryService import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.NetworkHostAndPort @@ -26,6 +25,8 @@ import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minCorrectReplicas import net.corda.nodeapi.internal.ServiceIdentityGenerator import net.corda.nodeapi.internal.network.NetworkParametersCopier +import net.corda.nodeapi.internal.IdentityGenerator +import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NotaryInfo import net.corda.testing.IntegrationTest import net.corda.testing.IntegrationTestSchemas @@ -68,10 +69,9 @@ class BFTNotaryServiceTests : IntegrationTest() { (Paths.get("config") / "currentView").deleteIfExists() // XXX: Make config object warn if this exists? val replicaIds = (0 until clusterSize) - notary = ServiceIdentityGenerator.generateToDisk( + notary = IdentityGenerator.generateDistributedNotaryIdentity( replicaIds.map { mockNet.baseDirectory(mockNet.nextNodeId + it) }, - CordaX500Name("BFT", "Zurich", "CH"), - NotaryService.constructId(validating = false, bft = true)) + CordaX500Name("BFT", "Zurich", "CH")) val networkParameters = NetworkParametersCopier(testNetworkParameters(listOf(NotaryInfo(notary, false)))) diff --git a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt index 00f6124301..7a32b8382a 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt @@ -11,15 +11,17 @@ import net.corda.core.internal.concurrent.map import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow import net.corda.node.internal.StartedNode -import net.corda.node.services.transactions.RaftValidatingNotaryService -import net.corda.testing.* +import net.corda.testing.DUMMY_BANK_A_NAME +import net.corda.testing.chooseIdentity import net.corda.testing.contracts.DummyContract import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver +import net.corda.testing.dummyCommand import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import net.corda.testing.node.startFlow import org.junit.ClassRule +import net.corda.testing.node.startFlow import org.junit.Test import java.util.* import kotlin.test.assertEquals @@ -31,15 +33,15 @@ class RaftNotaryServiceTests : IntegrationTest() { val databaseSchemas = IntegrationTestSchemas( "RAFTNotaryService_0", "RAFTNotaryService_1", "RAFTNotaryService_2", DUMMY_BANK_A_NAME.toDatabaseSchemaName()) } - private val notaryName = CordaX500Name(RaftValidatingNotaryService.id, "RAFT Notary Service", "London", "GB") + private val notaryName = CordaX500Name("RAFT Notary Service", "London", "GB") @Test fun `detect double spend`() { driver( startNodesInProcess = true, 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 inputState = issueState(bankA, defaultNotaryIdentity) diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt index 5dfa2963b1..189506d02b 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/P2PMessagingTest.kt @@ -19,6 +19,9 @@ import net.corda.node.services.messaging.ReceivedMessage import net.corda.node.services.messaging.send import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.testing.* +import net.corda.node.services.messaging.* +import net.corda.testing.ALICE_NAME +import net.corda.testing.chooseIdentity import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.driver @@ -38,7 +41,7 @@ class P2PMessagingTest : IntegrationTest() { @ClassRule @JvmField val databaseSchemas = IntegrationTestSchemas(ALICE_NAME.toDatabaseSchemaName(), "DistributedService_0", "DistributedService_1") - val DISTRIBUTED_SERVICE_NAME = CordaX500Name(RaftValidatingNotaryService.id, "DistributedService", "London", "GB") + val DISTRIBUTED_SERVICE_NAME = CordaX500Name("DistributedService", "London", "GB") } @Test diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index b25bb1df9d..2a1b1292f1 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -60,8 +60,12 @@ import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.shell.InteractiveShell import net.corda.node.utilities.AffinityExecutor +import net.corda.nodeapi.internal.IdentityGenerator 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.NetworkParameters import net.corda.nodeapi.internal.persistence.CordaPersistence @@ -137,25 +141,19 @@ abstract class AbstractNode(val configuration: NodeConfiguration, protected val services: ServiceHubInternal get() = _services private lateinit var _services: ServiceHubInternalImpl protected var myNotaryIdentity: PartyAndCertificate? = null - protected lateinit var checkpointStorage: CheckpointStorage + private lateinit var checkpointStorage: CheckpointStorage private lateinit var tokenizableServices: List protected lateinit var attachments: NodeAttachmentService protected lateinit var network: MessagingService protected val runOnStop = ArrayList<() -> Any?>() - protected val _nodeReadyFuture = openFuture() + private val _nodeReadyFuture = openFuture() protected var networkMapClient: NetworkMapClient? = null lateinit var securityManager: RPCSecurityManager get /** Completes once the node has successfully registered with the network map service * or has loaded network map data from local database */ - val nodeReadyFuture: CordaFuture - 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) - } + val nodeReadyFuture: CordaFuture get() = _nodeReadyFuture open val serializationWhitelists: List by lazy { cordappLoader.cordapps.flatMap { it.serializationWhitelists } @@ -330,7 +328,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 // a different timestamp. - networkMapCache.getNodesByLegalName(myLegalName).firstOrNull()?.let { + networkMapCache.getNodesByLegalName(configuration.myLegalName).firstOrNull()?.let { if (info.copy(serial = it.serial) == it) { info = it } @@ -756,13 +754,10 @@ abstract class AbstractNode(val configuration: NodeConfiguration, val (id, singleName) = if (notaryConfig == null || !notaryConfig.isClusterConfig) { // Node's main identity or if it's a single node notary - Pair("identity", myLegalName) + Pair(IdentityGenerator.NODE_IDENTITY_ALIAS_PREFIX, configuration.myLegalName) } else { - val notaryId = notaryConfig.run { - NotaryService.constructId(validating, raft != null, bftSMaRt != null, custom, mysql != null) - } // 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? val privateKeyAlias = "$id-private-key" @@ -770,7 +765,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, if (!keyStore.containsAlias(privateKeyAlias)) { singleName ?: throw IllegalArgumentException( "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!") keyStore.signAndSaveNewKeyPair(singleName, privateKeyAlias, generateKeyPair()) } diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index 7471d5d5ec..ed6028f13c 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -69,7 +69,7 @@ fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name) { val caKeyStore = loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/cordadevcakeys.jks"), "cordacadevpass") 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" if (distributedServiceKeystore.exists()) { val serviceKeystore = loadKeyStore(distributedServiceKeystore, "cordacadevpass") @@ -111,18 +111,17 @@ fun createKeystoreForCordaNode(sslKeyStorePath: Path, val (intermediateCACert, intermediateCAKeyPair) = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, caKeyPassword) 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, intermediateCACert, intermediateCAKeyPair, - clientName.copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN), + legalName, clientKey.public, nameConstraints = nameConstraints) 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() diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index bac9ef6902..4654473ef1 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -24,6 +24,7 @@ import javax.annotation.concurrent.ThreadSafe * * @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 class InMemoryIdentityService(identities: Array, trustRoot: X509CertificateHolder) : SingletonSerializeAsToken(), IdentityServiceInternal { diff --git a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt index 6dd98d0c63..83e7b0b267 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt @@ -26,6 +26,7 @@ import javax.persistence.Entity import javax.persistence.Id import javax.persistence.Lob +// TODO There is duplicated logic between this and InMemoryIdentityService @ThreadSafe class PersistentIdentityService(override val trustRoot: X509Certificate, vararg caCertificates: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal { diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt index 780c4815c6..9f6ffe1d08 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/BFTNonValidatingNotaryService.kt @@ -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. */ -class BFTNonValidatingNotaryService(override val services: ServiceHubInternal, - override val notaryIdentityKey: PublicKey, - private val bftSMaRtConfig: BFTSMaRtConfiguration, - cluster: BFTSMaRt.Cluster) : NotaryService() { +class BFTNonValidatingNotaryService( + override val services: ServiceHubInternal, + override val notaryIdentityKey: PublicKey, + private val bftSMaRtConfig: BFTSMaRtConfiguration, + cluster: BFTSMaRt.Cluster +) : NotaryService() { companion object { - val id = constructId(validating = false, bft = true) private val log = contextLogger() } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt index 1433e71f85..e672380398 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftNonValidatingNotaryService.kt @@ -8,14 +8,13 @@ import net.corda.core.node.services.TrustedAuthorityNotaryService import java.security.PublicKey /** 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, - override val notaryIdentityKey: PublicKey, - override val uniquenessProvider: RaftUniquenessProvider) : TrustedAuthorityNotaryService() { - companion object { - val id = constructId(validating = false, raft = true) - } - +class RaftNonValidatingNotaryService( + override val services: ServiceHub, + override val notaryIdentityKey: PublicKey, + override val uniquenessProvider: RaftUniquenessProvider +) : TrustedAuthorityNotaryService() { override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock) + override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service { return NonValidatingNotaryFlow(otherPartySession, this) } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt index 8fd3448512..3e4899ae0a 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/RaftValidatingNotaryService.kt @@ -8,14 +8,13 @@ import net.corda.core.node.services.TrustedAuthorityNotaryService import java.security.PublicKey /** 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, - override val notaryIdentityKey: PublicKey, - override val uniquenessProvider: RaftUniquenessProvider) : TrustedAuthorityNotaryService() { - companion object { - val id = constructId(validating = true, raft = true) - } - +class RaftValidatingNotaryService( + override val services: ServiceHub, + override val notaryIdentityKey: PublicKey, + override val uniquenessProvider: RaftUniquenessProvider +) : TrustedAuthorityNotaryService() { override val timeWindowChecker: TimeWindowChecker = TimeWindowChecker(services.clock) + override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service { return ValidatingNotaryFlow(otherPartySession, this) } diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt index 6c7e36046b..5e687c3b6d 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/ValidatingNotaryService.kt @@ -9,10 +9,8 @@ import java.security.PublicKey /** 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() { - companion object { - val id = constructId(validating = true) - } override val timeWindowChecker = TimeWindowChecker(services.clock) + override val uniquenessProvider = PersistentUniquenessProvider() override fun createServiceFlow(otherPartySession: FlowSession): NotaryFlow.Service = ValidatingNotaryFlow(otherPartySession, this) diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index f24cf56f11..698420ca0a 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -103,10 +103,9 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v println("Generating SSL certificate for node messaging service.") val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) 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) - sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), - arrayOf(sslCert.cert, *certificates)) + sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), arrayOf(sslCert.cert, *certificates)) sslKeyStore.save(config.sslKeystore, config.keyStorePassword) println("SSL private key and certificate stored in ${config.sslKeystore}.") // All done, clean up temp files. diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 1fe97568a8..2006c397bc 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -656,7 +656,7 @@ class FlowFrameworkTests { private inline fun > StartedNode.restartAndGetRestoredFlow() = internals.run { disableDBCloseOnStop() // Handover DB to new node copy stop() - val newNode = mockNet.createNode(MockNodeParameters(id)) + val newNode = mockNet.createNode(MockNodeParameters(id, configuration.myLegalName)) newNode.internals.acceptableLiveFiberCountOnStop = 1 manuallyCloseDB() mockNet.runNetwork() diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index 50922e9fde..209153fdb8 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -1,5 +1,7 @@ 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.doReturn 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.SecureHash 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.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.internal.rigorousMock +import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.After import org.junit.Before -import org.junit.Rule import org.junit.Test -import org.junit.rules.TemporaryFolder import java.security.cert.Certificate -import kotlin.test.assertEquals +import java.security.cert.X509Certificate import kotlin.test.assertFalse import kotlin.test.assertTrue class NetworkRegistrationHelperTest { - @Rule - @JvmField - val tempFolder = TemporaryFolder() - + private val fs = Jimfs.newFileSystem(unix()) 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 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 fun init() { + val baseDirectory = fs.getPath("/baseDir").createDirectories() abstract class AbstractNodeConfiguration : NodeConfiguration config = rigorousMock().also { - doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory + doReturn(baseDirectory).whenever(it).baseDirectory doReturn("trustpass").whenever(it).trustStorePassword doReturn("cordacadevpass").whenever(it).keyStorePassword - doReturn(ALICE_NAME).whenever(it).myLegalName + doReturn(nodeLegalName).whenever(it).myLegalName doReturn("").whenever(it).emailAddress } } + @After + fun cleanUp() { + fs.close() + } + @Test fun `successful registration`() { - assertFalse(config.nodeKeystore.exists()) - 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) - } + assertThat(config.nodeKeystore).doesNotExist() + assertThat(config.sslKeystore).doesNotExist() + assertThat(config.trustStoreFile).doesNotExist() - NetworkRegistrationHelper(config, certService).buildKeystore() + saveTrustStoreWithRootCa(rootCaCert) - assertTrue(config.nodeKeystore.exists()) - assertTrue(config.sslKeystore.exists()) - assertTrue(config.trustStoreFile.exists()) + createRegistrationHelper().buildKeystore() val nodeKeystore = loadKeyStore(config.nodeKeystore, 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_ROOT_CA)) assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS)) - val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA) - assertEquals(3, certificateChain.size) - assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { it.toX509CertHolder().subject.commonName }) + val nodeCaCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA) + assertThat(nodeCaCertChain).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert) } sslKeystore.run { @@ -89,46 +83,55 @@ class NetworkRegistrationHelperTest { assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA)) assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_TLS)) - val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) - assertEquals(4, certificateChain.size) - assertEquals(listOf(CordaX500Name(organisation = "R3 Ltd", locality = "London", country = "GB").x500Name) + identities.map { it.x500Name }, - certificateChain.map { it.toX509CertHolder().subject }) - assertEquals(CordaX500Name(organisation = "R3 Ltd", locality = "London", country = "GB").x500Principal, - getX509Certificate(X509Utilities.CORDA_CLIENT_TLS).subjectX500Principal) + val nodeTlsCertChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS) + assertThat(nodeTlsCertChain).hasSize(4) + // The TLS cert has the same subject as the node CA cert + assertThat(CordaX500Name.build((nodeTlsCertChain[0] as X509Certificate).subjectX500Principal)).isEqualTo(nodeLegalName) + assertThat(nodeTlsCertChain.drop(1)).containsExactly(nodeCaCert, intermediateCaCert, rootCaCert) } trustStore.run { assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA)) assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA)) + val trustStoreRootCaCert = getCertificate(X509Utilities.CORDA_ROOT_CA) + assertThat(trustStoreRootCaCert).isEqualTo(rootCaCert) } } @Test fun `missing truststore`() { assertThatThrownBy { - NetworkRegistrationHelper(config, certService).buildKeystore() + createRegistrationHelper() }.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.") - .isInstanceOf(IllegalArgumentException::class.java) } @Test fun `wrong root cert in truststore`() { - val someCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name("Foo", "MU", "GB"), Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)).cert - config.trustStoreFile.parent.createDirectories() - loadOrCreateKeyStore(config.trustStoreFile, config.trustStorePassword).also { - it.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, someCert) - it.save(config.trustStoreFile, config.trustStorePassword) - } + saveTrustStoreWithRootCa(createCaCert(CordaX500Name("Foo", "MU", "GB"))) + val registrationHelper = createRegistrationHelper() assertThatThrownBy { - NetworkRegistrationHelper(config, certService).buildKeystore() + registrationHelper.buildKeystore() }.isInstanceOf(WrongRootCertException::class.java) } - private fun mockRegistrationResponse(vararg response: Certificate): NetworkRegistrationService { - return rigorousMock().also { + private fun createRegistrationHelper(): NetworkRegistrationHelper { + val certService = rigorousMock().also { doReturn(requestId).whenever(it).submitRequest(any()) - doReturn(response).whenever(it).retrieveCertificates(eq(requestId)) + doReturn(arrayOf(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 + } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt index eb11a9b443..311a5c51b2 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt @@ -4,13 +4,11 @@ import net.corda.cordform.CordformContext import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode import net.corda.core.identity.CordaX500Name -import net.corda.core.node.services.NotaryService import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.config.BFTSMaRtConfiguration import net.corda.node.services.config.NotaryConfig -import net.corda.node.services.transactions.BFTNonValidatingNotaryService 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.ALICE_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 // NOT use this as a design to copy. class BFTNotaryCordform : CordformDefinition() { - private val clusterName = CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH") + private val clusterName = CordaX500Name("BFT", "Zurich", "CH") init { nodesDirectory = Paths.get("build", "nodes", "nodesBFT") @@ -64,10 +62,10 @@ class BFTNotaryCordform : CordformDefinition() { } override fun setup(context: CordformContext) { - ServiceIdentityGenerator.generateToDisk( + IdentityGenerator.generateDistributedNotaryIdentity( notaryNames.map { context.baseDirectory(it.toString()) }, clusterName, - NotaryService.constructId(validating = false, bft = true), - minCorrectReplicas(clusterSize)) + minCorrectReplicas(clusterSize) + ) } } diff --git a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt index cfc21fa060..59384a412f 100644 --- a/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt +++ b/samples/notary-demo/src/main/kotlin/net/corda/notarydemo/RaftNotaryCordform.kt @@ -8,8 +8,7 @@ import net.corda.core.node.services.NotaryService import net.corda.core.utilities.NetworkHostAndPort import net.corda.node.services.config.NotaryConfig import net.corda.node.services.config.RaftConfig -import net.corda.node.services.transactions.RaftValidatingNotaryService -import net.corda.nodeapi.internal.ServiceIdentityGenerator +import net.corda.nodeapi.internal.IdentityGenerator import net.corda.testing.node.internal.demorun.* import net.corda.testing.ALICE_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 // NOT use this as a design to copy. class RaftNotaryCordform : CordformDefinition() { - private val clusterName = CordaX500Name(RaftValidatingNotaryService.id, "Raft", "Zurich", "CH") + private val clusterName = CordaX500Name("Raft", "Zurich", "CH") init { nodesDirectory = Paths.get("build", "nodes", "nodesRaft") @@ -60,9 +59,9 @@ class RaftNotaryCordform : CordformDefinition() { } override fun setup(context: CordformContext) { - ServiceIdentityGenerator.generateToDisk( + IdentityGenerator.generateDistributedNotaryIdentity( notaryNames.map { context.baseDirectory(it.toString()) }, - clusterName, - NotaryService.constructId(validating = true, raft = true)) + clusterName + ) } } diff --git a/testing/node-driver/build.gradle b/testing/node-driver/build.gradle index c50a027047..a13ec48018 100644 --- a/testing/node-driver/build.gradle +++ b/testing/node-driver/build.gradle @@ -29,7 +29,7 @@ dependencies { compile "net.corda.plugins:cordform-common:$gradle_plugins_version" // Integration test helpers - integrationTestCompile "org.assertj:assertj-core:${assertj_version}" + testCompile "org.assertj:assertj-core:$assertj_version" integrationTestCompile "junit:junit:$junit_version" // Jetty dependencies for NetworkMapClient test. diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 34cd93d367..84fe07acc5 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -9,6 +9,7 @@ import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate +import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectory import net.corda.core.internal.uncheckedCast @@ -37,7 +38,7 @@ import net.corda.node.services.transactions.BFTSMaRt import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.utilities.AffinityExecutor 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.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NotaryInfo @@ -125,14 +126,14 @@ data class MockNodeArgs( * By default a single notary node is automatically started, which forms part of the network parameters for all the nodes. * This node is available by calling [defaultNotaryNode]. */ -class MockNetwork(private val cordappPackages: List, - defaultParameters: MockNetworkParameters = MockNetworkParameters(), - private val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped, - private val threadPerNode: Boolean = defaultParameters.threadPerNode, - servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, - private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory, - initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, - private val notarySpecs: List = defaultParameters.notarySpecs) { +open class MockNetwork(private val cordappPackages: List, + defaultParameters: MockNetworkParameters = MockNetworkParameters(), + private val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped, + private val threadPerNode: Boolean = defaultParameters.threadPerNode, + servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, + private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory, + initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, + private val notarySpecs: List = defaultParameters.notarySpecs) { /** Helper constructor for creating a [MockNetwork] with custom parameters from Java. */ @JvmOverloads constructor(cordappPackages: List, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters) @@ -141,7 +142,7 @@ class MockNetwork(private val cordappPackages: List, // Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS. // This SFTP support loads BouncyCastle, which we want to avoid. // Please see https://issues.apache.org/jira/browse/SSHD-736 - it's easier then to create our own fork of SSHD - SecurityUtils.setAPrioriDisabledProvider("BC", true) + SecurityUtils.setAPrioriDisabledProvider("BC", true) // XXX: Why isn't this static? } var nextNodeId = 0 @@ -159,6 +160,7 @@ class MockNetwork(private val cordappPackages: List, throw IllegalStateException("Using more than one MockNetwork simultaneously is not supported.", e) } private val sharedUserCount = AtomicInteger(0) + /** A read only view of the current set of nodes. */ val nodes: List get() = _nodes @@ -172,32 +174,29 @@ class MockNetwork(private val cordappPackages: List, * Returns the single notary node on the network. Throws if there are none or more than one. * @see notaryNodes */ - val defaultNotaryNode: StartedNode - get() { - return when (notaryNodes.size) { - 0 -> throw IllegalStateException("There are no notaries defined on the network") - 1 -> notaryNodes[0] - else -> throw IllegalStateException("There is more than one notary defined on the network") - } + val defaultNotaryNode: StartedNode get() { + return when (notaryNodes.size) { + 0 -> throw IllegalStateException("There are no notaries defined on the network") + 1 -> notaryNodes[0] + else -> throw IllegalStateException("There is more than one notary defined on the network") } + } /** * Return the identity of the default notary node. * @see defaultNotaryNode */ - val defaultNotaryIdentity: Party - get() { - return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") - } + val defaultNotaryIdentity: Party get() { + return defaultNotaryNode.info.legalIdentities.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") + } /** * Return the identity of the default notary node. * @see defaultNotaryNode */ - val defaultNotaryIdentityAndCert: PartyAndCertificate - get() { - return defaultNotaryNode.info.legalIdentitiesAndCerts.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") - } + val defaultNotaryIdentityAndCert: PartyAndCertificate get() { + return defaultNotaryNode.info.legalIdentitiesAndCerts.singleOrNull() ?: throw IllegalStateException("Default notary has multiple identities") + } /** * Because this executor is shared, we need to be careful about nodes shutting it down. @@ -222,27 +221,31 @@ class MockNetwork(private val cordappPackages: List, } init { - filesystem.getPath("/nodes").createDirectory() - val notaryInfos = generateNotaryIdentities() - // The network parameters must be serialised before starting any of the nodes - networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos)) - notaryNodes = createNotaries() + try { + filesystem.getPath("/nodes").createDirectory() + val notaryInfos = generateNotaryIdentities() + // The network parameters must be serialised before starting any of the nodes + networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos)) + @Suppress("LeakingThis") + notaryNodes = createNotaries() + } catch (t: Throwable) { + stopNodes() + throw t + } } private fun generateNotaryIdentities(): List { return notarySpecs.mapIndexed { index, (name, validating) -> - val identity = ServiceIdentityGenerator.generateToDisk( - dirs = listOf(baseDirectory(nextNodeId + index)), - serviceName = name, - serviceId = "identity") + val identity = IdentityGenerator.generateNodeIdentity(baseDirectory(nextNodeId + index), name) NotaryInfo(identity, validating) } } - private fun createNotaries(): List> { - return notarySpecs.map { spec -> - createNode(MockNodeParameters(legalName = spec.name, configOverrides = { - doReturn(NotaryConfig(spec.validating)).whenever(it).notary + @VisibleForTesting + internal open fun createNotaries(): List> { + return notarySpecs.map { (name, validating) -> + createNode(MockNodeParameters(legalName = name, configOverrides = { + doReturn(NotaryConfig(validating)).whenever(it).notary })) } } @@ -299,7 +302,7 @@ class MockNetwork(private val cordappPackages: List, id, serverThread, myNotaryIdentity, - myLegalName, + configuration.myLegalName, database).also { runOnStop += it::stop } } @@ -457,8 +460,11 @@ class MockNetwork(private val cordappPackages: List, } fun stopNodes() { - nodes.forEach { it.started?.dispose() } - serializationEnv.unset() + try { + nodes.forEach { it.started?.dispose() } + } finally { + serializationEnv.unset() // Must execute even if other parts of this method fail. + } messagingNetwork.stop() } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index 308ac4fe5a..5833840bb4 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -19,7 +19,6 @@ import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.messaging.CordaRPCOps import net.corda.core.node.services.NetworkMapCache -import net.corda.core.node.services.NotaryService import net.corda.core.toFuture import net.corda.core.utilities.NetworkHostAndPort 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.services.Permissions 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.NetworkRegistrationHelper -import net.corda.nodeapi.internal.ServiceIdentityGenerator +import net.corda.nodeapi.internal.IdentityGenerator import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.parseAs @@ -245,9 +241,9 @@ class DriverDSLImpl( } private enum class ClusterType(val validating: Boolean, val clusterName: CordaX500Name) { - VALIDATING_RAFT(true, CordaX500Name(RaftValidatingNotaryService.id, "Raft", "Zurich", "CH")), - NON_VALIDATING_RAFT(false, CordaX500Name(RaftNonValidatingNotaryService.id, "Raft", "Zurich", "CH")), - NON_VALIDATING_BFT(false, CordaX500Name(BFTNonValidatingNotaryService.id, "BFT", "Zurich", "CH")) + VALIDATING_RAFT(true, CordaX500Name("Raft", "Zurich", "CH")), + NON_VALIDATING_RAFT(false, CordaX500Name("Raft", "Zurich", "CH")), + NON_VALIDATING_BFT(false, CordaX500Name("BFT", "Zurich", "CH")) } internal fun startCordformNodes(cordforms: List): CordaFuture<*> { @@ -270,24 +266,15 @@ class DriverDSLImpl( clusterNodes.put(ClusterType.NON_VALIDATING_BFT, name) } else { // We have all we need here to generate the identity for single node notaries - val identity = ServiceIdentityGenerator.generateToDisk( - dirs = listOf(baseDirectory(name)), - serviceName = name, - serviceId = "identity" - ) + val identity = IdentityGenerator.generateNodeIdentity(baseDirectory(name), legalName = name) notaryInfos += NotaryInfo(identity, notaryConfig.validating) } } clusterNodes.asMap().forEach { type, nodeNames -> - val identity = ServiceIdentityGenerator.generateToDisk( + val identity = IdentityGenerator.generateDistributedNotaryIdentity( dirs = nodeNames.map { baseDirectory(it) }, - serviceName = type.clusterName, - serviceId = NotaryService.constructId( - validating = type.validating, - raft = type in setOf(VALIDATING_RAFT, NON_VALIDATING_RAFT), - bft = type == ClusterType.NON_VALIDATING_BFT - ) + notaryName = type.clusterName ) notaryInfos += NotaryInfo(identity, type.validating) } @@ -369,20 +356,11 @@ class DriverDSLImpl( private fun generateNotaryIdentities(): List { return notarySpecs.map { spec -> val identity = if (spec.cluster == null) { - ServiceIdentityGenerator.generateToDisk( - dirs = listOf(baseDirectory(spec.name)), - serviceName = spec.name, - serviceId = "identity", - customRootCert = compatibilityZone?.rootCert - ) + IdentityGenerator.generateNodeIdentity(baseDirectory(spec.name), spec.name, compatibilityZone?.rootCert) } else { - ServiceIdentityGenerator.generateToDisk( + IdentityGenerator.generateDistributedNotaryIdentity( dirs = generateNodeNames(spec).map { baseDirectory(it) }, - serviceName = spec.name, - serviceId = NotaryService.constructId( - validating = spec.validating, - raft = spec.cluster is ClusterSpec.Raft - ), + notaryName = spec.name, customRootCert = compatibilityZone?.rootCert ) } diff --git a/testing/node-driver/src/test/kotlin/net/corda/testing/node/MockNetworkTests.kt b/testing/node-driver/src/test/kotlin/net/corda/testing/node/MockNetworkTests.kt new file mode 100644 index 0000000000..10d9358243 --- /dev/null +++ b/testing/node-driver/src/test/kotlin/net/corda/testing/node/MockNetworkTests.kt @@ -0,0 +1,18 @@ +package net.corda.testing.node + +import net.corda.core.serialization.internal.effectiveSerializationEnv +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Test + +class MockNetworkTests { + @Test + fun `does not leak serialization env if init fails`() { + val e = Exception("didn't work") + assertThatThrownBy { + object : MockNetwork(emptyList(), initialiseSerialization = true) { + override fun createNotaries() = throw e + } + }.isSameAs(e) + assertThatThrownBy { effectiveSerializationEnv }.isInstanceOf(IllegalStateException::class.java) + } +} diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index 11e572e2d7..860af415ac 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -10,6 +10,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.cert +import net.corda.core.internal.unspecifiedCountry import net.corda.core.internal.x500Name import net.corda.core.node.NodeInfo import net.corda.core.utilities.NetworkHostAndPort @@ -91,13 +92,13 @@ fun getTestPartyAndCertificate(party: Party): PartyAndCertificate { val trustRoot: X509CertificateHolder = DEV_TRUST_ROOT 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 nodeCaCert = X509Utilities.createCertificate( CertificateType.NODE_CA, intermediate.certificate, intermediate.keyPair, - nodeCaName, + party.name, nodeCaKeyPair.public, nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, party.name.x500Name))), arrayOf())) diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt index 37e019b367..ee6b38fdb5 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt @@ -13,7 +13,7 @@ import net.corda.demobench.pty.R3Pty import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.network.NotaryInfo -import net.corda.nodeapi.internal.ServiceIdentityGenerator +import net.corda.nodeapi.internal.IdentityGenerator import tornadofx.* import java.io.IOException 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. private fun getNotaryIdentity(config: NodeConfigWrapper): Party { - return ServiceIdentityGenerator.generateToDisk( - dirs = listOf(config.nodeDir), - serviceName = config.nodeConfig.myLegalName, - serviceId = "identity") + return IdentityGenerator.generateNodeIdentity(config.nodeDir, config.nodeConfig.myLegalName) } fun reset() {