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 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.beans.value.ObservableValue
import javafx.collections.FXCollections import javafx.collections.FXCollections
import javafx.collections.ObservableList 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.fold
import net.corda.client.jfx.utils.map 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.NodeInfo
import net.corda.core.node.services.NetworkMapCache.MapChange import net.corda.core.node.services.NetworkMapCache.MapChange
import java.security.PublicKey import java.security.PublicKey
@ -29,6 +30,12 @@ class NetworkIdentityModel {
private val rpcProxy by observableValue(NodeMonitorModel::proxyObservable) 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 parties: ObservableList<NodeInfo> = networkIdentities.filtered { !it.isCordaService() }
val notaries: ObservableList<NodeInfo> = networkIdentities.filtered { it.advertisedServices.any { it.info.type.isNotary() } } val notaries: ObservableList<NodeInfo> = networkIdentities.filtered { it.advertisedServices.any { it.info.type.isNotary() } }
val myIdentity = rpcProxy.map { it?.nodeIdentity() } val myIdentity = rpcProxy.map { it?.nodeIdentity() }
@ -38,8 +45,5 @@ class NetworkIdentityModel {
return advertisedServices.any { it.info.type.isNetworkMap() || it.info.type.isNotary() } return advertisedServices.any { it.info.type.isNetworkMap() || it.info.type.isNotary() }
} }
// TODO: Use Identity Service in service hub instead? fun partyFromPublicKey(publicKey: PublicKey): ObservableValue<NodeInfo?> = identityCache[publicKey]
fun lookup(publicKey: PublicKey): ObservableValue<NodeInfo?> = parties.firstOrDefault(notaries.firstOrNullObservable { it.notaryIdentity.owningKey.keys.any { it == publicKey } }) {
it.legalIdentity.owningKey.keys.any { it == 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.model.NetworkIdentityModel
import net.corda.client.jfx.utils.map import net.corda.client.jfx.utils.map
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import tornadofx.* import tornadofx.*
import java.security.PublicKey
/** /**
* Helper method to reduce boiler plate code * 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 } } 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. // 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() .filterNotNull()
.map { it.stateAndRef.state.data }.getParties() .map { it.stateAndRef.state.data }.getParties()
val outputParties = it.transaction.tx.outputStates.observable().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. // 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. // TODO : Expose artemis queue to get real message information.
inputParties.cross(outputParties) + inputParties.cross(signingParties) inputParties.cross(outputParties) + inputParties.cross(signingParties)
@ -170,10 +170,10 @@ class Network : CordaView() {
zoomOutButton.setOnAction { zoom(0.8) } zoomOutButton.setOnAction { zoom(0.8) }
lastTransactions.addListener { _, _, new -> lastTransactions.addListener { _, _, new ->
new?.forEach { new?.forEach { (partyA, partyB) ->
it.first.value?.let { a -> partyA.value?.let { a ->
it.second.value?.let { b -> partyB.value?.let { b ->
fireBulletBetweenNodes(a.legalIdentity, b.legalIdentity, "bank", "bank") fireBulletBetweenNodes(a, b, "bank", "bank")
} }
} }
} }
@ -209,7 +209,7 @@ class Network : CordaView() {
return Point2D(x, y) 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) { private fun fireBulletBetweenNodes(senderNode: Party, destNode: Party, startType: String, endType: String) {
val senderNodeComp = allComponentMap[senderNode] ?: return 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.toBase58String
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.explorer.AmountDiff import net.corda.explorer.AmountDiff
import net.corda.explorer.formatters.AmountFormatter import net.corda.explorer.formatters.AmountFormatter
@ -70,8 +71,8 @@ class TransactionViewer : CordaView("Transactions") {
val id: SecureHash, val id: SecureHash,
val inputs: Inputs, val inputs: Inputs,
val outputs: ObservableList<StateAndRef<ContractState>>, val outputs: ObservableList<StateAndRef<ContractState>>,
val inputParties: ObservableList<List<ObservableValue<NodeInfo?>>>, val inputParties: ObservableList<List<ObservableValue<Party?>>>,
val outputParties: ObservableList<List<ObservableValue<NodeInfo?>>>, val outputParties: ObservableList<List<ObservableValue<Party?>>>,
val commandTypes: List<Class<CommandData>>, val commandTypes: List<Class<CommandData>>,
val totalValueEquiv: ObservableValue<AmountDiff<Currency>> val totalValueEquiv: ObservableValue<AmountDiff<Currency>>
) )
@ -135,8 +136,8 @@ class TransactionViewer : CordaView("Transactions") {
"Transaction ID" to { tx, s -> "${tx.id}".contains(s, true) }, "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) } }, "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) } }, "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 } } }, "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?.legalIdentity?.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) } } "Command Type" to { tx, s -> tx.commandTypes.any { it.simpleName.contains(s, true) } }
) )
root.top = searchField.root 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 { return flatten().map {
it.value?.legalIdentity?.let { formatter.format(it.name) } it.value?.let { formatter.format(it.name) }
}.filterNotNull().toSet().joinToString(separator) }.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 fun ObservableList<StateAndRef<ContractState>>.toText() = map { it.contract().javaClass.simpleName }.groupBy { it }.map { "${it.key} (${it.value.size})" }.joinToString()
private class TransactionWidget : BorderPane() { private class TransactionWidget : BorderPane() {
@ -248,8 +249,8 @@ class TransactionViewer : CordaView("Transactions") {
outputs.items = transaction.outputs.observable() outputs.items = transaction.outputs.observable()
signatures.children.addAll(signatureData.map { signature -> signatures.children.addAll(signatureData.map { signature ->
val nodeInfo = getModel<NetworkIdentityModel>().lookup(signature) val party = signature.toKnownParty()
copyableLabel(nodeInfo.map { "${signature.toStringShort()} (${it?.legalIdentity?.let { PartyNameFormatter.short.format(it.name)} ?: "???"})" }) copyableLabel(party.map { "${signature.toStringShort()} (${it?.let { PartyNameFormatter.short.format(it.name) } ?: "Anonymous"})" })
}) })
} }
@ -276,7 +277,7 @@ class TransactionViewer : CordaView("Transactions") {
row { row {
label("Issuer :") { gridpaneConstraints { hAlignment = HPos.RIGHT } } label("Issuer :") { gridpaneConstraints { hAlignment = HPos.RIGHT } }
val anonymousIssuer: AbstractParty = data.amount.token.issuer.party 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 // TODO: Anonymous should probably be italicised or similar
label(issuer.nameOrNull()?.let { PartyNameFormatter.short.format(it) } ?: "Anonymous") { label(issuer.nameOrNull()?.let { PartyNameFormatter.short.format(it) } ?: "Anonymous") {
tooltip(anonymousIssuer.owningKey.toBase58String()) tooltip(anonymousIssuer.owningKey.toBase58String())
@ -284,9 +285,8 @@ class TransactionViewer : CordaView("Transactions") {
} }
row { row {
label("Owner :") { gridpaneConstraints { hAlignment = HPos.RIGHT } } label("Owner :") { gridpaneConstraints { hAlignment = HPos.RIGHT } }
val owner = data.owner val owner = data.owner.owningKey.toKnownParty()
val nodeInfo = getModel<NetworkIdentityModel>().lookup(owner.owningKey) label(owner.map { it?.let { PartyNameFormatter.short.format(it.name) } ?: "Anonymous" }) {
label(nodeInfo.map { it?.legalIdentity?.let { PartyNameFormatter.short.format(it.name) } ?: "???" }) {
tooltip(data.owner.owningKey.toBase58String()) tooltip(data.owner.owningKey.toBase58String())
} }
} }
@ -300,27 +300,28 @@ class TransactionViewer : CordaView("Transactions") {
} }
private fun StateAndRef<ContractState>.contract() = this.state.data.contract 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 * 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>>, reportingCurrencyExchange: Pair<Currency, (Amount<Currency>) -> Amount<Currency>>,
inputs: List<ContractState>, inputs: List<ContractState>,
outputs: List<ContractState>): AmountDiff<Currency> { outputs: List<ContractState>): AmountDiff<Currency> {
val (reportingCurrency, exchange) = reportingCurrencyExchange val (reportingCurrency, exchange) = reportingCurrencyExchange
val legalIdentity = identity?.legalIdentity val myLegalIdentity = myIdentity?.legalIdentity
fun List<ContractState>.sum() = this.map { it as? Cash.State } fun List<ContractState>.sum() = this.map { it as? Cash.State }
.filterNotNull() .filterNotNull()
.filter { legalIdentity == it.owner } .filter { it.owner.owningKey.toKnownParty().value == myLegalIdentity }
.map { exchange(it.amount.withoutIssuer()).quantity } .map { exchange(it.amount.withoutIssuer()).quantity }
.sum() .sum()
// For issuing cash, if I am the issuer and not the owner (e.g. issuing cash to other party), count it as negative. // 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 } val issuedAmount = if (inputs.isEmpty()) outputs.map { it as? Cash.State }
.filterNotNull() .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 } .map { exchange(it.amount.withoutIssuer()).quantity }
.sum() else 0 .sum() else 0

View File

@ -205,7 +205,7 @@ class CashViewer : CordaView("Cash") {
/** /**
* Assemble the Issuer node. * 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. * Bind the children in the TreeTable structure.