RAFT notary demo refactoring (#629)

Specifically, make the IDE/driver and gradle/NodeRunner methods of launching the demo behave more similarly, with a view to configuring them the same way.
* Add option to driver to nominate a node as network map, so that the driver-based demo doesn't run an additional node
* Change gradle ports to match those chosen by driver
This commit is contained in:
Andrzej Cichocki
2017-05-12 11:33:26 +01:00
committed by GitHub
parent e2ce80c8ec
commit e981632184
10 changed files with 107 additions and 44 deletions

View File

@ -1,6 +1,7 @@
package net.corda.node package net.corda.node
import com.google.common.base.Stopwatch import com.google.common.base.Stopwatch
import net.corda.node.driver.FalseNetworkMap
import net.corda.node.driver.driver import net.corda.node.driver.driver
import org.junit.Ignore import org.junit.Ignore
import org.junit.Test import org.junit.Test
@ -12,7 +13,7 @@ class NodeStartupPerformanceTests {
// Measure the startup time of nodes. Note that this includes an RPC roundtrip, which causes e.g. Kryo initialisation. // Measure the startup time of nodes. Note that this includes an RPC roundtrip, which causes e.g. Kryo initialisation.
@Test @Test
fun `single node startup time`() { fun `single node startup time`() {
driver(automaticallyStartNetworkMap = false) { driver(networkMapStrategy = FalseNetworkMap) {
startNetworkMapService().get() startNetworkMapService().get()
val times = ArrayList<Long>() val times = ArrayList<Long>()
for (i in 1 .. 10) { for (i in 1 .. 10) {

View File

@ -102,7 +102,7 @@ interface DriverDSLExposedInterface {
/** /**
* Starts a network map service node. Note that only a single one should ever be running, so you will probably want * Starts a network map service node. Note that only a single one should ever be running, so you will probably want
* to set automaticallyStartNetworkMap to false in your [driver] call. * to set networkMapStrategy to FalseNetworkMap in your [driver] call.
*/ */
fun startNetworkMapService(): ListenableFuture<Unit> fun startNetworkMapService(): ListenableFuture<Unit>
@ -201,7 +201,7 @@ fun <A> driver(
debugPortAllocation: PortAllocation = PortAllocation.Incremental(5005), debugPortAllocation: PortAllocation = PortAllocation.Incremental(5005),
systemProperties: Map<String, String> = emptyMap(), systemProperties: Map<String, String> = emptyMap(),
useTestClock: Boolean = false, useTestClock: Boolean = false,
automaticallyStartNetworkMap: Boolean = true, networkMapStrategy: NetworkMapStrategy = DedicatedNetworkMap,
dsl: DriverDSLExposedInterface.() -> A dsl: DriverDSLExposedInterface.() -> A
) = genericDriver( ) = genericDriver(
driverDsl = DriverDSL( driverDsl = DriverDSL(
@ -210,7 +210,7 @@ fun <A> driver(
systemProperties = systemProperties, systemProperties = systemProperties,
driverDirectory = driverDirectory.toAbsolutePath(), driverDirectory = driverDirectory.toAbsolutePath(),
useTestClock = useTestClock, useTestClock = useTestClock,
automaticallyStartNetworkMap = automaticallyStartNetworkMap, networkMapStrategy = networkMapStrategy,
isDebug = isDebug isDebug = isDebug
), ),
coerce = { it }, coerce = { it },
@ -412,10 +412,9 @@ class DriverDSL(
val driverDirectory: Path, val driverDirectory: Path,
val useTestClock: Boolean, val useTestClock: Boolean,
val isDebug: Boolean, val isDebug: Boolean,
val automaticallyStartNetworkMap: Boolean val networkMapStrategy: NetworkMapStrategy
) : DriverDSLInternalInterface { ) : DriverDSLInternalInterface {
private val networkMapLegalName = DUMMY_MAP.name private val dedicatedNetworkMapAddress = portAllocation.nextHostAndPort()
private val networkMapAddress = portAllocation.nextHostAndPort()
val executorService: ListeningScheduledExecutorService = MoreExecutors.listeningDecorator( val executorService: ListeningScheduledExecutorService = MoreExecutors.listeningDecorator(
Executors.newScheduledThreadPool(2, ThreadFactoryBuilder().setNameFormat("driver-pool-thread-%d").build()) Executors.newScheduledThreadPool(2, ThreadFactoryBuilder().setNameFormat("driver-pool-thread-%d").build())
) )
@ -488,10 +487,7 @@ class DriverDSL(
"rpcAddress" to rpcAddress.toString(), "rpcAddress" to rpcAddress.toString(),
"webAddress" to webAddress.toString(), "webAddress" to webAddress.toString(),
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() }, "extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
"networkMapService" to mapOf( "networkMapService" to networkMapStrategy.serviceConfig(dedicatedNetworkMapAddress, name, p2pAddress),
"address" to networkMapAddress.toString(),
"legalName" to networkMapLegalName.toString()
),
"useTestClock" to useTestClock, "useTestClock" to useTestClock,
"rpcUsers" to rpcUsers.map { "rpcUsers" to rpcUsers.map {
mapOf( mapOf(
@ -578,7 +574,7 @@ class DriverDSL(
} }
override fun start() { override fun start() {
if (automaticallyStartNetworkMap) { if (networkMapStrategy.startDedicated) {
startNetworkMapService() startNetworkMapService()
} }
} }
@ -586,6 +582,7 @@ class DriverDSL(
override fun startNetworkMapService(): ListenableFuture<Unit> { override fun startNetworkMapService(): ListenableFuture<Unit> {
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
val apiAddress = portAllocation.nextHostAndPort().toString() val apiAddress = portAllocation.nextHostAndPort().toString()
val networkMapLegalName = networkMapStrategy.legalName
val baseDirectory = driverDirectory / networkMapLegalName.commonName val baseDirectory = driverDirectory / networkMapLegalName.commonName
val config = ConfigHelper.loadConfig( val config = ConfigHelper.loadConfig(
baseDirectory = baseDirectory, baseDirectory = baseDirectory,
@ -595,7 +592,7 @@ class DriverDSL(
// TODO: remove the webAddress as NMS doesn't need to run a web server. This will cause all // TODO: remove the webAddress as NMS doesn't need to run a web server. This will cause all
// node port numbers to be shifted, so all demos and docs need to be updated accordingly. // node port numbers to be shifted, so all demos and docs need to be updated accordingly.
"webAddress" to apiAddress, "webAddress" to apiAddress,
"p2pAddress" to networkMapAddress.toString(), "p2pAddress" to dedicatedNetworkMapAddress.toString(),
"useTestClock" to useTestClock "useTestClock" to useTestClock
) )
) )
@ -603,7 +600,7 @@ class DriverDSL(
log.info("Starting network-map-service") log.info("Starting network-map-service")
val startNode = startNode(executorService, config.parseAs<FullNodeConfiguration>(), config, quasarJarPath, debugPort, systemProperties) val startNode = startNode(executorService, config.parseAs<FullNodeConfiguration>(), config, quasarJarPath, debugPort, systemProperties)
registerProcess(startNode) registerProcess(startNode)
return startNode.flatMap { addressMustBeBound(executorService, networkMapAddress, it) } return startNode.flatMap { addressMustBeBound(executorService, dedicatedNetworkMapAddress, it) }
} }
override fun <A> pollUntilNonNull(pollName: String, pollInterval: Duration, warnCount: Int, check: () -> A?): ListenableFuture<A> { override fun <A> pollUntilNonNull(pollName: String, pollInterval: Duration, warnCount: Int, check: () -> A?): ListenableFuture<A> {

View File

@ -0,0 +1,47 @@
package net.corda.node.driver
import com.google.common.net.HostAndPort
import net.corda.core.utilities.DUMMY_MAP
import org.bouncycastle.asn1.x500.X500Name
/**
* Instruct the driver how to set up the network map, if at all.
* @see FalseNetworkMap
* @see DedicatedNetworkMap
* @see NominatedNetworkMap
*/
abstract class NetworkMapStrategy(internal val startDedicated: Boolean, internal val legalName: X500Name) {
internal abstract fun serviceConfig(dedicatedAddress: HostAndPort, nodeName: X500Name, p2pAddress: HostAndPort): Map<String, String>?
}
private fun toServiceConfig(address: HostAndPort, legalName: X500Name) = mapOf(
"address" to address.toString(),
"legalName" to legalName.toString()
)
abstract class AbstractDedicatedNetworkMap(start: Boolean) : NetworkMapStrategy(start, DUMMY_MAP.name) {
override fun serviceConfig(dedicatedAddress: HostAndPort, nodeName: X500Name, p2pAddress: HostAndPort) = toServiceConfig(dedicatedAddress, legalName)
}
/**
* Do not start a network map.
*/
object FalseNetworkMap : AbstractDedicatedNetworkMap(false)
/**
* Start a dedicated node to host the network map.
*/
object DedicatedNetworkMap : AbstractDedicatedNetworkMap(true)
/**
* As in gradle-based demos, nominate a node to host the network map, so that there is one fewer node in total than in the [DedicatedNetworkMap] case.
* Will fail if the port you pass in does not match the P2P port the driver assigns to the named node.
*/
class NominatedNetworkMap(legalName: X500Name, private val address: HostAndPort) : NetworkMapStrategy(false, legalName) {
override fun serviceConfig(dedicatedAddress: HostAndPort, nodeName: X500Name, p2pAddress: HostAndPort) = if (nodeName != legalName) {
toServiceConfig(address, legalName)
} else {
p2pAddress == address || throw IllegalArgumentException("Passed-in address $address of nominated network map $legalName is wrong, it should be: $p2pAddress")
null
}
}

View File

@ -7,7 +7,7 @@ apply plugin: 'net.corda.plugins.cordformation'
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
ext { ext {
deployTo = "./build/nodes" deployTo = "build/nodes"
notaryType = "corda.notary.validating.raft" notaryType = "corda.notary.validating.raft"
notaryName = "CN=Raft,O=R3,OU=corda,L=Zurich,C=CH" notaryName = "CN=Raft,O=R3,OU=corda,L=Zurich,C=CH"
advertisedNotary = "$notaryType|$notaryName" advertisedNotary = "$notaryType|$notaryName"
@ -53,7 +53,13 @@ publishing {
} }
} }
task generateNotaryIdentity(type: JavaExec) { task cleanNodes {
doLast {
delete deployTo
}
}
task generateNotaryIdentity(type: JavaExec, dependsOn: 'cleanNodes') {
classpath = sourceSets.main.runtimeClasspath classpath = sourceSets.main.runtimeClasspath
main = "net.corda.node.utilities.ServiceIdentityGeneratorKt" main = "net.corda.node.utilities.ServiceIdentityGeneratorKt"
def nodeDirs = ["$deployTo/Notary1", def nodeDirs = ["$deployTo/Notary1",
@ -66,7 +72,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', 'generateN
directory deployTo directory deployTo
networkMap "CN=Notary 1,O=R3,OU=corda,L=London,C=UK" networkMap "CN=Notary 1,O=R3,OU=corda,L=London,C=UK"
node { node {
name "CN=Party,O=R3,OU=corda,L=London,C=UK" name "CN=Alice Corp,O=Alice Corp,L=London,C=UK"
nearestCity "London" nearestCity "London"
advertisedServices = [] advertisedServices = []
p2pPort 10002 p2pPort 10002
@ -78,7 +84,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', 'generateN
]]] ]]]
} }
node { node {
name "CN=Counterparty,O=R3,OU=corda,L=London,C=UK" name "CN=Bob Plc,O=Bob Plc,L=London,C=UK"
nearestCity "New York" nearestCity "New York"
advertisedServices = [] advertisedServices = []
p2pPort 10005 p2pPort 10005
@ -89,30 +95,30 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', 'generateN
name "CN=Notary 1,O=R3,OU=corda,L=London,C=UK" name "CN=Notary 1,O=R3,OU=corda,L=London,C=UK"
nearestCity "London" nearestCity "London"
advertisedServices = [advertisedNotary] advertisedServices = [advertisedNotary]
p2pPort 10008 p2pPort 10009
rpcPort 10009 rpcPort 10010
cordapps = [] cordapps = []
notaryNodePort 11002 notaryNodePort 10008
} }
node { node {
name "CN=Notary 2,O=R3,OU=corda,L=London,C=UK" name "CN=Notary 2,O=R3,OU=corda,L=London,C=UK"
nearestCity "London" nearestCity "London"
advertisedServices = [advertisedNotary] advertisedServices = [advertisedNotary]
p2pPort 10011 p2pPort 10013
rpcPort 10012 rpcPort 10014
cordapps = [] cordapps = []
notaryNodePort 11004 notaryNodePort 10012
notaryClusterAddresses = ["localhost:11002"] notaryClusterAddresses = ["localhost:10008"]
} }
node { node {
name "CN=Notary 3,O=R3,OU=corda,L=London,C=UK" name "CN=Notary 3,O=R3,OU=corda,L=London,C=UK"
nearestCity "London" nearestCity "London"
advertisedServices = [advertisedNotary] advertisedServices = [advertisedNotary]
p2pPort 10014 p2pPort 10017
rpcPort 10015 rpcPort 10018
cordapps = [] cordapps = []
notaryNodePort 11006 notaryNodePort 10016
notaryClusterAddresses = ["localhost:11002"] notaryClusterAddresses = ["localhost:10008"]
} }
} }

View File

@ -1,22 +1,30 @@
package net.corda.notarydemo package net.corda.notarydemo
import com.google.common.net.HostAndPort
import net.corda.core.crypto.appendToCommonName
import net.corda.core.div import net.corda.core.div
import net.corda.core.utilities.ALICE import net.corda.core.utilities.ALICE
import net.corda.core.utilities.BOB import net.corda.core.utilities.BOB
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.flows.NotaryFlow
import net.corda.node.driver.NominatedNetworkMap
import net.corda.node.driver.PortAllocation
import net.corda.node.driver.driver import net.corda.node.driver.driver
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.services.transactions.RaftValidatingNotaryService
import net.corda.nodeapi.User import net.corda.nodeapi.User
import net.corda.notarydemo.flows.DummyIssueAndMove
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import java.nio.file.Paths import java.nio.file.Paths
/** Creates and starts all nodes required for the demo. */ /** Creates and starts all nodes required for the demo. */
fun main(args: Array<String>) { fun main(args: Array<String>) {
val demoUser = listOf(User("demo", "demo", setOf("StartFlow.net.corda.notarydemo.flows.DummyIssueAndMove", "StartFlow.net.corda.flows.NotaryFlow\$Client"))) val demoUser = listOf(User("demo", "demo", setOf(startFlowPermission<DummyIssueAndMove>(), startFlowPermission<NotaryFlow.Client>())))
driver(isDebug = true, driverDirectory = Paths.get("build") / "notary-demo-nodes") { val networkMap = NominatedNetworkMap(DUMMY_NOTARY.name.appendToCommonName("1"), HostAndPort.fromParts("localhost", 10009))
driver(isDebug = true, driverDirectory = Paths.get("build") / "notary-demo-nodes", networkMapStrategy = networkMap, portAllocation = PortAllocation.Incremental(10001)) {
startNode(ALICE.name, rpcUsers = demoUser) startNode(ALICE.name, rpcUsers = demoUser)
startNode(BOB.name) startNode(BOB.name)
startNotaryCluster(DUMMY_NOTARY.name, clusterSize = 3, type = RaftValidatingNotaryService.type) startNotaryCluster(X500Name("CN=Raft,O=R3,OU=corda,L=Zurich,C=CH"), clusterSize = 3, type = RaftValidatingNotaryService.type)
waitForAllNodesToFinish() waitForAllNodesToFinish()
} }
} }

View File

@ -11,6 +11,7 @@ import net.corda.core.getOrThrow
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.BOB
import net.corda.flows.NotaryFlow import net.corda.flows.NotaryFlow
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.config.SSLConfiguration
import net.corda.notarydemo.flows.DummyIssueAndMove import net.corda.notarydemo.flows.DummyIssueAndMove
@ -39,7 +40,7 @@ private class NotaryDemoClientApi(val rpc: CordaRPCOps) {
private val counterpartyNode by lazy { private val counterpartyNode by lazy {
val (parties, partyUpdates) = rpc.networkMapUpdates() val (parties, partyUpdates) = rpc.networkMapUpdates()
partyUpdates.notUsed() partyUpdates.notUsed()
parties.first { it.legalIdentity.name == X500Name("CN=Counterparty,O=R3,OU=corda,L=London,C=UK") } parties.first { it.legalIdentity.name == BOB.name }
} }
private companion object { private companion object {

View File

@ -108,10 +108,12 @@ task npmInstall(type: Exec) {
outputs.upToDateWhen { file('src/main/web/node_modules').exists() } outputs.upToDateWhen { file('src/main/web/node_modules').exists() }
} }
task cleanWeb() << { task cleanWeb() {
doLast {
delete 'src/main/resources/simmvaluationweb' delete 'src/main/resources/simmvaluationweb'
delete 'src/main/web/dist' delete 'src/main/web/dist'
} }
}
task buildWeb(type: Exec, dependsOn: [cleanWeb, npmInstall]) { task buildWeb(type: Exec, dependsOn: [cleanWeb, npmInstall]) {
workingDir 'src/main/web' workingDir 'src/main/web'

View File

@ -194,7 +194,7 @@ fun <A> rpcDriver(
debugPortAllocation: PortAllocation = globalDebugPortAllocation, debugPortAllocation: PortAllocation = globalDebugPortAllocation,
systemProperties: Map<String, String> = emptyMap(), systemProperties: Map<String, String> = emptyMap(),
useTestClock: Boolean = false, useTestClock: Boolean = false,
automaticallyStartNetworkMap: Boolean = false, networkMapStrategy: NetworkMapStrategy = FalseNetworkMap,
dsl: RPCDriverExposedDSLInterface.() -> A dsl: RPCDriverExposedDSLInterface.() -> A
) = genericDriver( ) = genericDriver(
driverDsl = RPCDriverDSL( driverDsl = RPCDriverDSL(
@ -204,7 +204,7 @@ fun <A> rpcDriver(
systemProperties = systemProperties, systemProperties = systemProperties,
driverDirectory = driverDirectory.toAbsolutePath(), driverDirectory = driverDirectory.toAbsolutePath(),
useTestClock = useTestClock, useTestClock = useTestClock,
automaticallyStartNetworkMap = automaticallyStartNetworkMap, networkMapStrategy = networkMapStrategy,
isDebug = isDebug isDebug = isDebug
) )
), ),

View File

@ -78,7 +78,7 @@ fun <A> verifierDriver(
debugPortAllocation: PortAllocation = PortAllocation.Incremental(5005), debugPortAllocation: PortAllocation = PortAllocation.Incremental(5005),
systemProperties: Map<String, String> = emptyMap(), systemProperties: Map<String, String> = emptyMap(),
useTestClock: Boolean = false, useTestClock: Boolean = false,
automaticallyStartNetworkMap: Boolean = true, networkMapStrategy: NetworkMapStrategy = DedicatedNetworkMap,
dsl: VerifierExposedDSLInterface.() -> A dsl: VerifierExposedDSLInterface.() -> A
) = genericDriver( ) = genericDriver(
driverDsl = VerifierDriverDSL( driverDsl = VerifierDriverDSL(
@ -88,7 +88,7 @@ fun <A> verifierDriver(
systemProperties = systemProperties, systemProperties = systemProperties,
driverDirectory = driverDirectory.toAbsolutePath(), driverDirectory = driverDirectory.toAbsolutePath(),
useTestClock = useTestClock, useTestClock = useTestClock,
automaticallyStartNetworkMap = automaticallyStartNetworkMap, networkMapStrategy = networkMapStrategy,
isDebug = isDebug isDebug = isDebug
) )
), ),

View File

@ -13,6 +13,7 @@ import net.corda.core.utilities.ALICE
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.flows.CashIssueFlow import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow import net.corda.flows.CashPaymentFlow
import net.corda.node.driver.FalseNetworkMap
import net.corda.node.services.config.VerifierType import net.corda.node.services.config.VerifierType
import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.node.services.transactions.ValidatingNotaryService
import org.junit.Test import org.junit.Test
@ -34,7 +35,7 @@ class VerifierTests {
@Test @Test
fun `single verifier works with requestor`() { fun `single verifier works with requestor`() {
verifierDriver(automaticallyStartNetworkMap = false) { verifierDriver(networkMapStrategy = FalseNetworkMap) {
val aliceFuture = startVerificationRequestor(ALICE.name) val aliceFuture = startVerificationRequestor(ALICE.name)
val transactions = generateTransactions(100) val transactions = generateTransactions(100)
val alice = aliceFuture.get() val alice = aliceFuture.get()
@ -51,7 +52,7 @@ class VerifierTests {
@Test @Test
fun `multiple verifiers work with requestor`() { fun `multiple verifiers work with requestor`() {
verifierDriver(automaticallyStartNetworkMap = false) { verifierDriver(networkMapStrategy = FalseNetworkMap) {
val aliceFuture = startVerificationRequestor(ALICE.name) val aliceFuture = startVerificationRequestor(ALICE.name)
val transactions = generateTransactions(100) val transactions = generateTransactions(100)
val alice = aliceFuture.get() val alice = aliceFuture.get()
@ -71,7 +72,7 @@ class VerifierTests {
@Test @Test
fun `verification redistributes on verifier death`() { fun `verification redistributes on verifier death`() {
verifierDriver(automaticallyStartNetworkMap = false) { verifierDriver(networkMapStrategy = FalseNetworkMap) {
val aliceFuture = startVerificationRequestor(ALICE.name) val aliceFuture = startVerificationRequestor(ALICE.name)
val numberOfTransactions = 100 val numberOfTransactions = 100
val transactions = generateTransactions(numberOfTransactions) val transactions = generateTransactions(numberOfTransactions)
@ -99,7 +100,7 @@ class VerifierTests {
@Test @Test
fun `verification request waits until verifier comes online`() { fun `verification request waits until verifier comes online`() {
verifierDriver(automaticallyStartNetworkMap = false) { verifierDriver(networkMapStrategy = FalseNetworkMap) {
val aliceFuture = startVerificationRequestor(ALICE.name) val aliceFuture = startVerificationRequestor(ALICE.name)
val transactions = generateTransactions(100) val transactions = generateTransactions(100)
val alice = aliceFuture.get() val alice = aliceFuture.get()