Merged in mike-ledgertx-refactoring-part-1 (pull request #251)

Small changes and tweaks in preparation for a later refactoring
This commit is contained in:
Mike Hearn 2016-08-01 18:11:42 +02:00
commit a26adb1291
16 changed files with 87 additions and 65 deletions

View File

@ -344,7 +344,7 @@ public class JavaCommercialPaper extends ClauseVerifier {
} }
public void generateRedeem(TransactionBuilder tx, StateAndRef<State> paper, List<StateAndRef<Cash.State>> wallet) throws InsufficientBalanceException { public void generateRedeem(TransactionBuilder tx, StateAndRef<State> paper, List<StateAndRef<Cash.State>> wallet) throws InsufficientBalanceException {
new Cash().generateSpend(tx, paper.getState().getData().getFaceValue(), paper.getState().getData().getOwner(), wallet); new Cash().generateSpend(tx, StructuresKt.withoutIssuer(paper.getState().getData().getFaceValue()), paper.getState().getData().getOwner(), wallet, null);
tx.addInputState(paper); tx.addInputState(paper);
tx.addCommand(new Command(new Commands.Redeem(paper.getState().getNotary()), paper.getState().getData().getOwner())); tx.addCommand(new Command(new Commands.Redeem(paper.getState().getNotary()), paper.getState().getData().getOwner()));
} }

View File

@ -8,6 +8,7 @@ import com.r3corda.core.contracts.clauses.*
import com.r3corda.core.crypto.* import com.r3corda.core.crypto.*
import com.r3corda.core.node.services.Wallet import com.r3corda.core.node.services.Wallet
import com.r3corda.core.utilities.Emoji import com.r3corda.core.utilities.Emoji
import java.math.BigInteger
import java.security.PublicKey import java.security.PublicKey
import java.util.* import java.util.*
@ -211,16 +212,6 @@ class Cash : ClauseVerifier() {
tx.addCommand(Cash.Commands.Issue(), at.party.owningKey) tx.addCommand(Cash.Commands.Issue(), at.party.owningKey)
} }
/**
* Generate a transaction that consumes one or more of the given input states to move money to the given pubkey.
* Note that the wallet list is not updated: it's up to you to do that.
*/
@Throws(InsufficientBalanceException::class)
fun generateSpend(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, to: PublicKey,
cashStates: List<StateAndRef<State>>): List<PublicKey> =
generateSpend(tx, Amount(amount.quantity, amount.token.product), to, cashStates,
setOf(amount.token.issuer.party))
/** /**
* Generate a transaction that consumes one or more of the given input states to move money to the given pubkey. * Generate a transaction that consumes one or more of the given input states to move money to the given pubkey.
* Note that the wallet list is not updated: it's up to you to do that. * Note that the wallet list is not updated: it's up to you to do that.
@ -301,21 +292,23 @@ class Cash : ClauseVerifier() {
/** /**
* Sums the cash states in the list belonging to a single owner, throwing an exception * Sums the cash states in the list belonging to a single owner, throwing an exception
* if there are none, or if any of the cash states cannot be added together (i.e. are * if there are none, or if any of the cash states cannot be added together (i.e. are
* different currencies). * different currencies or issuers).
*/ */
fun Iterable<ContractState>.sumCashBy(owner: PublicKey) = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow() fun Iterable<ContractState>.sumCashBy(owner: PublicKey): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow()
/** /**
* Sums the cash states in the list, throwing an exception if there are none, or if any of the cash * Sums the cash states in the list, throwing an exception if there are none, or if any of the cash
* states cannot be added together (i.e. are different currencies). * states cannot be added together (i.e. are different currencies).
*/ */
fun Iterable<ContractState>.sumCash() = filterIsInstance<Cash.State>().map { it.amount }.sumOrThrow() fun Iterable<ContractState>.sumCash(): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().map { it.amount }.sumOrThrow()
/** Sums the cash states in the list, returning null if there are none. */ /** Sums the cash states in the list, returning null if there are none. */
fun Iterable<ContractState>.sumCashOrNull() = filterIsInstance<Cash.State>().map { it.amount }.sumOrNull() fun Iterable<ContractState>.sumCashOrNull(): Amount<Issued<Currency>>? = filterIsInstance<Cash.State>().map { it.amount }.sumOrNull()
/** Sums the cash states in the list, returning zero of the given currency if there are none. */ /** Sums the cash states in the list, returning zero of the given currency+issuer if there are none. */
fun Iterable<ContractState>.sumCashOrZero(currency: Issued<Currency>) = filterIsInstance<Cash.State>().map { it.amount }.sumOrZero<Issued<Currency>>(currency) fun Iterable<ContractState>.sumCashOrZero(currency: Issued<Currency>): Amount<Issued<Currency>> {
return filterIsInstance<Cash.State>().map { it.amount }.sumOrZero<Issued<Currency>>(currency)
}
/** /**
* Returns a map of how much cash we have in each currency, ignoring details like issuer. Note: currencies for * Returns a map of how much cash we have in each currency, ignoring details like issuer. Note: currencies for
@ -342,7 +335,7 @@ infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = wi
// Unit testing helpers. These could go in a separate file but it's hardly worth it for just a few functions. // Unit testing helpers. These could go in a separate file but it's hardly worth it for just a few functions.
/** A randomly generated key. */ /** A randomly generated key. */
val DUMMY_CASH_ISSUER_KEY by lazy { generateKeyPair() } val DUMMY_CASH_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) }
/** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */ /** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */
val DUMMY_CASH_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_CASH_ISSUER_KEY.public).ref(1) } val DUMMY_CASH_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_CASH_ISSUER_KEY.public).ref(1) }
/** An extension property that lets you write 100.DOLLARS.CASH */ /** An extension property that lets you write 100.DOLLARS.CASH */

