Use identity service to resolve anonymised identities in explorer

This commit is contained in:
Patrick Kuo 2017-08-11 17:02:17 +01:00 committed by Ross Nicoll
parent bc5aceddbf
commit 3b9c1ec0ff
5 changed files with 39 additions and 34 deletions

View File

@ -1,13 +1,14 @@
package net.corda.client.jfx.model
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import javafx.beans.value.ObservableValue
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import net.corda.client.jfx.utils.firstOrDefault
import net.corda.client.jfx.utils.firstOrNullObservable
import net.corda.client.jfx.utils.fold
import net.corda.client.jfx.utils.map
import net.corda.core.crypto.keys
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache.MapChange
import java.security.PublicKey
@ -29,6 +30,12 @@ class NetworkIdentityModel {
private val rpcProxy by observableValue(NodeMonitorModel::proxyObservable)
private val identityCache = CacheBuilder.newBuilder()
.build<PublicKey, ObservableValue<NodeInfo?>>(CacheLoader.from {
publicKey ->
publicKey?.let { rpcProxy.map { it?.nodeIdentityFromParty(AnonymousParty(publicKey)) } }
})
val parties: ObservableList<NodeInfo> = networkIdentities.filtered { !it.isCordaService() }
val notaries: ObservableList<NodeInfo> = networkIdentities.filtered { it.advertisedServices.any { it.info.type.isNotary() } }
val myIdentity = rpcProxy.map { it?.nodeIdentity() }
@ -38,8 +45,5 @@ class NetworkIdentityModel {
return advertisedServices.any { it.info.type.isNetworkMap() || it.info.type.isNotary() }
}
// TODO: Use Identity Service in service hub instead?
fun lookup(publicKey: PublicKey): ObservableValue<NodeInfo?> = parties.firstOrDefault(notaries.firstOrNullObservable { it.notaryIdentity.owningKey.keys.any { it == publicKey } }) {
it.legalIdentity.owningKey.keys.any { it == publicKey }
}
fun partyFromPublicKey(publicKey: PublicKey): ObservableValue<NodeInfo?> = identityCache[publicKey]
}

View File

