Merged in mike-ledgertx-refactoring-part-2 (pull request #260)

More simplifications to unit testing, introduce UnitTestServices
This commit is contained in:
Mike Hearn 2016-08-04 12:54:28 +02:00
commit 918de94a22
17 changed files with 136 additions and 113 deletions

View File

@ -2,6 +2,8 @@
package com.r3corda.contracts.testing
import com.r3corda.contracts.asset.Cash
import com.r3corda.contracts.asset.DUMMY_CASH_ISSUER
import com.r3corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
import com.r3corda.core.contracts.Amount
import com.r3corda.core.contracts.Issued
import com.r3corda.core.contracts.SignedTransaction
@ -11,16 +13,14 @@ import com.r3corda.core.node.ServiceHub
import com.r3corda.core.node.services.Wallet
import com.r3corda.core.serialization.OpaqueBytes
import com.r3corda.core.testing.DUMMY_NOTARY
import java.security.PublicKey
import java.util.*
/**
* Creates a random set of between (by default) 3 and 10 cash states that add up to the given amount and adds them
* to the wallet. This is intended for unit tests.
*
* The cash is self issued with the current nodes identity, as fetched from the storage service. Thus it
* would not be trusted by any sensible market participant and is effectively an IOU. If it had been issued by
* the central bank, well ... that'd be a different story altogether.
* to the wallet. This is intended for unit tests. The cash is issued by [DUMMY_CASH_ISSUER] and owned by the legal
* identity key from the storage service.
*
* The service hub needs to provide at least a key management service and a storage service.
*
@ -31,23 +31,18 @@ fun ServiceHub.fillWithSomeTestCash(howMuch: Amount<Currency>,
atLeastThisManyStates: Int = 3,
atMostThisManyStates: Int = 10,
rng: Random = Random(),
ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 0 }))): Wallet {
ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })),
ownedBy: PublicKey? = null): Wallet {
val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng)
val myIdentity = storageService.myLegalIdentity
val myKey = storageService.myLegalIdentityKey
val myKey: PublicKey = ownedBy ?: storageService.myLegalIdentityKey.public
// We will allocate one state to one transaction, for simplicities sake.
val cash = Cash()
val transactions: List<SignedTransaction> = amounts.map { pennies ->
// This line is what makes the cash self issued. We just use zero as our deposit reference: we don't need
// this field as there's no other database or source of truth we need to sync with.
val depositRef = myIdentity.ref(ref)
val issuance = TransactionType.General.Builder()
val freshKey = keyManagementService.freshKey()
cash.generateIssue(issuance, Amount(pennies, Issued(depositRef, howMuch.token)), freshKey.public, notary)
issuance.signWith(myKey)
cash.generateIssue(issuance, Amount(pennies, Issued(DUMMY_CASH_ISSUER.copy(reference = ref), howMuch.token)), myKey, notary)
issuance.signWith(DUMMY_CASH_ISSUER_KEY)
return@map issuance.toSignedTransaction(true)
}
@ -77,8 +72,8 @@ private fun calculateRandomlySizedAmounts(howMuch: Amount<Currency>, min: Int, m
// Handle inexact rounding.
amounts[i] = howMuch.quantity - filledSoFar
}
check(amounts[i] >= 0) { amounts[i] }
check(amounts[i] >= 0) { "${amounts[i]} : $filledSoFar : $howMuch" }
}
check(amounts.sum() == howMuch.quantity)
return amounts
}
}

View File

@ -1,13 +1,20 @@
package com.r3corda.core.node.services.testing
import com.google.common.util.concurrent.ListenableFuture
import com.r3corda.core.contracts.Attachment
import com.r3corda.core.contracts.SignedTransaction
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.generateKeyPair
import com.r3corda.core.crypto.sha256
import com.r3corda.core.messaging.MessagingService
import com.r3corda.core.node.ServiceHub
import com.r3corda.core.node.services.*
import com.r3corda.core.protocols.ProtocolLogic
import com.r3corda.core.serialization.SingletonSerializeAsToken
import com.r3corda.core.testing.DUMMY_NOTARY
import com.r3corda.core.testing.MEGA_CORP
import com.r3corda.core.testing.MINI_CORP
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
@ -15,10 +22,40 @@ import java.io.InputStream
import java.security.KeyPair
import java.security.PrivateKey
import java.security.PublicKey
import java.time.Clock
import java.util.*
import java.util.jar.JarInputStream
import javax.annotation.concurrent.ThreadSafe
// TODO: We need a single, rationalised unit testing environment that is usable for everything. Fix this!
// That means it probably shouldn't be in the 'core' module, which lacks enough code to create a realistic test env.
/**
* A singleton utility that only provides a mock identity, key and storage service. However, this is sufficient for
* building chains of transactions and verifying them. It isn't sufficient for testing protocols however.
*/
open class MockServices(val key: KeyPair = generateKeyPair()) : ServiceHub {
override fun <T : Any> invokeProtocolAsync(logicType: Class<out ProtocolLogic<T>>, vararg args: Any?): ListenableFuture<T> {
throw UnsupportedOperationException("not implemented")
}
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
for (stx in txs) {
storageService.validatedTransactions.addTransaction(stx)
}
}
override val storageService: TxWritableStorageService = MockStorageService(myLegalIdentityKey = key)
override val identityService: MockIdentityService = MockIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY))
override val keyManagementService: MockKeyManagementService = MockKeyManagementService(key)
override val walletService: WalletService get() = throw UnsupportedOperationException()
override val networkService: MessagingService get() = throw UnsupportedOperationException()
override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException()
override val clock: Clock get() = throw UnsupportedOperationException()
override val schedulerService: SchedulerService get() = throw UnsupportedOperationException()
}
@ThreadSafe
class MockIdentityService(val identities: List<Party>) : IdentityService, SingletonSerializeAsToken() {
private val keyToParties: Map<PublicKey, Party>

View File

@ -4,14 +4,12 @@ package com.r3corda.core.testing
import com.google.common.base.Throwables
import com.google.common.net.HostAndPort
import com.r3corda.core.contracts.Attachment
import com.r3corda.core.contracts.StateRef
import com.r3corda.core.contracts.TransactionBuilder
import com.r3corda.core.crypto.*
import com.r3corda.core.node.services.IdentityService
import com.r3corda.core.node.services.StorageService
import com.r3corda.core.node.ServiceHub
import com.r3corda.core.node.services.testing.MockIdentityService
import com.r3corda.core.node.services.testing.MockStorageService
import com.r3corda.core.node.services.testing.MockServices
import java.math.BigInteger
import java.net.ServerSocket
import java.security.KeyPair
@ -95,17 +93,14 @@ fun freeLocalHostAndPort(): HostAndPort {
}
/**
* Creates and tests a ledger built by the passed in dsl.
* @param identityService: The [IdentityService] to be used while building the ledger.
* @param storageService: The [StorageService] to be used for storing e.g. [Attachment]s.
* @param dsl: The dsl building the ledger.
* Creates and tests a ledger built by the passed in dsl. The provided services can be customised, otherwise a default
* of a freshly built [MockServices] is used.
*/
@JvmOverloads fun ledger(
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
storageService: StorageService = MockStorageService(),
services: ServiceHub = MockServices(),
dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
): LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(identityService, storageService))
val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(services))
dsl(ledgerDsl)
return ledgerDsl
}