View File

@ -4,7 +4,9 @@ import com.r3corda.core.contracts.*
import java.security.PublicKey import java.security.PublicKey
import java.util.* import java.util.*
class InsufficientBalanceException(val amountMissing: Amount<*>) : Exception() class InsufficientBalanceException(val amountMissing: Amount<*>) : Exception() {
override fun toString() = "Insufficient balance, missing $amountMissing"
}
/** /**
* Interface for contract states representing assets which are fungible, countable and issued by a * Interface for contract states representing assets which are fungible, countable and issued by a

View File

@ -47,7 +47,7 @@ object TwoPartyTradeProtocol {
val TOPIC = "platform.trade" val TOPIC = "platform.trade"
class UnacceptablePriceException(val givenPrice: Amount<Issued<Currency>>) : Exception() class UnacceptablePriceException(val givenPrice: Amount<Currency>) : Exception()
class AssetMismatchException(val expectedTypeName: String, val typeName: String) : Exception() { class AssetMismatchException(val expectedTypeName: String, val typeName: String) : Exception() {
override fun toString() = "The submitted asset didn't match the expected type: $expectedTypeName vs $typeName" override fun toString() = "The submitted asset didn't match the expected type: $expectedTypeName vs $typeName"
} }
@ -55,7 +55,7 @@ object TwoPartyTradeProtocol {
// This object is serialised to the network and is the first protocol message the seller sends to the buyer. // This object is serialised to the network and is the first protocol message the seller sends to the buyer.
class SellerTradeInfo( class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>, val assetForSale: StateAndRef<OwnableState>,
val price: Amount<Issued<Currency>>, val price: Amount<Currency>,
val sellerOwnerKey: PublicKey, val sellerOwnerKey: PublicKey,
val sessionID: Long val sessionID: Long
) )
@ -66,7 +66,7 @@ object TwoPartyTradeProtocol {
open class Seller(val otherSide: Party, open class Seller(val otherSide: Party,
val notaryNode: NodeInfo, val notaryNode: NodeInfo,
val assetToSell: StateAndRef<OwnableState>, val assetToSell: StateAndRef<OwnableState>,
val price: Amount<Issued<Currency>>, val price: Amount<Currency>,
val myKeyPair: KeyPair, val myKeyPair: KeyPair,
val buyerSessionID: Long, val buyerSessionID: Long,
override val progressTracker: ProgressTracker = Seller.tracker()) : ProtocolLogic<SignedTransaction>() { override val progressTracker: ProgressTracker = Seller.tracker()) : ProtocolLogic<SignedTransaction>() {
@ -133,7 +133,7 @@ object TwoPartyTradeProtocol {
// This verifies that the transaction is contract-valid, even though it is missing signatures. // This verifies that the transaction is contract-valid, even though it is missing signatures.
serviceHub.verifyTransaction(wtx.toLedgerTransaction(serviceHub.identityService, serviceHub.storageService.attachments)) serviceHub.verifyTransaction(wtx.toLedgerTransaction(serviceHub.identityService, serviceHub.storageService.attachments))
if (wtx.outputs.map { it.data }.sumCashBy(myKeyPair.public) != price) if (wtx.outputs.map { it.data }.sumCashBy(myKeyPair.public).withoutIssuer() != price)
throw IllegalArgumentException("Transaction is not sending us the right amount of cash") throw IllegalArgumentException("Transaction is not sending us the right amount of cash")
// There are all sorts of funny games a malicious secondary might play here, we should fix them: // There are all sorts of funny games a malicious secondary might play here, we should fix them:
@ -178,7 +178,7 @@ object TwoPartyTradeProtocol {
open class Buyer(val otherSide: Party, open class Buyer(val otherSide: Party,
val notary: Party, val notary: Party,
val acceptablePrice: Amount<Issued<Currency>>, val acceptablePrice: Amount<Currency>,
val typeToBuy: Class<out OwnableState>, val typeToBuy: Class<out OwnableState>,
val sessionID: Long) : ProtocolLogic<SignedTransaction>() { val sessionID: Long) : ProtocolLogic<SignedTransaction>() {

View File

@ -31,6 +31,11 @@ val Long.bd: BigDecimal get() = BigDecimal(this)
fun String.abbreviate(maxWidth: Int): String = if (length <= maxWidth) this else take(maxWidth - 1) + "" fun String.abbreviate(maxWidth: Int): String = if (length <= maxWidth) this else take(maxWidth - 1) + ""
/** Like the + operator but throws an exception in case of integer overflow. */
infix fun Int.checkedAdd(b: Int) = Math.addExact(this, b)
/** Like the + operator but throws an exception in case of integer overflow. */
infix fun Long.checkedAdd(b: Long) = Math.addExact(this, b)
/** /**
* Returns a random positive long generated using a secure RNG. This function sacrifies a bit of entropy in order to * Returns a random positive long generated using a secure RNG. This function sacrifies a bit of entropy in order to
* avoid potential bugs where the value is used in a context where negative numbers are not expected. * avoid potential bugs where the value is used in a context where negative numbers are not expected.

View File

@ -61,7 +61,7 @@ data class Amount<T>(val quantity: Long, val token: T) : Comparable<Amount<T>> {
operator fun div(other: Int): Amount<T> = Amount(quantity / other, token) operator fun div(other: Int): Amount<T> = Amount(quantity / other, token)
operator fun times(other: Int): Amount<T> = Amount(Math.multiplyExact(quantity, other.toLong()), token) operator fun times(other: Int): Amount<T> = Amount(Math.multiplyExact(quantity, other.toLong()), token)
override fun toString(): String = (BigDecimal(quantity).divide(BigDecimal(100))).setScale(2).toPlainString() override fun toString(): String = (BigDecimal(quantity).divide(BigDecimal(100))).setScale(2).toPlainString() + " " + token
override fun compareTo(other: Amount<T>): Int { override fun compareTo(other: Amount<T>): Int {
checkCurrency(other) checkCurrency(other)

View File

@ -146,10 +146,16 @@ interface IssuanceDefinition
* *
* @param P the type of product underlying the definition, for example [Currency]. * @param P the type of product underlying the definition, for example [Currency].
*/ */
data class Issued<out P>( data class Issued<out P>(val issuer: PartyAndReference, val product: P) {
val issuer: PartyAndReference, override fun toString() = "$product issued by $issuer"
val product: P }
)
/**
* Strips the issuer and returns an [Amount] of the raw token directly. This is useful when you are mixing code that
* cares about specific issuers with code that will accept any, or which is imposing issuer constraints via some
* other mechanism and the additional type safety is not wanted.
*/
fun <T> Amount<Issued<T>>.withoutIssuer(): Amount<T> = Amount(quantity, token.product)
/** /**
* A contract state that can have a single owner. * A contract state that can have a single owner.

View File

@ -120,9 +120,9 @@ open class TransactionBuilder(
fun toSignedTransaction(checkSufficientSignatures: Boolean = true): SignedTransaction { fun toSignedTransaction(checkSufficientSignatures: Boolean = true): SignedTransaction {
if (checkSufficientSignatures) { if (checkSufficientSignatures) {
val gotKeys = currentSigs.map { it.by }.toSet() val gotKeys = currentSigs.map { it.by }.toSet()
val missing = signers - gotKeys val missing: Set<PublicKey> = signers - gotKeys
if (missing.isNotEmpty()) if (missing.isNotEmpty())
throw IllegalStateException("Missing signatures on the transaction for the public keys: ${missing.map { it.toStringShort() }}") throw IllegalStateException("Missing signatures on the transaction for the public keys: ${missing.toStringsShort()}")
} }
return SignedTransaction(toWireTransaction().serialize(), ArrayList(currentSigs)) return SignedTransaction(toWireTransaction().serialize(), ArrayList(currentSigs))
} }
@ -131,7 +131,6 @@ open class TransactionBuilder(
fun addInputState(stateRef: StateRef, notary: Party) { fun addInputState(stateRef: StateRef, notary: Party) {
check(currentSigs.isEmpty()) check(currentSigs.isEmpty())
signers.add(notary.owningKey) signers.add(notary.owningKey)
inputs.add(stateRef) inputs.add(stateRef)
} }

View File

@ -5,10 +5,14 @@ import com.r3corda.core.serialization.OpaqueBytes
import com.r3corda.core.serialization.SerializedBytes import com.r3corda.core.serialization.SerializedBytes
import com.r3corda.core.serialization.deserialize import com.r3corda.core.serialization.deserialize
import net.i2p.crypto.eddsa.EdDSAEngine import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.KeyPairGenerator
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import java.math.BigInteger import java.math.BigInteger
import java.security.* import java.security.*
import net.i2p.crypto.eddsa.KeyPairGenerator as EddsaKeyPairGenerator
fun newSecureRandom(): SecureRandom { fun newSecureRandom(): SecureRandom {
if (System.getProperty("os.name") == "Linux") { if (System.getProperty("os.name") == "Linux") {
@ -115,6 +119,7 @@ object NullPublicKey : PublicKey, Comparable<PublicKey> {
override fun toString() = "NULL_KEY" override fun toString() = "NULL_KEY"
} }
// TODO: Clean up this duplication between Null and Dummy public key
class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> { class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
override fun getAlgorithm() = "DUMMY" override fun getAlgorithm() = "DUMMY"
override fun getEncoded() = s.toByteArray() override fun getEncoded() = s.toByteArray()
@ -125,6 +130,9 @@ class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
override fun toString() = "PUBKEY[$s]" override fun toString() = "PUBKEY[$s]"
} }
/** A signature with a key and value of zero. Useful when you want a signature object that you know won't ever be used. */
object NullSignature : DigitalSignature.WithKey(NullPublicKey, ByteArray(32))
/** Utility to simplify the act of signing a byte array */ /** Utility to simplify the act of signing a byte array */
fun PrivateKey.signWithECDSA(bits: ByteArray): DigitalSignature { fun PrivateKey.signWithECDSA(bits: ByteArray): DigitalSignature {
val signer = EdDSAEngine() val signer = EdDSAEngine()
@ -163,10 +171,24 @@ fun PublicKey.toStringShort(): String {
} ?: toString() } ?: toString()
} }
fun Iterable<PublicKey>.toStringsShort(): String = map { it.toStringShort() }.toString()
// Allow Kotlin destructuring: val (private, public) = keypair // Allow Kotlin destructuring: val (private, public) = keypair
operator fun KeyPair.component1() = this.private operator fun KeyPair.component1() = this.private
operator fun KeyPair.component2() = this.public operator fun KeyPair.component2() = this.public
/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future */ /** A simple wrapper that will make it easier to swap out the EC algorithm we use in future */
fun generateKeyPair(): KeyPair = EddsaKeyPairGenerator().generateKeyPair() fun generateKeyPair(): KeyPair = KeyPairGenerator().generateKeyPair()
/**
* Returns a keypair derived from the given private key entropy. This is useful for unit tests and other cases where
* you want hard-coded private keys.
*/
fun entropyToKeyPair(entropy: BigInteger): KeyPair {
val params = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512)
val bits = entropy.toByteArray().copyOf(params.curve.field.getb() / 8)
val priv = EdDSAPrivateKeySpec(bits, params)
val pub = EdDSAPublicKeySpec(priv.a, params)
val key = KeyPair(EdDSAPublicKey(pub), EdDSAPrivateKey(priv))
return key
}

