diff --git a/contracts/src/main/kotlin/com/r3corda/contracts/testing/WalletFiller.kt b/contracts/src/main/kotlin/com/r3corda/contracts/testing/WalletFiller.kt index eed49f7d4f..e59f8bf086 100644 --- a/contracts/src/main/kotlin/com/r3corda/contracts/testing/WalletFiller.kt +++ b/contracts/src/main/kotlin/com/r3corda/contracts/testing/WalletFiller.kt @@ -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, 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 = 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, 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 -} +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/r3corda/core/node/services/testing/MockServices.kt b/core/src/main/kotlin/com/r3corda/core/node/services/testing/MockServices.kt index d69331f1e1..477d55fce0 100644 --- a/core/src/main/kotlin/com/r3corda/core/node/services/testing/MockServices.kt +++ b/core/src/main/kotlin/com/r3corda/core/node/services/testing/MockServices.kt @@ -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 invokeProtocolAsync(logicType: Class>, vararg args: Any?): ListenableFuture { + throw UnsupportedOperationException("not implemented") + } + + override fun recordTransactions(txs: Iterable) { + 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) : IdentityService, SingletonSerializeAsToken() { private val keyToParties: Map diff --git a/core/src/main/kotlin/com/r3corda/core/testing/CoreTestUtils.kt b/core/src/main/kotlin/com/r3corda/core/testing/CoreTestUtils.kt index 1a80c1a024..952484a335 100644 --- a/core/src/main/kotlin/com/r3corda/core/testing/CoreTestUtils.kt +++ b/core/src/main/kotlin/com/r3corda/core/testing/CoreTestUtils.kt @@ -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.() -> Unit ): LedgerDSL { - val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(identityService, storageService)) + val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(services)) dsl(ledgerDsl) return ledgerDsl } diff --git a/core/src/main/kotlin/com/r3corda/core/testing/LedgerDSLInterpreter.kt b/core/src/main/kotlin/com/r3corda/core/testing/LedgerDSLInterpreter.kt index 3fc960871f..280d515ec3 100644 --- a/core/src/main/kotlin/com/r3corda/core/testing/LedgerDSLInterpreter.kt +++ b/core/src/main/kotlin/com/r3corda/core/testing/LedgerDSLInterpreter.kt @@ -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 ) } } diff --git a/core/src/main/kotlin/com/r3corda/core/testing/TestDSL.kt b/core/src/main/kotlin/com/r3corda/core/testing/TestDSL.kt index 4e0fc5a048..e3f18ea53d 100644 --- a/core/src/main/kotlin/com/r3corda/core/testing/TestDSL.kt +++ b/core/src/main/kotlin/com/r3corda/core/testing/TestDSL.kt @@ -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(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> = HashMap(), - private val transactionWithLocations: HashMap = HashMap(), + private val transactionWithLocations: HashMap = LinkedHashMap(), private val nonVerifiedTransactionWithLocations: HashMap = HashMap() ) : LedgerDSLInterpreter { val wireTransactions: List 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(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 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 } } + + val transactionsToVerify: List get() = transactionWithLocations.values.map { it.transaction } + val transactionsUnverified: List 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, extraKeys: Array) = transactionsToSign.map { wtx -> +fun signAll(transactionsToSign: List, extraKeys: List) = transactionsToSign.map { wtx -> val allPubKeys = wtx.signers.toMutableSet() val bits = wtx.serialize() require(bits == wtx.serialized) @@ -350,4 +352,4 @@ fun signAll(transactionsToSign: List, extraKeys: Array.signAll( - vararg extraKeys: KeyPair) = signAll(this.interpreter.wireTransactions, extraKeys) + vararg extraKeys: KeyPair) = signAll(this.interpreter.wireTransactions, extraKeys.toList()) diff --git a/experimental/src/main/kotlin/com/r3corda/contracts/BillOfLadingAgreement.kt b/experimental/src/main/kotlin/com/r3corda/contracts/BillOfLadingAgreement.kt index 4bf0cad38c..59db2d87f5 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/BillOfLadingAgreement.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/BillOfLadingAgreement.kt @@ -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)) } /** diff --git a/experimental/src/main/kotlin/com/r3corda/contracts/LOC.kt b/experimental/src/main/kotlin/com/r3corda/contracts/LOC.kt index 846b0fd5b7..7fc58657f0 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/LOC.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/LOC.kt @@ -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)) } diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/BillOfLadingAgreementTests.kt b/experimental/src/test/kotlin/com/r3corda/contracts/BillOfLadingAgreementTests.kt index 200d32ff64..4c0c169302 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/BillOfLadingAgreementTests.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/BillOfLadingAgreementTests.kt @@ -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() diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/LOCTests.kt b/experimental/src/test/kotlin/com/r3corda/contracts/LOCTests.kt index e8a5a31087..3c1f7d8c78 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/LOCTests.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/LOCTests.kt @@ -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() diff --git a/node/src/test/kotlin/com/r3corda/node/messaging/TwoPartyTradeProtocolTests.kt b/node/src/test/kotlin/com/r3corda/node/messaging/TwoPartyTradeProtocolTests.kt index aec019bb17..0beef77c22 100644 --- a/node/src/test/kotlin/com/r3corda/node/messaging/TwoPartyTradeProtocolTests.kt +++ b/node/src/test/kotlin/com/r3corda/node/messaging/TwoPartyTradeProtocolTests.kt @@ -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, services: ServiceHub, vararg extraKeys: KeyPair): Map { - val signed: List = signAll(wtxToSign, extraKeys) + val signed: List = signAll(wtxToSign, extraKeys.toList()) services.recordTransactions(signed) val validatedTransactions = services.storageService.validatedTransactions if (validatedTransactions is RecordingTransactionStorage) { diff --git a/node/src/test/kotlin/com/r3corda/node/services/MockServices.kt b/node/src/test/kotlin/com/r3corda/node/services/MockServiceHubInternal.kt similarity index 99% rename from node/src/test/kotlin/com/r3corda/node/services/MockServices.kt rename to node/src/test/kotlin/com/r3corda/node/services/MockServiceHubInternal.kt index 4bf957f5d4..251d3652c2 100644 --- a/node/src/test/kotlin/com/r3corda/node/services/MockServices.kt +++ b/node/src/test/kotlin/com/r3corda/node/services/MockServiceHubInternal.kt @@ -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, diff --git a/node/src/test/kotlin/com/r3corda/node/services/NodeSchedulerServiceTest.kt b/node/src/test/kotlin/com/r3corda/node/services/NodeSchedulerServiceTest.kt index c2f20ec8b3..1b298893e9 100644 --- a/node/src/test/kotlin/com/r3corda/node/services/NodeSchedulerServiceTest.kt +++ b/node/src/test/kotlin/com/r3corda/node/services/NodeSchedulerServiceTest.kt @@ -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 diff --git a/node/src/test/kotlin/com/r3corda/node/services/WalletWithCashTest.kt b/node/src/test/kotlin/com/r3corda/node/services/WalletWithCashTest.kt index 8f8cd5ba3b..4582b89fa3 100644 --- a/node/src/test/kotlin/com/r3corda/node/services/WalletWithCashTest.kt +++ b/node/src/test/kotlin/com/r3corda/node/services/WalletWithCashTest.kt @@ -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) { + 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 { - 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") diff --git a/node/src/test/kotlin/com/r3corda/node/services/statemachine/StateMachineManagerTests.kt b/node/src/test/kotlin/com/r3corda/node/services/statemachine/StateMachineManagerTests.kt index 4fd25015bd..8c9ab91a95 100644 --- a/node/src/test/kotlin/com/r3corda/node/services/statemachine/StateMachineManagerTests.kt +++ b/node/src/test/kotlin/com/r3corda/node/services/statemachine/StateMachineManagerTests.kt @@ -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) diff --git a/node/src/test/kotlin/com/r3corda/node/visualiser/GroupToGraphConversion.kt b/node/src/test/kotlin/com/r3corda/node/visualiser/GroupToGraphConversion.kt index 4dc34b2f5d..51ce731508 100644 --- a/node/src/test/kotlin/com/r3corda/node/visualiser/GroupToGraphConversion.kt +++ b/node/src/test/kotlin/com/r3corda/node/visualiser/GroupToGraphConversion.kt @@ -17,13 +17,13 @@ class GraphVisualiser(val dsl: LedgerDSL("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("tx$txIndex-in$inputIndex", ref.toString(), "tx$txIndex", true) edge.weight = 1.2 diff --git a/src/integration-test/kotlin/com/r3corda/core/testing/TraderDemoTest.kt b/src/integration-test/kotlin/com/r3corda/core/testing/TraderDemoTest.kt index cd8e15a2b0..c917aa80e4 100644 --- a/src/integration-test/kotlin/com/r3corda/core/testing/TraderDemoTest.kt +++ b/src/integration-test/kotlin/com/r3corda/core/testing/TraderDemoTest.kt @@ -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 { diff --git a/src/main/kotlin/com/r3corda/demos/TraderDemo.kt b/src/main/kotlin/com/r3corda/demos/TraderDemo.kt index c98449523b..ed6c6509c2 100644 --- a/src/main/kotlin/com/r3corda/demos/TraderDemo.kt +++ b/src/main/kotlin/com/r3corda/demos/TraderDemo.kt @@ -136,7 +136,6 @@ fun runTraderDemo(args: Array): 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): Int { val path = Paths.get(baseDirectory, Role.BUYER.name.toLowerCase(), "identity-public") val party = Files.readAllBytes(path).deserialize() advertisedServices = emptySet() - cashIssuer = party NodeInfo(ArtemisMessagingService.makeRecipient(theirNetAddr), party, setOf(NetworkMapService.Type)) } @@ -157,18 +155,15 @@ fun runTraderDemo(args: Array): 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) { // 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)