Enforce separation of Party and AnonymousParty

This commit is contained in:
Ross Nicoll 2017-02-06 11:03:23 +00:00
parent fa33336d38
commit ed093cdb9d
18 changed files with 81 additions and 53 deletions

View File

@ -0,0 +1,22 @@
package net.corda.core.crypto
import net.corda.core.contracts.PartyAndReference
import net.corda.core.serialization.OpaqueBytes
import java.security.PublicKey
/**
* An [AbstractParty] contains the common elements of [Party] and [AnonymousParty], specifically the owning key of
* the party. In most cases [Party] or [AnonymousParty] should be used, depending on use-case.
*/
abstract class AbstractParty(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 AbstractParty && this.owningKey == other.owningKey
override fun hashCode(): Int = owningKey.hashCode()
abstract fun toAnonymous() : AnonymousParty
abstract fun ref(bytes: OpaqueBytes): PartyAndReference
fun ref(vararg bytes: Byte) = ref(OpaqueBytes.of(*bytes))
}

View File

@ -8,18 +8,14 @@ import java.security.PublicKey
* The [AnonymousParty] class contains enough information to uniquely identify a [Party] while excluding private * 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. * information such as name. It is intended to represent a party on the distributed ledger.
*/ */
open class AnonymousParty(val owningKey: CompositeKey) { class AnonymousParty(owningKey: CompositeKey) : AbstractParty(owningKey) {
/** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */ /** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */
constructor(owningKey: PublicKey) : this(owningKey.composite) 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()
open fun toAnonymous() : AnonymousParty = this
// Use the key as the bulk of the toString(), but include a human readable identifier as well, so that [Party] // 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 // can put in the key and actual name
override fun toString() = "${owningKey.toBase58String()} <Anonymous>" override fun toString() = "${owningKey.toBase58String()} <Anonymous>"
fun ref(bytes: OpaqueBytes) = PartyAndReference(this, bytes) override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
fun ref(vararg bytes: Byte) = ref(OpaqueBytes.of(*bytes)) override fun toAnonymous() = this
} }

View File

