mirror of
https://github.com/corda/corda.git
synced 2025-06-22 00:57:21 +00:00
Merge remote-tracking branch 'remotes/open/master' into feature/vkolomeyko/os-merge
# Conflicts: # node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SharedContexts.kt # node/src/integration-test/kotlin/net/corda/node/services/RaftNotaryServiceTests.kt # tools/explorer/src/main/kotlin/net/corda/explorer/views/SearchField.kt # tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt
This commit is contained in:
@ -19,17 +19,21 @@ import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.flows.CashConfigDataFlow
|
||||
import tornadofx.*
|
||||
import java.util.*
|
||||
|
||||
class IssuerModel {
|
||||
|
||||
private val defaultCurrency = Currency.getInstance("USD")
|
||||
|
||||
private val proxy by observableValue(NodeMonitorModel::proxyObservable)
|
||||
private val cashAppConfiguration = proxy.map { it?.startFlow(::CashConfigDataFlow)?.returnValue?.getOrThrow() }
|
||||
val supportedCurrencies = ChosenList(cashAppConfiguration.map { it?.supportedCurrencies?.observable() ?: FXCollections.emptyObservableList() })
|
||||
val currencyTypes = ChosenList(cashAppConfiguration.map { it?.issuableCurrencies?.observable() ?: FXCollections.emptyObservableList() })
|
||||
private val cashAppConfiguration = proxy.map { it?.cordaRPCOps?.startFlow(::CashConfigDataFlow)?.returnValue?.getOrThrow() }
|
||||
val supportedCurrencies = ChosenList(cashAppConfiguration.map { it?.supportedCurrencies?.observable() ?: FXCollections.singletonObservableList(defaultCurrency) }, "supportedCurrencies")
|
||||
val currencyTypes = ChosenList(cashAppConfiguration.map { it?.issuableCurrencies?.observable() ?: FXCollections.emptyObservableList() }, "currencyTypes")
|
||||
|
||||
val transactionTypes = ChosenList(cashAppConfiguration.map {
|
||||
if (it?.issuableCurrencies?.isNotEmpty() == true)
|
||||
CashTransaction.values().asList().observable()
|
||||
else
|
||||
listOf(CashTransaction.Pay).observable()
|
||||
})
|
||||
}, "transactionTypes")
|
||||
}
|
||||
|
@ -16,9 +16,7 @@ import javafx.beans.binding.Bindings
|
||||
import javafx.geometry.Insets
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.Parent
|
||||
import javafx.scene.control.ContentDisplay
|
||||
import javafx.scene.control.MenuButton
|
||||
import javafx.scene.control.MenuItem
|
||||
import javafx.scene.control.*
|
||||
import javafx.scene.input.MouseButton
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.StackPane
|
||||
@ -27,10 +25,7 @@ import javafx.scene.text.Font
|
||||
import javafx.scene.text.TextAlignment
|
||||
import javafx.stage.Stage
|
||||
import javafx.stage.WindowEvent
|
||||
import net.corda.client.jfx.model.NetworkIdentityModel
|
||||
import net.corda.client.jfx.model.objectProperty
|
||||
import net.corda.client.jfx.model.observableList
|
||||
import net.corda.client.jfx.model.observableValue
|
||||
import net.corda.client.jfx.model.*
|
||||
import net.corda.client.jfx.utils.ChosenList
|
||||
import net.corda.client.jfx.utils.map
|
||||
import net.corda.explorer.formatters.PartyNameFormatter
|
||||
@ -48,11 +43,14 @@ class MainView : View(WINDOW_TITLE) {
|
||||
private val exit by fxid<MenuItem>()
|
||||
private val sidebar by fxid<VBox>()
|
||||
private val selectionBorderPane by fxid<BorderPane>()
|
||||
private val mainSplitPane by fxid<SplitPane>()
|
||||
private val rpcWarnLabel by fxid<Label>()
|
||||
|
||||
// Inject data.
|
||||
private val myIdentity by observableValue(NetworkIdentityModel::myIdentity)
|
||||
private val selectedView by objectProperty(CordaViewModel::selectedView)
|
||||
private val registeredViews by observableList(CordaViewModel::registeredViews)
|
||||
private val proxy by observableValue(NodeMonitorModel::proxyObservable)
|
||||
|
||||
private val menuItemCSS = "sidebar-menu-item"
|
||||
private val menuItemArrowCSS = "sidebar-menu-item-arrow"
|
||||
@ -69,7 +67,7 @@ class MainView : View(WINDOW_TITLE) {
|
||||
// This needed to be declared val or else it will get GCed and listener unregistered.
|
||||
val buttonStyle = ChosenList(selectedView.map { selected ->
|
||||
if (selected == it) listOf(menuItemCSS, menuItemSelectedCSS).observable() else listOf(menuItemCSS).observable()
|
||||
})
|
||||
}, "buttonStyle")
|
||||
stackpane {
|
||||
button(it.title) {
|
||||
graphic = FontAwesomeIconView(it.icon).apply {
|
||||
@ -103,5 +101,9 @@ class MainView : View(WINDOW_TITLE) {
|
||||
Bindings.bindContent(sidebar.children, menuItems)
|
||||
// Main view
|
||||
selectionBorderPane.centerProperty().bind(selectedView.map { it?.root })
|
||||
// Trigger depending on RPC connectivity status.
|
||||
val proxyNotAvailable = proxy.map { it == null }
|
||||
mainSplitPane.disableProperty().bind(proxyNotAvailable)
|
||||
rpcWarnLabel.visibleProperty().bind(proxyNotAvailable)
|
||||
}
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ class SearchField<T>(private val data: ObservableList<T>, vararg filterCriteria:
|
||||
filterCriteria.toMap()[category]?.invoke(data, text) == true
|
||||
}
|
||||
}
|
||||
}, arrayOf<Observable>(textField.textProperty(), searchCategory.valueProperty(), textField.visibleProperty())))
|
||||
}, arrayOf<Observable>(textField.textProperty(), searchCategory.valueProperty(), textField.visibleProperty())), "filteredData")
|
||||
|
||||
init {
|
||||
clearButton.setOnMouseClicked { event: MouseEvent ->
|
||||
|
@ -12,6 +12,7 @@ package net.corda.explorer.views
|
||||
|
||||
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.beans.binding.ObjectBinding
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.geometry.HPos
|
||||
@ -27,17 +28,13 @@ import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.Pane
|
||||
import javafx.scene.layout.VBox
|
||||
import net.corda.client.jfx.model.*
|
||||
import net.corda.client.jfx.utils.filterNotNull
|
||||
import net.corda.client.jfx.utils.lift
|
||||
import net.corda.client.jfx.utils.map
|
||||
import net.corda.client.jfx.utils.sequence
|
||||
import net.corda.client.jfx.utils.*
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.utilities.toBase58String
|
||||
import net.corda.sample.businessnetwork.iou.IOUState
|
||||
@ -74,7 +71,7 @@ class TransactionViewer : CordaView("Transactions") {
|
||||
|
||||
private var scrollPosition: Int = 0
|
||||
private lateinit var expander: ExpanderColumn<TransactionViewer.Transaction>
|
||||
var txIdToScroll: SecureHash? = null // Passed as param.
|
||||
private var txIdToScroll: SecureHash? = null // Passed as param.
|
||||
|
||||
/**
|
||||
* This is what holds data for a single transaction node. Note how a lot of these are nullable as we often simply don't
|
||||
@ -149,7 +146,7 @@ class TransactionViewer : CordaView("Transactions") {
|
||||
resolvedInputs.map { it.state.data }.lift(),
|
||||
resolvedOutputs.map { it.state.data }.lift())
|
||||
)
|
||||
}
|
||||
}.distinctBy { it.id }
|
||||
|
||||
val searchField = SearchField(transactions,
|
||||
"Transaction ID" to { tx, s -> "${tx.id}".contains(s, true) },
|
||||
@ -206,7 +203,7 @@ class TransactionViewer : CordaView("Transactions") {
|
||||
}
|
||||
}
|
||||
}
|
||||
column("Command type", Transaction::commandTypes).cellFormat { text = it.map { it.simpleName }.joinToString() }
|
||||
column("Command type", Transaction::commandTypes).cellFormat { text = it.joinToString { it.simpleName } }
|
||||
column("Total value", Transaction::totalValueEquiv).cellFormat {
|
||||
text = "${it.positivity.sign}${AmountFormatter.boring.format(it.amount)}"
|
||||
titleProperty.bind(reportingCurrency.map { "Total value ($it equiv)" })
|
||||
@ -228,9 +225,9 @@ class TransactionViewer : CordaView("Transactions") {
|
||||
}
|
||||
|
||||
private fun ObservableList<List<ObservableValue<Party?>>>.formatJoinPartyNames(separator: String = ",", formatter: Formatter<CordaX500Name>): String {
|
||||
return flatten().map {
|
||||
return flatten().mapNotNull {
|
||||
it.value?.let { formatter.format(it.name) }
|
||||
}.filterNotNull().toSet().joinToString(separator)
|
||||
}.toSet().joinToString(separator)
|
||||
}
|
||||
|
||||
private fun ObservableList<StateAndRef<ContractState>>.getParties() = map { it.state.data.participants.map { it.owningKey.toKnownParty() } }
|
||||
@ -244,8 +241,17 @@ class TransactionViewer : CordaView("Transactions") {
|
||||
init {
|
||||
right {
|
||||
label {
|
||||
val hash = SecureHash.randomSHA256()
|
||||
graphic = identicon(hash, 30.0)
|
||||
val hashList = partiallyResolvedTransactions.map { it.id }
|
||||
val hashBinding = object : ObjectBinding<SecureHash>() {
|
||||
init {
|
||||
bind(hashList)
|
||||
}
|
||||
override fun computeValue(): SecureHash {
|
||||
return if (hashList.isEmpty()) SecureHash.zeroHash
|
||||
else hashList.fold(hashList[0], { one, another -> one.hashConcat(another) })
|
||||
}
|
||||
}
|
||||
graphicProperty().bind(hashBinding.map { identicon(it, 30.0) })
|
||||
textProperty().bind(Bindings.size(partiallyResolvedTransactions).map(Number::toString))
|
||||
BorderPane.setAlignment(this, Pos.BOTTOM_RIGHT)
|
||||
}
|
||||
@ -367,8 +373,7 @@ private fun calculateTotalEquiv(myIdentity: Party?,
|
||||
}
|
||||
|
||||
// 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()
|
||||
val issuedAmount = if (inputs.isEmpty()) outputs.mapNotNull { it as? Cash.State }
|
||||
.filter { it.amount.token.issuer.party.owningKey.toKnownParty().value == myIdentity && it.owner.owningKey.toKnownParty().value != myIdentity }
|
||||
.map { exchange(it.amount.withoutIssuer()).quantity }
|
||||
.sum() else 0
|
||||
|
@ -87,7 +87,7 @@ class CashViewer : CordaView("Cash") {
|
||||
null -> FXCollections.observableArrayList(leftPane)
|
||||
else -> FXCollections.observableArrayList(leftPane, rightPane)
|
||||
}
|
||||
})
|
||||
}, "CashViewerSplitPane")
|
||||
|
||||
/**
|
||||
* This holds the data for each row in the TreeTable.
|
||||
|
@ -94,7 +94,7 @@ class NewTransaction : Fragment() {
|
||||
CashTransaction.Exit -> currencyTypes
|
||||
else -> FXCollections.emptyObservableList()
|
||||
}
|
||||
})
|
||||
}, "NewTransactionCurrencyItems")
|
||||
|
||||
fun show(window: Window) {
|
||||
newTransactionDialog(window).showAndWait().ifPresent { request ->
|
||||
@ -106,9 +106,9 @@ class NewTransaction : Fragment() {
|
||||
show()
|
||||
}
|
||||
val handle: FlowHandle<AbstractCashFlow.Result> = when (request) {
|
||||
is IssueAndPaymentRequest -> rpcProxy.value!!.startFlow(::CashIssueAndPaymentFlow, request)
|
||||
is PaymentRequest -> rpcProxy.value!!.startFlow(::CashPaymentFlow, request)
|
||||
is ExitRequest -> rpcProxy.value!!.startFlow(::CashExitFlow, request)
|
||||
is IssueAndPaymentRequest -> rpcProxy.value!!.cordaRPCOps.startFlow(::CashIssueAndPaymentFlow, request)
|
||||
is PaymentRequest -> rpcProxy.value!!.cordaRPCOps.startFlow(::CashPaymentFlow, request)
|
||||
is ExitRequest -> rpcProxy.value!!.cordaRPCOps.startFlow(::CashExitFlow, request)
|
||||
else -> throw IllegalArgumentException("Unexpected request type: $request")
|
||||
}
|
||||
runAsync {
|
||||
|
@ -33,4 +33,12 @@
|
||||
|
||||
.corda-text-logo {
|
||||
-fx-image: url("../images/Logo-04.png");
|
||||
}
|
||||
|
||||
.warning-label {
|
||||
-fx-text-fill: red;
|
||||
-fx-font-size: 14;
|
||||
-fx-font-family: 'sans-serif';
|
||||
-fx-font-weight: bold;
|
||||
-fx-label-padding: 5;
|
||||
}
|
@ -23,6 +23,9 @@
|
||||
<ImageView styleClass="corda-text-logo" fitHeight="35" preserveRatio="true" GridPane.hgrow="ALWAYS"
|
||||
fx:id="cordaLogo"/>
|
||||
|
||||
<!-- Normally hidden warning label -->
|
||||
<Label fx:id="rpcWarnLabel" styleClass="warning-label" text="Status: RPC connection not available" GridPane.columnIndex="1" visible="true"/>
|
||||
|
||||
<!-- User account menu -->
|
||||
<MenuButton fx:id="userButton" mnemonicParsing="false" GridPane.columnIndex="3">
|
||||
<items>
|
||||
@ -35,7 +38,7 @@
|
||||
</GridPane>
|
||||
</top>
|
||||
<center>
|
||||
<SplitPane id="mainSplitPane" dividerPositions="0.0">
|
||||
<SplitPane fx:id="mainSplitPane" dividerPositions="0.0">
|
||||
<VBox styleClass="sidebar" fx:id="sidebar" SplitPane.resizableWithParent="false">
|
||||
<StackPane>
|
||||
<Button fx:id="template" text="Template" styleClass="sidebar-menu-item"/>
|
||||
|
Reference in New Issue
Block a user