From 521994ce2396158c38661f4d9968f4457b151ea3 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Thu, 2 Feb 2017 16:56:25 +0000 Subject: [PATCH] Add AnonymousParty superclass of Party Add AnonymousParty superclass of Party in preparation for anonymising parties stored in contract states. Signed-off-by: Ross Nicoll --- .../net/corda/core/contracts/Structures.kt | 5 ++-- .../net/corda/core/crypto/AnonymousParty.kt | 24 +++++++++++++++++ .../kotlin/net/corda/core/crypto/Party.kt | 13 ++------- .../core/node/services/IdentityService.kt | 6 +++++ .../net/corda/core/node/services/Services.kt | 7 ++--- .../net/corda/core/node/isolated.jar | Bin 7968 -> 7893 bytes .../kotlin/net/corda/core/crypto/PartyTest.kt | 20 ++++++++++++++ docs/source/key-concepts-core-types.rst | 15 ++++++----- docs/source/release-notes.rst | 7 +++++ .../kotlin/net/corda/contracts/asset/Cash.kt | 4 +-- .../main/kotlin/net/corda/flows/CashFlow.kt | 1 + .../kotlin/net/corda/flows/IssuerFlowTest.kt | 6 ++--- .../identity/InMemoryIdentityService.kt | 9 +++++++ .../node/services/vault/NodeVaultService.kt | 4 +-- .../node/messaging/TwoPartyTradeFlowTests.kt | 7 ++--- .../net/corda/testing/node/MockServices.kt | 3 +++ .../views/cordapps/cash/CashViewer.kt | 3 ++- .../net/corda/loadtest/tests/CrossCashTest.kt | 25 +++++++++--------- .../corda/loadtest/tests/GenerateHelpers.kt | 3 ++- .../net/corda/loadtest/tests/SelfIssueTest.kt | 7 ++--- 20 files changed, 115 insertions(+), 54 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/crypto/AnonymousParty.kt create mode 100644 core/src/test/kotlin/net/corda/core/crypto/PartyTest.kt diff --git a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt index 3aa1298d62..aeefce5674 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt @@ -1,6 +1,7 @@ package net.corda.core.contracts import net.corda.core.contracts.clauses.Clause +import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.Party import net.corda.core.crypto.SecureHash @@ -349,8 +350,8 @@ inline fun Iterable>.filt * Reference to something being stored or issued by a party e.g. in a vault or (more likely) on their normal * ledger. The reference is intended to be encrypted so it's meaningless to anyone other than the party. */ -data class PartyAndReference(val party: Party, val reference: OpaqueBytes) { - override fun toString() = "${party.name}$reference" +data class PartyAndReference(val party: AnonymousParty, val reference: OpaqueBytes) { + override fun toString() = "${party}$reference" } /** Marker interface for classes that represent commands */ diff --git a/core/src/main/kotlin/net/corda/core/crypto/AnonymousParty.kt b/core/src/main/kotlin/net/corda/core/crypto/AnonymousParty.kt new file mode 100644 index 0000000000..30745dec02 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/crypto/AnonymousParty.kt @@ -0,0 +1,24 @@ +package net.corda.core.crypto + +import net.corda.core.contracts.PartyAndReference +import net.corda.core.serialization.OpaqueBytes +import java.security.PublicKey + +/** + * The [AnonymousParty] class contains enough information to uniquely identify a [Party] while excluding private + * information such as name. It is intended to represent a party on the distributed ledger. + */ +open class AnonymousParty(val owningKey: CompositeKey) { + /** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */ + constructor(owningKey: PublicKey) : this(owningKey.composite) + + /** Anonymised parties do not include any detail apart from owning key, so equality is dependent solely on the key */ + override fun equals(other: Any?): Boolean = other is AnonymousParty && this.owningKey == other.owningKey + override fun hashCode(): Int = owningKey.hashCode() + // Use the key as the bulk of the toString(), but include a human readable identifier as well, so that [Party] + // can put in the key and actual name + override fun toString() = "${owningKey.toBase58String()} " + + fun ref(bytes: OpaqueBytes) = PartyAndReference(this, bytes) + fun ref(vararg bytes: Byte) = ref(OpaqueBytes.of(*bytes)) +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/crypto/Party.kt b/core/src/main/kotlin/net/corda/core/crypto/Party.kt index a8f0dd7378..600db47bb5 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Party.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Party.kt @@ -1,7 +1,5 @@ package net.corda.core.crypto -import net.corda.core.contracts.PartyAndReference -import net.corda.core.serialization.OpaqueBytes import java.security.PublicKey /** @@ -22,15 +20,8 @@ import java.security.PublicKey * * @see CompositeKey */ -class Party(val name: String, val owningKey: CompositeKey) { +class Party(val name: String, owningKey: CompositeKey) : AnonymousParty(owningKey) { /** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */ constructor(name: String, owningKey: PublicKey) : this(name, owningKey.composite) - - /** Anonymised parties do not include any detail apart from owning key, so equality is dependent solely on the key */ - override fun equals(other: Any?): Boolean = other is Party && this.owningKey == other.owningKey - override fun hashCode(): Int = owningKey.hashCode() - override fun toString() = name - - fun ref(bytes: OpaqueBytes) = PartyAndReference(this, bytes) - fun ref(vararg bytes: Byte) = ref(OpaqueBytes.of(*bytes)) + override fun toString() = "${owningKey.toBase58String()} (name)" } \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt b/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt index e3a17ba222..ef1dcf4d9b 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt @@ -1,5 +1,7 @@ package net.corda.core.node.services +import net.corda.core.contracts.PartyAndReference +import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.Party @@ -10,6 +12,7 @@ import net.corda.core.crypto.Party */ interface IdentityService { fun registerIdentity(party: Party) + /** * Get all identities known to the service. This is expensive, and [partyFromKey] or [partyFromName] should be * used in preference where possible. @@ -22,4 +25,7 @@ interface IdentityService { fun partyFromKey(key: CompositeKey): Party? fun partyFromName(name: String): Party? + + fun partyFromAnonymous(party: AnonymousParty): Party? + fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party) } diff --git a/core/src/main/kotlin/net/corda/core/node/services/Services.kt b/core/src/main/kotlin/net/corda/core/node/services/Services.kt index 8d90ba7c77..645dbe43ee 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/Services.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/Services.kt @@ -2,10 +2,7 @@ package net.corda.core.node.services import com.google.common.util.concurrent.ListenableFuture import net.corda.core.contracts.* -import net.corda.core.crypto.CompositeKey -import net.corda.core.crypto.Party -import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.toStringShort +import net.corda.core.crypto.* import net.corda.core.toFuture import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction @@ -198,7 +195,7 @@ interface VaultService { fun generateSpend(tx: TransactionBuilder, amount: Amount, to: CompositeKey, - onlyFromParties: Set? = null): Pair> + onlyFromParties: Set? = null): Pair> } inline fun VaultService.linearHeadsOfType() = linearHeadsOfType_(T::class.java) diff --git a/core/src/main/resources/net/corda/core/node/isolated.jar b/core/src/main/resources/net/corda/core/node/isolated.jar index 1b898104509733aba0b3967813598182d35b8fc2..0b6d090c585b0e9a4d2da725f3644b98aaeff491 100644 GIT binary patch delta 3303 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 diff --git a/core/src/test/kotlin/net/corda/core/crypto/PartyTest.kt b/core/src/test/kotlin/net/corda/core/crypto/PartyTest.kt new file mode 100644 index 0000000000..d1a4a0b2b3 --- /dev/null +++ b/core/src/test/kotlin/net/corda/core/crypto/PartyTest.kt @@ -0,0 +1,20 @@ +package net.corda.core.crypto + +import org.junit.Test +import java.math.BigInteger +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +class PartyTest { + @Test + fun `equality`() { + val key = entropyToKeyPair(BigInteger.valueOf(20170207L)).public.composite + val differentKey = entropyToKeyPair(BigInteger.valueOf(7201702L)).public.composite + val anonymousParty = AnonymousParty(key) + val party = Party("test key", key) + assertEquals(party, anonymousParty) + assertEquals(anonymousParty, party) + assertNotEquals(AnonymousParty(differentKey), anonymousParty) + assertNotEquals(AnonymousParty(differentKey), party) + } +} \ No newline at end of file diff --git a/docs/source/key-concepts-core-types.rst b/docs/source/key-concepts-core-types.rst index a052a2cebd..4fca77796f 100644 --- a/docs/source/key-concepts-core-types.rst +++ b/docs/source/key-concepts-core-types.rst @@ -87,14 +87,15 @@ Party and CompositeKey Entities using the network are called *parties*. Parties can sign structures using keys, and a party may have many keys under their control. -Parties may sometimes be identified pseudonymously. For example, in a transaction sent to your node as part of a -chain of custody it is important you can convince yourself of the transaction's validity, but equally important that -you don't learn anything about who was involved in that transaction. In these cases a public key may be present -without any identifying information about who owns it. +Parties can be represented either in full (including name) or pseudonymously, using the ``Party`` or ``AnonymousParty`` +classes respectively. For example, in a transaction sent to your node as part of a chain of custody it is important you +can convince yourself of the transaction's validity, but equally important that you don't learn anything about who was +involved in that transaction. In these cases ``AnonymousParty`` should be used, which contains a composite public key +without any identifying information about who owns it. In contrast, for internal processing where extended details of +a party are required, the ``Party`` class should be used. The identity service provides functionality for resolving +anonymous parties to full parties. -Identities of parties involved in signing a transaction can be represented simply by a ``CompositeKey``, or by further -information (such as name) using the ``Party`` class. An ``AuthenticatedObject`` represents an object (like a command) -that has been signed by a set of parties. +An ``AuthenticatedObject`` represents an object (like a command) that has been signed by a set of parties. .. note:: These types are provisional and will change significantly in future as the identity framework becomes more fleshed out. diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 7d0af253a8..95a0e925ab 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -3,6 +3,13 @@ Release notes Here are brief summaries of what's changed between each snapshot release. +Milestone 9 +----------- + +* API: + + * Pseudonymous ``AnonymousParty`` class added as a superclass of ``Party``. + Milestone 8 ----------- diff --git a/finance/src/main/kotlin/net/corda/contracts/asset/Cash.kt b/finance/src/main/kotlin/net/corda/contracts/asset/Cash.kt index 55d195a8fd..0cce72c41b 100644 --- a/finance/src/main/kotlin/net/corda/contracts/asset/Cash.kt +++ b/finance/src/main/kotlin/net/corda/contracts/asset/Cash.kt @@ -192,12 +192,12 @@ fun Iterable.sumCashOrZero(currency: Issued): Amount, issueToPartyAndRef: PartyAndReference) : RunResult { + val resolvedIssuerParty = bankOfCordaNode.services.identityService.partyFromAnonymous(issueToPartyAndRef) ?: throw IllegalStateException() val issuerFuture = bankOfCordaNode.initiateSingleShotFlow(IssuerFlow.IssuanceRequester::class) { - otherParty -> IssuerFlow.Issuer(issueToPartyAndRef.party) + otherParty -> IssuerFlow.Issuer(resolvedIssuerParty) }.map { it.stateMachine } - val issueRequest = IssuanceRequester(amount, issueToPartyAndRef.party, issueToPartyAndRef.reference, bankOfCordaNode.info.legalIdentity) + val issueRequest = IssuanceRequester(amount, resolvedIssuerParty, issueToPartyAndRef.reference, bankOfCordaNode.info.legalIdentity) val issueRequestResultFuture = bankClientNode.services.startFlow(issueRequest).resultFuture return IssuerFlowTest.RunResult(issuerFuture, issueRequestResultFuture) diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index 0c4ee18a6c..639bc7e951 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -1,5 +1,7 @@ package net.corda.node.services.identity +import net.corda.core.contracts.PartyAndReference +import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.Party import net.corda.core.node.services.IdentityService @@ -26,4 +28,11 @@ class InMemoryIdentityService() : SingletonSerializeAsToken(), IdentityService { override fun partyFromKey(key: CompositeKey): Party? = keyToParties[key] override fun partyFromName(name: String): Party? = nameToParties[name] + override fun partyFromAnonymous(party: AnonymousParty): Party? { + return if (party is Party) + party + else + partyFromKey(party.owningKey) + } + override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party) } diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index 80154b1c07..b0b899153b 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -5,8 +5,8 @@ import net.corda.contracts.asset.Cash import net.corda.core.ThreadBox import net.corda.core.bufferUntilSubscribed import net.corda.core.contracts.* +import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.CompositeKey -import net.corda.core.crypto.Party import net.corda.core.crypto.SecureHash import net.corda.core.node.ServiceHub import net.corda.core.node.services.Vault @@ -198,7 +198,7 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT override fun generateSpend(tx: TransactionBuilder, amount: Amount, to: CompositeKey, - onlyFromParties: Set?): Pair> { + onlyFromParties: Set?): Pair> { // Discussion // // This code is analogous to the Wallet.send() set of methods in bitcoinj, and has the same general outline. diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index b469f33c79..de17df82a4 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -4,10 +4,7 @@ import net.corda.contracts.CommercialPaper import net.corda.contracts.asset.* import net.corda.contracts.testing.fillWithSomeTestCash import net.corda.core.contracts.* -import net.corda.core.crypto.CompositeKey -import net.corda.core.crypto.Party -import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.composite +import net.corda.core.crypto.* import net.corda.core.days import net.corda.core.flows.FlowStateMachine import net.corda.core.flows.StateMachineRunId @@ -490,7 +487,7 @@ class TwoPartyTradeFlowTests { private fun LedgerDSL.fillUpForBuyer( withError: Boolean, owner: CompositeKey, - issuer: Party, + issuer: AnonymousParty, notary: Party): Pair> { val interimOwnerKey = MEGA_CORP_PUBKEY // Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt index 97607e55a4..c72bb66b17 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -2,6 +2,7 @@ package net.corda.testing.node import kotlinx.support.jdk8.collections.putIfAbsent import net.corda.core.contracts.Attachment +import net.corda.core.contracts.PartyAndReference import net.corda.core.crypto.* import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowStateMachine @@ -73,6 +74,8 @@ class MockIdentityService(val identities: List) : IdentityService, Single override fun registerIdentity(party: Party) { throw UnsupportedOperationException() } override fun getAllIdentities(): Iterable = ArrayList(keyToParties.values) + override fun partyFromAnonymous(party: AnonymousParty): Party? = keyToParties[party.owningKey] + override fun partyFromAnonymous(partyRef: PartyAndReference): Party? = partyFromAnonymous(partyRef.party) override fun partyFromKey(key: CompositeKey): Party? = keyToParties[key] override fun partyFromName(name: String): Party? = nameToParties[name] } diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/CashViewer.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/CashViewer.kt index 477ccfab6d..7bf95c0372 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/CashViewer.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/CashViewer.kt @@ -22,6 +22,7 @@ import net.corda.contracts.asset.Cash import net.corda.core.contracts.Amount import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.withoutIssuer +import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.Party import net.corda.explorer.formatters.AmountFormatter import net.corda.explorer.identicon.identicon @@ -84,7 +85,7 @@ class CashViewer : CordaView("Cash") { */ sealed class ViewerNode(val equivAmount: ObservableValue>, val states: ObservableList>) { - class IssuerNode(val issuer: Party, + class IssuerNode(val issuer: AnonymousParty, sumEquivAmount: ObservableValue>, states: ObservableList>) : ViewerNode(sumEquivAmount, states) diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt index 495f468a7d..86f90fd09c 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt @@ -6,6 +6,7 @@ import net.corda.contracts.asset.Cash import net.corda.core.contracts.Issued import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.USD +import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.Party import net.corda.core.flows.FlowException import net.corda.core.getOrThrow @@ -49,7 +50,7 @@ data class CrossCashCommand( * Map from node to (map from issuer to USD quantity) */ data class CrossCashState( - val nodeVaults: Map>, + val nodeVaults: Map>, // node -> (notifying node -> [(issuer, amount)]) // This map holds the queues that encode the non-determinism of how tx notifications arrive in the background. @@ -67,20 +68,20 @@ data class CrossCashState( // requires more concurrent code which is conceptually also more complex than the current design. // TODO: Alternative: We may possibly reduce the complexity of the search even further using some form of // knapsack instead of the naive search - val diffQueues: Map>>> + val diffQueues: Map>>> ) { - fun copyVaults(): HashMap> { - val newNodeVaults = HashMap>() + fun copyVaults(): HashMap> { + val newNodeVaults = HashMap>() for ((key, value) in nodeVaults) { newNodeVaults[key] = HashMap(value) } return newNodeVaults } - fun copyQueues(): HashMap>>> { - val newDiffQueues = HashMap>>>() + fun copyQueues(): HashMap>>> { + val newDiffQueues = HashMap>>>() for ((node, queues) in diffQueues) { - val newQueues = HashMap>>() + val newQueues = HashMap>>() for ((sender, value) in queues) { newQueues[sender] = ArrayList(value) } @@ -216,9 +217,9 @@ val crossCashTest = LoadTest( gatherRemoteState = { previousState -> log.info("Reifying state...") - val currentNodeVaults = HashMap>() + val currentNodeVaults = HashMap>() simpleNodes.forEach { - val quantities = HashMap() + val quantities = HashMap() val vault = it.connection.proxy.vaultAndUpdates().first vault.forEach { val state = it.state.data @@ -230,7 +231,7 @@ val crossCashTest = LoadTest( currentNodeVaults.put(it.info.legalIdentity, quantities) } val (consistentVaults, diffQueues) = if (previousState == null) { - Pair(currentNodeVaults, mapOf>>>()) + Pair(currentNodeVaults, mapOf>>>()) } else { log.info("${previousState.diffQueues.values.sumBy { it.values.sumBy { it.size } }} txs in limbo") val newDiffQueues = previousState.copyQueues() @@ -248,12 +249,12 @@ val crossCashTest = LoadTest( "\nActual gathered state:\n${CrossCashState(currentNodeVaults, mapOf())}" ) // TODO We should terminate here with an exception, we cannot carry on as we have an inconsistent model. We carry on currently because we always diverge due to notarisation failures - return@LoadTest CrossCashState(currentNodeVaults, mapOf>>>()) + return@LoadTest CrossCashState(currentNodeVaults, mapOf>>>()) } if (matches.size > 1) { log.warn("Multiple predicted states match the remote state") } - val minimumMatches = matches.fold, HashMap?>(null) { minimum, next -> + val minimumMatches = matches.fold, HashMap?>(null) { minimum, next -> if (minimum == null) { HashMap(next) } else { diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/GenerateHelpers.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/GenerateHelpers.kt index d8e51aee2a..8bce6439a9 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/GenerateHelpers.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/GenerateHelpers.kt @@ -5,6 +5,7 @@ import net.corda.client.mock.generateAmount import net.corda.client.mock.pickOne import net.corda.core.contracts.Issued import net.corda.core.contracts.PartyAndReference +import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.Party import net.corda.core.serialization.OpaqueBytes import net.corda.flows.CashCommand @@ -27,7 +28,7 @@ fun generateIssue( fun generateMove( max: Long, currency: Currency, - issuer: Party, + issuer: AnonymousParty, possibleRecipients: List ): Generator { return generateAmount(1, max, Generator.pure(Issued(PartyAndReference(issuer, OpaqueBytes.of(0)), currency))).combine( diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt index 51b0767b1c..c4e7106094 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/SelfIssueTest.kt @@ -6,6 +6,7 @@ import net.corda.client.mock.pickOne import net.corda.client.mock.replicatePoisson import net.corda.contracts.asset.Cash import net.corda.core.contracts.USD +import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.Party import net.corda.core.flows.FlowException import net.corda.core.getOrThrow @@ -26,9 +27,9 @@ data class SelfIssueCommand( ) data class SelfIssueState( - val vaultsSelfIssued: Map + val vaultsSelfIssued: Map ) { - fun copyVaults(): HashMap { + fun copyVaults(): HashMap { return HashMap(vaultsSelfIssued) } } @@ -70,7 +71,7 @@ val selfIssueTest = LoadTest( }, gatherRemoteState = { previousState -> - val selfIssueVaults = HashMap() + val selfIssueVaults = HashMap() simpleNodes.forEach { node -> val vault = node.connection.proxy.vaultAndUpdates().first vault.forEach {