View File

@ -7,14 +7,12 @@ import com.google.common.net.HostAndPort
import com.r3corda.core.contracts.Attachment import com.r3corda.core.contracts.Attachment
import com.r3corda.core.contracts.StateRef import com.r3corda.core.contracts.StateRef
import com.r3corda.core.contracts.TransactionBuilder import com.r3corda.core.contracts.TransactionBuilder
import com.r3corda.core.crypto.DummyPublicKey import com.r3corda.core.crypto.*
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.generateKeyPair
import com.r3corda.core.node.services.IdentityService import com.r3corda.core.node.services.IdentityService
import com.r3corda.core.node.services.StorageService import com.r3corda.core.node.services.StorageService
import com.r3corda.core.node.services.testing.MockIdentityService import com.r3corda.core.node.services.testing.MockIdentityService
import com.r3corda.core.node.services.testing.MockStorageService import com.r3corda.core.node.services.testing.MockStorageService
import java.math.BigInteger
import java.net.ServerSocket import java.net.ServerSocket
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
@ -73,7 +71,7 @@ val CHARLIE: Party get() = Party("Charlie", CHARLIE_PUBKEY)
val MEGA_CORP: Party get() = Party("MegaCorp", MEGA_CORP_PUBKEY) val MEGA_CORP: Party get() = Party("MegaCorp", MEGA_CORP_PUBKEY)
val MINI_CORP: Party get() = Party("MiniCorp", MINI_CORP_PUBKEY) val MINI_CORP: Party get() = Party("MiniCorp", MINI_CORP_PUBKEY)
val DUMMY_NOTARY_KEY: KeyPair by lazy { generateKeyPair() } val DUMMY_NOTARY_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(20)) }
val DUMMY_NOTARY: Party get() = Party("Notary Service", DUMMY_NOTARY_KEY.public) val DUMMY_NOTARY: Party get() = Party("Notary Service", DUMMY_NOTARY_KEY.public)
val ALL_TEST_KEYS: List<KeyPair> get() = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, ALICE_KEY, BOB_KEY, DUMMY_NOTARY_KEY) val ALL_TEST_KEYS: List<KeyPair> get() = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, ALICE_KEY, BOB_KEY, DUMMY_NOTARY_KEY)

