mirror of
https://github.com/corda/corda.git
synced 2025-04-09 04:15:35 +00:00
Merged in mike-ledgertx-refactoring-part-2 (pull request #260)
More simplifications to unit testing, introduce UnitTestServices
This commit is contained in:
commit
918de94a22
@ -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
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user