mirror of
https://github.com/corda/corda.git
synced 2025-06-05 00:50:52 +00:00
Moved the CZ URL and node registration logic of the driver to be more internal, not available through the standard driver call, as these are not testing features for an app dev.
Also cleanup up some of the related tests.
This commit is contained in:
parent
66515d24b5
commit
89256a7f16
@ -9,13 +9,13 @@ import net.corda.core.internal.createDirectories
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
import net.corda.nodeapi.internal.crypto.*
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
|
||||||
object ServiceIdentityGenerator {
|
object ServiceIdentityGenerator {
|
||||||
private val log = LoggerFactory.getLogger(javaClass)
|
private val log = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@ -24,25 +24,21 @@ object ServiceIdentityGenerator {
|
|||||||
* @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 serviceName The legal name of the distributed service.
|
||||||
* @param threshold The threshold for the generated group [CompositeKey].
|
* @param threshold The threshold for the generated group [CompositeKey].
|
||||||
* @param rootCertertificate the certificate to use a Corda root CA. If not specified the one in
|
* @param customRootCert the certificate to use a Corda root CA. If not specified the one in
|
||||||
* net/corda/node/internal/certificates/cordadevcakeys.jks is used.
|
* certificates/cordadevcakeys.jks is used.
|
||||||
*/
|
*/
|
||||||
fun generateToDisk(dirs: List<Path>,
|
fun generateToDisk(dirs: List<Path>,
|
||||||
serviceName: CordaX500Name,
|
serviceName: CordaX500Name,
|
||||||
serviceId: String,
|
serviceId: String,
|
||||||
threshold: Int = 1,
|
threshold: Int = 1,
|
||||||
rootCertertificate: X509CertificateHolder? = null): Party {
|
customRootCert: X509Certificate? = null): Party {
|
||||||
log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" }
|
log.trace { "Generating a group identity \"serviceName\" 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 notaryKey = 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 issuer = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
val issuer = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA, "cordacadevkeypass")
|
||||||
val rootCert: Certificate = if (rootCertertificate != null) {
|
val rootCert = customRootCert ?: caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
|
||||||
rootCertertificate.cert
|
|
||||||
} else {
|
|
||||||
caKeyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)
|
|
||||||
}
|
|
||||||
|
|
||||||
keyPairs.zip(dirs) { keyPair, dir ->
|
keyPairs.zip(dirs) { keyPair, dir ->
|
||||||
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, keyPair.public)
|
val serviceKeyCert = X509Utilities.createCertificate(CertificateType.CLIENT_CA, issuer.certificate, issuer.keyPair, serviceName, keyPair.public)
|
||||||
|
@ -15,5 +15,5 @@ interface SSLConfiguration {
|
|||||||
interface NodeSSLConfiguration : SSLConfiguration {
|
interface NodeSSLConfiguration : SSLConfiguration {
|
||||||
val baseDirectory: Path
|
val baseDirectory: Path
|
||||||
override val certificatesDirectory: Path get() = baseDirectory / "certificates"
|
override val certificatesDirectory: Path get() = baseDirectory / "certificates"
|
||||||
val rootCaCertFile: Path get() = certificatesDirectory / "rootcacert.cer"
|
val rootCertFile: Path get() = certificatesDirectory / "rootcert.pem"
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ import net.corda.core.crypto.Crypto
|
|||||||
import net.corda.core.crypto.SignatureScheme
|
import net.corda.core.crypto.SignatureScheme
|
||||||
import net.corda.core.crypto.random63BitValue
|
import net.corda.core.crypto.random63BitValue
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.internal.cert
|
||||||
import net.corda.core.internal.read
|
import net.corda.core.internal.read
|
||||||
import net.corda.core.internal.write
|
|
||||||
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
|
||||||
@ -27,10 +27,8 @@ import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder
|
|||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder
|
||||||
import org.bouncycastle.util.io.pem.PemReader
|
import org.bouncycastle.util.io.pem.PemReader
|
||||||
import java.io.FileWriter
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -164,7 +162,7 @@ object X509Utilities {
|
|||||||
* @param file Target file.
|
* @param file Target file.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun saveCertificateAsPEMFile(x509Certificate: X509CertificateHolder, file: Path) {
|
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, file: Path) {
|
||||||
JcaPEMWriter(file.toFile().writer()).use {
|
JcaPEMWriter(file.toFile().writer()).use {
|
||||||
it.writeObject(x509Certificate)
|
it.writeObject(x509Certificate)
|
||||||
}
|
}
|
||||||
@ -176,14 +174,14 @@ object X509Utilities {
|
|||||||
* @return The X509Certificate that was encoded in the file.
|
* @return The X509Certificate that was encoded in the file.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun loadCertificateFromPEMFile(file: Path): X509CertificateHolder {
|
fun loadCertificateFromPEMFile(file: Path): X509Certificate {
|
||||||
val cert = file.read {
|
return file.read {
|
||||||
val reader = PemReader(it.reader())
|
val reader = PemReader(it.reader())
|
||||||
val pemObject = reader.readPemObject()
|
val pemObject = reader.readPemObject()
|
||||||
X509CertificateHolder(pemObject.content)
|
val certHolder = X509CertificateHolder(pemObject.content)
|
||||||
|
certHolder.isValidOn(Date())
|
||||||
|
certHolder.cert
|
||||||
}
|
}
|
||||||
cert.isValidOn(Date())
|
|
||||||
return cert
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,7 +71,7 @@ class X509UtilitiesTest {
|
|||||||
fun `load and save a PEM file certificate`() {
|
fun `load and save a PEM file certificate`() {
|
||||||
val tmpCertificateFile = tempFile("cacert.pem")
|
val tmpCertificateFile = tempFile("cacert.pem")
|
||||||
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey)
|
val caCert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Test Cert", organisation = "R3 Ltd", locality = "London", country = "GB"), caKey).cert
|
||||||
X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile)
|
X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile)
|
||||||
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
|
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
|
||||||
assertEquals(caCert, readCertificate)
|
assertEquals(caCert, readCertificate)
|
||||||
|
@ -4,85 +4,96 @@ import net.corda.core.node.NodeInfo
|
|||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
|
import net.corda.testing.driver.CompatibilityZoneParams
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.internalDriver
|
||||||
import net.corda.testing.node.network.NetworkMapServer
|
import net.corda.testing.node.network.NetworkMapServer
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
|
// TODO There is a unit test class with the same name. Rename this to something else.
|
||||||
class NetworkMapClientTest {
|
class NetworkMapClientTest {
|
||||||
|
private val cacheTimeout = 1.seconds
|
||||||
private val portAllocation = PortAllocation.Incremental(10000)
|
private val portAllocation = PortAllocation.Incremental(10000)
|
||||||
|
|
||||||
|
private lateinit var networkMapServer: NetworkMapServer
|
||||||
|
private lateinit var compatibilityZone: CompatibilityZoneParams
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun start() {
|
||||||
|
networkMapServer = NetworkMapServer(cacheTimeout, portAllocation.nextHostAndPort())
|
||||||
|
val address = networkMapServer.start()
|
||||||
|
compatibilityZone = CompatibilityZoneParams(URL("http://$address"), rootCert = null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun cleanUp() {
|
||||||
|
networkMapServer.close()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `nodes can see each other using the http network map`() {
|
fun `nodes can see each other using the http network map`() {
|
||||||
NetworkMapServer(1.seconds, portAllocation.nextHostAndPort()).use {
|
internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone) {
|
||||||
val (host, port) = it.start()
|
val alice = startNode(providedName = ALICE.name)
|
||||||
driver(portAllocation = portAllocation, compatibilityZoneURL = URL("http://$host:$port")) {
|
val bob = startNode(providedName = BOB.name)
|
||||||
val alice = startNode(providedName = ALICE.name)
|
|
||||||
val bob = startNode(providedName = BOB.name)
|
|
||||||
|
|
||||||
val notaryNode = defaultNotaryNode.get()
|
val notaryNode = defaultNotaryNode.get()
|
||||||
val aliceNode = alice.get()
|
val aliceNode = alice.get()
|
||||||
val bobNode = bob.get()
|
val bobNode = bob.get()
|
||||||
|
|
||||||
notaryNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
notaryNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
||||||
aliceNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
aliceNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
||||||
bobNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
bobNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `nodes process network map add updates correctly when adding new node to network map`() {
|
fun `nodes process network map add updates correctly when adding new node to network map`() {
|
||||||
NetworkMapServer(1.seconds, portAllocation.nextHostAndPort()).use {
|
internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone) {
|
||||||
val (host, port) = it.start()
|
val alice = startNode(providedName = ALICE.name)
|
||||||
driver(portAllocation = portAllocation, compatibilityZoneURL = URL("http://$host:$port")) {
|
val notaryNode = defaultNotaryNode.get()
|
||||||
val alice = startNode(providedName = ALICE.name)
|
val aliceNode = alice.get()
|
||||||
val notaryNode = defaultNotaryNode.get()
|
|
||||||
val aliceNode = alice.get()
|
|
||||||
|
|
||||||
notaryNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo)
|
notaryNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo)
|
||||||
aliceNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo)
|
aliceNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo)
|
||||||
|
|
||||||
val bob = startNode(providedName = BOB.name)
|
val bob = startNode(providedName = BOB.name)
|
||||||
val bobNode = bob.get()
|
val bobNode = bob.get()
|
||||||
|
|
||||||
// Wait for network map client to poll for the next update.
|
// Wait for network map client to poll for the next update.
|
||||||
Thread.sleep(2.seconds.toMillis())
|
Thread.sleep(cacheTimeout.toMillis() * 2)
|
||||||
|
|
||||||
bobNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
bobNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
||||||
notaryNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
notaryNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
||||||
aliceNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
aliceNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `nodes process network map remove updates correctly`() {
|
fun `nodes process network map remove updates correctly`() {
|
||||||
NetworkMapServer(1.seconds, portAllocation.nextHostAndPort()).use {
|
internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone) {
|
||||||
val (host, port) = it.start()
|
val alice = startNode(providedName = ALICE.name)
|
||||||
driver(portAllocation = portAllocation, compatibilityZoneURL = URL("http://$host:$port")) {
|
val bob = startNode(providedName = BOB.name)
|
||||||
val alice = startNode(providedName = ALICE.name)
|
|
||||||
val bob = startNode(providedName = BOB.name)
|
|
||||||
|
|
||||||
val notaryNode = defaultNotaryNode.get()
|
val notaryNode = defaultNotaryNode.get()
|
||||||
val aliceNode = alice.get()
|
val aliceNode = alice.get()
|
||||||
val bobNode = bob.get()
|
val bobNode = bob.get()
|
||||||
|
|
||||||
notaryNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
notaryNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
||||||
aliceNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
aliceNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
||||||
bobNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
bobNode.onlySees(notaryNode.nodeInfo, aliceNode.nodeInfo, bobNode.nodeInfo)
|
||||||
|
|
||||||
it.removeNodeInfo(aliceNode.nodeInfo)
|
networkMapServer.removeNodeInfo(aliceNode.nodeInfo)
|
||||||
|
|
||||||
// Wait for network map client to poll for the next update.
|
// Wait for network map client to poll for the next update.
|
||||||
Thread.sleep(2.seconds.toMillis())
|
Thread.sleep(cacheTimeout.toMillis() * 2)
|
||||||
|
|
||||||
notaryNode.onlySees(notaryNode.nodeInfo, bobNode.nodeInfo)
|
notaryNode.onlySees(notaryNode.nodeInfo, bobNode.nodeInfo)
|
||||||
bobNode.onlySees(notaryNode.nodeInfo, bobNode.nodeInfo)
|
bobNode.onlySees(notaryNode.nodeInfo, bobNode.nodeInfo)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import net.corda.core.crypto.Crypto
|
|||||||
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.toX509CertHolder
|
import net.corda.core.internal.toX509CertHolder
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.minutes
|
import net.corda.core.utilities.minutes
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
@ -13,12 +13,11 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
|
|||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.driver.CompatibilityZoneParams
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.internalDriver
|
||||||
import net.corda.testing.node.network.NetworkMapServer
|
import net.corda.testing.node.network.NetworkMapServer
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
@ -30,37 +29,26 @@ import java.net.URL
|
|||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.Certificate
|
import java.security.cert.Certificate
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import java.util.concurrent.TimeoutException
|
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
import javax.ws.rs.*
|
import javax.ws.rs.*
|
||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.MediaType
|
||||||
import javax.ws.rs.core.Response
|
import javax.ws.rs.core.Response
|
||||||
|
|
||||||
private const val REQUEST_ID = "requestId"
|
// TODO Rename this to NodeRegistrationTest
|
||||||
|
|
||||||
private val x509CertificateFactory = X509CertificateFactory()
|
|
||||||
private val portAllocation = PortAllocation.Incremental(13000)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Driver based tests for [NetworkRegistrationHelper]
|
|
||||||
*/
|
|
||||||
class NetworkRegistrationHelperDriverTest {
|
class NetworkRegistrationHelperDriverTest {
|
||||||
val rootCertAndKeyPair = createSelfKeyAndSelfSignedCertificate()
|
private val portAllocation = PortAllocation.Incremental(13000)
|
||||||
val rootCert = rootCertAndKeyPair.certificate
|
private val rootCertAndKeyPair = createSelfKeyAndSelfSignedCertificate()
|
||||||
val handler = RegistrationHandler(rootCertAndKeyPair)
|
private val registrationHandler = RegistrationHandler(rootCertAndKeyPair)
|
||||||
lateinit var server: NetworkMapServer
|
|
||||||
lateinit var host: String
|
private lateinit var server: NetworkMapServer
|
||||||
var port: Int = 0
|
private lateinit var compatibilityZone: CompatibilityZoneParams
|
||||||
val compatibilityZoneUrl get() = URL("http", host, port, "")
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun startServer() {
|
fun startServer() {
|
||||||
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), handler)
|
server = NetworkMapServer(1.minutes, portAllocation.nextHostAndPort(), registrationHandler)
|
||||||
val (host, port) = server.start()
|
val address = server.start()
|
||||||
this.host = host
|
compatibilityZone = CompatibilityZoneParams(URL("http://$address"), rootCertAndKeyPair.certificate.cert)
|
||||||
this.port = port
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -68,115 +56,84 @@ class NetworkRegistrationHelperDriverTest {
|
|||||||
server.close()
|
server.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Ideally this test should be checking that two nodes that register are able to transact with each other. However
|
||||||
|
// starting a second node hangs so that needs to be fixed.
|
||||||
@Test
|
@Test
|
||||||
fun `node registration correct root cert`() {
|
fun `node registration correct root cert`() {
|
||||||
driver(portAllocation = portAllocation,
|
internalDriver(
|
||||||
compatibilityZoneURL = compatibilityZoneUrl,
|
portAllocation = portAllocation,
|
||||||
startNodesInProcess = true,
|
notarySpecs = emptyList(),
|
||||||
rootCertificate = rootCert
|
compatibilityZone = compatibilityZone
|
||||||
) {
|
) {
|
||||||
startNode(providedName = ALICE_NAME, initialRegistration = true).get()
|
startNode(providedName = CordaX500Name("Alice", "London", "GB")).getOrThrow()
|
||||||
}
|
assertThat(registrationHandler.idsPolled).contains("Alice")
|
||||||
|
|
||||||
// We're getting:
|
|
||||||
// a request to sign the certificate then
|
|
||||||
// at least one poll request to see if the request has been approved.
|
|
||||||
// all the network map registration and download.
|
|
||||||
assertThat(handler.requests).startsWith("/certificate", "/certificate/" + REQUEST_ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `node registration without root cert`() {
|
|
||||||
driver(portAllocation = portAllocation,
|
|
||||||
compatibilityZoneURL = compatibilityZoneUrl,
|
|
||||||
startNodesInProcess = true
|
|
||||||
) {
|
|
||||||
assertThatThrownBy {
|
|
||||||
startNode(providedName = ALICE_NAME, initialRegistration = true).get()
|
|
||||||
}.isInstanceOf(java.nio.file.NoSuchFileException::class.java)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private fun createSelfKeyAndSelfSignedCertificate(): CertificateAndKeyPair {
|
||||||
fun `node registration wrong root cert`() {
|
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||||
driver(portAllocation = portAllocation,
|
val rootCACert = X509Utilities.createSelfSignedCACertificate(
|
||||||
compatibilityZoneURL = compatibilityZoneUrl,
|
CordaX500Name(
|
||||||
startNodesInProcess = true,
|
commonName = "Integration Test Corda Node Root CA",
|
||||||
rootCertificate = createSelfKeyAndSelfSignedCertificate().certificate
|
organisation = "R3 Ltd",
|
||||||
) {
|
locality = "London",
|
||||||
assertThatThrownBy {
|
country = "GB"),
|
||||||
startNode(providedName = ALICE_NAME, initialRegistration = true).get()
|
rootCAKey)
|
||||||
}.isInstanceOf(WrongRootCaCertificateException::class.java)
|
return CertificateAndKeyPair(rootCACert, rootCAKey)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple registration handler which can handle a single request, which will be given request id [REQUEST_ID].
|
|
||||||
*/
|
|
||||||
@Path("certificate")
|
@Path("certificate")
|
||||||
class RegistrationHandler(private val certificateAndKeyPair: CertificateAndKeyPair) {
|
class RegistrationHandler(private val rootCertAndKeyPair: CertificateAndKeyPair) {
|
||||||
val requests = mutableListOf<String>()
|
private val certPaths = HashMap<String, CertPath>()
|
||||||
lateinit var certificationRequest: JcaPKCS10CertificationRequest
|
val idsPolled = HashSet<String>()
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
|
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
fun registration(input: InputStream): Response {
|
fun registration(input: InputStream): Response {
|
||||||
requests += "/certificate"
|
val certificationRequest = input.use { JcaPKCS10CertificationRequest(it.readBytes()) }
|
||||||
certificationRequest = input.use { JcaPKCS10CertificationRequest(it.readBytes()) }
|
val (certPath, name) = createSignedClientCertificate(
|
||||||
return Response.ok(REQUEST_ID).build()
|
certificationRequest,
|
||||||
|
rootCertAndKeyPair.keyPair,
|
||||||
|
arrayOf(rootCertAndKeyPair.certificate.cert))
|
||||||
|
certPaths[name.organisation] = certPath
|
||||||
|
return Response.ok(name.organisation).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path(REQUEST_ID)
|
@Path("{id}")
|
||||||
fun reply(): Response {
|
fun reply(@PathParam("id") id: String): Response {
|
||||||
requests += "/certificate/" + REQUEST_ID
|
idsPolled += id
|
||||||
val certPath = createSignedClientCertificate(certificationRequest,
|
return buildResponse(certPaths[id]!!.certificates)
|
||||||
certificateAndKeyPair.keyPair, arrayOf(certificateAndKeyPair.certificate.cert))
|
|
||||||
return buildDoormanReply(certPath.certificates.toTypedArray())
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// TODO this logic is shared with doorman itself, refactor this to be somewhere where both doorman and these tests
|
private fun buildResponse(certificates: List<Certificate>): Response {
|
||||||
// can depend on
|
val baos = ByteArrayOutputStream()
|
||||||
private fun createSignedClientCertificate(certificationRequest: PKCS10CertificationRequest,
|
ZipOutputStream(baos).use { zip ->
|
||||||
caKeyPair: KeyPair,
|
listOf(CORDA_CLIENT_CA, CORDA_INTERMEDIATE_CA, CORDA_ROOT_CA).zip(certificates).forEach {
|
||||||
caCertPath: Array<Certificate>): CertPath {
|
zip.putNextEntry(ZipEntry("${it.first}.cer"))
|
||||||
val request = JcaPKCS10CertificationRequest(certificationRequest)
|
zip.write(it.second.encoded)
|
||||||
val x509CertificateHolder = X509Utilities.createCertificate(CertificateType.CLIENT_CA,
|
zip.closeEntry()
|
||||||
caCertPath.first().toX509CertHolder(),
|
}
|
||||||
caKeyPair,
|
|
||||||
CordaX500Name.parse(request.subject.toString()).copy(commonName = X509Utilities.CORDA_CLIENT_CA_CN),
|
|
||||||
request.publicKey,
|
|
||||||
nameConstraints = null)
|
|
||||||
return x509CertificateFactory.generateCertPath(x509CertificateHolder.cert, *caCertPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO this logic is shared with doorman itself, refactor this to be somewhere where both doorman and these tests
|
|
||||||
// can depend on
|
|
||||||
private fun buildDoormanReply(certificates: Array<Certificate>): Response {
|
|
||||||
// Write certificate chain to a zip stream and extract the bit array output.
|
|
||||||
val baos = ByteArrayOutputStream()
|
|
||||||
ZipOutputStream(baos).use { zip ->
|
|
||||||
// Client certificate must come first and root certificate should come last.
|
|
||||||
listOf(CORDA_CLIENT_CA, CORDA_INTERMEDIATE_CA, CORDA_ROOT_CA).zip(certificates).forEach {
|
|
||||||
zip.putNextEntry(ZipEntry("${it.first}.cer"))
|
|
||||||
zip.write(it.second.encoded)
|
|
||||||
zip.closeEntry()
|
|
||||||
}
|
}
|
||||||
|
return Response.ok(baos.toByteArray())
|
||||||
|
.type("application/zip")
|
||||||
|
.header("Content-Disposition", "attachment; filename=\"certificates.zip\"").build()
|
||||||
}
|
}
|
||||||
return Response.ok(baos.toByteArray())
|
|
||||||
.type("application/zip")
|
|
||||||
.header("Content-Disposition", "attachment; filename=\"certificates.zip\"").build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createSelfKeyAndSelfSignedCertificate(): CertificateAndKeyPair {
|
private fun createSignedClientCertificate(certificationRequest: PKCS10CertificationRequest,
|
||||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
caKeyPair: KeyPair,
|
||||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(
|
caCertPath: Array<Certificate>): Pair<CertPath, CordaX500Name> {
|
||||||
CordaX500Name(commonName = "Integration Test Corda Node Root CA",
|
val request = JcaPKCS10CertificationRequest(certificationRequest)
|
||||||
organisation = "R3 Ltd", locality = "London",
|
val name = CordaX500Name.parse(request.subject.toString())
|
||||||
country = "GB"), rootCAKey)
|
val x509CertificateHolder = X509Utilities.createCertificate(CertificateType.CLIENT_CA,
|
||||||
return CertificateAndKeyPair(rootCACert, rootCAKey)
|
caCertPath.first().toX509CertHolder(),
|
||||||
|
caKeyPair,
|
||||||
|
name,
|
||||||
|
request.publicKey,
|
||||||
|
nameConstraints = null)
|
||||||
|
val certPath = X509CertificateFactory().generateCertPath(x509CertificateHolder.cert, *caCertPath)
|
||||||
|
return Pair(certPath, name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,10 +28,18 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
|||||||
val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key"
|
val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(config.rootCertFile.exists()) {
|
||||||
|
"${config.rootCertFile} does not exist. This file must contain the root CA cert of your compatibility zone. " +
|
||||||
|
"Please contact your CZ operator."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt"
|
private val requestIdStore = config.certificatesDirectory / "certificate-request-id.txt"
|
||||||
private val keystorePassword = config.keyStorePassword
|
private val keystorePassword = config.keyStorePassword
|
||||||
// TODO: Use different password for private key.
|
// TODO: Use different password for private key.
|
||||||
private val privateKeyPassword = config.keyStorePassword
|
private val privateKeyPassword = config.keyStorePassword
|
||||||
|
private val rootCert = X509Utilities.loadCertificateFromPEMFile(config.rootCertFile)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure the initial keystore for a node is set up; note that this function may cause the process to exit under
|
* Ensure the initial keystore for a node is set up; note that this function may cause the process to exit under
|
||||||
@ -106,12 +114,11 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks that the passed Certificate is the expected root CA.
|
* Checks that the passed Certificate is the expected root CA.
|
||||||
* @throws WrongRootCaCertificateException if the certificates don't match.
|
* @throws WrongRootCertException if the certificates don't match.
|
||||||
*/
|
*/
|
||||||
private fun checkReturnedRootCaMatchesExpectedCa(returnedRootCa: Certificate) {
|
private fun checkReturnedRootCaMatchesExpectedCa(returnedRootCa: Certificate) {
|
||||||
val expected = X509Utilities.loadCertificateFromPEMFile(config.rootCaCertFile).cert
|
if (rootCert != returnedRootCa) {
|
||||||
if (expected != returnedRootCa) {
|
throw WrongRootCertException(rootCert, returnedRootCa, config.rootCertFile)
|
||||||
throw WrongRootCaCertificateException(expected, returnedRootCa, config.rootCaCertFile)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,9 +180,9 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, private v
|
|||||||
* Exception thrown when the doorman root certificate doesn't match the expected (out-of-band) root certificate.
|
* Exception thrown when the doorman root certificate doesn't match the expected (out-of-band) root certificate.
|
||||||
* This usually means the has been a Man-in-the-middle attack when contacting the doorman.
|
* This usually means the has been a Man-in-the-middle attack when contacting the doorman.
|
||||||
*/
|
*/
|
||||||
class WrongRootCaCertificateException(expected: Certificate,
|
class WrongRootCertException(expected: Certificate,
|
||||||
actual: Certificate,
|
actual: Certificate,
|
||||||
expectedFilePath: Path):
|
expectedFilePath: Path):
|
||||||
Exception("""
|
Exception("""
|
||||||
The Root CA returned back from the registration process does not match the expected Root CA
|
The Root CA returned back from the registration process does not match the expected Root CA
|
||||||
expected: $expected
|
expected: $expected
|
||||||
|
@ -1,53 +1,56 @@
|
|||||||
package net.corda.node.utilities.registration
|
package net.corda.node.utilities.registration
|
||||||
|
|
||||||
import com.nhaarman.mockito_kotlin.*
|
import com.nhaarman.mockito_kotlin.any
|
||||||
|
import com.nhaarman.mockito_kotlin.doReturn
|
||||||
|
import com.nhaarman.mockito_kotlin.eq
|
||||||
|
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.*
|
||||||
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
import net.corda.nodeapi.internal.crypto.getX509Certificate
|
||||||
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
import net.corda.nodeapi.internal.crypto.loadKeyStore
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.rigorousMock
|
import net.corda.testing.rigorousMock
|
||||||
import net.corda.testing.testNodeConfiguration
|
import net.corda.testing.testNodeConfiguration
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
import org.junit.Before
|
||||||
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
|
||||||
|
import java.security.cert.Certificate
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
val X500Name.commonName: String? get() = getRDNs(BCStyle.CN).firstOrNull()?.first?.value?.toString()
|
|
||||||
|
|
||||||
class NetworkRegistrationHelperTest {
|
class NetworkRegistrationHelperTest {
|
||||||
@Rule
|
@Rule
|
||||||
@JvmField
|
@JvmField
|
||||||
val tempFolder = TemporaryFolder()
|
val tempFolder = TemporaryFolder()
|
||||||
|
|
||||||
@Test
|
private val requestId = SecureHash.randomSHA256().toString()
|
||||||
fun buildKeyStore() {
|
private lateinit var config: NodeConfiguration
|
||||||
val id = SecureHash.randomSHA256().toString()
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun init() {
|
||||||
|
config = testNodeConfiguration(baseDirectory = tempFolder.root.toPath(), myLegalName = ALICE.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `successful registration`() {
|
||||||
val identities = listOf("CORDA_CLIENT_CA",
|
val identities = listOf("CORDA_CLIENT_CA",
|
||||||
"CORDA_INTERMEDIATE_CA",
|
"CORDA_INTERMEDIATE_CA",
|
||||||
"CORDA_ROOT_CA")
|
"CORDA_ROOT_CA")
|
||||||
.map { CordaX500Name(commonName = it, organisation = "R3 Ltd", locality = "London", country = "GB") }
|
.map { CordaX500Name(commonName = it, organisation = "R3 Ltd", locality = "London", country = "GB") }
|
||||||
val certs = identities.stream().map { X509Utilities.createSelfSignedCACertificate(it, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) }
|
val certs = identities.stream().map { X509Utilities.createSelfSignedCACertificate(it, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) }
|
||||||
.map { it.cert }.toTypedArray()
|
.map { it.cert }.toTypedArray()
|
||||||
val certService = rigorousMock<NetworkRegistrationService>().also {
|
|
||||||
doReturn(id).whenever(it).submitRequest(any())
|
|
||||||
doReturn(certs).whenever(it).retrieveCertificates(eq(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
val config = testNodeConfiguration(
|
val certService = mockRegistrationResponse(*certs)
|
||||||
baseDirectory = tempFolder.root.toPath(),
|
|
||||||
myLegalName = ALICE.name)
|
|
||||||
|
|
||||||
config.rootCaCertFile.parent.createDirectories()
|
config.rootCertFile.parent.createDirectories()
|
||||||
X509Utilities.saveCertificateAsPEMFile(certs.last().toX509CertHolder(), config.rootCaCertFile)
|
X509Utilities.saveCertificateAsPEMFile(certs.last(), config.rootCertFile)
|
||||||
|
|
||||||
assertFalse(config.nodeKeystore.exists())
|
assertFalse(config.nodeKeystore.exists())
|
||||||
assertFalse(config.sslKeystore.exists())
|
assertFalse(config.sslKeystore.exists())
|
||||||
@ -92,4 +95,44 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
assertTrue(containsAlias(X509Utilities.CORDA_ROOT_CA))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `rootCertFile doesn't exist`() {
|
||||||
|
val certService = rigorousMock<NetworkRegistrationService>()
|
||||||
|
|
||||||
|
assertThatThrownBy {
|
||||||
|
NetworkRegistrationHelper(config, certService)
|
||||||
|
}.hasMessageContaining(config.rootCertFile.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `root cert in response doesn't match expected`() {
|
||||||
|
val identities = listOf("CORDA_CLIENT_CA",
|
||||||
|
"CORDA_INTERMEDIATE_CA",
|
||||||
|
"CORDA_ROOT_CA")
|
||||||
|
.map { CordaX500Name(commonName = it, organisation = "R3 Ltd", locality = "London", country = "GB") }
|
||||||
|
val certs = identities.stream().map { X509Utilities.createSelfSignedCACertificate(it, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) }
|
||||||
|
.map { it.cert }.toTypedArray()
|
||||||
|
|
||||||
|
val certService = mockRegistrationResponse(*certs)
|
||||||
|
|
||||||
|
config.rootCertFile.parent.createDirectories()
|
||||||
|
X509Utilities.saveCertificateAsPEMFile(
|
||||||
|
X509Utilities.createSelfSignedCACertificate(
|
||||||
|
CordaX500Name("CORDA_ROOT_CA", "R3 Ltd", "London", "GB"),
|
||||||
|
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)).cert,
|
||||||
|
config.rootCertFile
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThatThrownBy {
|
||||||
|
NetworkRegistrationHelper(config, certService).buildKeystore()
|
||||||
|
}.isInstanceOf(WrongRootCertException::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mockRegistrationResponse(vararg response: Certificate): NetworkRegistrationService {
|
||||||
|
return rigorousMock<NetworkRegistrationService>().also {
|
||||||
|
doReturn(requestId).whenever(it).submitRequest(any())
|
||||||
|
doReturn(response).whenever(it).retrieveCertificates(eq(requestId))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -76,8 +76,8 @@ class DriverTests {
|
|||||||
assertThat(baseDirectory / "process-id").exists()
|
assertThat(baseDirectory / "process-id").exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
val baseDirectory = driver(notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name))) {
|
val baseDirectory = internalDriver(notarySpecs = listOf(NotarySpec(DUMMY_NOTARY.name))) {
|
||||||
(this as DriverDSL).baseDirectory(DUMMY_NOTARY.name)
|
baseDirectory(DUMMY_NOTARY.name)
|
||||||
}
|
}
|
||||||
assertThat(baseDirectory / "process-id").doesNotExist()
|
assertThat(baseDirectory / "process-id").doesNotExist()
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,6 @@ import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
|||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.observables.ConnectableObservable
|
import rx.observables.ConnectableObservable
|
||||||
@ -57,6 +56,7 @@ import java.net.*
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
|
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.ZoneOffset.UTC
|
import java.time.ZoneOffset.UTC
|
||||||
@ -165,8 +165,8 @@ interface DriverDSLExposedInterface : CordformContext {
|
|||||||
verifierType: VerifierType = defaultParameters.verifierType,
|
verifierType: VerifierType = defaultParameters.verifierType,
|
||||||
customOverrides: Map<String, Any?> = defaultParameters.customOverrides,
|
customOverrides: Map<String, Any?> = defaultParameters.customOverrides,
|
||||||
startInSameProcess: Boolean? = defaultParameters.startInSameProcess,
|
startInSameProcess: Boolean? = defaultParameters.startInSameProcess,
|
||||||
maximumHeapSize: String = defaultParameters.maximumHeapSize,
|
maximumHeapSize: String = defaultParameters.maximumHeapSize
|
||||||
initialRegistration: Boolean = defaultParameters.initialRegistration): CordaFuture<NodeHandle>
|
): CordaFuture<NodeHandle>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -301,8 +301,7 @@ data class NodeParameters(
|
|||||||
val verifierType: VerifierType = VerifierType.InMemory,
|
val verifierType: VerifierType = VerifierType.InMemory,
|
||||||
val customOverrides: Map<String, Any?> = emptyMap(),
|
val customOverrides: Map<String, Any?> = emptyMap(),
|
||||||
val startInSameProcess: Boolean? = null,
|
val startInSameProcess: Boolean? = null,
|
||||||
val maximumHeapSize: String = "200m",
|
val maximumHeapSize: String = "200m"
|
||||||
val initialRegistration: Boolean = false
|
|
||||||
) {
|
) {
|
||||||
fun setProvidedName(providedName: CordaX500Name?) = copy(providedName = providedName)
|
fun setProvidedName(providedName: CordaX500Name?) = copy(providedName = providedName)
|
||||||
fun setRpcUsers(rpcUsers: List<User>) = copy(rpcUsers = rpcUsers)
|
fun setRpcUsers(rpcUsers: List<User>) = copy(rpcUsers = rpcUsers)
|
||||||
@ -339,13 +338,9 @@ data class NodeParameters(
|
|||||||
* not. Note that this may be overridden in [DriverDSLExposedInterface.startNode].
|
* not. Note that this may be overridden in [DriverDSLExposedInterface.startNode].
|
||||||
* @param notarySpecs The notaries advertised for this network. These nodes will be started automatically and will be
|
* @param notarySpecs The notaries advertised for this network. These nodes will be started automatically and will be
|
||||||
* available from [DriverDSLExposedInterface.notaryHandles]. Defaults to a simple validating notary.
|
* available from [DriverDSLExposedInterface.notaryHandles]. Defaults to a simple validating notary.
|
||||||
* @param compatibilityZoneURL if not null each node is started once in registration mode (which makes the node register and quit),
|
|
||||||
* and then re-starts the node with the given parameters.
|
|
||||||
* @param rootCertificate if not null every time a node is started for registration that certificate is written on disk
|
|
||||||
* @param dsl The dsl itself.
|
* @param dsl The dsl itself.
|
||||||
* @return The value returned in the [dsl] closure.
|
* @return The value returned in the [dsl] closure.
|
||||||
*/
|
*/
|
||||||
// TODO: make the registration testing parameters internal.
|
|
||||||
fun <A> driver(
|
fun <A> driver(
|
||||||
defaultParameters: DriverParameters = DriverParameters(),
|
defaultParameters: DriverParameters = DriverParameters(),
|
||||||
isDebug: Boolean = defaultParameters.isDebug,
|
isDebug: Boolean = defaultParameters.isDebug,
|
||||||
@ -359,8 +354,6 @@ fun <A> driver(
|
|||||||
waitForAllNodesToFinish: Boolean = defaultParameters.waitForNodesToFinish,
|
waitForAllNodesToFinish: Boolean = defaultParameters.waitForNodesToFinish,
|
||||||
notarySpecs: List<NotarySpec> = defaultParameters.notarySpecs,
|
notarySpecs: List<NotarySpec> = defaultParameters.notarySpecs,
|
||||||
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
||||||
compatibilityZoneURL: URL? = defaultParameters.compatibilityZoneURL,
|
|
||||||
rootCertificate: X509CertificateHolder? = defaultParameters.rootCertificate,
|
|
||||||
dsl: DriverDSLExposedInterface.() -> A
|
dsl: DriverDSLExposedInterface.() -> A
|
||||||
): A {
|
): A {
|
||||||
return genericDriver(
|
return genericDriver(
|
||||||
@ -375,8 +368,51 @@ fun <A> driver(
|
|||||||
waitForNodesToFinish = waitForAllNodesToFinish,
|
waitForNodesToFinish = waitForAllNodesToFinish,
|
||||||
notarySpecs = notarySpecs,
|
notarySpecs = notarySpecs,
|
||||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||||
compatibilityZoneURL = compatibilityZoneURL,
|
compatibilityZone = null
|
||||||
rootCertificate = rootCertificate
|
),
|
||||||
|
coerce = { it },
|
||||||
|
dsl = dsl,
|
||||||
|
initialiseSerialization = initialiseSerialization
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Move CompatibilityZoneParams and internalDriver into internal package
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property url The base CZ URL for registration and network map updates
|
||||||
|
* @property rootCert If specified then the node will register itself using [url] and expect the registration response
|
||||||
|
* to be rooted at this cert.
|
||||||
|
*/
|
||||||
|
data class CompatibilityZoneParams(val url: URL, val rootCert: X509Certificate?)
|
||||||
|
|
||||||
|
fun <A> internalDriver(
|
||||||
|
isDebug: Boolean = DriverParameters().isDebug,
|
||||||
|
driverDirectory: Path = DriverParameters().driverDirectory,
|
||||||
|
portAllocation: PortAllocation = DriverParameters().portAllocation,
|
||||||
|
debugPortAllocation: PortAllocation = DriverParameters().debugPortAllocation,
|
||||||
|
systemProperties: Map<String, String> = DriverParameters().systemProperties,
|
||||||
|
useTestClock: Boolean = DriverParameters().useTestClock,
|
||||||
|
initialiseSerialization: Boolean = DriverParameters().initialiseSerialization,
|
||||||
|
startNodesInProcess: Boolean = DriverParameters().startNodesInProcess,
|
||||||
|
waitForAllNodesToFinish: Boolean = DriverParameters().waitForNodesToFinish,
|
||||||
|
notarySpecs: List<NotarySpec> = DriverParameters().notarySpecs,
|
||||||
|
extraCordappPackagesToScan: List<String> = DriverParameters().extraCordappPackagesToScan,
|
||||||
|
compatibilityZone: CompatibilityZoneParams? = null,
|
||||||
|
dsl: DriverDSL.() -> A
|
||||||
|
): A {
|
||||||
|
return genericDriver(
|
||||||
|
driverDsl = DriverDSL(
|
||||||
|
portAllocation = portAllocation,
|
||||||
|
debugPortAllocation = debugPortAllocation,
|
||||||
|
systemProperties = systemProperties,
|
||||||
|
driverDirectory = driverDirectory.toAbsolutePath(),
|
||||||
|
useTestClock = useTestClock,
|
||||||
|
isDebug = isDebug,
|
||||||
|
startNodesInProcess = startNodesInProcess,
|
||||||
|
waitForNodesToFinish = waitForAllNodesToFinish,
|
||||||
|
notarySpecs = notarySpecs,
|
||||||
|
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||||
|
compatibilityZone = compatibilityZone
|
||||||
),
|
),
|
||||||
coerce = { it },
|
coerce = { it },
|
||||||
dsl = dsl,
|
dsl = dsl,
|
||||||
@ -411,9 +447,7 @@ data class DriverParameters(
|
|||||||
val startNodesInProcess: Boolean = false,
|
val startNodesInProcess: Boolean = false,
|
||||||
val waitForNodesToFinish: Boolean = false,
|
val waitForNodesToFinish: Boolean = false,
|
||||||
val notarySpecs: List<NotarySpec> = listOf(NotarySpec(DUMMY_NOTARY.name)),
|
val notarySpecs: List<NotarySpec> = listOf(NotarySpec(DUMMY_NOTARY.name)),
|
||||||
val extraCordappPackagesToScan: List<String> = emptyList(),
|
val extraCordappPackagesToScan: List<String> = emptyList()
|
||||||
val compatibilityZoneURL: URL? = null,
|
|
||||||
val rootCertificate: X509CertificateHolder? = null
|
|
||||||
) {
|
) {
|
||||||
fun setIsDebug(isDebug: Boolean) = copy(isDebug = isDebug)
|
fun setIsDebug(isDebug: Boolean) = copy(isDebug = isDebug)
|
||||||
fun setDriverDirectory(driverDirectory: Path) = copy(driverDirectory = driverDirectory)
|
fun setDriverDirectory(driverDirectory: Path) = copy(driverDirectory = driverDirectory)
|
||||||
@ -426,8 +460,6 @@ data class DriverParameters(
|
|||||||
fun setTerminateNodesOnShutdown(terminateNodesOnShutdown: Boolean) = copy(waitForNodesToFinish = terminateNodesOnShutdown)
|
fun setTerminateNodesOnShutdown(terminateNodesOnShutdown: Boolean) = copy(waitForNodesToFinish = terminateNodesOnShutdown)
|
||||||
fun setNotarySpecs(notarySpecs: List<NotarySpec>) = copy(notarySpecs = notarySpecs)
|
fun setNotarySpecs(notarySpecs: List<NotarySpec>) = copy(notarySpecs = notarySpecs)
|
||||||
fun setExtraCordappPackagesToScan(extraCordappPackagesToScan: List<String>) = copy(extraCordappPackagesToScan = extraCordappPackagesToScan)
|
fun setExtraCordappPackagesToScan(extraCordappPackagesToScan: List<String>) = copy(extraCordappPackagesToScan = extraCordappPackagesToScan)
|
||||||
fun setCompatibilityZoneURL(compatibilityZoneURL: URL?) = copy(compatibilityZoneURL = compatibilityZoneURL)
|
|
||||||
fun setRootCertificate(rootCertificate: X509CertificateHolder?) = copy(rootCertificate = rootCertificate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -480,10 +512,9 @@ fun <DI : DriverDSLExposedInterface, D : DriverDSLInternalInterface, A> genericD
|
|||||||
startNodesInProcess: Boolean = defaultParameters.startNodesInProcess,
|
startNodesInProcess: Boolean = defaultParameters.startNodesInProcess,
|
||||||
notarySpecs: List<NotarySpec>,
|
notarySpecs: List<NotarySpec>,
|
||||||
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
||||||
compatibilityZoneURL: URL? = defaultParameters.compatibilityZoneURL,
|
|
||||||
rootCertificate: X509CertificateHolder? = defaultParameters.rootCertificate,
|
|
||||||
driverDslWrapper: (DriverDSL) -> D,
|
driverDslWrapper: (DriverDSL) -> D,
|
||||||
coerce: (D) -> DI, dsl: DI.() -> A
|
coerce: (D) -> DI,
|
||||||
|
dsl: DI.() -> A
|
||||||
): A {
|
): A {
|
||||||
val serializationEnv = setGlobalSerialization(initialiseSerialization)
|
val serializationEnv = setGlobalSerialization(initialiseSerialization)
|
||||||
val driverDsl = driverDslWrapper(
|
val driverDsl = driverDslWrapper(
|
||||||
@ -498,8 +529,7 @@ fun <DI : DriverDSLExposedInterface, D : DriverDSLInternalInterface, A> genericD
|
|||||||
waitForNodesToFinish = waitForNodesToFinish,
|
waitForNodesToFinish = waitForNodesToFinish,
|
||||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||||
notarySpecs = notarySpecs,
|
notarySpecs = notarySpecs,
|
||||||
compatibilityZoneURL = compatibilityZoneURL,
|
compatibilityZone = null
|
||||||
rootCertificate = rootCertificate
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val shutdownHook = addShutdownHook(driverDsl::shutdown)
|
val shutdownHook = addShutdownHook(driverDsl::shutdown)
|
||||||
@ -606,8 +636,7 @@ class DriverDSL(
|
|||||||
val waitForNodesToFinish: Boolean,
|
val waitForNodesToFinish: Boolean,
|
||||||
extraCordappPackagesToScan: List<String>,
|
extraCordappPackagesToScan: List<String>,
|
||||||
val notarySpecs: List<NotarySpec>,
|
val notarySpecs: List<NotarySpec>,
|
||||||
val compatibilityZoneURL: URL?,
|
val compatibilityZone: CompatibilityZoneParams?
|
||||||
val rootCertificate: X509CertificateHolder?
|
|
||||||
) : DriverDSLInternalInterface {
|
) : DriverDSLInternalInterface {
|
||||||
private var _executorService: ScheduledExecutorService? = null
|
private var _executorService: ScheduledExecutorService? = null
|
||||||
val executorService get() = _executorService!!
|
val executorService get() = _executorService!!
|
||||||
@ -679,16 +708,14 @@ class DriverDSL(
|
|||||||
verifierType: VerifierType,
|
verifierType: VerifierType,
|
||||||
customOverrides: Map<String, Any?>,
|
customOverrides: Map<String, Any?>,
|
||||||
startInSameProcess: Boolean?,
|
startInSameProcess: Boolean?,
|
||||||
maximumHeapSize: String,
|
maximumHeapSize: String
|
||||||
initialRegistration: Boolean
|
|
||||||
): CordaFuture<NodeHandle> {
|
): CordaFuture<NodeHandle> {
|
||||||
val p2pAddress = portAllocation.nextHostAndPort()
|
val p2pAddress = portAllocation.nextHostAndPort()
|
||||||
// TODO: Derive name from the full picked name, don't just wrap the common name
|
// TODO: Derive name from the full picked name, don't just wrap the common name
|
||||||
val name = providedName ?: CordaX500Name(organisation = "${oneOf(names).organisation}-${p2pAddress.port}", locality = "London", country = "GB")
|
val name = providedName ?: CordaX500Name(organisation = "${oneOf(names).organisation}-${p2pAddress.port}", locality = "London", country = "GB")
|
||||||
|
|
||||||
val registrationFuture = if (initialRegistration) {
|
val registrationFuture = if (compatibilityZone?.rootCert != null) {
|
||||||
compatibilityZoneURL ?: throw IllegalArgumentException("Compatibility zone URL must be provided for initial registration.")
|
nodeRegistration(name, compatibilityZone.rootCert, compatibilityZone.url)
|
||||||
registerNode(name, compatibilityZoneURL)
|
|
||||||
} else {
|
} else {
|
||||||
doneFuture(Unit)
|
doneFuture(Unit)
|
||||||
}
|
}
|
||||||
@ -709,8 +736,8 @@ class DriverDSL(
|
|||||||
val config = ConfigHelper.loadConfig(
|
val config = ConfigHelper.loadConfig(
|
||||||
baseDirectory = baseDirectory(name),
|
baseDirectory = baseDirectory(name),
|
||||||
allowMissingConfig = true,
|
allowMissingConfig = true,
|
||||||
configOverrides = if (compatibilityZoneURL != null) {
|
configOverrides = if (compatibilityZone != null) {
|
||||||
configMap + mapOf("compatibilityZoneURL" to compatibilityZoneURL.toString())
|
configMap + mapOf("compatibilityZoneURL" to compatibilityZone.url.toString())
|
||||||
} else {
|
} else {
|
||||||
configMap
|
configMap
|
||||||
}
|
}
|
||||||
@ -719,14 +746,10 @@ class DriverDSL(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeRootCaCertificateForNode(path: Path, caRootCertificate: X509CertificateHolder) {
|
private fun nodeRegistration(providedName: CordaX500Name, rootCert: X509Certificate, compatibilityZoneURL: URL): CordaFuture<Unit> {
|
||||||
path.parent.createDirectories()
|
val baseDirectory = baseDirectory(providedName).createDirectories()
|
||||||
X509Utilities.saveCertificateAsPEMFile(caRootCertificate, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun registerNode(providedName: CordaX500Name, compatibilityZoneURL: URL): CordaFuture<Unit> {
|
|
||||||
val config = ConfigHelper.loadConfig(
|
val config = ConfigHelper.loadConfig(
|
||||||
baseDirectory = baseDirectory(providedName),
|
baseDirectory = baseDirectory,
|
||||||
allowMissingConfig = true,
|
allowMissingConfig = true,
|
||||||
configOverrides = configOf(
|
configOverrides = configOf(
|
||||||
"p2pAddress" to "localhost:1222", // required argument, not really used
|
"p2pAddress" to "localhost:1222", // required argument, not really used
|
||||||
@ -734,16 +757,17 @@ class DriverDSL(
|
|||||||
"myLegalName" to providedName.toString())
|
"myLegalName" to providedName.toString())
|
||||||
)
|
)
|
||||||
val configuration = config.parseAsNodeConfiguration()
|
val configuration = config.parseAsNodeConfiguration()
|
||||||
// If a rootCertificate is specified, put that in the node expected path.
|
|
||||||
rootCertificate?.let { writeRootCaCertificateForNode(configuration.rootCaCertFile, it) }
|
configuration.rootCertFile.parent.createDirectories()
|
||||||
if (startNodesInProcess) {
|
X509Utilities.saveCertificateAsPEMFile(rootCert, configuration.rootCertFile)
|
||||||
|
|
||||||
|
return if (startNodesInProcess) {
|
||||||
// This is a bit cheating, we're not starting a full node, we're just calling the code nodes call
|
// This is a bit cheating, we're not starting a full node, we're just calling the code nodes call
|
||||||
// when registering.
|
// when registering.
|
||||||
NetworkRegistrationHelper(configuration, HTTPNetworkRegistrationService(compatibilityZoneURL))
|
NetworkRegistrationHelper(configuration, HTTPNetworkRegistrationService(compatibilityZoneURL)).buildKeystore()
|
||||||
.buildKeystore()
|
doneFuture(Unit)
|
||||||
return doneFuture(Unit)
|
|
||||||
} else {
|
} else {
|
||||||
return startNodeForRegistration(config)
|
startOutOfProcessNodeRegistration(config, configuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -872,18 +896,18 @@ class DriverDSL(
|
|||||||
ServiceIdentityGenerator.generateToDisk(
|
ServiceIdentityGenerator.generateToDisk(
|
||||||
dirs = listOf(baseDirectory(spec.name)),
|
dirs = listOf(baseDirectory(spec.name)),
|
||||||
serviceName = spec.name,
|
serviceName = spec.name,
|
||||||
rootCertertificate = rootCertificate,
|
serviceId = "identity",
|
||||||
serviceId = "identity"
|
customRootCert = compatibilityZone?.rootCert
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ServiceIdentityGenerator.generateToDisk(
|
ServiceIdentityGenerator.generateToDisk(
|
||||||
dirs = generateNodeNames(spec).map { baseDirectory(it) },
|
dirs = generateNodeNames(spec).map { baseDirectory(it) },
|
||||||
serviceName = spec.name,
|
serviceName = spec.name,
|
||||||
rootCertertificate = rootCertificate,
|
|
||||||
serviceId = NotaryService.constructId(
|
serviceId = NotaryService.constructId(
|
||||||
validating = spec.validating,
|
validating = spec.validating,
|
||||||
raft = spec.cluster is ClusterSpec.Raft
|
raft = spec.cluster is ClusterSpec.Raft
|
||||||
)
|
),
|
||||||
|
customRootCert = compatibilityZone?.rootCert
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
NotaryInfo(identity, spec.validating)
|
NotaryInfo(identity, spec.validating)
|
||||||
@ -1008,16 +1032,12 @@ class DriverDSL(
|
|||||||
return future
|
return future
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startNodeForRegistration(config: Config): CordaFuture<Unit> {
|
private fun startOutOfProcessNodeRegistration(config: Config, configuration: NodeConfiguration): CordaFuture<Unit> {
|
||||||
val maximumHeapSize = "200m"
|
|
||||||
val configuration = config.parseAsNodeConfiguration()
|
|
||||||
configuration.baseDirectory.createDirectories()
|
|
||||||
|
|
||||||
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
||||||
val process = startOutOfProcessNode(configuration, config, quasarJarPath, debugPort,
|
val process = startOutOfProcessNode(configuration, config, quasarJarPath, debugPort,
|
||||||
systemProperties, cordappPackages, maximumHeapSize, initialRegistration = true)
|
systemProperties, cordappPackages, "200m", initialRegistration = true)
|
||||||
|
|
||||||
return poll(executorService, "process exit") {
|
return poll(executorService, "node registration (${configuration.myLegalName})") {
|
||||||
if (process.isAlive) null else Unit
|
if (process.isAlive) null else Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1030,7 +1050,7 @@ class DriverDSL(
|
|||||||
val baseDirectory = configuration.baseDirectory.createDirectories()
|
val baseDirectory = configuration.baseDirectory.createDirectories()
|
||||||
// Distribute node info file using file copier when network map service URL (compatibilityZoneURL) is null.
|
// Distribute node info file using file copier when network map service URL (compatibilityZoneURL) is null.
|
||||||
// TODO: need to implement the same in cordformation?
|
// TODO: need to implement the same in cordformation?
|
||||||
val nodeInfoFilesCopier = if (compatibilityZoneURL == null) nodeInfoFilesCopier else null
|
val nodeInfoFilesCopier = if (compatibilityZone == null) nodeInfoFilesCopier else null
|
||||||
|
|
||||||
nodeInfoFilesCopier?.addConfig(baseDirectory)
|
nodeInfoFilesCopier?.addConfig(baseDirectory)
|
||||||
networkParameters!!.install(baseDirectory)
|
networkParameters!!.install(baseDirectory)
|
||||||
@ -1068,7 +1088,7 @@ class DriverDSL(
|
|||||||
}
|
}
|
||||||
val p2pReadyFuture = addressMustBeBoundFuture(executorService, configuration.p2pAddress, process)
|
val p2pReadyFuture = addressMustBeBoundFuture(executorService, configuration.p2pAddress, process)
|
||||||
return p2pReadyFuture.flatMap {
|
return p2pReadyFuture.flatMap {
|
||||||
val processDeathFuture = poll(executorService, "process death") {
|
val processDeathFuture = poll(executorService, "process death while waiting for RPC (${configuration.myLegalName})") {
|
||||||
if (process.isAlive) null else process
|
if (process.isAlive) null else process
|
||||||
}
|
}
|
||||||
establishRpc(configuration, processDeathFuture).flatMap { rpc ->
|
establishRpc(configuration, processDeathFuture).flatMap { rpc ->
|
||||||
|
@ -47,9 +47,7 @@ import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy
|
|||||||
import org.apache.activemq.artemis.core.settings.impl.AddressSettings
|
import org.apache.activemq.artemis.core.settings.impl.AddressSettings
|
||||||
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection
|
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection
|
||||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager3
|
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager3
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.net.URL
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -225,6 +223,7 @@ val fakeNodeLegalName = CordaX500Name(organisation = "Not:a:real:name", locality
|
|||||||
// Use a global pool so that we can run RPC tests in parallel
|
// Use a global pool so that we can run RPC tests in parallel
|
||||||
private val globalPortAllocation = PortAllocation.Incremental(10000)
|
private val globalPortAllocation = PortAllocation.Incremental(10000)
|
||||||
private val globalDebugPortAllocation = PortAllocation.Incremental(5005)
|
private val globalDebugPortAllocation = PortAllocation.Incremental(5005)
|
||||||
|
|
||||||
fun <A> rpcDriver(
|
fun <A> rpcDriver(
|
||||||
isDebug: Boolean = false,
|
isDebug: Boolean = false,
|
||||||
driverDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
driverDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
||||||
@ -237,8 +236,6 @@ fun <A> rpcDriver(
|
|||||||
extraCordappPackagesToScan: List<String> = emptyList(),
|
extraCordappPackagesToScan: List<String> = emptyList(),
|
||||||
notarySpecs: List<NotarySpec> = emptyList(),
|
notarySpecs: List<NotarySpec> = emptyList(),
|
||||||
externalTrace: Trace? = null,
|
externalTrace: Trace? = null,
|
||||||
compatibilityZoneURL: URL? = null,
|
|
||||||
rootCertificate: X509CertificateHolder? = null,
|
|
||||||
dsl: RPCDriverExposedDSLInterface.() -> A
|
dsl: RPCDriverExposedDSLInterface.() -> A
|
||||||
) = genericDriver(
|
) = genericDriver(
|
||||||
driverDsl = RPCDriverDSL(
|
driverDsl = RPCDriverDSL(
|
||||||
@ -253,8 +250,7 @@ fun <A> rpcDriver(
|
|||||||
waitForNodesToFinish = waitForNodesToFinish,
|
waitForNodesToFinish = waitForNodesToFinish,
|
||||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||||
notarySpecs = notarySpecs,
|
notarySpecs = notarySpecs,
|
||||||
compatibilityZoneURL = compatibilityZoneURL,
|
compatibilityZone = null
|
||||||
rootCertificate = rootCertificate
|
|
||||||
), externalTrace
|
), externalTrace
|
||||||
),
|
),
|
||||||
coerce = { it },
|
coerce = { it },
|
||||||
|
@ -6,9 +6,8 @@ import net.corda.cordform.CordformDefinition
|
|||||||
import net.corda.cordform.CordformNode
|
import net.corda.cordform.CordformNode
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.testing.driver.DriverDSL
|
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.internalDriver
|
||||||
|
|
||||||
fun CordformDefinition.clean() {
|
fun CordformDefinition.clean() {
|
||||||
System.err.println("Deleting: $nodesDirectory")
|
System.err.println("Deleting: $nodesDirectory")
|
||||||
@ -39,7 +38,7 @@ private fun CordformDefinition.runNodes(waitForAllNodesToFinish: Boolean, block:
|
|||||||
.flatMap { listOf(it.p2pAddress, it.rpcAddress, it.webAddress) }
|
.flatMap { listOf(it.p2pAddress, it.rpcAddress, it.webAddress) }
|
||||||
.mapNotNull { address -> address?.let { NetworkHostAndPort.parse(it).port } }
|
.mapNotNull { address -> address?.let { NetworkHostAndPort.parse(it).port } }
|
||||||
.max()!!
|
.max()!!
|
||||||
driver(
|
internalDriver(
|
||||||
isDebug = true,
|
isDebug = true,
|
||||||
driverDirectory = nodesDirectory,
|
driverDirectory = nodesDirectory,
|
||||||
extraCordappPackagesToScan = cordappPackages,
|
extraCordappPackagesToScan = cordappPackages,
|
||||||
@ -50,7 +49,7 @@ private fun CordformDefinition.runNodes(waitForAllNodesToFinish: Boolean, block:
|
|||||||
waitForAllNodesToFinish = waitForAllNodesToFinish
|
waitForAllNodesToFinish = waitForAllNodesToFinish
|
||||||
) {
|
) {
|
||||||
setup(this)
|
setup(this)
|
||||||
(this as DriverDSL).startCordformNodes(nodes).getOrThrow() // Only proceed once everything is up and running
|
startCordformNodes(nodes).getOrThrow() // Only proceed once everything is up and running
|
||||||
println("All nodes and webservers are ready...")
|
println("All nodes and webservers are ready...")
|
||||||
block()
|
block()
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,9 @@ import net.corda.node.services.config.configureDevKeyAndTrustStores
|
|||||||
import net.corda.nodeapi.ArtemisTcpTransport
|
import net.corda.nodeapi.ArtemisTcpTransport
|
||||||
import net.corda.nodeapi.ConnectionDirection
|
import net.corda.nodeapi.ConnectionDirection
|
||||||
import net.corda.nodeapi.VerifierApi
|
import net.corda.nodeapi.VerifierApi
|
||||||
|
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
||||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
|
||||||
import net.corda.testing.driver.*
|
import net.corda.testing.driver.*
|
||||||
import net.corda.testing.internal.ProcessUtilities
|
import net.corda.testing.internal.ProcessUtilities
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
@ -37,8 +37,6 @@ import org.apache.activemq.artemis.core.security.CheckType
|
|||||||
import org.apache.activemq.artemis.core.security.Role
|
import org.apache.activemq.artemis.core.security.Role
|
||||||
import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl
|
import org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl
|
||||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager
|
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager
|
||||||
import org.bouncycastle.cert.X509CertificateHolder
|
|
||||||
import java.net.URL
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
@ -87,8 +85,6 @@ fun <A> verifierDriver(
|
|||||||
waitForNodesToFinish: Boolean = false,
|
waitForNodesToFinish: Boolean = false,
|
||||||
extraCordappPackagesToScan: List<String> = emptyList(),
|
extraCordappPackagesToScan: List<String> = emptyList(),
|
||||||
notarySpecs: List<NotarySpec> = emptyList(),
|
notarySpecs: List<NotarySpec> = emptyList(),
|
||||||
compatibilityZoneURL: URL? = null,
|
|
||||||
rootCertificate: X509CertificateHolder? = null,
|
|
||||||
dsl: VerifierExposedDSLInterface.() -> A
|
dsl: VerifierExposedDSLInterface.() -> A
|
||||||
) = genericDriver(
|
) = genericDriver(
|
||||||
driverDsl = VerifierDriverDSL(
|
driverDsl = VerifierDriverDSL(
|
||||||
@ -103,8 +99,7 @@ fun <A> verifierDriver(
|
|||||||
waitForNodesToFinish = waitForNodesToFinish,
|
waitForNodesToFinish = waitForNodesToFinish,
|
||||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||||
notarySpecs = notarySpecs,
|
notarySpecs = notarySpecs,
|
||||||
compatibilityZoneURL = compatibilityZoneURL,
|
compatibilityZone = null
|
||||||
rootCertificate = rootCertificate
|
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
coerce = { it },
|
coerce = { it },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user