mirror of
https://github.com/corda/corda.git
synced 2024-12-19 04:57:58 +00:00
Minor: make TwoPartyTradeProtocolTests use the new MockNode infrastructure
This commit is contained in:
parent
a69a663354
commit
7f5eb5bf2f
@ -83,7 +83,7 @@ class CommercialPaper : Contract {
|
||||
// Here, we match acceptable timestamp authorities by name. The list of acceptable TSAs (oracles) must be
|
||||
// hard coded into the contract because otherwise we could fail to gain consensus, if nodes disagree about
|
||||
// who or what is a trusted authority.
|
||||
val timestamp: TimestampCommand? = tx.commands.getTimestampByName("The dummy timestamper", "Bank of Zurich")
|
||||
val timestamp: TimestampCommand? = tx.commands.getTimestampByName("Mock Company 0", "Bank of Zurich")
|
||||
|
||||
for (group in groups) {
|
||||
when (command.value) {
|
||||
|
@ -25,5 +25,5 @@ interface TimestamperService {
|
||||
// The timestamper itself is implemented in the unit test part of the code (in TestUtils.kt).
|
||||
object DummyTimestampingAuthority {
|
||||
val key = generateKeyPair()
|
||||
val identity = Party("The dummy timestamper", key.public)
|
||||
val identity = Party("Mock Company 0", key.public)
|
||||
}
|
||||
|
@ -16,6 +16,6 @@ import java.security.PublicKey
|
||||
* Scaffolding: a dummy identity service that just expects to have identities loaded off disk or found elsewhere.
|
||||
*/
|
||||
class FixedIdentityService(private val identities: List<Party>) : IdentityService {
|
||||
private val keyToParties = identities.associateBy { it.owningKey }
|
||||
private val keyToParties: Map<PublicKey, Party> get() = identities.associateBy { it.owningKey }
|
||||
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]
|
||||
}
|
@ -8,18 +8,25 @@
|
||||
|
||||
package core.messaging
|
||||
|
||||
import com.google.common.util.concurrent.MoreExecutors
|
||||
import contracts.Cash
|
||||
import contracts.CommercialPaper
|
||||
import contracts.protocols.TwoPartyTradeProtocol
|
||||
import core.*
|
||||
import core.crypto.SecureHash
|
||||
import core.node.MockNetwork
|
||||
import core.node.NodeAttachmentStorage
|
||||
import core.node.NodeWalletService
|
||||
import core.testutils.*
|
||||
import core.utilities.BriefLogFormatter
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyPair
|
||||
import java.security.PublicKey
|
||||
import java.util.*
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
@ -33,9 +40,17 @@ import kotlin.test.assertTrue
|
||||
* We assume that Alice and Bob already found each other via some market, and have agreed the details already.
|
||||
*/
|
||||
class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
||||
lateinit var net: MockNetwork
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
BriefLogFormatter.initVerbose("platform.trade", "core.TransactionGroup", "recordingmap")
|
||||
net = MockNetwork(false)
|
||||
BriefLogFormatter.loggingOn("platform.trade", "core.TransactionGroup", "recordingmap")
|
||||
}
|
||||
|
||||
@After
|
||||
fun after() {
|
||||
BriefLogFormatter.loggingOff("platform.trade", "core.TransactionGroup", "recordingmap")
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -43,96 +58,76 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
||||
// We run this in parallel threads to help catch any race conditions that may exist. The other tests
|
||||
// we run in the unit test thread exclusively to speed things up, ensure deterministic results and
|
||||
// allow interruption half way through.
|
||||
val backgroundThread = Executors.newSingleThreadExecutor()
|
||||
net = MockNetwork(true)
|
||||
transactionGroupFor<ContractState> {
|
||||
val (bobsWallet, bobsFakeCash) = fillUpForBuyer(false)
|
||||
val alicesFakePaper = fillUpForSeller(false).second
|
||||
val (aliceNode, bobNode) = net.createTwoNodes()
|
||||
(bobNode.wallet as NodeWalletService).fillWithSomeTestCash(2000.DOLLARS)
|
||||
val alicesFakePaper = fillUpForSeller(false, aliceNode.legallyIdentifableAddress.identity).second
|
||||
|
||||
val (alicesAddress, alicesNode) = makeNode(inBackground = true)
|
||||
val (bobsAddress, bobsNode) = makeNode(inBackground = true)
|
||||
val timestamper = network.setupTimestampingNode(false).first
|
||||
|
||||
val alicesServices = MockServices(net = alicesNode)
|
||||
val bobsServices = MockServices(
|
||||
wallet = MockWalletService(bobsWallet.states),
|
||||
keyManagement = MockKeyManagementService(BOB_KEY),
|
||||
net = bobsNode,
|
||||
storage = MockStorageService()
|
||||
)
|
||||
loadFakeTxnsIntoStorage(bobsFakeCash, bobsServices.storageService)
|
||||
loadFakeTxnsIntoStorage(alicesFakePaper, alicesServices.storageService)
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode.services, aliceNode.storage.myLegalIdentityKey)
|
||||
|
||||
val buyerSessionID = random63BitValue()
|
||||
|
||||
val aliceResult = TwoPartyTradeProtocol.runSeller(
|
||||
StateMachineManager(alicesServices, backgroundThread),
|
||||
timestamper,
|
||||
bobsAddress,
|
||||
aliceNode.smm,
|
||||
aliceNode.legallyIdentifableAddress,
|
||||
bobNode.net.myAddress,
|
||||
lookup("alice's paper"),
|
||||
1000.DOLLARS,
|
||||
ALICE_KEY,
|
||||
buyerSessionID
|
||||
)
|
||||
val bobResult = TwoPartyTradeProtocol.runBuyer(
|
||||
StateMachineManager(bobsServices, backgroundThread),
|
||||
timestamper,
|
||||
alicesAddress,
|
||||
bobNode.smm,
|
||||
aliceNode.legallyIdentifableAddress,
|
||||
aliceNode.net.myAddress,
|
||||
1000.DOLLARS,
|
||||
CommercialPaper.State::class.java,
|
||||
buyerSessionID
|
||||
)
|
||||
|
||||
// TODO: Verify that the result was inserted into the transaction database.
|
||||
// assertEquals(bobResult.get(), aliceNode.storage.validatedTransactions[aliceResult.get().id])
|
||||
assertEquals(aliceResult.get(), bobResult.get())
|
||||
|
||||
txns.add(aliceResult.get().tx)
|
||||
verify()
|
||||
aliceNode.stop()
|
||||
bobNode.stop()
|
||||
}
|
||||
backgroundThread.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shut down and restore`() {
|
||||
fun shutdownAndRestore() {
|
||||
transactionGroupFor<ContractState> {
|
||||
val (wallet, bobsFakeCash) = fillUpForBuyer(false)
|
||||
val alicesFakePaper = fillUpForSeller(false).second
|
||||
var (aliceNode, bobNode) = net.createTwoNodes()
|
||||
val aliceAddr = aliceNode.net.myAddress
|
||||
val bobAddr = bobNode.net.myAddress as InMemoryMessagingNetwork.Handle
|
||||
val timestamperAddr = aliceNode.legallyIdentifableAddress
|
||||
|
||||
val (alicesAddress, alicesNode) = makeNode()
|
||||
var (bobsAddress, bobsNode) = makeNode()
|
||||
val timestamper = network.setupTimestampingNode(true)
|
||||
(bobNode.wallet as NodeWalletService).fillWithSomeTestCash(2000.DOLLARS)
|
||||
val alicesFakePaper = fillUpForSeller(false, timestamperAddr.identity).second
|
||||
|
||||
val bobsStorage = MockStorageService()
|
||||
|
||||
val alicesServices = MockServices(wallet = null, keyManagement = null, net = alicesNode)
|
||||
var bobsServices = MockServices(
|
||||
wallet = MockWalletService(wallet.states),
|
||||
keyManagement = MockKeyManagementService(BOB_KEY),
|
||||
net = bobsNode,
|
||||
storage = bobsStorage
|
||||
)
|
||||
loadFakeTxnsIntoStorage(bobsFakeCash, bobsStorage)
|
||||
loadFakeTxnsIntoStorage(alicesFakePaper, alicesServices.storageService)
|
||||
|
||||
val smmBuyer = StateMachineManager(bobsServices, MoreExecutors.directExecutor())
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode.services, aliceNode.storage.myLegalIdentityKey)
|
||||
|
||||
// Horrible Gradle/Kryo/Quasar FUBAR workaround: just skip these tests when run under Gradle for now.
|
||||
if (!smmBuyer.checkpointing)
|
||||
// TODO: Fix this once Quasar issue 153 is resolved.
|
||||
if (!bobNode.smm.checkpointing)
|
||||
return
|
||||
|
||||
val buyerSessionID = random63BitValue()
|
||||
|
||||
TwoPartyTradeProtocol.runSeller(
|
||||
StateMachineManager(alicesServices, MoreExecutors.directExecutor()),
|
||||
timestamper.first,
|
||||
bobsAddress,
|
||||
val aliceFuture = TwoPartyTradeProtocol.runSeller(
|
||||
aliceNode.smm,
|
||||
timestamperAddr,
|
||||
bobAddr,
|
||||
lookup("alice's paper"),
|
||||
1000.DOLLARS,
|
||||
ALICE_KEY,
|
||||
buyerSessionID
|
||||
)
|
||||
TwoPartyTradeProtocol.runBuyer(
|
||||
smmBuyer,
|
||||
timestamper.first,
|
||||
alicesAddress,
|
||||
bobNode.smm,
|
||||
timestamperAddr,
|
||||
aliceAddr,
|
||||
1000.DOLLARS,
|
||||
CommercialPaper.State::class.java,
|
||||
buyerSessionID
|
||||
@ -140,95 +135,118 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
||||
|
||||
// Everything is on this thread so we can now step through the protocol one step at a time.
|
||||
// Seller Alice already sent a message to Buyer Bob. Pump once:
|
||||
bobsNode.pump(false)
|
||||
fun pumpAlice() = (aliceNode.net as InMemoryMessagingNetwork.InMemoryMessaging).pump(false)
|
||||
fun pumpBob() = (bobNode.net as InMemoryMessagingNetwork.InMemoryMessaging).pump(false)
|
||||
|
||||
pumpBob()
|
||||
|
||||
// Bob sends a couple of queries for the dependencies back to Alice. Alice reponds.
|
||||
alicesNode.pump(false)
|
||||
bobsNode.pump(false)
|
||||
alicesNode.pump(false)
|
||||
bobsNode.pump(false)
|
||||
pumpAlice()
|
||||
pumpBob()
|
||||
pumpAlice()
|
||||
pumpBob()
|
||||
|
||||
// OK, now Bob has sent the partial transaction back to Alice and is waiting for Alice's signature.
|
||||
// Save the state machine to "disk" (i.e. a variable, here)
|
||||
assertEquals(1, bobsStorage.getMap<Any, Any>("state machines").size)
|
||||
val savedCheckpoints = HashMap(bobNode.storage.getMap<Any, Any>("state machines"))
|
||||
assertEquals(1, savedCheckpoints.size)
|
||||
|
||||
// .. and let's imagine that Bob's computer has a power cut. He now has nothing now beyond what was on disk.
|
||||
bobsNode.stop()
|
||||
bobNode.stop()
|
||||
|
||||
// Alice doesn't know that and carries on: she wants to know about the cash transactions he's trying to use.
|
||||
// She will wait around until Bob comes back.
|
||||
assertTrue(alicesNode.pump(false))
|
||||
assertTrue(pumpAlice())
|
||||
|
||||
// ... bring the node back up ... the act of constructing the SMM will re-register the message handlers
|
||||
// that Bob was waiting on before the reboot occurred.
|
||||
bobsNode = network.createNodeWithID(true, bobsAddress.id).start().get()
|
||||
val smm = StateMachineManager(
|
||||
MockServices(wallet = null, keyManagement = null, net = bobsNode, storage = bobsStorage),
|
||||
MoreExecutors.directExecutor()
|
||||
)
|
||||
bobNode = net.createNode(timestamperAddr, bobAddr.id) { path, nodeConfiguration, net, timestamper ->
|
||||
object : MockNetwork.MockNode(path, nodeConfiguration, net, timestamper, bobAddr.id) {
|
||||
override fun initialiseStorageService(dir: Path): StorageService {
|
||||
val ss = super.initialiseStorageService(dir)
|
||||
val smMap = ss.getMap<Any, Any>("state machines")
|
||||
smMap.putAll(savedCheckpoints)
|
||||
return ss
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the future representing the result of this state machine again.
|
||||
var bobFuture = smm.findStateMachines(TwoPartyTradeProtocol.Buyer::class.java).single().second
|
||||
var bobFuture = bobNode.smm.findStateMachines(TwoPartyTradeProtocol.Buyer::class.java).single().second
|
||||
|
||||
// And off we go again.
|
||||
runNetwork()
|
||||
net.runNetwork()
|
||||
|
||||
// Bob is now finished and has the same transaction as Alice.
|
||||
val stx = bobFuture.get()
|
||||
txns.add(stx.tx)
|
||||
verify()
|
||||
assertEquals(bobFuture.get(), aliceFuture.get())
|
||||
|
||||
assertTrue(smm.findStateMachines(TwoPartyTradeProtocol.Buyer::class.java).isEmpty())
|
||||
|
||||
assertTrue(bobNode.smm.findStateMachines(TwoPartyTradeProtocol.Buyer::class.java).isEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a mock node with an overridden storage service that uses a RecordingMap, that lets us test the order
|
||||
// of gets and puts.
|
||||
private fun makeNodeWithTracking(name: String): MockNetwork.MockNode {
|
||||
// Create a node in the mock network ...
|
||||
return net.createNode(null) { path, config, net, tsNode ->
|
||||
object : MockNetwork.MockNode(path, config, net, tsNode) {
|
||||
// That constructs the storage service object in a customised way ...
|
||||
override fun constructStorageService(attachments: NodeAttachmentStorage, identity: Party, keypair: KeyPair): StorageServiceImpl {
|
||||
// By tweaking the standard StorageServiceImpl class ...
|
||||
return object : StorageServiceImpl(attachments, identity, keypair) {
|
||||
// To use RecordingMaps instead of ordinary HashMaps.
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <K, V> getMap(tableName: String): MutableMap<K, V> {
|
||||
synchronized(tables) {
|
||||
return tables.getOrPut(tableName) {
|
||||
val map = Collections.synchronizedMap(HashMap<Any, Any>())
|
||||
RecordingMap(map, LoggerFactory.getLogger("recordingmap.$name"))
|
||||
} as MutableMap<K, V>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check dependencies of the sale asset are resolved`() {
|
||||
fun checkDependenciesOfSaleAssetAreResolved() {
|
||||
transactionGroupFor<ContractState> {
|
||||
val (bobsWallet, bobsFakeCash) = fillUpForBuyer(false)
|
||||
val alicesFakePaper = fillUpForSeller(false).second
|
||||
val aliceNode = makeNodeWithTracking("alice")
|
||||
val timestamperAddr = aliceNode.legallyIdentifableAddress
|
||||
val bobNode = makeNodeWithTracking("bob")
|
||||
|
||||
val (alicesAddress, alicesNode) = makeNode()
|
||||
val (bobsAddress, bobsNode) = makeNode()
|
||||
val timestamper = network.setupTimestampingNode(true).first
|
||||
|
||||
val alicesServices = MockServices(
|
||||
net = alicesNode,
|
||||
storage = MockStorageService(mapOf("validated-transactions" to "alice"))
|
||||
)
|
||||
val bobsServices = MockServices(
|
||||
wallet = MockWalletService(bobsWallet.states),
|
||||
keyManagement = MockKeyManagementService(BOB_KEY),
|
||||
net = bobsNode,
|
||||
storage = MockStorageService(mapOf("validated-transactions" to "bob"))
|
||||
)
|
||||
val bobsSignedTxns = loadFakeTxnsIntoStorage(bobsFakeCash, bobsServices.storageService)
|
||||
val alicesSignedTxns = loadFakeTxnsIntoStorage(alicesFakePaper, alicesServices.storageService)
|
||||
val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public).second
|
||||
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode.services)
|
||||
val alicesFakePaper = fillUpForSeller(false, timestamperAddr.identity).second
|
||||
val alicesSignedTxns = insertFakeTransactions(alicesFakePaper, aliceNode.services, aliceNode.storage.myLegalIdentityKey)
|
||||
|
||||
val buyerSessionID = random63BitValue()
|
||||
|
||||
TwoPartyTradeProtocol.runSeller(
|
||||
StateMachineManager(alicesServices, RunOnCallerThread),
|
||||
timestamper,
|
||||
bobsAddress,
|
||||
aliceNode.smm,
|
||||
timestamperAddr,
|
||||
bobNode.net.myAddress,
|
||||
lookup("alice's paper"),
|
||||
1000.DOLLARS,
|
||||
ALICE_KEY,
|
||||
buyerSessionID
|
||||
)
|
||||
TwoPartyTradeProtocol.runBuyer(
|
||||
StateMachineManager(bobsServices, RunOnCallerThread),
|
||||
timestamper,
|
||||
alicesAddress,
|
||||
bobNode.smm,
|
||||
timestamperAddr,
|
||||
aliceNode.net.myAddress,
|
||||
1000.DOLLARS,
|
||||
CommercialPaper.State::class.java,
|
||||
buyerSessionID
|
||||
)
|
||||
|
||||
runNetwork()
|
||||
net.runNetwork()
|
||||
|
||||
run {
|
||||
val records = (bobsServices.storageService.validatedTransactions as RecordingMap).records
|
||||
val records = (bobNode.storage.validatedTransactions as RecordingMap).records
|
||||
// Check Bobs's database accesses as Bob's cash transactions are downloaded by Alice.
|
||||
val expected = listOf(
|
||||
// Buyer Bob is told about Alice's commercial paper, but doesn't know it ..
|
||||
@ -246,7 +264,7 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
||||
|
||||
// And from Alice's perspective ...
|
||||
run {
|
||||
val records = (alicesServices.storageService.validatedTransactions as RecordingMap).records
|
||||
val records = (aliceNode.storage.validatedTransactions as RecordingMap).records
|
||||
val expected = listOf(
|
||||
// Seller Alice sends her seller info to Bob, who wants to check the asset for sale.
|
||||
// He requests, Alice looks up in her DB to send the tx to Bob
|
||||
@ -276,115 +294,83 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
||||
@Test
|
||||
fun `dependency with error on buyer side`() {
|
||||
transactionGroupFor<ContractState> {
|
||||
val (bobsWallet, fakeBobCash) = fillUpForBuyer(withError = true)
|
||||
val fakeAlicePaper = fillUpForSeller(false).second
|
||||
|
||||
val (alicesAddress, alicesNode) = makeNode()
|
||||
val (bobsAddress, bobsNode) = makeNode()
|
||||
val timestamper = network.setupTimestampingNode(true).first
|
||||
|
||||
val alicesServices = MockServices(net = alicesNode)
|
||||
val bobsServices = MockServices(
|
||||
wallet = MockWalletService(bobsWallet.states),
|
||||
keyManagement = MockKeyManagementService(BOB_KEY),
|
||||
net = bobsNode,
|
||||
storage = MockStorageService(mapOf("validated-transactions" to "bob"))
|
||||
)
|
||||
loadFakeTxnsIntoStorage(fakeBobCash, bobsServices.storageService)
|
||||
loadFakeTxnsIntoStorage(fakeAlicePaper, alicesServices.storageService)
|
||||
|
||||
val buyerSessionID = random63BitValue()
|
||||
|
||||
val aliceResult = TwoPartyTradeProtocol.runSeller(
|
||||
StateMachineManager(alicesServices, RunOnCallerThread),
|
||||
timestamper,
|
||||
bobsAddress,
|
||||
lookup("alice's paper"),
|
||||
1000.DOLLARS,
|
||||
ALICE_KEY,
|
||||
buyerSessionID
|
||||
)
|
||||
TwoPartyTradeProtocol.runBuyer(
|
||||
StateMachineManager(bobsServices, RunOnCallerThread),
|
||||
timestamper,
|
||||
alicesAddress,
|
||||
1000.DOLLARS,
|
||||
CommercialPaper.State::class.java,
|
||||
buyerSessionID
|
||||
)
|
||||
|
||||
runNetwork()
|
||||
|
||||
val e = assertFailsWith<ExecutionException> {
|
||||
aliceResult.get()
|
||||
}
|
||||
assertTrue(e.cause is TransactionVerificationException)
|
||||
assertTrue(e.cause!!.cause!!.message!!.contains("at least one cash input"))
|
||||
runWithError(true, false, "at least one cash input")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `dependency with error on seller side`() {
|
||||
transactionGroupFor<ContractState> {
|
||||
val (bobsWallet, fakeBobCash) = fillUpForBuyer(withError = false)
|
||||
val fakeAlicePaper = fillUpForSeller(withError = true).second
|
||||
|
||||
val (alicesAddress, alicesNode) = makeNode()
|
||||
val (bobsAddress, bobsNode) = makeNode()
|
||||
val timestamper = network.setupTimestampingNode(true).first
|
||||
|
||||
val alicesServices = MockServices(net = alicesNode)
|
||||
val bobsServices = MockServices(
|
||||
wallet = MockWalletService(bobsWallet.states),
|
||||
keyManagement = MockKeyManagementService(BOB_KEY),
|
||||
net = bobsNode,
|
||||
storage = MockStorageService(mapOf("validated-transactions" to "bob"))
|
||||
)
|
||||
loadFakeTxnsIntoStorage(fakeBobCash, bobsServices.storageService)
|
||||
loadFakeTxnsIntoStorage(fakeAlicePaper, alicesServices.storageService)
|
||||
|
||||
val buyerSessionID = random63BitValue()
|
||||
|
||||
TwoPartyTradeProtocol.runSeller(
|
||||
StateMachineManager(alicesServices, RunOnCallerThread),
|
||||
timestamper,
|
||||
bobsAddress,
|
||||
lookup("alice's paper"),
|
||||
1000.DOLLARS,
|
||||
ALICE_KEY,
|
||||
buyerSessionID
|
||||
)
|
||||
val bobResult = TwoPartyTradeProtocol.runBuyer(
|
||||
StateMachineManager(bobsServices, RunOnCallerThread),
|
||||
timestamper,
|
||||
alicesAddress,
|
||||
1000.DOLLARS,
|
||||
CommercialPaper.State::class.java,
|
||||
buyerSessionID
|
||||
)
|
||||
|
||||
runNetwork()
|
||||
|
||||
val e = assertFailsWith<ExecutionException> {
|
||||
bobResult.get()
|
||||
}
|
||||
assertTrue(e.cause is TransactionVerificationException)
|
||||
assertTrue(e.cause!!.cause!!.message!!.contains("must be timestamped"))
|
||||
runWithError(false, true, "must be timestamped")
|
||||
}
|
||||
}
|
||||
|
||||
private fun TransactionGroupDSL<ContractState>.loadFakeTxnsIntoStorage(wtxToSign: List<WireTransaction>,
|
||||
ss: StorageService): Map<SecureHash, SignedTransaction> {
|
||||
val txStorage = ss.validatedTransactions
|
||||
val map = signAll(wtxToSign).associateBy { it.id }
|
||||
if (txStorage is RecordingMap) {
|
||||
txStorage.putAllUnrecorded(map)
|
||||
} else
|
||||
txStorage.putAll(map)
|
||||
return map
|
||||
private fun TransactionGroupDSL<ContractState>.runWithError(bobError: Boolean, aliceError: Boolean,
|
||||
expectedMessageSubstring: String) {
|
||||
var (aliceNode, bobNode) = net.createTwoNodes()
|
||||
val aliceAddr = aliceNode.net.myAddress
|
||||
val bobAddr = bobNode.net.myAddress as InMemoryMessagingNetwork.Handle
|
||||
val timestamperAddr = aliceNode.legallyIdentifableAddress
|
||||
|
||||
val bobKey = bobNode.keyManagement.freshKey()
|
||||
val bobsBadCash = fillUpForBuyer(bobError, bobKey.public).second
|
||||
val alicesFakePaper = fillUpForSeller(aliceError, timestamperAddr.identity).second
|
||||
|
||||
insertFakeTransactions(bobsBadCash, bobNode.services, bobNode.storage.myLegalIdentityKey, bobKey)
|
||||
insertFakeTransactions(alicesFakePaper, aliceNode.services, aliceNode.storage.myLegalIdentityKey)
|
||||
|
||||
val buyerSessionID = random63BitValue()
|
||||
|
||||
val aliceResult = TwoPartyTradeProtocol.runSeller(
|
||||
aliceNode.smm,
|
||||
timestamperAddr,
|
||||
bobAddr,
|
||||
lookup("alice's paper"),
|
||||
1000.DOLLARS,
|
||||
ALICE_KEY,
|
||||
buyerSessionID
|
||||
)
|
||||
val bobResult = TwoPartyTradeProtocol.runBuyer(
|
||||
bobNode.smm,
|
||||
timestamperAddr,
|
||||
aliceAddr,
|
||||
1000.DOLLARS,
|
||||
CommercialPaper.State::class.java,
|
||||
buyerSessionID
|
||||
)
|
||||
|
||||
net.runNetwork()
|
||||
|
||||
val e = assertFailsWith<ExecutionException> {
|
||||
if (bobError)
|
||||
aliceResult.get()
|
||||
else
|
||||
bobResult.get()
|
||||
}
|
||||
assertTrue(e.cause is TransactionVerificationException)
|
||||
assertTrue(e.cause!!.cause!!.message!!.contains(expectedMessageSubstring))
|
||||
}
|
||||
|
||||
private fun TransactionGroupDSL<ContractState>.fillUpForBuyer(withError: Boolean): Pair<Wallet, List<WireTransaction>> {
|
||||
private fun TransactionGroupDSL<ContractState>.insertFakeTransactions(wtxToSign: List<WireTransaction>,
|
||||
services: ServiceHub,
|
||||
vararg extraKeys: KeyPair): Map<SecureHash, SignedTransaction> {
|
||||
val txStorage = services.storageService.validatedTransactions
|
||||
val signed = signAll(wtxToSign, *extraKeys).associateBy { it.id }
|
||||
if (txStorage is RecordingMap) {
|
||||
txStorage.putAllUnrecorded(signed)
|
||||
} else
|
||||
txStorage.putAll(signed)
|
||||
|
||||
try {
|
||||
services.walletService.notifyAll(signed.map { it.value.tx })
|
||||
} catch(e: Throwable) {
|
||||
// TODO: Remove this hack once all the tests are converted to use MockNode.
|
||||
}
|
||||
|
||||
return signed
|
||||
}
|
||||
|
||||
private fun TransactionGroupDSL<ContractState>.fillUpForBuyer(withError: Boolean, bobKey: PublicKey = BOB): Pair<Wallet, List<WireTransaction>> {
|
||||
// Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she
|
||||
// wants to sell to Bob.
|
||||
|
||||
@ -400,13 +386,13 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
||||
// Bob gets some cash onto the ledger from BoE
|
||||
val bc1 = transaction {
|
||||
input("elbonian money 1")
|
||||
output("bob cash 1") { 800.DOLLARS.CASH `issued by` MEGA_CORP `owned by` BOB }
|
||||
output("bob cash 1") { 800.DOLLARS.CASH `issued by` MEGA_CORP `owned by` bobKey }
|
||||
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
}
|
||||
|
||||
val bc2 = transaction {
|
||||
input("elbonian money 2")
|
||||
output("bob cash 2") { 300.DOLLARS.CASH `issued by` MEGA_CORP `owned by` BOB }
|
||||
output("bob cash 2") { 300.DOLLARS.CASH `issued by` MEGA_CORP `owned by` bobKey }
|
||||
output { 700.DOLLARS.CASH `issued by` MEGA_CORP `owned by` MEGA_CORP_PUBKEY } // Change output.
|
||||
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
}
|
||||
@ -415,14 +401,14 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
||||
return Pair(wallet, listOf(eb1, bc1, bc2))
|
||||
}
|
||||
|
||||
private fun TransactionGroupDSL<ContractState>.fillUpForSeller(withError: Boolean): Pair<Wallet, List<WireTransaction>> {
|
||||
private fun TransactionGroupDSL<ContractState>.fillUpForSeller(withError: Boolean, timestamper: Party): Pair<Wallet, List<WireTransaction>> {
|
||||
val ap = transaction {
|
||||
output("alice's paper") {
|
||||
CommercialPaper.State(MEGA_CORP.ref(1, 2, 3), ALICE, 1200.DOLLARS, TEST_TX_TIME + 7.days)
|
||||
}
|
||||
arg(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||
if (!withError)
|
||||
timestamp(TEST_TX_TIME)
|
||||
arg(timestamper.owningKey) { TimestampCommand(TEST_TX_TIME, 30.seconds) }
|
||||
}
|
||||
|
||||
val wallet = Wallet(listOf<StateAndRef<Cash.State>>(lookup("alice's paper")))
|
||||
|
Loading…
Reference in New Issue
Block a user