View File

@ -53,9 +53,7 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
fun tweak(dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail): EnforceVerifyOrFail fun tweak(dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail): EnforceVerifyOrFail
} }
class TransactionDSL<out T : TransactionDSLInterpreter> (val interpreter: T) : class TransactionDSL<out T : TransactionDSLInterpreter>(val interpreter: T) : TransactionDSLInterpreter by interpreter {
TransactionDSLInterpreter by interpreter {
/** /**
* Looks up the output label and adds the found state as an input. * Looks up the output label and adds the found state as an input.
* @param stateLabel The label of the output state specified when calling [TransactionDSLInterpreter._output] and friends. * @param stateLabel The label of the output state specified when calling [TransactionDSLInterpreter._output] and friends.

View File

@ -8,8 +8,6 @@ import com.r3corda.contracts.testing.fillWithSomeTestCash
import com.r3corda.core.contracts.DOLLARS import com.r3corda.core.contracts.DOLLARS
import com.r3corda.core.contracts.SignedTransaction import com.r3corda.core.contracts.SignedTransaction
import com.r3corda.core.contracts.`issued by` import com.r3corda.core.contracts.`issued by`
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.generateKeyPair
import com.r3corda.core.days import com.r3corda.core.days
import com.r3corda.core.random63BitValue import com.r3corda.core.random63BitValue
import com.r3corda.core.seconds import com.r3corda.core.seconds
@ -43,8 +41,7 @@ class TradeSimulation(runAsync: Boolean, latencyInjector: InMemoryMessagingNetwo
} }
seller.services.recordTransactions(issuance) seller.services.recordTransactions(issuance)
val cashIssuerKey = generateKeyPair() val amount = 1000.DOLLARS
val amount = 1000.DOLLARS `issued by` Party("Big friendly bank", cashIssuerKey.public).ref(1)
val sessionID = random63BitValue() val sessionID = random63BitValue()
val buyerProtocol = TwoPartyTradeProtocol.Buyer( val buyerProtocol = TwoPartyTradeProtocol.Buyer(
seller.info.identity, seller.info.identity,

View File

@ -270,8 +270,10 @@ class StateMachineManager(val serviceHub: ServiceHubInternal, tokenizableService
request.payload?.let { request.payload?.let {
psm.logger.trace { "Sending message of type ${it.javaClass.name} using queue $queueID to ${request.destination} (${it.toString().abbreviate(50)})" } psm.logger.trace { "Sending message of type ${it.javaClass.name} using queue $queueID to ${request.destination} (${it.toString().abbreviate(50)})" }
val node = serviceHub.networkMapCache.getNodeByLegalName(request.destination!!.name) val node = serviceHub.networkMapCache.getNodeByLegalName(request.destination!!.name)
requireNotNull(node) { "Don't know about ${request.destination}" } if (node == null) {
serviceHub.networkService.send(queueID, it, node!!.address) throw IllegalArgumentException("Don't know about ${request.destination} but trying to send a message of type ${it.javaClass.name} on $queueID (${it.toString().abbreviate(50)})", request.stackTraceInCaseOfProblems)
}
serviceHub.networkService.send(queueID, it, node.address)
} }
if (request is FiberRequest.NotExpectingResponse) { if (request is FiberRequest.NotExpectingResponse) {
// We sent a message, but don't expect a response, so re-enter the continuation to let it keep going. // We sent a message, but don't expect a response, so re-enter the continuation to let it keep going.

View File

@ -54,14 +54,14 @@ class TwoPartyTradeProtocolTests {
lateinit var net: MockNetwork lateinit var net: MockNetwork
private fun runSeller(smm: StateMachineManager, notary: NodeInfo, private fun runSeller(smm: StateMachineManager, notary: NodeInfo,
otherSide: Party, assetToSell: StateAndRef<OwnableState>, price: Amount<Issued<Currency>>, otherSide: Party, assetToSell: StateAndRef<OwnableState>, price: Amount<Currency>,
myKeyPair: KeyPair, buyerSessionID: Long): ListenableFuture<SignedTransaction> { myKeyPair: KeyPair, buyerSessionID: Long): ListenableFuture<SignedTransaction> {
val seller = TwoPartyTradeProtocol.Seller(otherSide, notary, assetToSell, price, myKeyPair, buyerSessionID) val seller = TwoPartyTradeProtocol.Seller(otherSide, notary, assetToSell, price, myKeyPair, buyerSessionID)
return smm.add("${TwoPartyTradeProtocol.TOPIC}.seller", seller) return smm.add("${TwoPartyTradeProtocol.TOPIC}.seller", seller)
} }
private fun runBuyer(smm: StateMachineManager, notaryNode: NodeInfo, private fun runBuyer(smm: StateMachineManager, notaryNode: NodeInfo,
otherSide: Party, acceptablePrice: Amount<Issued<Currency>>, typeToBuy: Class<out OwnableState>, otherSide: Party, acceptablePrice: Amount<Currency>, typeToBuy: Class<out OwnableState>,
sessionID: Long): ListenableFuture<SignedTransaction> { sessionID: Long): ListenableFuture<SignedTransaction> {
val buyer = TwoPartyTradeProtocol.Buyer(otherSide, notaryNode.identity, acceptablePrice, typeToBuy, sessionID) val buyer = TwoPartyTradeProtocol.Buyer(otherSide, notaryNode.identity, acceptablePrice, typeToBuy, sessionID)
return smm.add("${TwoPartyTradeProtocol.TOPIC}.buyer", buyer) return smm.add("${TwoPartyTradeProtocol.TOPIC}.buyer", buyer)
@ -106,7 +106,7 @@ class TwoPartyTradeProtocolTests {
bobNode.smm, bobNode.smm,
notaryNode.info, notaryNode.info,
aliceNode.info.identity, aliceNode.info.identity,
1000.DOLLARS `issued by` issuer, 1000.DOLLARS,
CommercialPaper.State::class.java, CommercialPaper.State::class.java,
buyerSessionID buyerSessionID
) )
@ -115,7 +115,7 @@ class TwoPartyTradeProtocolTests {
notaryNode.info, notaryNode.info,
bobNode.info.identity, bobNode.info.identity,
"alice's paper".outputStateAndRef(), "alice's paper".outputStateAndRef(),
1000.DOLLARS `issued by` issuer, 1000.DOLLARS,
ALICE_KEY, ALICE_KEY,
buyerSessionID buyerSessionID
) )
@ -158,7 +158,7 @@ class TwoPartyTradeProtocolTests {
notaryNode.info, notaryNode.info,
bobNode.info.identity, bobNode.info.identity,
"alice's paper".outputStateAndRef(), "alice's paper".outputStateAndRef(),
1000.DOLLARS `issued by` issuer, 1000.DOLLARS,
ALICE_KEY, ALICE_KEY,
buyerSessionID buyerSessionID
) )
@ -166,7 +166,7 @@ class TwoPartyTradeProtocolTests {
bobNode.smm, bobNode.smm,
notaryNode.info, notaryNode.info,
aliceNode.info.identity, aliceNode.info.identity,
1000.DOLLARS `issued by` issuer, 1000.DOLLARS,
CommercialPaper.State::class.java, CommercialPaper.State::class.java,
buyerSessionID buyerSessionID
) )
@ -279,7 +279,7 @@ class TwoPartyTradeProtocolTests {
notaryNode.info, notaryNode.info,
bobNode.info.identity, bobNode.info.identity,
"alice's paper".outputStateAndRef(), "alice's paper".outputStateAndRef(),
1000.DOLLARS `issued by` issuer, 1000.DOLLARS,
ALICE_KEY, ALICE_KEY,
buyerSessionID buyerSessionID
) )
@ -287,7 +287,7 @@ class TwoPartyTradeProtocolTests {
bobNode.smm, bobNode.smm,
notaryNode.info, notaryNode.info,
aliceNode.info.identity, aliceNode.info.identity,
1000.DOLLARS `issued by` issuer, 1000.DOLLARS,
CommercialPaper.State::class.java, CommercialPaper.State::class.java,
buyerSessionID buyerSessionID
) )
@ -390,7 +390,7 @@ class TwoPartyTradeProtocolTests {
notaryNode.info, notaryNode.info,
bobNode.info.identity, bobNode.info.identity,
"alice's paper".outputStateAndRef(), "alice's paper".outputStateAndRef(),
1000.DOLLARS `issued by` issuer, 1000.DOLLARS,
ALICE_KEY, ALICE_KEY,
buyerSessionID buyerSessionID
) )
@ -398,7 +398,7 @@ class TwoPartyTradeProtocolTests {
bobNode.smm, bobNode.smm,
notaryNode.info, notaryNode.info,
aliceNode.info.identity, aliceNode.info.identity,
1000.DOLLARS `issued by` issuer, 1000.DOLLARS,
CommercialPaper.State::class.java, CommercialPaper.State::class.java,
buyerSessionID buyerSessionID
) )

View File

@ -76,7 +76,7 @@ class WalletWithCashTest {
// A tx that spends our money. // A tx that spends our money.
val spendTX = TransactionType.General.Builder().apply { val spendTX = TransactionType.General.Builder().apply {
Cash().generateSpend(this, 80.DOLLARS `issued by` MEGA_CORP.ref(1), BOB_PUBKEY, listOf(myOutput)) Cash().generateSpend(this, 80.DOLLARS, BOB_PUBKEY, listOf(myOutput))
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()

View File

@ -164,7 +164,7 @@ fun runTraderDemo(args: Array<String>): Int {
// What happens next depends on the role. The buyer sits around waiting for a trade to start. The seller role // What happens next depends on the role. The buyer sits around waiting for a trade to start. The seller role
// will contact the buyer and actually make something happen. // will contact the buyer and actually make something happen.
val amount = 1000.DOLLARS `issued by` cashIssuer.ref(0) // Note: "0" has to match the reference used in the wallet filler val amount = 1000.DOLLARS
if (role == Role.BUYER) { if (role == Role.BUYER) {
runBuyer(node, amount) runBuyer(node, amount)
} else { } else {
@ -174,7 +174,7 @@ fun runTraderDemo(args: Array<String>): Int {
return 0 return 0
} }
private fun runSeller(node: Node, amount: Amount<Issued<Currency>>, otherSide: Party) { private fun runSeller(node: Node, amount: Amount<Currency>, otherSide: Party) {
// The seller will sell some commercial paper to the buyer, who will pay with (self issued) cash. // The seller will sell some commercial paper to the buyer, who will pay with (self issued) cash.
// //
// The CP sale transaction comes with a prospectus PDF, which will tag along for the ride in an // The CP sale transaction comes with a prospectus PDF, which will tag along for the ride in an
@ -202,7 +202,7 @@ private fun runSeller(node: Node, amount: Amount<Issued<Currency>>, otherSide: P
node.stop() node.stop()
} }
private fun runBuyer(node: Node, amount: Amount<Issued<Currency>>) { private fun runBuyer(node: Node, amount: Amount<Currency>) {
// Buyer will fetch the attachment from the seller automatically when it resolves the transaction. // Buyer will fetch the attachment from the seller automatically when it resolves the transaction.
// For demo purposes just extract attachment jars when saved to disk, so the user can explore them. // For demo purposes just extract attachment jars when saved to disk, so the user can explore them.
val attachmentsPath = (node.storage.attachments as NodeAttachmentService).let { val attachmentsPath = (node.storage.attachments as NodeAttachmentService).let {
@ -236,7 +236,7 @@ val DEMO_TOPIC = "initiate.demo.trade"
private class TraderDemoProtocolBuyer(val otherSide: Party, private class TraderDemoProtocolBuyer(val otherSide: Party,
private val attachmentsPath: Path, private val attachmentsPath: Path,
val amount: Amount<Issued<Currency>>, val amount: Amount<Currency>,
override val progressTracker: ProgressTracker = ProgressTracker(STARTING_BUY)) : ProtocolLogic<Unit>() { override val progressTracker: ProgressTracker = ProgressTracker(STARTING_BUY)) : ProtocolLogic<Unit>() {
object STARTING_BUY : ProgressTracker.Step("Seller connected, purchasing commercial paper asset") object STARTING_BUY : ProgressTracker.Step("Seller connected, purchasing commercial paper asset")
@ -294,7 +294,7 @@ ${Emoji.renderIfSupported(cpIssuance)}""")
} }
private class TraderDemoProtocolSeller(val otherSide: Party, private class TraderDemoProtocolSeller(val otherSide: Party,
val amount: Amount<Issued<Currency>>, val amount: Amount<Currency>,
override val progressTracker: ProgressTracker = TraderDemoProtocolSeller.tracker()) : ProtocolLogic<SignedTransaction>() { override val progressTracker: ProgressTracker = TraderDemoProtocolSeller.tracker()) : ProtocolLogic<SignedTransaction>() {
companion object { companion object {
val PROSPECTUS_HASH = SecureHash.parse("decd098666b9657314870e192ced0c3519c2c9d395507a238338f8d003929de9") val PROSPECTUS_HASH = SecureHash.parse("decd098666b9657314870e192ced0c3519c2c9d395507a238338f8d003929de9")