diff --git a/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/Main.kt b/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/Main.kt index a06a3192ab..015710c596 100644 --- a/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/Main.kt +++ b/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/Main.kt @@ -1,14 +1,18 @@ package net.corda.notarydemo +import net.corda.core.div import net.corda.node.driver.driver +import net.corda.node.services.User import net.corda.node.services.transactions.RaftValidatingNotaryService +import java.nio.file.Paths /** Creates and starts all nodes required for the demo. */ fun main(args: Array) { - driver(dsl = { - startNode("Party") + val demoUser = listOf(User("demo", "demo", setOf("StartFlow.net.corda.notarydemo.flows.DummyIssueAndMove", "StartFlow.net.corda.flows.NotaryFlow\$Client"))) + driver(isDebug = true, driverDirectory = Paths.get("build") / "notary-demo-nodes") { + startNode("Party", rpcUsers = demoUser) startNode("Counterparty") startNotaryCluster("Raft notary", clusterSize = 3, type = RaftValidatingNotaryService.type) waitForAllNodesToFinish() - }, isDebug = true) + } } diff --git a/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/NotaryDemo.kt b/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/NotaryDemo.kt index 5eeb8a518d..22ff89970a 100644 --- a/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/NotaryDemo.kt +++ b/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/NotaryDemo.kt @@ -1,30 +1,99 @@ package net.corda.notarydemo import com.google.common.net.HostAndPort -import okhttp3.OkHttpClient -import okhttp3.Request -import java.util.concurrent.TimeUnit +import net.corda.core.crypto.toStringShort +import net.corda.core.div +import net.corda.core.messaging.CordaRPCOps +import net.corda.core.messaging.startFlow +import net.corda.core.transactions.SignedTransaction +import net.corda.flows.NotaryFlow +import net.corda.node.services.config.NodeSSLConfiguration +import net.corda.node.services.messaging.CordaRPCClient +import net.corda.notarydemo.flows.DummyIssueAndMove +import java.nio.file.Path +import java.nio.file.Paths fun main(args: Array) { - val api = NotaryDemoClientApi(HostAndPort.fromString("localhost:10003")) - api.startNotarisation() + val host = HostAndPort.fromString("localhost:10002") + println("Connecting to the recipient node ($host)") + CordaRPCClient(host, sslConfigFor("nodeb", "build/notary-demo-nodes/Party/certificates")).use("demo", "demo") { + val api = NotaryDemoClientApi(this) + api.startNotarisation() + } } /** Interface for using the notary demo API from a client. */ -private class NotaryDemoClientApi(val hostAndPort: HostAndPort) { - companion object { - private val API_ROOT = "api/notarydemo" +private class NotaryDemoClientApi(val rpc: CordaRPCOps) { + + private val notary by lazy { + rpc.networkMapUpdates().first.first { it.advertisedServices.any { it.info.type.isNotary() } }.notaryIdentity + } + + private val counterpartyNode by lazy { + rpc.networkMapUpdates().first.first { it.legalIdentity.name == "Counterparty" } + } + + private companion object { private val TRANSACTION_COUNT = 10 } /** Makes a call to the demo api to start transaction notarisation. */ fun startNotarisation() { - val request = buildRequest() - val response = buildClient().newCall(request).execute() - println(response.body().string()) - require(response.isSuccessful) + val response = notarise(TRANSACTION_COUNT) + println(response) } - private fun buildRequest() = Request.Builder().url("http://$hostAndPort/$API_ROOT/notarise/$TRANSACTION_COUNT").build() - private fun buildClient() = OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(60, TimeUnit.SECONDS).build() + fun notarise(count: Int): String { + val transactions = buildTransactions(count) + val signers = notariseTransactions(transactions) + + return buildResponse(transactions, signers) + } + + /** + * Builds a number of dummy transactions (as specified by [count]). The party first self-issues a state (asset), + * and builds a transaction to transfer the asset to the counterparty. The *move* transaction requires notarisation, + * as it consumes the original asset and creates a copy with the new owner as its output. + */ + private fun buildTransactions(count: Int): List { + val moveTransactions = (1..count).map { + rpc.startFlow(::DummyIssueAndMove, notary, counterpartyNode.legalIdentity).returnValue.toBlocking().toFuture() + } + return moveTransactions.map { it.get() } + } + + /** + * For every transactions invokes the notary flow and obtains a notary signature. + * The signer can be any of the nodes in the notary cluster. + * + * @return a list of encoded signer public keys – one for every transaction + */ + private fun notariseTransactions(transactions: List): List { + val signatureFutures = transactions.map { + rpc.startFlow(NotaryFlow::Client, it).returnValue.toBlocking().toFuture() + } + val signers = signatureFutures.map { it.get().by.toStringShort() } + return signers + } + + /** Builds a response for the caller containing the list of transaction ids and corresponding signer keys. */ + private fun buildResponse(transactions: List, signers: List): String { + val transactionSigners = transactions.zip(signers).map { + val (tx, signer) = it + "Tx [${tx.tx.id.prefixChars()}..] signed by $signer" + }.joinToString("\n") + + val response = "Notary: \"${notary.name}\", with composite key: ${notary.owningKey}\n" + + "Notarised ${transactions.size} transactions:\n" + transactionSigners + return response + } +} + +// TODO: Take this out once we have a dedicated RPC port and allow SSL on it to be optional. +private fun sslConfigFor(nodename: String, certsPath: String?): NodeSSLConfiguration { + return object : NodeSSLConfiguration { + override val keyStorePassword: String = "cordacadevpass" + override val trustStorePassword: String = "trustpass" + override val certificatesDirectory: Path = if (certsPath != null) Paths.get(certsPath) else Paths.get("build") / "nodes" / nodename / "certificates" + } } diff --git a/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/api/NotaryDemoApi.kt b/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/api/NotaryDemoApi.kt deleted file mode 100644 index 6f9e82ee93..0000000000 --- a/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/api/NotaryDemoApi.kt +++ /dev/null @@ -1,76 +0,0 @@ -package net.corda.notarydemo.api - -import net.corda.core.contracts.DummyContract -import net.corda.core.crypto.DigitalSignature -import net.corda.core.crypto.toStringShort -import net.corda.core.messaging.CordaRPCOps -import net.corda.core.messaging.startFlow -import net.corda.core.node.ServiceHub -import net.corda.core.node.recordTransactions -import net.corda.core.transactions.SignedTransaction -import net.corda.flows.NotaryFlow -import net.corda.notarydemo.flows.DummyIssueAndMove -import java.util.* -import javax.ws.rs.GET -import javax.ws.rs.Path -import javax.ws.rs.PathParam -import javax.ws.rs.core.Response - -@Path("notarydemo") -class NotaryDemoApi(val rpc: CordaRPCOps) { - private val notary by lazy { - rpc.networkMapUpdates().first.first { it.advertisedServices.any { it.info.type.isNotary() } }.notaryIdentity - } - - private val counterpartyNode by lazy { - rpc.networkMapUpdates().first.first { it.legalIdentity.name == "Counterparty" } - } - - @GET - @Path("/notarise/{count}") - fun notarise(@PathParam("count") count: Int): Response { - val transactions = buildTransactions(count) - val signers = notariseTransactions(transactions) - - val response = buildResponse(transactions, signers) - return Response.ok(response).build() - } - - /** - * Builds a number of dummy transactions (as specified by [count]). The party first self-issues a state (asset), - * and builds a transaction to transfer the asset to the counterparty. The *move* transaction requires notarisation, - * as it consumes the original asset and creates a copy with the new owner as its output. - */ - private fun buildTransactions(count: Int): List { - val moveTransactions = (1..count).map { - rpc.startFlow(::DummyIssueAndMove, notary, counterpartyNode.legalIdentity).returnValue.toBlocking().toFuture() - } - return moveTransactions.map { it.get() } - } - - /** - * For every transactions invokes the notary flow and obtains a notary signature. - * The signer can be any of the nodes in the notary cluster. - * - * @return a list of encoded signer public keys – one for every transaction - */ - private fun notariseTransactions(transactions: List): List { - val signatureFutures = transactions.map { - rpc.startFlow(NotaryFlow::Client, it).returnValue.toBlocking().toFuture() - } - val signers = signatureFutures.map { it.get().by.toStringShort() } - return signers - } - - /** Builds a response for the caller containing the list of transaction ids and corresponding signer keys. */ - private fun buildResponse(transactions: List, signers: List): String { - val transactionSigners = transactions.zip(signers).map { - val (tx, signer) = it - "Tx [${tx.tx.id.prefixChars()}..] signed by $signer" - }.joinToString("\n") - - val response = "Notary: \"${notary.name}\", with composite key: ${notary.owningKey}\n" + - "Notarised ${transactions.size} transactions:\n" + transactionSigners - return response - } -} diff --git a/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/plugin/NotaryDemoPlugin.kt b/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/plugin/NotaryDemoPlugin.kt index 598de289dc..f6f05665d8 100644 --- a/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/plugin/NotaryDemoPlugin.kt +++ b/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/plugin/NotaryDemoPlugin.kt @@ -4,13 +4,10 @@ import net.corda.core.crypto.Party import net.corda.core.node.CordaPluginRegistry import net.corda.core.transactions.SignedTransaction import net.corda.flows.NotaryFlow -import net.corda.notarydemo.api.NotaryDemoApi import net.corda.notarydemo.flows.DummyIssueAndMove import java.util.function.Function class NotaryDemoPlugin : CordaPluginRegistry() { - // A list of classes that expose web APIs. - override val webApis = listOf(Function(::NotaryDemoApi)) // A list of protocols that are required for this cordapp override val requiredFlows = mapOf( NotaryFlow.Client::class.java.name to setOf(SignedTransaction::class.java.name, setOf(Unit).javaClass.name),