From 9f4897f7c4debc4497cb2367459365a52d5e5e39 Mon Sep 17 00:00:00 2001 From: Mike Hearn <mike@r3cev.com> Date: Fri, 12 Feb 2016 15:49:18 +0100 Subject: [PATCH] Simplifies two-party trade protocol to return SignedTransaction instead of a pair of other forms. --- docs/source/protocol-state-machines.rst | 28 +++++++++---------- .../protocols/TwoPartyTradeProtocol.kt | 27 +++++++++--------- src/main/kotlin/core/node/TraderDemo.kt | 4 +-- .../messaging/TwoPartyTradeProtocolTests.kt | 6 ++-- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/docs/source/protocol-state-machines.rst b/docs/source/protocol-state-machines.rst index 578b1afe0d..1cd7724e50 100644 --- a/docs/source/protocol-state-machines.rst +++ b/docs/source/protocol-state-machines.rst @@ -111,7 +111,7 @@ each side. fun runSeller(smm: StateMachineManager, timestampingAuthority: LegallyIdentifiableNode, otherSide: SingleMessageRecipient, assetToSell: StateAndRef<OwnableState>, price: Amount, - myKeyPair: KeyPair, buyerSessionID: Long): ListenableFuture<Pair<WireTransaction, LedgerTransaction>> { + myKeyPair: KeyPair, buyerSessionID: Long): ListenableFuture<SignedTransaction> { val seller = Seller(otherSide, timestampingAuthority, assetToSell, price, myKeyPair, buyerSessionID) smm.add("$TRADE_TOPIC.seller", seller) return seller.resultFuture @@ -119,7 +119,7 @@ each side. fun runBuyer(smm: StateMachineManager, timestampingAuthority: LegallyIdentifiableNode, otherSide: SingleMessageRecipient, acceptablePrice: Amount, typeToBuy: Class<out OwnableState>, - sessionID: Long): ListenableFuture<Pair<WireTransaction, LedgerTransaction>> { + sessionID: Long): ListenableFuture<SignedTransaction> { val buyer = Buyer(otherSide, timestampingAuthority.identity, acceptablePrice, typeToBuy, sessionID) smm.add("$TRADE_TOPIC.buyer", buyer) return buyer.resultFuture @@ -140,9 +140,9 @@ each side. val assetToSell: StateAndRef<OwnableState>, val price: Amount, val myKeyPair: KeyPair, - val buyerSessionID: Long) : ProtocolStateMachine<Pair<WireTransaction, LedgerTransaction>>() { + val buyerSessionID: Long) : ProtocolStateMachine<SignedTransaction>() { @Suspendable - override fun call(): Pair<WireTransaction, LedgerTransaction> { + override fun call(): SignedTransaction { TODO() } } @@ -156,9 +156,9 @@ each side. val timestampingAuthority: Party, val acceptablePrice: Amount, val typeToBuy: Class<out OwnableState>, - val sessionID: Long) : ProtocolStateMachine<Pair<WireTransaction, LedgerTransaction>>() { + val sessionID: Long) : ProtocolStateMachine<SignedTransaction>() { @Suspendable - override fun call(): Pair<WireTransaction, LedgerTransaction> { + override fun call(): SignedTransaction { TODO() } } @@ -251,9 +251,9 @@ Let's implement the ``Seller.call`` method. This will be invoked by the platform val ourSignature = signWithOurKey(partialTX) val tsaSig = timestamp(partialTX) - val ledgerTX = sendSignatures(partialTX, ourSignature, tsaSig) + val stx: SignedTransaction = sendSignatures(partialTX, ourSignature, tsaSig) - return Pair(partialTX.tx, ledgerTX) + return stx Here we see the outline of the procedure. We receive a proposed trade transaction from the buyer and check that it's valid. Then we sign with our own key, request a timestamping authority to assert with another signature that the @@ -338,16 +338,16 @@ Here's the rest of the code: @Suspendable open fun sendSignatures(partialTX: SignedTransaction, ourSignature: DigitalSignature.WithKey, - tsaSig: DigitalSignature.LegallyIdentifiable): LedgerTransaction { + tsaSig: DigitalSignature.LegallyIdentifiable): SignedTransaction { val fullySigned = partialTX + tsaSig + ourSignature - val ltx = fullySigned.verifyToLedgerTransaction(serviceHub.identityService) + fullySigned.verify() // TODO: We should run it through our full TransactionGroup of all transactions here. logger.trace { "Built finished transaction, sending back to secondary!" } send(TRADE_TOPIC, otherSide, buyerSessionID, SignaturesFromSeller(tsaSig, ourSignature)) - return ltx + return fullySigned } It's should be all pretty straightforward: here, ``txBits`` is the raw byte array representing the transaction. @@ -372,7 +372,7 @@ OK, let's do the same for the buyer side: .. sourcecode:: kotlin @Suspendable - override fun call(): Pair<WireTransaction, LedgerTransaction> { + override fun call(): SignedTransaction { val tradeRequest = receiveAndValidateTradeRequest() val (ptx, cashSigningPubKeys) = assembleSharedTX(tradeRequest) val stx = signWithOurKeys(cashSigningPubKeys, ptx) @@ -380,10 +380,10 @@ OK, let's do the same for the buyer side: logger.trace { "Got signatures from seller, verifying ... "} val fullySigned = stx + signatures.timestampAuthoritySig + signatures.sellerSig - val ltx = fullySigned.verifyToLedgerTransaction(serviceHub.identityService) + fullySigned.verify() logger.trace { "Fully signed transaction was valid. Trade complete! :-)" } - return Pair(fullySigned.tx, ltx) + return fullySigned } @Suspendable diff --git a/src/main/kotlin/contracts/protocols/TwoPartyTradeProtocol.kt b/src/main/kotlin/contracts/protocols/TwoPartyTradeProtocol.kt index e825d7cd86..2d8a65fd06 100644 --- a/src/main/kotlin/contracts/protocols/TwoPartyTradeProtocol.kt +++ b/src/main/kotlin/contracts/protocols/TwoPartyTradeProtocol.kt @@ -53,7 +53,7 @@ object TwoPartyTradeProtocol { fun runSeller(smm: StateMachineManager, timestampingAuthority: LegallyIdentifiableNode, otherSide: SingleMessageRecipient, assetToSell: StateAndRef<OwnableState>, price: Amount, - myKeyPair: KeyPair, buyerSessionID: Long): ListenableFuture<Pair<WireTransaction, LedgerTransaction>> { + myKeyPair: KeyPair, buyerSessionID: Long): ListenableFuture<SignedTransaction> { val seller = Seller(otherSide, timestampingAuthority, assetToSell, price, myKeyPair, buyerSessionID) smm.add("$TRADE_TOPIC.seller", seller) return seller.resultFuture @@ -61,7 +61,7 @@ object TwoPartyTradeProtocol { fun runBuyer(smm: StateMachineManager, timestampingAuthority: LegallyIdentifiableNode, otherSide: SingleMessageRecipient, acceptablePrice: Amount, typeToBuy: Class<out OwnableState>, - sessionID: Long): ListenableFuture<Pair<WireTransaction, LedgerTransaction>> { + sessionID: Long): ListenableFuture<SignedTransaction> { val buyer = Buyer(otherSide, timestampingAuthority.identity, acceptablePrice, typeToBuy, sessionID) smm.add("$TRADE_TOPIC.buyer", buyer) return buyer.resultFuture @@ -82,18 +82,18 @@ object TwoPartyTradeProtocol { val assetToSell: StateAndRef<OwnableState>, val price: Amount, val myKeyPair: KeyPair, - val buyerSessionID: Long) : ProtocolStateMachine<Pair<WireTransaction, LedgerTransaction>>() { + val buyerSessionID: Long) : ProtocolStateMachine<SignedTransaction>() { @Suspendable - override fun call(): Pair<WireTransaction, LedgerTransaction> { + override fun call(): SignedTransaction { val partialTX: SignedTransaction = receiveAndCheckProposedTransaction() // These two steps could be done in parallel, in theory. Our framework doesn't support that yet though. val ourSignature = signWithOurKey(partialTX) val tsaSig = timestamp(partialTX) - val ledgerTX = sendSignatures(partialTX, ourSignature, tsaSig) + val signedTransaction = sendSignatures(partialTX, ourSignature, tsaSig) - return Pair(partialTX.tx, ledgerTX) + return signedTransaction } @Suspendable @@ -136,16 +136,15 @@ object TwoPartyTradeProtocol { @Suspendable open fun sendSignatures(partialTX: SignedTransaction, ourSignature: DigitalSignature.WithKey, - tsaSig: DigitalSignature.LegallyIdentifiable): LedgerTransaction { + tsaSig: DigitalSignature.LegallyIdentifiable): SignedTransaction { val fullySigned = partialTX + tsaSig + ourSignature - val ltx = fullySigned.verifyToLedgerTransaction(serviceHub.identityService) // TODO: We should run it through our full TransactionGroup of all transactions here. logger.trace { "Built finished transaction, sending back to secondary!" } send(TRADE_TOPIC, otherSide, buyerSessionID, SignaturesFromSeller(tsaSig, ourSignature)) - return ltx + return fullySigned } } @@ -158,9 +157,9 @@ object TwoPartyTradeProtocol { val timestampingAuthority: Party, val acceptablePrice: Amount, val typeToBuy: Class<out OwnableState>, - val sessionID: Long) : ProtocolStateMachine<Pair<WireTransaction, LedgerTransaction>>() { + val sessionID: Long) : ProtocolStateMachine<SignedTransaction>() { @Suspendable - override fun call(): Pair<WireTransaction, LedgerTransaction> { + override fun call(): SignedTransaction { val tradeRequest = receiveAndValidateTradeRequest() val (ptx, cashSigningPubKeys) = assembleSharedTX(tradeRequest) val stx = signWithOurKeys(cashSigningPubKeys, ptx) @@ -168,10 +167,10 @@ object TwoPartyTradeProtocol { logger.trace { "Got signatures from seller, verifying ... "} val fullySigned = stx + signatures.timestampAuthoritySig + signatures.sellerSig - val ltx = fullySigned.verifyToLedgerTransaction(serviceHub.identityService) + fullySigned.verify() - logger.trace { "Fully signed transaction was valid. Trade complete! :-)" } - return Pair(fullySigned.tx, ltx) + logger.trace { "Signatures received are valid. Trade complete! :-)" } + return fullySigned } @Suspendable diff --git a/src/main/kotlin/core/node/TraderDemo.kt b/src/main/kotlin/core/node/TraderDemo.kt index 01c84d6fcd..5380c9c7d7 100644 --- a/src/main/kotlin/core/node/TraderDemo.kt +++ b/src/main/kotlin/core/node/TraderDemo.kt @@ -110,7 +110,7 @@ fun main(args: Array<String>) { println() println("Purchase complete - we are a happy customer! Final transaction is:") println() - println(Emoji.renderIfSupported(it.first)) + println(Emoji.renderIfSupported(it)) println() println("Waiting for another seller to connect. Or press Ctrl-C to shut me down.") } @@ -145,7 +145,7 @@ fun main(args: Array<String>) { println() println("Final transaction is") println() - println(Emoji.renderIfSupported(it.first)) + println(Emoji.renderIfSupported(it)) println() node.stop() } diff --git a/src/test/kotlin/core/messaging/TwoPartyTradeProtocolTests.kt b/src/test/kotlin/core/messaging/TwoPartyTradeProtocolTests.kt index 312cecf61e..f1cdd3562f 100644 --- a/src/test/kotlin/core/messaging/TwoPartyTradeProtocolTests.kt +++ b/src/test/kotlin/core/messaging/TwoPartyTradeProtocolTests.kt @@ -88,7 +88,7 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() { assertEquals(aliceResult.get(), bobResult.get()) - txns.add(aliceResult.get().first) + txns.add(aliceResult.get().tx) verify() } } @@ -178,8 +178,8 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() { assertTrue(bobsNode.pump(false)) // Bob is now finished and has the same transaction as Alice. - val tx = bobFuture.get() - txns.add(tx.first) + val stx = bobFuture.get() + txns.add(stx.tx) verify() assertTrue(smm.stateMachines.isEmpty())