@ -1,5 +1,7 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.contracts.PartyAndReference
import net.corda.core.serialization.OpaqueBytes
import java.security.PublicKey import java.security.PublicKey
/** /**
@ -9,7 +11,7 @@ import java.security.PublicKey
* cryptographic public key primitives into a tree structure. * cryptographic public key primitives into a tree structure.
* *
* For example: Alice has two key pairs (pub1/priv1 and pub2/priv2), and wants to be able to sign transactions with either of them. * For example: Alice has two key pairs (pub1/priv1 and pub2/priv2), and wants to be able to sign transactions with either of them.
* Her advertised [Party] then has a legal [name] "Alice" and an [owingKey] "pub1 or pub2". * Her advertised [Party] then has a legal [name] "Alice" and an [owningKey] "pub1 or pub2".
* *
* [Party] is also used for service identities. E.g. Alice may also be running an interest rate oracle on her Corda node, * [Party] is also used for service identities. E.g. Alice may also be running an interest rate oracle on her Corda node,
* which requires a separate signing key (and an identifying name). Services can also be distributed run by a coordinated * which requires a separate signing key (and an identifying name). Services can also be distributed run by a coordinated
@ -20,9 +22,11 @@ import java.security.PublicKey
* *
* @see CompositeKey * @see CompositeKey
*/ */
class Party(val name: String, owningKey: CompositeKey) : AnonymousParty(owningKey) { class Party(val name: String, owningKey: CompositeKey) : AbstractParty(owningKey) {
/** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */ /** 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) constructor(name: String, owningKey: PublicKey) : this(name, owningKey.composite)
override fun toAnonymous(): AnonymousParty = AnonymousParty(owningKey) override fun toAnonymous(): AnonymousParty = AnonymousParty(owningKey)
override fun toString() = "${owningKey.toBase58String()} (${name})" override fun toString() = "${owningKey.toBase58String()} (${name})"
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this.toAnonymous(), bytes)
} }

View File

@ -197,7 +197,7 @@ interface VaultService {
fun generateSpend(tx: TransactionBuilder, fun generateSpend(tx: TransactionBuilder,
amount: Amount<Currency>, amount: Amount<Currency>,
to: CompositeKey, to: CompositeKey,
onlyFromParties: Set<AnonymousParty>? = null): Pair<TransactionBuilder, List<CompositeKey>> onlyFromParties: Set<AbstractParty>? = null): Pair<TransactionBuilder, List<CompositeKey>>
/** /**
* Return [ContractState]s of a given [Contract] type and list of [Vault.StateStatus] * Return [ContractState]s of a given [Contract] type and list of [Vault.StateStatus]
@ -216,7 +216,7 @@ inline fun <reified T : LinearState> VaultService.linearHeadsOfType() =
states(setOf(T::class.java), EnumSet.of(Vault.StateStatus.UNCONSUMED)) states(setOf(T::class.java), EnumSet.of(Vault.StateStatus.UNCONSUMED))
.associateBy { it.state.data.linearId }.mapValues { it.value } .associateBy { it.state.data.linearId }.mapValues { it.value }
inline fun <reified T : DealState> VaultService.dealsWith(party: AnonymousParty) = linearHeadsOfType<T>().values.filter { inline fun <reified T : DealState> VaultService.dealsWith(party: AbstractParty) = linearHeadsOfType<T>().values.filter {
it.state.data.parties.any { it == party } it.state.data.parties.any { it == party }
} }

View File

@ -300,7 +300,7 @@ object TwoPartyDealFlow {
// And add a request for timestamping: it may be that none of the contracts need this! But it can't hurt // And add a request for timestamping: it may be that none of the contracts need this! But it can't hurt
// to have one. // to have one.
ptx.setTime(serviceHub.clock.instant(), 30.seconds) ptx.setTime(serviceHub.clock.instant(), 30.seconds)
return Pair(ptx, arrayListOf(deal.parties.single { it == serviceHub.myInfo.legalIdentity }.owningKey)) return Pair(ptx, arrayListOf(deal.parties.single { it == serviceHub.myInfo.legalIdentity as AbstractParty }.owningKey))
} }
} }

View File

@ -12,9 +12,9 @@ class PartyTest {
val differentKey = entropyToKeyPair(BigInteger.valueOf(7201702L)).public.composite val differentKey = entropyToKeyPair(BigInteger.valueOf(7201702L)).public.composite
val anonymousParty = AnonymousParty(key) val anonymousParty = AnonymousParty(key)
val party = Party("test key", key) val party = Party("test key", key)
assertEquals(party, anonymousParty) assertEquals<AbstractParty>(party, anonymousParty)
assertEquals(anonymousParty, party) assertEquals<AbstractParty>(anonymousParty, party)
assertNotEquals(AnonymousParty(differentKey), anonymousParty) assertNotEquals<AbstractParty>(AnonymousParty(differentKey), anonymousParty)
assertNotEquals(AnonymousParty(differentKey), party) assertNotEquals<AbstractParty>(AnonymousParty(differentKey), party)
} }
} }

View File

@ -192,12 +192,12 @@ fun Iterable<ContractState>.sumCashOrZero(currency: Issued<Currency>): Amount<Is
} }
fun Cash.State.ownedBy(owner: CompositeKey) = copy(owner = owner) fun Cash.State.ownedBy(owner: CompositeKey) = copy(owner = owner)
fun Cash.State.issuedBy(party: AnonymousParty) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party.toAnonymous())))) fun Cash.State.issuedBy(party: AbstractParty) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party.toAnonymous()))))
fun Cash.State.issuedBy(deposit: PartyAndReference) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = deposit))) fun Cash.State.issuedBy(deposit: PartyAndReference) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = deposit)))
fun Cash.State.withDeposit(deposit: PartyAndReference): Cash.State = copy(amount = amount.copy(token = amount.token.copy(issuer = deposit))) fun Cash.State.withDeposit(deposit: PartyAndReference): Cash.State = copy(amount = amount.copy(token = amount.token.copy(issuer = deposit)))
infix fun Cash.State.`owned by`(owner: CompositeKey) = ownedBy(owner) infix fun Cash.State.`owned by`(owner: CompositeKey) = ownedBy(owner)
infix fun Cash.State.`issued by`(party: AnonymousParty) = issuedBy(party) infix fun Cash.State.`issued by`(party: AbstractParty) = issuedBy(party)
infix fun Cash.State.`issued by`(deposit: PartyAndReference) = issuedBy(deposit) infix fun Cash.State.`issued by`(deposit: PartyAndReference) = issuedBy(deposit)
infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = withDeposit(deposit) infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = withDeposit(deposit)

View File

@ -463,7 +463,7 @@ class Obligation<P> : Contract {
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey. * Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
*/ */
fun generateIssue(tx: TransactionBuilder, fun generateIssue(tx: TransactionBuilder,
obligor: AnonymousParty, obligor: AbstractParty,
issuanceDef: Terms<P>, issuanceDef: Terms<P>,
pennies: Long, pennies: Long,
beneficiary: CompositeKey, beneficiary: CompositeKey,
@ -707,9 +707,9 @@ fun <P> Iterable<ContractState>.sumObligationsOrZero(issuanceDef: Issued<Obligat
= filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef) = filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef)
infix fun <T> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore)) infix fun <T> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore))
infix fun <T> Obligation.State<T>.between(parties: Pair<AnonymousParty, CompositeKey>) = copy(obligor = parties.first.toAnonymous(), beneficiary = parties.second) infix fun <T> Obligation.State<T>.between(parties: Pair<AbstractParty, CompositeKey>) = copy(obligor = parties.first.toAnonymous(), beneficiary = parties.second)
infix fun <T> Obligation.State<T>.`owned by`(owner: CompositeKey) = copy(beneficiary = owner) infix fun <T> Obligation.State<T>.`owned by`(owner: CompositeKey) = copy(beneficiary = owner)
infix fun <T> Obligation.State<T>.`issued by`(party: AnonymousParty) = copy(obligor = party.toAnonymous()) infix fun <T> Obligation.State<T>.`issued by`(party: AbstractParty) = copy(obligor = party.toAnonymous())
// For Java users: // For Java users:
@Suppress("unused") fun <T> Obligation.State<T>.ownedBy(owner: CompositeKey) = copy(beneficiary = owner) @Suppress("unused") fun <T> Obligation.State<T>.ownedBy(owner: CompositeKey) = copy(beneficiary = owner)

