Make the trader demo attach a PDF to the commercial paper self-issuance and then scan for it on the buyer side, print out the path on the filesystem where the PDF can be found.

This commit is contained in:
Mike Hearn 2016-03-02 14:45:53 +01:00
parent 8d906c703d
commit 1123c28f02
8 changed files with 75 additions and 19 deletions

View File

@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="core.node.TraderDemoKt" />
<option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar -Dco.paralleluniverse.fibers.verifyInstrumentation" />
<option name="PROGRAM_PARAMETERS" value="--dir=seller --fake-trade-with=localhost --network-address=localhost:31338 --timestamper-identity-file=buyer/identity-public --timestamper-address=localhost" />
<option name="PROGRAM_PARAMETERS" value="--dir=seller --fake-trade-with=localhost --network-address=localhost:31327 --timestamper-identity-file=buyer/identity-public --timestamper-address=localhost" />
<option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />

View File

@ -12,7 +12,8 @@ allprojects {
buildscript {
ext.kotlin_version = '1.0.0'
ext.quasar_version = '0.7.4'
// TODO: Reset to 0.7.5 when released. We need the snapshot for a TLS serialization related fix.
ext.quasar_version = '0.7.5-SNAPSHOT'
ext.asm_version = '0.5.3'
ext.artemis_version = '1.2.0'
ext.jetty_version = '9.3.7.v20160115'
@ -28,7 +29,7 @@ buildscript {
repositories {
// mavenLocal()
mavenLocal()
mavenCentral()
maven {
url 'http://oss.sonatype.org/content/repositories/snapshots'
@ -67,12 +68,7 @@ dependencies {
// JOpt: for command line flags.
compile "net.sf.jopt-simple:jopt-simple:4.9"
// Kryo: object graph serialization.
compile "com.esotericsoftware:kryo:3.0.3"
compile "de.javakaffee:kryo-serializers:0.37"
// Quasar: for the bytecode rewriting for state machines.
compile("co.paralleluniverse:quasar-core:${quasar_version}:jdk8")
quasar("co.paralleluniverse:quasar-core:${quasar_version}:jdk8@jar")
// Artemis: for reliable p2p message queues.

View File

@ -67,6 +67,12 @@ apply plugin: CanonicalizerPlugin
repositories {
mavenCentral()
mavenLocal()
mavenCentral()
jcenter()
maven {
url 'http://oss.sonatype.org/content/repositories/snapshots'
}
}
dependencies {

View File

@ -7,7 +7,6 @@ apply plugin: 'kotlin'
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
// Dokka (JavaDoc equivalent for Kotlin) download is a big download and also
@ -22,7 +21,12 @@ buildscript {
// apply plugin: 'org.jetbrains.dokka'
repositories {
mavenLocal()
mavenCentral()
jcenter()
maven {
url 'http://oss.sonatype.org/content/repositories/snapshots'
}
}
dependencies {
@ -41,6 +45,10 @@ dependencies {
// RxJava: observable streams of events.
compile "io.reactivex:rxkotlin:0.40.1"
// Kryo: object graph serialization.
compile "com.esotericsoftware:kryo:3.0.3"
compile "de.javakaffee:kryo-serializers:0.37"
// Quasar: for the bytecode rewriting for state machines.
compile "co.paralleluniverse:quasar-core:${quasar_version}:jdk8"

View File

@ -100,9 +100,10 @@ data class WireTransaction(val inputs: List<StateRef>,
override fun toString(): String {
val buf = StringBuilder()
buf.appendln("Transaction:")
for (input in inputs) buf.appendln("${Emoji.rightArrow}INPUT: $input")
for (output in outputs) buf.appendln("${Emoji.leftArrow}OUTPUT: $output")
for (command in commands) buf.appendln("${Emoji.diamond}COMMAND: $command")
for (input in inputs) buf.appendln("${Emoji.rightArrow}INPUT: $input")
for (output in outputs) buf.appendln("${Emoji.leftArrow}OUTPUT: $output")
for (command in commands) buf.appendln("${Emoji.diamond}COMMAND: $command")
for (attachment in attachments) buf.appendln("${Emoji.paperclip}ATTACHMENT: $attachment")
return buf.toString()
}
}

View File

@ -21,7 +21,7 @@ if [[ "$mode" == "buyer" ]]; then
elif [[ "$mode" == "seller" ]]; then
if [ ! -d seller ]; then
mkdir seller
echo "myLegalName = Bank of Giza" >seller/config
echo "myLegalName = Bank of London" >seller/config
fi
build/install/r3prototyping/bin/r3prototyping --dir=seller --fake-trade-with=localhost --network-address=localhost:31340 --timestamper-identity-file=buyer/identity-public --timestamper-address=localhost

View File

@ -14,6 +14,7 @@ import contracts.CommercialPaper
import contracts.protocols.TwoPartyTradeProtocol
import core.*
import core.crypto.DigitalSignature
import core.crypto.SecureHash
import core.crypto.generateKeyPair
import core.messaging.LegallyIdentifiableNode
import core.messaging.SingleMessageRecipient
@ -31,6 +32,7 @@ import java.security.PublicKey
import java.time.Instant
import java.util.*
import kotlin.system.exitProcess
import kotlin.test.assertEquals
// TRADING DEMO
//
@ -93,7 +95,14 @@ fun main(args: Array<String>) {
val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, timestamperId).start() }
if (listening) {
val buyer = TraderDemoProtocolBuyer()
// For demo purposes just extract attachment jars when saved to disk, so the user can explore them.
// Buyer will fetch the attachment from the seller.
val attachmentsPath = (node.storage.attachments as NodeAttachmentStorage).let {
it.automaticallyExtractAttachments = true
it.storePath
}
val buyer = TraderDemoProtocolBuyer(attachmentsPath)
ANSIProgressRenderer.progressTracker = buyer.progressTracker
node.smm.add("demo.buyer", buyer).get() // This thread will halt forever here.
} else {
@ -101,6 +110,15 @@ fun main(args: Array<String>) {
println("Need the --fake-trade-with command line argument")
exitProcess(1)
}
// Make sure we have the transaction prospectus attachment loaded into our store.
if (node.storage.attachments.openAttachment(TraderDemoProtocolSeller.PROSPECTUS_HASH) == null) {
TraderDemoProtocolSeller::class.java.getResourceAsStream("bank-of-london-cp.jar").use {
val id = node.storage.attachments.importAttachment(it)
assertEquals(TraderDemoProtocolSeller.PROSPECTUS_HASH, id)
}
}
val peerAddr = HostAndPort.fromString(options.valuesOf(fakeTradeWithArg).single()).withDefaultPort(Node.DEFAULT_PORT)
val otherSide = ArtemisMessagingService.makeRecipient(peerAddr)
val seller = TraderDemoProtocolSeller(myNetAddr, otherSide)
@ -112,7 +130,7 @@ fun main(args: Array<String>) {
// We create a couple of ad-hoc test protocols that wrap the two party trade protocol, to give us the demo logic.
class TraderDemoProtocolBuyer() : ProtocolLogic<Unit>() {
class TraderDemoProtocolBuyer(private val attachmentsPath: Path) : ProtocolLogic<Unit>() {
companion object {
object WAITING_FOR_SELLER_TO_CONNECT : ProgressTracker.Step("Waiting for seller to connect to us")
object STARTING_BUY : ProgressTracker.Step("Seller connected, purchasing commercial paper asset")
@ -146,17 +164,38 @@ class TraderDemoProtocolBuyer() : ProtocolLogic<Unit>() {
logger.info("Purchase complete - we are a happy customer! Final transaction is: " +
"\n\n${Emoji.renderIfSupported(tradeTX.tx)}")
logIssuanceAttachment(tradeTX)
} catch(e: Exception) {
logger.error("Something went wrong whilst trading!", e)
}
}
}
private fun logIssuanceAttachment(tradeTX: SignedTransaction) {
// Find the original CP issuance.
val search = TransactionGraphSearch(serviceHub.storageService.validatedTransactions, listOf(tradeTX.tx))
search.query = TransactionGraphSearch.Query(withCommandOfType = CommercialPaper.Commands.Issue::class.java)
val cpIssuance = search.call().single()
cpIssuance.attachments.first().let {
val p = attachmentsPath.toAbsolutePath().resolve("$it.jar")
logger.info("""
The issuance of the commercial paper came with an attachment. You can find it expanded in this directory:
$p
${Emoji.renderIfSupported(cpIssuance)}""")
}
}
}
class TraderDemoProtocolSeller(val myAddress: HostAndPort,
val otherSide: SingleMessageRecipient,
override val progressTracker: ProgressTracker = TraderDemoProtocolSeller.tracker()) : ProtocolLogic<Unit>() {
companion object {
val PROSPECTUS_HASH = SecureHash.parse("decd098666b9657314870e192ced0c3519c2c9d395507a238338f8d003929de9")
object ANNOUNCING : ProgressTracker.Step("Announcing to the buyer node")
object SELF_ISSUING : ProgressTracker.Step("Got session ID back, issuing and timestamping some commercial paper")
object TRADING : ProgressTracker.Step("Starting the trade protocol")
@ -179,7 +218,7 @@ class TraderDemoProtocolSeller(val myAddress: HostAndPort,
val tsa = serviceHub.networkMapService.timestampingNodes[0]
val cpOwnerKey = serviceHub.keyManagementService.freshKey()
val commercialPaper = makeFakeCommercialPaper(cpOwnerKey.public, tsa)
val commercialPaper = selfIssueSomeCommercialPaper(cpOwnerKey.public, tsa)
progressTracker.currentStep = TRADING
@ -199,19 +238,25 @@ class TraderDemoProtocolSeller(val myAddress: HostAndPort,
}
@Suspendable
fun makeFakeCommercialPaper(ownedBy: PublicKey, tsa: LegallyIdentifiableNode): StateAndRef<CommercialPaper.State> {
fun selfIssueSomeCommercialPaper(ownedBy: PublicKey, tsa: LegallyIdentifiableNode): StateAndRef<CommercialPaper.State> {
// Make a fake company that's issued its own paper.
val keyPair = generateKeyPair()
val party = Party("MegaCorp, Inc", keyPair.public)
val party = Party("Bank of London", keyPair.public)
val issuance = run {
val tx = CommercialPaper().generateIssue(party.ref(1,2,3), 1100.DOLLARS, Instant.now() + 10.days)
// TODO: Consider moving these two steps below into generateIssue.
// Attach the prospectus.
tx.addAttachment(serviceHub.storageService.attachments.openAttachment(PROSPECTUS_HASH)!!)
// Timestamp it, all CP must be timestamped.
tx.setTime(Instant.now(), tsa.identity, 30.seconds)
val tsaSig = subProtocol(TimestampingProtocol(tsa, tx.toWireTransaction().serialized))
tx.checkAndAddSignature(tsaSig)
tx.signWith(keyPair)
tx.toSignedTransaction(true)
}

Binary file not shown.