mirror of
https://github.com/corda/corda.git
synced 2025-06-17 06:38:21 +00:00
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:
@ -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(
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user