View File

@ -40,11 +40,13 @@ interface Verifies {
val exceptionMessage = exception.message
if (exceptionMessage == null) {
throw AssertionError(
"Expected exception containing '$expectedMessage' but raised exception had no message"
"Expected exception containing '$expectedMessage' but raised exception had no message",
exception
)
} else if (!exceptionMessage.toLowerCase().contains(expectedMessage.toLowerCase())) {
throw AssertionError(
"Expected exception containing '$expectedMessage' but raised exception was '$exception'"
"Expected exception containing '$expectedMessage' but raised exception was '$exception'",
exception
)
}
}

View File

@ -5,8 +5,7 @@ import com.r3corda.core.crypto.DigitalSignature
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.signWithECDSA
import com.r3corda.core.node.services.IdentityService
import com.r3corda.core.node.services.StorageService
import com.r3corda.core.node.ServiceHub
import com.r3corda.core.serialization.serialize
import java.io.InputStream
import java.security.KeyPair
@ -95,6 +94,10 @@ data class TestTransactionDSLInterpreter private constructor(
transactionBuilder: TransactionBuilder
) : this(ledgerInterpreter, transactionBuilder, HashMap())
val services = object : ServiceHub by ledgerInterpreter.services {
override fun loadState(stateRef: StateRef) = ledgerInterpreter.resolveStateRef<ContractState>(stateRef)
}
private fun copy(): TestTransactionDSLInterpreter =
TestTransactionDSLInterpreter(
ledgerInterpreter = ledgerInterpreter,
@ -141,18 +144,15 @@ data class TestTransactionDSLInterpreter private constructor(
}
data class TestLedgerDSLInterpreter private constructor (
private val identityService: IdentityService,
private val storageService: StorageService,
val services: ServiceHub,
internal val labelToOutputStateAndRefs: HashMap<String, StateAndRef<ContractState>> = HashMap(),
private val transactionWithLocations: HashMap<SecureHash, WireTransactionWithLocation> = HashMap(),
private val transactionWithLocations: HashMap<SecureHash, WireTransactionWithLocation> = LinkedHashMap(),
private val nonVerifiedTransactionWithLocations: HashMap<SecureHash, WireTransactionWithLocation> = HashMap()
) : LedgerDSLInterpreter<TestTransactionDSLInterpreter> {
val wireTransactions: List<WireTransaction> get() = transactionWithLocations.values.map { it.transaction }
// We specify [labelToOutputStateAndRefs] just so that Kotlin picks the primary constructor instead of cycling
constructor(identityService: IdentityService, storageService: StorageService) : this(
identityService, storageService, labelToOutputStateAndRefs = HashMap()
)
constructor(services: ServiceHub) : this(services, labelToOutputStateAndRefs = HashMap())
companion object {
private fun getCallerLocation(): String? {
@ -179,8 +179,7 @@ data class TestLedgerDSLInterpreter private constructor (
internal fun copy(): TestLedgerDSLInterpreter =
TestLedgerDSLInterpreter(
identityService,
storageService,
services,
labelToOutputStateAndRefs = HashMap(labelToOutputStateAndRefs),
transactionWithLocations = HashMap(transactionWithLocations),
nonVerifiedTransactionWithLocations = HashMap(nonVerifiedTransactionWithLocations)
@ -189,7 +188,7 @@ data class TestLedgerDSLInterpreter private constructor (
internal fun resolveWireTransaction(wireTransaction: WireTransaction): TransactionForVerification {
return wireTransaction.run {
val authenticatedCommands = commands.map {
AuthenticatedObject(it.signers, it.signers.mapNotNull { identityService.partyFromKey(it) }, it.value)
AuthenticatedObject(it.signers, it.signers.mapNotNull { services.identityService.partyFromKey(it) }, it.value)
}
val resolvedInputStates = inputs.map { resolveStateRef<ContractState>(it) }
val resolvedAttachments = attachments.map { resolveAttachment(it) }
@ -220,7 +219,7 @@ data class TestLedgerDSLInterpreter private constructor (
}
internal fun resolveAttachment(attachmentId: SecureHash): Attachment =
storageService.attachments.openAttachment(attachmentId) ?: throw AttachmentResolutionException(attachmentId)
services.storageService.attachments.openAttachment(attachmentId) ?: throw AttachmentResolutionException(attachmentId)
private fun <R> interpretTransactionDsl(
transactionBuilder: TransactionBuilder,
@ -233,10 +232,10 @@ data class TestLedgerDSLInterpreter private constructor (
fun toTransactionGroup(): TransactionGroup {
val ledgerTransactions = transactionWithLocations.map {
it.value.transaction.toLedgerTransaction(identityService, storageService.attachments)
it.value.transaction.toLedgerTransaction(services.identityService, services.storageService.attachments)
}
val nonVerifiedLedgerTransactions = nonVerifiedTransactionWithLocations.map {
it.value.transaction.toLedgerTransaction(identityService, storageService.attachments)
it.value.transaction.toLedgerTransaction(services.identityService, services.storageService.attachments)
}
return TransactionGroup(ledgerTransactions.toSet(), nonVerifiedLedgerTransactions.toSet())
}
@ -295,7 +294,7 @@ data class TestLedgerDSLInterpreter private constructor (
dsl(LedgerDSL(copy()))
override fun attachment(attachment: InputStream): SecureHash {
return storageService.attachments.importAttachment(attachment)
return services.storageService.attachments.importAttachment(attachment)
}
override fun verifies(): EnforceVerifyOrFail {
@ -322,6 +321,9 @@ data class TestLedgerDSLInterpreter private constructor (
return stateAndRef as StateAndRef<S>
}
}
val transactionsToVerify: List<WireTransaction> get() = transactionWithLocations.values.map { it.transaction }
val transactionsUnverified: List<WireTransaction> get() = nonVerifiedTransactionWithLocations.values.map { it.transaction }
}
/**
@ -330,7 +332,7 @@ data class TestLedgerDSLInterpreter private constructor (
* @param extraKeys extra keys to sign transactions with.
* @return List of [SignedTransaction]s.
*/
fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: Array<out KeyPair>) = transactionsToSign.map { wtx ->
fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>) = transactionsToSign.map { wtx ->
val allPubKeys = wtx.signers.toMutableSet()
val bits = wtx.serialize()
require(bits == wtx.serialized)
@ -350,4 +352,4 @@ fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: Array<out KeyP
* @return List of [SignedTransaction]s.
*/
fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.signAll(
vararg extraKeys: KeyPair) = signAll(this.interpreter.wireTransactions, extraKeys)
vararg extraKeys: KeyPair) = signAll(this.interpreter.wireTransactions, extraKeys.toList())

View File

@ -3,7 +3,9 @@ package com.r3corda.contracts
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.days
import java.security.PublicKey
import java.time.Instant
import java.time.LocalDate
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -109,9 +111,11 @@ class BillOfLadingAgreement : Contract {
/**
* Returns a transaction that issues a Bill of Lading Agreement
*/
fun generateIssue(owner: PublicKey, beneficiary: Party, props: BillOfLadingProperties, notary: Party? = null): TransactionBuilder {
fun generateIssue(owner: PublicKey, beneficiary: Party, props: BillOfLadingProperties, notary: Party): TransactionBuilder {
val state = State(owner, beneficiary, props)
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.IssueBL(), props.carrierOwner.owningKey))
val builder = TransactionType.General.Builder(notary = notary)
builder.setTime(Instant.now(), notary, 1.days)
return builder.withItems(state, Command(Commands.IssueBL(), props.carrierOwner.owningKey))
}
/**

View File

@ -4,7 +4,10 @@ import com.r3corda.contracts.asset.sumCashBy
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.days
import com.r3corda.core.testing.DUMMY_NOTARY
import java.security.PublicKey
import java.time.Instant
import java.time.LocalDate
import java.time.Period
import java.time.ZoneOffset
@ -142,7 +145,9 @@ class LOC : Contract {
fun generateIssue(beneficiaryPaid: Boolean, issued: Boolean, terminated: Boolean, props: LOCProperties, notary: Party): TransactionBuilder {
val state = State(beneficiaryPaid, issued, terminated, props)
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Issuance(), props.issuingbank.owningKey))
val builder = TransactionType.General.Builder(notary = notary)
builder.setTime(Instant.now(), notary, 1.days)
return builder.withItems(state, Command(Commands.Issuance(), props.issuingbank.owningKey))
}

View File

@ -48,8 +48,9 @@ class BillOfLadingAgreementTests {
@Test
fun issueGenerationMethod() {
val ptx = BillOfLadingAgreement().generateIssue(Bill.owner, Bill.beneficiary,Bill.props, notary = DUMMY_NOTARY).apply {
val ptx = BillOfLadingAgreement().generateIssue(Bill.owner, Bill.beneficiary, Bill.props, DUMMY_NOTARY).apply {
signWith(ALICE_KEY)
signWith(DUMMY_NOTARY_KEY)
}
val stx = ptx.toSignedTransaction()
stx.verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE,attachments)
@ -57,14 +58,14 @@ class BillOfLadingAgreementTests {
@Test(expected = IllegalStateException::class)
fun issueGenerationMethod_Unsigned() {
val ptx = BillOfLadingAgreement().generateIssue(Bill.owner, Bill.beneficiary, Bill.props)
val ptx = BillOfLadingAgreement().generateIssue(Bill.owner, Bill.beneficiary, Bill.props, DUMMY_NOTARY)
val stx = ptx.toSignedTransaction()
stx.verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE,attachments)
}
@Test(expected = IllegalStateException::class)
fun issueGenerationMethod_KeyMismatch() {
val ptx = BillOfLadingAgreement().generateIssue(Bill.owner, Bill.beneficiary, Bill.props).apply {
val ptx = BillOfLadingAgreement().generateIssue(Bill.owner, Bill.beneficiary, Bill.props, DUMMY_NOTARY).apply {
signWith(BOB_KEY)
}
val stx = ptx.toSignedTransaction()

View File

@ -120,6 +120,7 @@ class LOCTests {
fun issueSignedByBank() {
val ptx = LOC().generateIssue(LOCstate.beneficiaryPaid, LOCstate.issued, LOCstate.terminated, LOCstate.props, DUMMY_NOTARY).apply {
signWith(MEGA_CORP_KEY)
signWith(DUMMY_NOTARY_KEY)
}
val stx = ptx.toSignedTransaction()
stx.verify()

View File

@ -2,10 +2,7 @@ package com.r3corda.node.messaging
import com.google.common.util.concurrent.ListenableFuture
import com.r3corda.contracts.CommercialPaper
import com.r3corda.contracts.asset.CASH
import com.r3corda.contracts.asset.Cash
import com.r3corda.contracts.asset.`issued by`
import com.r3corda.contracts.asset.`owned by`
import com.r3corda.contracts.asset.*
import com.r3corda.contracts.testing.fillWithSomeTestCash
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.Party
@ -93,9 +90,8 @@ class TwoPartyTradeProtocolTests {
val bobNode = net.createPartyNode(notaryNode.info, BOB.name, BOB_KEY)
bobNode.services.fillWithSomeTestCash(2000.DOLLARS)
val issuer = bobNode.services.storageService.myLegalIdentity.ref(0)
val alicesFakePaper = fillUpForSeller(false, aliceNode.storage.myLegalIdentity.owningKey,
1200.DOLLARS `issued by` issuer, notaryNode.info.identity, null).second
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, notaryNode.info.identity, null).second
insertFakeTransactions(alicesFakePaper, aliceNode.services, aliceNode.storage.myLegalIdentityKey, notaryNode.storage.myLegalIdentityKey)
@ -134,7 +130,6 @@ class TwoPartyTradeProtocolTests {
@Test
fun `shutdown and restore`() {
ledger {
val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
val aliceNode = net.createPartyNode(notaryNode.info, ALICE.name, ALICE_KEY)
@ -142,13 +137,12 @@ class TwoPartyTradeProtocolTests {
val bobAddr = bobNode.net.myAddress as InMemoryMessagingNetwork.Handle
val networkMapAddr = notaryNode.info
val issuer = bobNode.services.storageService.myLegalIdentity.ref(0)
net.runNetwork() // Clear network map registration messages
bobNode.services.fillWithSomeTestCash(2000.DOLLARS)
val alicesFakePaper = fillUpForSeller(false, aliceNode.storage.myLegalIdentity.owningKey,
1200.DOLLARS `issued by` issuer, notaryNode.info.identity, null).second
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, notaryNode.info.identity, null).second
insertFakeTransactions(alicesFakePaper, aliceNode.services, aliceNode.storage.myLegalIdentityKey)
val buyerSessionID = random63BitValue()
@ -252,7 +246,7 @@ class TwoPartyTradeProtocolTests {
val aliceNode = makeNodeWithTracking(notaryNode.info, ALICE.name, ALICE_KEY)
val bobNode = makeNodeWithTracking(notaryNode.info, BOB.name, BOB_KEY)
ledger(storageService = aliceNode.storage) {
ledger(aliceNode.services) {
// Insert a prospectus type attachment into the commercial paper transaction.
val stream = ByteArrayOutputStream()
@ -419,7 +413,7 @@ class TwoPartyTradeProtocolTests {
wtxToSign: List<WireTransaction>,
services: ServiceHub,
vararg extraKeys: KeyPair): Map<SecureHash, SignedTransaction> {
val signed: List<SignedTransaction> = signAll(wtxToSign, extraKeys)
val signed: List<SignedTransaction> = signAll(wtxToSign, extraKeys.toList())
services.recordTransactions(signed)
val validatedTransactions = services.storageService.validatedTransactions
if (validatedTransactions is RecordingTransactionStorage) {

View File

@ -19,7 +19,7 @@ import com.r3corda.node.services.statemachine.StateMachineManager
import com.r3corda.node.services.wallet.NodeWalletService
import java.time.Clock
open class MockServices(
open class MockServiceHubInternal(
customWallet: WalletService? = null,
val keyManagement: KeyManagementService? = null,
val net: MessagingServiceInternal? = null,

View File

@ -60,7 +60,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
init {
val kms = MockKeyManagementService(ALICE_KEY)
val mockMessagingService = InMemoryMessagingNetwork(false).InMemoryMessaging(false, InMemoryMessagingNetwork.Handle(0, "None"))
val mockServices = object : MockServices(overrideClock = testClock, keyManagement = kms, net = mockMessagingService), TestReference {
val mockServices = object : MockServiceHubInternal(overrideClock = testClock, keyManagement = kms, net = mockMessagingService), TestReference {
override val testReference = this@NodeSchedulerServiceTest
}
services = mockServices

View File

@ -1,14 +1,14 @@
package com.r3corda.node.services
import com.r3corda.contracts.asset.Cash
import com.r3corda.contracts.asset.DUMMY_CASH_ISSUER
import com.r3corda.contracts.asset.cashBalances
import com.r3corda.contracts.testing.fillWithSomeTestCash
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.node.ServiceHub
import com.r3corda.core.node.services.testing.MockKeyManagementService
import com.r3corda.core.node.services.WalletService
import com.r3corda.core.node.services.testing.MockStorageService
import com.r3corda.core.serialization.OpaqueBytes
import com.r3corda.core.node.services.testing.MockServices
import com.r3corda.core.testing.*
import com.r3corda.core.utilities.BriefLogFormatter
import com.r3corda.node.services.wallet.NodeWalletService
@ -23,11 +23,22 @@ import kotlin.test.assertNull
// TODO: Move this to the cash contract tests once mock services are further split up.
class WalletWithCashTest {
val kms = MockKeyManagementService(ALICE_KEY)
lateinit var services: MockServices
val wallet: WalletService get() = services.walletService
@Before
fun setUp() {
BriefLogFormatter.loggingOn(NodeWalletService::class)
services = object : MockServices() {
override val walletService: WalletService = NodeWalletService(this)
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
for (stx in txs) {
storageService.validatedTransactions.addTransaction(stx)
walletService.notify(stx.tx)
}
}
}
}
@After
@ -35,37 +46,24 @@ class WalletWithCashTest {
BriefLogFormatter.loggingOff(NodeWalletService::class)
}
fun make(): Pair<NodeWalletService, ServiceHub> {
val services = MockServices(keyManagement = kms)
return Pair(services.walletService as NodeWalletService, services)
}
@Test
fun splits() {
val (wallet, services) = make()
val ref = OpaqueBytes(ByteArray(1, {0}))
kms.nextKeys += Array(3) { ALICE_KEY }
// Fix the PRNG so that we get the same splits every time.
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L), ref)
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
val w = wallet.currentWallet
assertEquals(3, w.states.size)
val state = w.states[0].state.data as Cash.State
val myIdentity = services.storageService.myLegalIdentity
val myPartyRef = myIdentity.ref(ref)
assertEquals(29.01.DOLLARS `issued by` myPartyRef, state.amount)
assertEquals(ALICE_PUBKEY, state.owner)
assertEquals(29.01.DOLLARS `issued by` DUMMY_CASH_ISSUER, state.amount)
assertEquals(services.key.public, state.owner)
assertEquals(35.38.DOLLARS `issued by` myPartyRef, (w.states[2].state.data as Cash.State).amount)
assertEquals(35.61.DOLLARS `issued by` myPartyRef, (w.states[1].state.data as Cash.State).amount)
assertEquals(35.38.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w.states[2].state.data as Cash.State).amount)
assertEquals(35.61.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w.states[1].state.data as Cash.State).amount)
}
@Test
fun basics() {
val (wallet, services) = make()
// A tx that sends us money.
val freshKey = services.keyManagementService.freshKey()
val usefulTX = TransactionType.General.Builder().apply {
@ -102,10 +100,7 @@ class WalletWithCashTest {
@Test
fun branchingLinearStatesFails() {
val (wallet, services) = make()
val freshKey = services.keyManagementService.freshKey()
val thread = SecureHash.sha256("thread")
// Issue a linear state
@ -131,8 +126,6 @@ class WalletWithCashTest {
@Test
fun sequencingLinearStatesWorks() {
val (wallet, services) = make()
val freshKey = services.keyManagementService.freshKey()
val thread = SecureHash.sha256("thread")

View File

@ -4,7 +4,7 @@ import co.paralleluniverse.fibers.Fiber
import co.paralleluniverse.fibers.Suspendable
import com.r3corda.core.messaging.MessagingService
import com.r3corda.core.protocols.ProtocolLogic
import com.r3corda.node.services.MockServices
import com.r3corda.node.services.MockServiceHubInternal
import com.r3corda.node.services.api.Checkpoint
import com.r3corda.node.services.api.CheckpointStorage
import com.r3corda.node.services.api.MessagingServiceInternal
@ -45,7 +45,7 @@ class StateMachineManagerTests {
assertThat(protocol.lazyTime).isNotNull()
}
private fun createManager() = StateMachineManager(object : MockServices() {
private fun createManager() = StateMachineManager(object : MockServiceHubInternal() {
override val networkService: MessagingServiceInternal get() = network
}, emptyList(), checkpointStorage, AffinityExecutor.SAME_THREAD)

View File

@ -17,13 +17,13 @@ class GraphVisualiser(val dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedg
}
fun convert(): SingleGraph {
val tg = dsl.interpreter.toTransactionGroup()
val testLedger: TestLedgerDSLInterpreter = dsl.interpreter
val graph = createGraph("Transaction group", css)
// Map all the transactions, including the bogus non-verified ones (with no inputs) to graph nodes.
for ((txIndex, tx) in (tg.transactions + tg.nonVerifiedRoots).withIndex()) {
for ((txIndex, tx) in (testLedger.transactionsToVerify + testLedger.transactionsUnverified).withIndex()) {
val txNode = graph.addNode<Node>("tx$txIndex")
if (tx !in tg.nonVerifiedRoots)
if (tx !in testLedger.transactionsUnverified)
txNode.label = dsl.interpreter.transactionName(tx.id).let { it ?: "TX[${tx.id.prefixChars()}]" }
txNode.styleClass = "tx"
@ -48,7 +48,7 @@ class GraphVisualiser(val dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedg
}
}
// And now all states and transactions were mapped to graph nodes, hook up the input edges.
for ((txIndex, tx) in tg.transactions.withIndex()) {
for ((txIndex, tx) in testLedger.transactionsToVerify.withIndex()) {
for ((inputIndex, ref) in tx.inputs.withIndex()) {
val edge = graph.addEdge<Edge>("tx$txIndex-in$inputIndex", ref.toString(), "tx$txIndex", true)
edge.weight = 1.2

View File

@ -6,9 +6,6 @@ import com.r3corda.core.testing.utilities.TestTimestamp
import com.r3corda.core.testing.utilities.assertExitOrKill
import com.r3corda.core.testing.utilities.spawn
import org.junit.Test
import java.nio.file.Paths
import java.text.SimpleDateFormat
import java.util.*
import kotlin.test.assertEquals
class TraderDemoTest {

View File

@ -136,7 +136,6 @@ fun runTraderDemo(args: Array<String>): Int {
// One of the two servers needs to run the network map and notary services. In such a trivial two-node network
// the map is not very helpful, but we need one anyway. So just make the buyer side run the network map as it's
// the side that sticks around waiting for the seller.
var cashIssuer: Party? = null
val networkMapId = if (role == Role.BUYER) {
advertisedServices = setOf(NetworkMapService.Type, SimpleNotaryService.Type)
null
@ -148,7 +147,6 @@ fun runTraderDemo(args: Array<String>): Int {
val path = Paths.get(baseDirectory, Role.BUYER.name.toLowerCase(), "identity-public")
val party = Files.readAllBytes(path).deserialize<Party>()
advertisedServices = emptySet()
cashIssuer = party
NodeInfo(ArtemisMessagingService.makeRecipient(theirNetAddr), party, setOf(NetworkMapService.Type))
}
@ -157,18 +155,15 @@ fun runTraderDemo(args: Array<String>): Int {
Node(directory, myNetAddr, apiNetAddr, config, networkMapId, advertisedServices).setup().start()
}
// TODO: Replace with a separate trusted cash issuer
if (cashIssuer == null) {
cashIssuer = node.services.storageService.myLegalIdentity
}
// 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.
val amount = 1000.DOLLARS
if (role == Role.BUYER) {
runBuyer(node, amount)
} else {
runSeller(node, amount, cashIssuer)
node.networkMapRegistrationFuture.get()
val party = node.netMapCache.getNodeByLegalName("Bank A")?.identity ?: throw IllegalStateException("Cannot find other node?!")
runSeller(node, amount, party)
}
return 0
@ -213,7 +208,9 @@ private fun runBuyer(node: Node, amount: Amount<Currency>) {
// Self issue some cash.
//
// TODO: At some point this demo should be extended to have a central bank node.
node.services.fillWithSomeTestCash(3000.DOLLARS, node.info.identity)
node.services.fillWithSomeTestCash(3000.DOLLARS,
notary = node.info.identity, // In this demo, the buyer and notary are the same.
ownedBy = node.services.keyManagementService.freshKey().public)
// Wait around until a node asks to start a trade with us. In a real system, this part would happen out of band
// via some other system like an exchange or maybe even a manual messaging system like Bloomberg. But for the
@ -250,7 +247,7 @@ private class TraderDemoProtocolBuyer(val otherSide: Party,
progressTracker.currentStep = STARTING_BUY
send(otherSide, 0, sessionID)
val notary = serviceHub.networkMapCache.notaryNodes[0]
val notary: NodeInfo = serviceHub.networkMapCache.notaryNodes[0]
val buyer = TwoPartyTradeProtocol.Buyer(
otherSide,
notary.identity,
@ -323,7 +320,7 @@ private class TraderDemoProtocolSeller(val otherSide: Party,
progressTracker.currentStep = SELF_ISSUING
val notary = serviceHub.networkMapCache.notaryNodes[0]
val notary: NodeInfo = serviceHub.networkMapCache.notaryNodes[0]
val cpOwnerKey = serviceHub.keyManagementService.freshKey()
val commercialPaper = selfIssueSomeCommercialPaper(cpOwnerKey.public, notary)