View File

@ -4,6 +4,7 @@ import net.corda.core.contracts.Contract
import net.corda.core.contracts.DealState import net.corda.core.contracts.DealState
import net.corda.core.contracts.TransactionForContract import net.corda.core.contracts.TransactionForContract
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
@ -20,10 +21,11 @@ class DummyDealContract: Contract {
override val participants: List<CompositeKey> = listOf(), override val participants: List<CompositeKey> = listOf(),
override val linearId: UniqueIdentifier = UniqueIdentifier(), override val linearId: UniqueIdentifier = UniqueIdentifier(),
override val ref: String, override val ref: String,
override val parties: List<Party> = listOf()) : DealState { override val parties: List<AnonymousParty> = listOf()) : DealState {
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean { override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return participants.any { it.containsAny(ourKeys) } return participants.any { it.containsAny(ourKeys) }
} }
override fun generateAgreement(notary: Party): TransactionBuilder { override fun generateAgreement(notary: Party): TransactionBuilder {
throw UnsupportedOperationException("not implemented") throw UnsupportedOperationException("not implemented")
} }

View File

@ -164,7 +164,7 @@ class CashTests {
assertTrue(tx.inputs.isEmpty()) assertTrue(tx.inputs.isEmpty())
val s = tx.outputs[0].data as Cash.State val s = tx.outputs[0].data as Cash.State
assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount) assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount)
assertEquals(MINI_CORP, s.amount.token.issuer.party) assertEquals(MINI_CORP as AbstractParty, s.amount.token.issuer.party)
assertEquals(DUMMY_PUBKEY_1, s.owner) assertEquals(DUMMY_PUBKEY_1, s.owner)
assertTrue(tx.commands[0].value is Cash.Commands.Issue) assertTrue(tx.commands[0].value is Cash.Commands.Issue)
assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0]) assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0])

