Revert design changes to attachment demo (#540)

Restructure attachment demo

Restructure attachment demo to pass the attachment hash in the transaction outputs,
rather than trying to use shared memory. Also remove 1kb test for attachments as there's little value in having a smaller test case.
This commit is contained in:
Ross Nicoll
2017-04-19 10:12:01 +01:00
committed by GitHub
parent 8e0a0ba8fb
commit 022b38b7dc
2 changed files with 33 additions and 34 deletions

View File

@ -13,18 +13,9 @@ import org.junit.Test
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
class AttachmentDemoTest { class AttachmentDemoTest {
// run with the default 1K bytes in-memory zip file.
@Test fun `attachment demo`() {
attachmentDemo()
}
// run with a 10,000,000 bytes in-memory zip file. In practice, a slightly bigger file will be used (~10,002,000 bytes). // run with a 10,000,000 bytes in-memory zip file. In practice, a slightly bigger file will be used (~10,002,000 bytes).
@Test fun `attachment demo using a 10MB zip file`() { @Test fun `attachment demo using a 10MB zip file`() {
attachmentDemo(10000000) val numOfExpectedBytes = 10_000_000
}
// An in-memory zip file will be used as InputStream, with a size slightly bigger than numOfExpectedBytes.
private fun attachmentDemo(numOfExpectedBytes: Int = 1024) {
driver(dsl = { driver(dsl = {
val demoUser = listOf(User("demo", "demo", setOf("StartFlow.net.corda.flows.FinalityFlow"))) val demoUser = listOf(User("demo", "demo", setOf("StartFlow.net.corda.flows.FinalityFlow")))
val (nodeA, nodeB) = Futures.allAsList( val (nodeA, nodeB) = Futures.allAsList(

View File

@ -3,21 +3,22 @@ package net.corda.attachmentdemo
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import joptsimple.OptionParser import joptsimple.OptionParser
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClient
import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionForContract
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.div
import net.corda.core.getOrThrow 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.sizedInputStreamAndHash import net.corda.core.sizedInputStreamAndHash
import net.corda.core.utilities.ALICE_KEY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.core.utilities.Emoji import net.corda.core.utilities.Emoji
import net.corda.flows.FinalityFlow import net.corda.flows.FinalityFlow
import net.corda.nodeapi.config.SSLConfiguration
import java.io.InputStream import java.io.InputStream
import java.nio.file.Path import java.security.PublicKey
import java.nio.file.Paths
import kotlin.system.exitProcess import kotlin.system.exitProcess
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -57,8 +58,6 @@ fun main(args: Array<String>) {
} }
} }
var EXPECTED_HASH = SecureHash.zeroHash // Note: We could use another random default value to initialize it.
/** An in memory test zip attachment of at least numOfClearBytes size, will be used. */ /** An in memory test zip attachment of at least numOfClearBytes size, will be used. */
fun sender(rpc: CordaRPCOps, numOfClearBytes: Int = 1024) { // default size 1K. fun sender(rpc: CordaRPCOps, numOfClearBytes: Int = 1024) { // default size 1K.
val (inputStream, hash) = sizedInputStreamAndHash(numOfClearBytes) val (inputStream, hash) = sizedInputStreamAndHash(numOfClearBytes)
@ -66,7 +65,6 @@ fun sender(rpc: CordaRPCOps, numOfClearBytes: Int = 1024) { // default size 1K.
} }
fun sender(rpc: CordaRPCOps, inputStream: InputStream, hash: SecureHash.SHA256) { fun sender(rpc: CordaRPCOps, inputStream: InputStream, hash: SecureHash.SHA256) {
EXPECTED_HASH = hash
// Get the identity key of the other side (the recipient). // Get the identity key of the other side (the recipient).
val otherSide: Party = rpc.partyFromName("Bank B")!! val otherSide: Party = rpc.partyFromName("Bank B")!!
@ -78,15 +76,14 @@ fun sender(rpc: CordaRPCOps, inputStream: InputStream, hash: SecureHash.SHA256)
} }
} }
// Create a trivial transaction that just passes across the attachment - in normal cases there would be // Create a trivial transaction with an output that describes the attachment, and the attachment itself
// inputs, outputs and commands that refer to this attachment. val ptx = TransactionType.General.Builder(notary = DUMMY_NOTARY)
val ptx = TransactionType.General.Builder(notary = null)
require(rpc.attachmentExists(hash)) require(rpc.attachmentExists(hash))
ptx.addOutputState(AttachmentContract.State(hash))
ptx.addAttachment(hash) ptx.addAttachment(hash)
// TODO: Add a dummy state and specify a notary, so that the tx hash is randomised each time and the demo can be repeated.
// Despite not having any states, we have to have at least one signature on the transaction // Sign with the notary key
ptx.signWith(ALICE_KEY) ptx.signWith(DUMMY_NOTARY_KEY)
// Send the transaction to the other recipient // Send the transaction to the other recipient
val stx = ptx.toSignedTransaction() val stx = ptx.toSignedTransaction()
@ -101,9 +98,13 @@ fun recipient(rpc: CordaRPCOps) {
val stx = rpc.verifiedTransactions().second.toBlocking().first() val stx = rpc.verifiedTransactions().second.toBlocking().first()
val wtx = stx.tx val wtx = stx.tx
if (wtx.attachments.isNotEmpty()) { if (wtx.attachments.isNotEmpty()) {
assertEquals(EXPECTED_HASH, wtx.attachments.first()) if (wtx.outputs.isNotEmpty()) {
require(rpc.attachmentExists(EXPECTED_HASH)) val state = wtx.outputs.map { it.data }.filterIsInstance<AttachmentContract.State>().single()
println("File received - we're happy!\n\nFinal transaction is:\n\n${Emoji.renderIfSupported(wtx)}") require(rpc.attachmentExists(state.hash))
println("File received - we're happy!\n\nFinal transaction is:\n\n${Emoji.renderIfSupported(wtx)}")
} else {
println("Error: no output state found in ${wtx.id}")
}
} else { } else {
println("Error: no attachments found in ${wtx.id}") println("Error: no attachments found in ${wtx.id}")
} }
@ -118,11 +119,18 @@ private fun printHelp(parser: OptionParser) {
parser.printHelpOn(System.out) parser.printHelpOn(System.out)
} }
// TODO: Take this out once we have a dedicated RPC port and allow SSL on it to be optional. class AttachmentContract : Contract {
private fun sslConfigFor(nodename: String, certsPath: String?): SSLConfiguration { override val legalContractReference: SecureHash
return object : SSLConfiguration { get() = TODO("not implemented")
override val keyStorePassword: String = "cordacadevpass"
override val trustStorePassword: String = "trustpass" override fun verify(tx: TransactionForContract) {
override val certificatesDirectory: Path = if (certsPath != null) Paths.get(certsPath) else Paths.get("build") / "nodes" / nodename / "certificates" val state = tx.outputs.filterIsInstance<AttachmentContract.State>().single()
val attachment = tx.attachments.single()
require(state.hash == attachment.id)
}
data class State(val hash: SecureHash.SHA256) : ContractState {
override val contract: Contract = AttachmentContract()
override val participants: List<PublicKey> = emptyList()
} }
} }