@ -14,10 +14,10 @@ import net.corda.client.jfx.model.Models
import net.corda.client.jfx.model.NetworkIdentityModel
import net.corda.client.jfx.utils.map
import net.corda.core.contracts.StateAndRef
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.finance.contracts.asset.Cash
import tornadofx.*
import java.security.PublicKey
/**
* Helper method to reduce boiler plate code
@ -89,6 +89,6 @@ inline fun <reified M : Any> UIComponent.getModel(): M = Models.get(M::class, th
fun <A, B> Collection<A>.cross(other: Collection<B>) = this.flatMap { a -> other.map { b -> a to b } }
// TODO: This is a temporary fix for the UI to show the correct issuer identity, this will break when we start randomizing keys. More work is needed here when the identity work is done.
fun StateAndRef<Cash.State>.resolveIssuer(): ObservableValue<Party?> = state.data.amount.token.issuer.party.resolveIssuer()
fun StateAndRef<Cash.State>.resolveIssuer(): ObservableValue<Party?> = state.data.amount.token.issuer.party.owningKey.toKnownParty()
fun AbstractParty.resolveIssuer(): ObservableValue<Party?> = Models.get(NetworkIdentityModel::class, javaClass.kotlin).lookup(owningKey).map { it?.legalIdentity }
fun PublicKey.toKnownParty() = Models.get(NetworkIdentityModel::class, javaClass.kotlin).partyFromPublicKey(this).map { it?.legalIdentity }

View File

@ -79,7 +79,7 @@ class Network : CordaView() {
.filterNotNull()
.map { it.stateAndRef.state.data }.getParties()
val outputParties = it.transaction.tx.outputStates.observable().getParties()
val signingParties = it.transaction.sigs.map { getModel<NetworkIdentityModel>().lookup(it.by) }
val signingParties = it.transaction.sigs.map { it.by.toKnownParty() }
// Input parties fire a bullets to all output parties, and to the signing parties. !! This is a rough guess of how the message moves in the network.
// TODO : Expose artemis queue to get real message information.
inputParties.cross(outputParties) + inputParties.cross(signingParties)
@ -170,10 +170,10 @@ class Network : CordaView() {
zoomOutButton.setOnAction { zoom(0.8) }
lastTransactions.addListener { _, _, new ->
new?.forEach {
it.first.value?.let { a ->
it.second.value?.let { b ->
fireBulletBetweenNodes(a.legalIdentity, b.legalIdentity, "bank", "bank")
new?.forEach { (partyA, partyB) ->
partyA.value?.let { a ->
partyB.value?.let { b ->
fireBulletBetweenNodes(a, b, "bank", "bank")
}
}
}
@ -209,7 +209,7 @@ class Network : CordaView() {
return Point2D(x, y)
}
private fun List<ContractState>.getParties() = map { it.participants.map { getModel<NetworkIdentityModel>().lookup(it.owningKey) } }.flatten()
private fun List<ContractState>.getParties() = map { it.participants.map { it.owningKey.toKnownParty() } }.flatten()
private fun fireBulletBetweenNodes(senderNode: Party, destNode: Party, startType: String, endType: String) {
val senderNodeComp = allComponentMap[senderNode] ?: return

View File

@ -26,6 +26,7 @@ import net.corda.core.crypto.commonName
import net.corda.core.crypto.toBase58String
import net.corda.core.crypto.toStringShort
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.NodeInfo
import net.corda.explorer.AmountDiff
import net.corda.explorer.formatters.AmountFormatter
@ -70,8 +71,8 @@ class TransactionViewer : CordaView("Transactions") {
val id: SecureHash,
val inputs: Inputs,
val outputs: ObservableList<StateAndRef<ContractState>>,
val inputParties: ObservableList<List<ObservableValue<NodeInfo?>>>,
val outputParties: ObservableList<List<ObservableValue<NodeInfo?>>>,
val inputParties: ObservableList<List<ObservableValue<Party?>>>,
val outputParties: ObservableList<List<ObservableValue<Party?>>>,
val commandTypes: List<Class<CommandData>>,
val totalValueEquiv: ObservableValue<AmountDiff<Currency>>
)
@ -135,8 +136,8 @@ class TransactionViewer : CordaView("Transactions") {
"Transaction ID" to { tx, s -> "${tx.id}".contains(s, true) },
"Input" to { tx, s -> tx.inputs.resolved.any { it.state.data.contract.javaClass.simpleName.contains(s, true) } },
"Output" to { tx, s -> tx.outputs.any { it.state.data.contract.javaClass.simpleName.contains(s, true) } },
"Input Party" to { tx, s -> tx.inputParties.any { it.any { it.value?.legalIdentity?.name?.commonName?.contains(s, true) ?: false } } },
"Output Party" to { tx, s -> tx.outputParties.any { it.any { it.value?.legalIdentity?.name?.commonName?.contains(s, true) ?: false } } },
"Input Party" to { tx, s -> tx.inputParties.any { it.any { it.value?.name?.commonName?.contains(s, true) ?: false } } },
"Output Party" to { tx, s -> tx.outputParties.any { it.any { it.value?.name?.commonName?.contains(s, true) ?: false } } },
"Command Type" to { tx, s -> tx.commandTypes.any { it.simpleName.contains(s, true) } }
)
root.top = searchField.root
@ -199,13 +200,13 @@ class TransactionViewer : CordaView("Transactions") {
})
}
private fun ObservableList<List<ObservableValue<NodeInfo?>>>.formatJoinPartyNames(separator: String = "", formatter: Formatter<X500Name>): String {
private fun ObservableList<List<ObservableValue<Party?>>>.formatJoinPartyNames(separator: String = ",", formatter: Formatter<X500Name>): String {
return flatten().map {
it.value?.legalIdentity?.let { formatter.format(it.name) }
it.value?.let { formatter.format(it.name) }
}.filterNotNull().toSet().joinToString(separator)
}
private fun ObservableList<StateAndRef<ContractState>>.getParties() = map { it.state.data.participants.map { getModel<NetworkIdentityModel>().lookup(it.owningKey) } }
private fun ObservableList<StateAndRef<ContractState>>.getParties() = map { it.state.data.participants.map { it.owningKey.toKnownParty() } }
private fun ObservableList<StateAndRef<ContractState>>.toText() = map { it.contract().javaClass.simpleName }.groupBy { it }.map { "${it.key} (${it.value.size})" }.joinToString()
private class TransactionWidget : BorderPane() {
@ -248,8 +249,8 @@ class TransactionViewer : CordaView("Transactions") {
outputs.items = transaction.outputs.observable()
signatures.children.addAll(signatureData.map { signature ->
val nodeInfo = getModel<NetworkIdentityModel>().lookup(signature)
copyableLabel(nodeInfo.map { "${signature.toStringShort()} (${it?.legalIdentity?.let { PartyNameFormatter.short.format(it.name)} ?: "???"})" })
val party = signature.toKnownParty()
copyableLabel(party.map { "${signature.toStringShort()} (${it?.let { PartyNameFormatter.short.format(it.name) } ?: "Anonymous"})" })
})
}
@ -276,7 +277,7 @@ class TransactionViewer : CordaView("Transactions") {
row {
label("Issuer :") { gridpaneConstraints { hAlignment = HPos.RIGHT } }
val anonymousIssuer: AbstractParty = data.amount.token.issuer.party
val issuer: AbstractParty = anonymousIssuer.resolveIssuer().value ?: anonymousIssuer
val issuer: AbstractParty = anonymousIssuer.owningKey.toKnownParty().value ?: anonymousIssuer
// TODO: Anonymous should probably be italicised or similar
label(issuer.nameOrNull()?.let { PartyNameFormatter.short.format(it) } ?: "Anonymous") {
tooltip(anonymousIssuer.owningKey.toBase58String())
@ -284,9 +285,8 @@ class TransactionViewer : CordaView("Transactions") {
}
row {
label("Owner :") { gridpaneConstraints { hAlignment = HPos.RIGHT } }
val owner = data.owner
val nodeInfo = getModel<NetworkIdentityModel>().lookup(owner.owningKey)
label(nodeInfo.map { it?.legalIdentity?.let { PartyNameFormatter.short.format(it.name) } ?: "???" }) {
val owner = data.owner.owningKey.toKnownParty()
label(owner.map { it?.let { PartyNameFormatter.short.format(it.name) } ?: "Anonymous" }) {
tooltip(data.owner.owningKey.toBase58String())
}
}
@ -300,27 +300,28 @@ class TransactionViewer : CordaView("Transactions") {
}
private fun StateAndRef<ContractState>.contract() = this.state.data.contract
}
/**
* We calculate the total value by subtracting relevant input states and adding relevant output states, as long as they're cash
*/
private fun calculateTotalEquiv(identity: NodeInfo?,
private fun calculateTotalEquiv(myIdentity: NodeInfo?,
reportingCurrencyExchange: Pair<Currency, (Amount<Currency>) -> Amount<Currency>>,
inputs: List<ContractState>,
outputs: List<ContractState>): AmountDiff<Currency> {
val (reportingCurrency, exchange) = reportingCurrencyExchange
val legalIdentity = identity?.legalIdentity
val myLegalIdentity = myIdentity?.legalIdentity
fun List<ContractState>.sum() = this.map { it as? Cash.State }
.filterNotNull()
.filter { legalIdentity == it.owner }
.filter { it.owner.owningKey.toKnownParty().value == myLegalIdentity }
.map { exchange(it.amount.withoutIssuer()).quantity }
.sum()
// For issuing cash, if I am the issuer and not the owner (e.g. issuing cash to other party), count it as negative.
val issuedAmount = if (inputs.isEmpty()) outputs.map { it as? Cash.State }
.filterNotNull()
.filter { legalIdentity == it.amount.token.issuer.party && legalIdentity != it.owner }
.filter { it.amount.token.issuer.party.owningKey.toKnownParty().value == myLegalIdentity && it.owner.owningKey.toKnownParty().value != myLegalIdentity }
.map { exchange(it.amount.withoutIssuer()).quantity }
.sum() else 0

View File

@ -205,7 +205,7 @@ class CashViewer : CordaView("Cash") {
/**
* Assemble the Issuer node.
*/
val treeItem = TreeItem(ViewerNode.IssuerNode(issuer.resolveIssuer().value ?: issuer, equivSumAmount, memberStates))
val treeItem = TreeItem(ViewerNode.IssuerNode(issuer.owningKey.toKnownParty().value ?: issuer, equivSumAmount, memberStates))
/**
* Bind the children in the TreeTable structure.