View File

@ -7,6 +7,7 @@ import net.corda.contracts.asset.Cash
import net.corda.core.ThreadBox import net.corda.core.ThreadBox
import net.corda.core.bufferUntilSubscribed import net.corda.core.bufferUntilSubscribed
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.AbstractParty
import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
@ -222,7 +223,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
override fun generateSpend(tx: TransactionBuilder, override fun generateSpend(tx: TransactionBuilder,
amount: Amount<Currency>, amount: Amount<Currency>,
to: CompositeKey, to: CompositeKey,
onlyFromParties: Set<AnonymousParty>?): Pair<TransactionBuilder, List<CompositeKey>> { onlyFromParties: Set<AbstractParty>?): Pair<TransactionBuilder, List<CompositeKey>> {
// Discussion // Discussion
// //
// This code is analogous to the Wallet.send() set of methods in bitcoinj, and has the same general outline. // This code is analogous to the Wallet.send() set of methods in bitcoinj, and has the same general outline.

View File

@ -2,7 +2,7 @@ package net.corda.irs.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.DealState import net.corda.core.contracts.DealState
import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.AbstractParty
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
@ -78,7 +78,7 @@ object AutoOfferFlow {
return stx return stx
} }
private fun <T: AnonymousParty> notUs(parties: List<T>): List<T> { private fun <T: AbstractParty> notUs(parties: List<T>): List<T> {
val notUsParties: MutableList<T> = arrayListOf() val notUsParties: MutableList<T> = arrayListOf()
for (party in parties) { for (party in parties) {
if (serviceHub.myInfo.legalIdentity != party) { if (serviceHub.myInfo.legalIdentity != party) {

View File

@ -4,7 +4,7 @@ import com.opengamma.strata.basics.currency.MultiCurrencyAmount
import net.corda.core.contracts.DealState import net.corda.core.contracts.DealState
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.filterStatesOfType import net.corda.core.contracts.filterStatesOfType
import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.AbstractParty
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
@ -34,7 +34,7 @@ class PortfolioApi(val rpc: CordaRPCOps) {
private val ownParty: Party get() = rpc.nodeIdentity().legalIdentity private val ownParty: Party get() = rpc.nodeIdentity().legalIdentity
private val portfolioUtils = PortfolioApiUtils(ownParty) private val portfolioUtils = PortfolioApiUtils(ownParty)
private inline fun <reified T : DealState> dealsWith(party: AnonymousParty): List<StateAndRef<T>> { private inline fun <reified T : DealState> dealsWith(party: AbstractParty): List<StateAndRef<T>> {
return rpc.vaultAndUpdates().first.filterStatesOfType<T>().filter { it.state.data.parties.any { it == party } } return rpc.vaultAndUpdates().first.filterStatesOfType<T>().filter { it.state.data.parties.any { it == party } }
} }
@ -221,7 +221,7 @@ class PortfolioApi(val rpc: CordaRPCOps) {
return withParty(partyName) { party -> return withParty(partyName) { party ->
withPortfolio(party) { state -> withPortfolio(party) { state ->
if (state.valuation != null) { if (state.valuation != null) {
val isValuer = state.valuer as AnonymousParty == ownParty val isValuer = state.valuer as AbstractParty == ownParty
val rawMtm = state.valuation.presentValues.map { val rawMtm = state.valuation.presentValues.map {
it.value.amounts.first().amount it.value.amounts.first().amount
}.reduce { a, b -> a + b } }.reduce { a, b -> a + b }

View File

@ -5,6 +5,7 @@ import com.opengamma.strata.product.swap.IborRateCalculation
import com.opengamma.strata.product.swap.RateCalculationSwapLeg import com.opengamma.strata.product.swap.RateCalculationSwapLeg
import com.opengamma.strata.product.swap.SwapLegType import com.opengamma.strata.product.swap.SwapLegType
import net.corda.core.contracts.hash import net.corda.core.contracts.hash
import net.corda.core.crypto.AbstractParty
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.vega.contracts.IRSState import net.corda.vega.contracts.IRSState
import net.corda.vega.contracts.PortfolioState import net.corda.vega.contracts.PortfolioState
@ -122,7 +123,7 @@ class PortfolioApiUtils(private val ownParty: Party) {
val ref: String) val ref: String)
fun createTradeView(state: IRSState): TradeView { fun createTradeView(state: IRSState): TradeView {
val trade = if (state.buyer == ownParty) state.swap.toFloatingLeg() else state.swap.toFloatingLeg() val trade = if (state.buyer == ownParty as AbstractParty) state.swap.toFloatingLeg() else state.swap.toFloatingLeg()
val fixedLeg = trade.product.legs.first { it.type == SwapLegType.FIXED } as RateCalculationSwapLeg val fixedLeg = trade.product.legs.first { it.type == SwapLegType.FIXED } as RateCalculationSwapLeg
val floatingLeg = trade.product.legs.first { it.type != SwapLegType.FIXED } as RateCalculationSwapLeg val floatingLeg = trade.product.legs.first { it.type != SwapLegType.FIXED } as RateCalculationSwapLeg
val fixedRate = fixedLeg.calculation as FixedRateCalculation val fixedRate = fixedLeg.calculation as FixedRateCalculation

View File

@ -9,7 +9,7 @@ import com.opengamma.strata.product.common.BuySell
import com.opengamma.strata.product.swap.SwapTrade import com.opengamma.strata.product.swap.SwapTrade
import com.opengamma.strata.product.swap.type.FixedIborSwapConvention import com.opengamma.strata.product.swap.type.FixedIborSwapConvention
import com.opengamma.strata.product.swap.type.FixedIborSwapConventions import com.opengamma.strata.product.swap.type.FixedIborSwapConventions
import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.AbstractParty
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import java.math.BigDecimal import java.math.BigDecimal
@ -48,7 +48,7 @@ data class SwapData(
val notional: BigDecimal, val notional: BigDecimal,
val fixedRate: BigDecimal) { val fixedRate: BigDecimal) {
fun getLegForParty(party: AnonymousParty): Leg { fun getLegForParty(party: AbstractParty): Leg {
return if (party == buyer.second) FixedLeg(notional) else FloatingLeg(notional) return if (party == buyer.second) FixedLeg(notional) else FloatingLeg(notional)
} }

View File

@ -15,6 +15,7 @@ import net.corda.core.contracts.Amount
import net.corda.core.contracts.Issued import net.corda.core.contracts.Issued
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.withoutIssuer import net.corda.core.contracts.withoutIssuer
import net.corda.core.crypto.AbstractParty
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
@ -199,7 +200,7 @@ class NewTransaction : Fragment() {
) )
availableAmount.textProperty() availableAmount.textProperty()
.bind(Bindings.createStringBinding({ .bind(Bindings.createStringBinding({
val filteredCash = cash.filtered { it.token.issuer.party == issuer.value && it.token.product == currencyChoiceBox.value } val filteredCash = cash.filtered { it.token.issuer.party as AbstractParty == issuer.value && it.token.product == currencyChoiceBox.value }
.map { it.withoutIssuer().quantity } .map { it.withoutIssuer().quantity }
"${filteredCash.sum()} ${currencyChoiceBox.value?.currencyCode} Available" "${filteredCash.sum()} ${currencyChoiceBox.value?.currencyCode} Available"
}, arrayOf(currencyChoiceBox.valueProperty(), issuerChoiceBox.valueProperty()))) }, arrayOf(currencyChoiceBox.valueProperty(), issuerChoiceBox.valueProperty())))

View File

@ -6,6 +6,7 @@ import net.corda.contracts.asset.Cash
import net.corda.core.contracts.Issued import net.corda.core.contracts.Issued
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.USD import net.corda.core.contracts.USD
import net.corda.core.crypto.AbstractParty
import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.AnonymousParty
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
@ -50,7 +51,7 @@ data class CrossCashCommand(
* Map from node to (map from issuer to USD quantity) * Map from node to (map from issuer to USD quantity)
*/ */
data class CrossCashState( data class CrossCashState(
val nodeVaults: Map<AnonymousParty, Map<AnonymousParty, Long>>, val nodeVaults: Map<AbstractParty, Map<AbstractParty, Long>>,
// node -> (notifying node -> [(issuer, amount)]) // node -> (notifying node -> [(issuer, amount)])
// This map holds the queues that encode the non-determinism of how tx notifications arrive in the background. // This map holds the queues that encode the non-determinism of how tx notifications arrive in the background.
@ -68,20 +69,20 @@ data class CrossCashState(
// requires more concurrent code which is conceptually also more complex than the current design. // 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 // TODO: Alternative: We may possibly reduce the complexity of the search even further using some form of
// knapsack instead of the naive search // knapsack instead of the naive search
val diffQueues: Map<AnonymousParty, Map<AnonymousParty, List<Pair<AnonymousParty, Long>>>> val diffQueues: Map<AbstractParty, Map<AbstractParty, List<Pair<AbstractParty, Long>>>>
) { ) {
fun copyVaults(): HashMap<AnonymousParty, HashMap<AnonymousParty, Long>> { fun copyVaults(): HashMap<AbstractParty, HashMap<AbstractParty, Long>> {
val newNodeVaults = HashMap<AnonymousParty, HashMap<AnonymousParty, Long>>() val newNodeVaults = HashMap<AbstractParty, HashMap<AbstractParty, Long>>()
for ((key, value) in nodeVaults) { for ((key, value) in nodeVaults) {
newNodeVaults[key] = HashMap(value) newNodeVaults[key] = HashMap(value)
} }
return newNodeVaults return newNodeVaults
} }
fun copyQueues(): HashMap<AnonymousParty, HashMap<AnonymousParty, ArrayList<Pair<AnonymousParty, Long>>>> { fun copyQueues(): HashMap<AbstractParty, HashMap<AbstractParty, ArrayList<Pair<AbstractParty, Long>>>> {
val newDiffQueues = HashMap<AnonymousParty, HashMap<AnonymousParty, ArrayList<Pair<AnonymousParty, Long>>>>() val newDiffQueues = HashMap<AbstractParty, HashMap<AbstractParty, ArrayList<Pair<AbstractParty, Long>>>>()
for ((node, queues) in diffQueues) { for ((node, queues) in diffQueues) {
val newQueues = HashMap<AnonymousParty, ArrayList<Pair<AnonymousParty, Long>>>() val newQueues = HashMap<AbstractParty, ArrayList<Pair<AbstractParty, Long>>>()
for ((sender, value) in queues) { for ((sender, value) in queues) {
newQueues[sender] = ArrayList(value) newQueues[sender] = ArrayList(value)
} }
@ -217,9 +218,9 @@ val crossCashTest = LoadTest<CrossCashCommand, CrossCashState>(
gatherRemoteState = { previousState -> gatherRemoteState = { previousState ->
log.info("Reifying state...") log.info("Reifying state...")
val currentNodeVaults = HashMap<AnonymousParty, HashMap<AnonymousParty, Long>>() val currentNodeVaults = HashMap<AbstractParty, HashMap<AbstractParty, Long>>()
simpleNodes.forEach { simpleNodes.forEach {
val quantities = HashMap<AnonymousParty, Long>() val quantities = HashMap<AbstractParty, Long>()
val vault = it.connection.proxy.vaultAndUpdates().first val vault = it.connection.proxy.vaultAndUpdates().first
vault.forEach { vault.forEach {
val state = it.state.data val state = it.state.data
@ -231,7 +232,7 @@ val crossCashTest = LoadTest<CrossCashCommand, CrossCashState>(
currentNodeVaults.put(it.info.legalIdentity, quantities) currentNodeVaults.put(it.info.legalIdentity, quantities)
} }
val (consistentVaults, diffQueues) = if (previousState == null) { val (consistentVaults, diffQueues) = if (previousState == null) {
Pair(currentNodeVaults, mapOf<AnonymousParty, Map<AnonymousParty, List<Pair<AnonymousParty, Long>>>>()) Pair(currentNodeVaults, mapOf<AbstractParty, Map<AbstractParty, List<Pair<AbstractParty, Long>>>>())
} else { } else {
log.info("${previousState.diffQueues.values.sumBy { it.values.sumBy { it.size } }} txs in limbo") log.info("${previousState.diffQueues.values.sumBy { it.values.sumBy { it.size } }} txs in limbo")
val newDiffQueues = previousState.copyQueues() val newDiffQueues = previousState.copyQueues()
@ -249,12 +250,12 @@ val crossCashTest = LoadTest<CrossCashCommand, CrossCashState>(
"\nActual gathered state:\n${CrossCashState(currentNodeVaults, mapOf())}" "\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 // 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<AnonymousParty, Map<AnonymousParty, List<Pair<AnonymousParty, Long>>>>()) return@LoadTest CrossCashState(currentNodeVaults, mapOf<AbstractParty, Map<AbstractParty, List<Pair<AbstractParty, Long>>>>())
} }
if (matches.size > 1) { if (matches.size > 1) {
log.warn("Multiple predicted states match the remote state") log.warn("Multiple predicted states match the remote state")
} }
val minimumMatches = matches.fold<Map<AnonymousParty, Int>, HashMap<AnonymousParty, Int>?>(null) { minimum, next -> val minimumMatches = matches.fold<Map<AbstractParty, Int>, HashMap<AbstractParty, Int>?>(null) { minimum, next ->
if (minimum == null) { if (minimum == null) {
HashMap(next) HashMap(next)
} else { } else {

View File

@ -6,7 +6,7 @@ import net.corda.client.mock.pickOne
import net.corda.client.mock.replicatePoisson import net.corda.client.mock.replicatePoisson
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.contracts.USD import net.corda.core.contracts.USD
import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.AbstractParty
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
@ -28,9 +28,9 @@ data class SelfIssueCommand(
) )
data class SelfIssueState( data class SelfIssueState(
val vaultsSelfIssued: Map<AnonymousParty, Long> val vaultsSelfIssued: Map<AbstractParty, Long>
) { ) {
fun copyVaults(): HashMap<AnonymousParty, Long> { fun copyVaults(): HashMap<AbstractParty, Long> {
return HashMap(vaultsSelfIssued) return HashMap(vaultsSelfIssued)
} }
} }
@ -72,14 +72,14 @@ val selfIssueTest = LoadTest<SelfIssueCommand, SelfIssueState>(
}, },
gatherRemoteState = { previousState -> gatherRemoteState = { previousState ->
val selfIssueVaults = HashMap<AnonymousParty, Long>() val selfIssueVaults = HashMap<AbstractParty, Long>()
simpleNodes.forEach { node -> simpleNodes.forEach { node ->
val vault = node.connection.proxy.vaultAndUpdates().first val vault = node.connection.proxy.vaultAndUpdates().first
vault.forEach { vault.forEach {
val state = it.state.data val state = it.state.data
if (state is Cash.State) { if (state is Cash.State) {
val issuer = state.amount.token.issuer.party val issuer = state.amount.token.issuer.party
if (issuer == node.info.legalIdentity as AnonymousParty) { if (issuer == node.info.legalIdentity as AbstractParty) {
selfIssueVaults.put(issuer, (selfIssueVaults[issuer] ?: 0L) + state.amount.quantity) selfIssueVaults.put(issuer, (selfIssueVaults[issuer] ?: 0L) + state.amount.quantity)
} }
} }