mirror of
https://github.com/corda/corda.git
synced 2025-06-17 22:58:19 +00:00
Reimport samples to main repo
This commit is contained in:
@ -0,0 +1,35 @@
|
||||
package net.corda.attachmentdemo
|
||||
|
||||
import net.corda.core.crypto.toBase58String
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
import net.corda.node.driver.driver
|
||||
import net.corda.node.services.transactions.SimpleNotaryService
|
||||
import net.corda.testing.getHostAndPort
|
||||
import org.junit.Test
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class AttachmentDemoTest {
|
||||
@Test fun `runs attachment demo`() {
|
||||
driver(dsl = {
|
||||
startNode("Notary", setOf(ServiceInfo(SimpleNotaryService.Companion.type)))
|
||||
val nodeA = startNode("Bank A").get()
|
||||
val nodeAApiAddr = nodeA.config.getHostAndPort("webAddress")
|
||||
val nodeBApiAddr = startNode("Bank B").get().config.getHostAndPort("webAddress")
|
||||
|
||||
var recipientReturn: Boolean? = null
|
||||
var senderReturn: Boolean? = null
|
||||
val recipientThread = thread {
|
||||
recipientReturn = AttachmentDemoClientApi(nodeAApiAddr).runRecipient()
|
||||
}
|
||||
val senderThread = thread {
|
||||
val counterpartyKey = nodeA.nodeInfo.legalIdentity.owningKey.toBase58String()
|
||||
senderReturn = AttachmentDemoClientApi(nodeBApiAddr).runSender(counterpartyKey)
|
||||
}
|
||||
recipientThread.join()
|
||||
senderThread.join()
|
||||
|
||||
assert(recipientReturn == true)
|
||||
assert(senderReturn == true)
|
||||
}, isDebug = true)
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package net.corda.attachmentdemo
|
||||
|
||||
import com.google.common.net.HostAndPort
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.testing.http.HttpUtils
|
||||
import joptsimple.OptionParser
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
AttachmentDemo().main(args)
|
||||
}
|
||||
|
||||
private class AttachmentDemo {
|
||||
internal enum class Role() {
|
||||
SENDER,
|
||||
RECIPIENT
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val log = loggerFor<AttachmentDemo>()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val parser = OptionParser()
|
||||
|
||||
val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java).required()
|
||||
val options = try {
|
||||
parser.parse(*args)
|
||||
} catch (e: Exception) {
|
||||
log.error(e.message)
|
||||
printHelp(parser)
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
val role = options.valueOf(roleArg)!!
|
||||
when (role) {
|
||||
Role.SENDER -> {
|
||||
val api = AttachmentDemoClientApi(HostAndPort.fromString("localhost:10005"))
|
||||
api.runSender(api.getOtherSideKey())
|
||||
}
|
||||
Role.RECIPIENT -> AttachmentDemoClientApi(HostAndPort.fromString("localhost:10007")).runRecipient()
|
||||
}
|
||||
}
|
||||
|
||||
private fun printHelp(parser: OptionParser) {
|
||||
println("""
|
||||
Usage: attachment-demo --role [RECIPIENT|SENDER] [options]
|
||||
Please refer to the documentation in docs/build/index.html for more info.
|
||||
|
||||
""".trimIndent())
|
||||
parser.printHelpOn(System.out)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package net.corda.attachmentdemo
|
||||
|
||||
import com.google.common.net.HostAndPort
|
||||
import net.corda.testing.http.HttpApi
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Interface for using the attachment demo API from a client.
|
||||
*/
|
||||
class AttachmentDemoClientApi(val hostAndPort: HostAndPort) {
|
||||
private val api = HttpApi.fromHostAndPort(hostAndPort, apiRoot)
|
||||
|
||||
fun runRecipient(): Boolean {
|
||||
return api.postJson("await-transaction")
|
||||
}
|
||||
|
||||
fun runSender(otherSide: String): Boolean {
|
||||
return api.postJson("$otherSide/send")
|
||||
}
|
||||
|
||||
fun getOtherSideKey(): String {
|
||||
// TODO: Add getJson to the API utils
|
||||
val client = OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(60, TimeUnit.SECONDS).build()
|
||||
val request = Request.Builder().url("http://$hostAndPort/$apiRoot/other-side-key").build()
|
||||
val response = client.newCall(request).execute()
|
||||
require(response.isSuccessful) // TODO: Handle more gracefully.
|
||||
return response.body().string()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private val apiRoot = "api/attachmentdemo"
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package net.corda.attachmentdemo
|
||||
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
import net.corda.node.driver.driver
|
||||
import net.corda.node.services.transactions.SimpleNotaryService
|
||||
|
||||
/**
|
||||
* This file is exclusively for being able to run your nodes through an IDE (as opposed to running deployNodes)
|
||||
* Do not use in a production environment.
|
||||
*/
|
||||
fun main(args: Array<String>) {
|
||||
driver(dsl = {
|
||||
startNode("Notary", setOf(ServiceInfo(SimpleNotaryService.Companion.type)))
|
||||
startNode("Bank A")
|
||||
startNode("Bank B")
|
||||
waitForAllNodesToFinish()
|
||||
}, isDebug = true)
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
package net.corda.attachmentdemo.api
|
||||
|
||||
import net.corda.core.contracts.TransactionType
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.failure
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.success
|
||||
import net.corda.core.utilities.ApiUtils
|
||||
import net.corda.core.utilities.Emoji
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.protocols.CashCommand
|
||||
import net.corda.protocols.FinalityProtocol
|
||||
import net.corda.testing.ALICE_KEY
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import javax.ws.rs.*
|
||||
import javax.ws.rs.core.MediaType
|
||||
import javax.ws.rs.core.Response
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@Path("attachmentdemo")
|
||||
class AttachmentDemoApi(val services: ServiceHub) {
|
||||
private val utils = ApiUtils(services)
|
||||
|
||||
private companion object {
|
||||
val PROSPECTUS_HASH = SecureHash.parse("decd098666b9657314870e192ced0c3519c2c9d395507a238338f8d003929de9")
|
||||
val logger = loggerFor<AttachmentDemoApi>()
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{party}/send")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
fun runSender(@PathParam("party") partyKey: String): Response {
|
||||
return utils.withParty(partyKey) {
|
||||
// Make sure we have the file in storage
|
||||
// TODO: We should have our own demo file, not share the trader demo file
|
||||
if (services.storageService.attachments.openAttachment(PROSPECTUS_HASH) == null) {
|
||||
javaClass.classLoader.getResourceAsStream("bank-of-london-cp.jar").use {
|
||||
val id = services.storageService.attachments.importAttachment(it)
|
||||
assertEquals(PROSPECTUS_HASH, id)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a trivial transaction that just passes across the attachment - in normal cases there would be
|
||||
// inputs, outputs and commands that refer to this attachment.
|
||||
val ptx = TransactionType.General.Builder(notary = null)
|
||||
ptx.addAttachment(services.storageService.attachments.openAttachment(PROSPECTUS_HASH)!!.id)
|
||||
|
||||
// Despite not having any states, we have to have at least one signature on the transaction
|
||||
ptx.signWith(ALICE_KEY)
|
||||
|
||||
// Send the transaction to the other recipient
|
||||
val tx = ptx.toSignedTransaction()
|
||||
services.invokeProtocolAsync<Unit>(FinalityProtocol::class.java, tx, emptySet<CashCommand>(), setOf(it)).resultFuture.success {
|
||||
logger.info("Successfully sent attachment with the FinalityProtocol")
|
||||
}.failure {
|
||||
logger.error("Failed to send attachment with the FinalityProtocol")
|
||||
}
|
||||
|
||||
Response.accepted().build()
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("await-transaction")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
fun runRecipient(): Response {
|
||||
val future = CompletableFuture<Response>()
|
||||
// Normally we would receive the transaction from a more specific protocol, but in this case we let [FinalityProtocol]
|
||||
// handle receiving it for us.
|
||||
services.storageService.validatedTransactions.updates.subscribe { event ->
|
||||
// When the transaction is received, it's passed through [ResolveTransactionsProtocol], which first fetches any
|
||||
// attachments for us, then verifies the transaction. As such, by the time it hits the validated transaction store,
|
||||
// we have a copy of the attachment.
|
||||
val tx = event.tx
|
||||
val response = if (tx.attachments.isNotEmpty()) {
|
||||
val attachment = services.storageService.attachments.openAttachment(tx.attachments.first())
|
||||
assertEquals(PROSPECTUS_HASH, attachment?.id)
|
||||
|
||||
logger.info("File received - we're happy!\n\nFinal transaction is:\n\n${Emoji.renderIfSupported(event.tx)}")
|
||||
Response.ok().entity("Final transaction is: ${Emoji.renderIfSupported(event.tx)}").build()
|
||||
} else {
|
||||
Response.serverError().entity("No attachments passed").build()
|
||||
}
|
||||
future.complete(response)
|
||||
}
|
||||
|
||||
return future.get()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets details of the other side. To be removed when identity API is added.
|
||||
*/
|
||||
@GET
|
||||
@Path("other-side-key")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
fun getOtherSide(): Response? {
|
||||
val key = services.networkMapCache.partyNodes.first { it != services.myInfo }.legalIdentity.owningKey.toBase58String()
|
||||
return Response.ok().entity(key).build()
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package net.corda.attachmentdemo.plugin
|
||||
|
||||
import net.corda.attachmentdemo.api.AttachmentDemoApi
|
||||
import net.corda.core.node.CordaPluginRegistry
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.protocols.FinalityProtocol
|
||||
|
||||
class AttachmentDemoPlugin : CordaPluginRegistry() {
|
||||
// A list of classes that expose web APIs.
|
||||
override val webApis: List<Class<*>> = listOf(AttachmentDemoApi::class.java)
|
||||
// A list of protocols that are required for this cordapp
|
||||
override val requiredProtocols: Map<String, Set<String>> = mapOf(
|
||||
FinalityProtocol::class.java.name to setOf(SignedTransaction::class.java.name, setOf(Unit).javaClass.name, setOf(Unit).javaClass.name)
|
||||
)
|
||||
override val servicePlugins: List<Class<*>> = listOf()
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
# Register a ServiceLoader service extending from net.corda.node.CordaPluginRegistry
|
||||
net.corda.attachmentdemo.plugin.AttachmentDemoPlugin
|
BIN
samples/attachment-demo/src/main/resources/bank-of-london-cp.jar
Normal file
BIN
samples/attachment-demo/src/main/resources/bank-of-london-cp.jar
Normal file
Binary file not shown.
@ -0,0 +1 @@
|
||||
These certificates are used for development mode only (and are copies of those contained within the TraderDemo jar file)
|
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user