mirror of
https://github.com/corda/corda.git
synced 2025-01-15 01:10:33 +00:00
Merge commit 'a64474181971e79638d6c23cb677a91a6c0f1bb5' into chrisr3-os-merge
This commit is contained in:
commit
d0efe85a44
@ -98,14 +98,13 @@ class NodeMonitorModel {
|
|||||||
stateMachineUpdates.startWith(currentStateMachines).subscribe(stateMachineUpdatesSubject)
|
stateMachineUpdates.startWith(currentStateMachines).subscribe(stateMachineUpdatesSubject)
|
||||||
|
|
||||||
// Vault snapshot (force single page load with MAX_PAGE_SIZE) + updates
|
// Vault snapshot (force single page load with MAX_PAGE_SIZE) + updates
|
||||||
val (_, vaultUpdates) = proxy.vaultTrackBy<ContractState>(QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL),
|
val (statesSnapshot, vaultUpdates) = proxy.vaultTrackBy<ContractState>(QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL),
|
||||||
PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE))
|
PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE))
|
||||||
|
val unconsumedStates = statesSnapshot.states.filterIndexed { index, _ ->
|
||||||
val vaultSnapshot = proxy.vaultQueryBy<ContractState>(QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED),
|
statesSnapshot.statesMetadata[index].status == Vault.StateStatus.UNCONSUMED
|
||||||
PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE))
|
}.toSet()
|
||||||
// We have to fetch the snapshot separately since vault query API doesn't allow different criteria for snapshot and updates.
|
val consumedStates = statesSnapshot.states.toSet() - unconsumedStates
|
||||||
// TODO : This will create a small window of opportunity for inconsistent updates, might need to change the vault API to handle this case.
|
val initialVaultUpdate = Vault.Update(consumedStates, unconsumedStates)
|
||||||
val initialVaultUpdate = Vault.Update(setOf(), vaultSnapshot.states.toSet())
|
|
||||||
vaultUpdates.startWith(initialVaultUpdate).subscribe(vaultUpdatesSubject)
|
vaultUpdates.startWith(initialVaultUpdate).subscribe(vaultUpdatesSubject)
|
||||||
|
|
||||||
// Transactions
|
// Transactions
|
||||||
|
@ -11,13 +11,14 @@
|
|||||||
package net.corda.client.jfx.model
|
package net.corda.client.jfx.model
|
||||||
|
|
||||||
import javafx.beans.value.ObservableValue
|
import javafx.beans.value.ObservableValue
|
||||||
|
import javafx.collections.FXCollections
|
||||||
import javafx.collections.ObservableMap
|
import javafx.collections.ObservableMap
|
||||||
import net.corda.client.jfx.utils.*
|
import net.corda.client.jfx.utils.*
|
||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
|
import net.corda.core.transactions.WireTransaction
|
||||||
import org.fxmisc.easybind.EasyBind
|
import org.fxmisc.easybind.EasyBind
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,7 +28,8 @@ import org.fxmisc.easybind.EasyBind
|
|||||||
*/
|
*/
|
||||||
data class PartiallyResolvedTransaction(
|
data class PartiallyResolvedTransaction(
|
||||||
val transaction: SignedTransaction,
|
val transaction: SignedTransaction,
|
||||||
val inputs: List<ObservableValue<InputResolution>>) {
|
val inputs: List<ObservableValue<InputResolution>>,
|
||||||
|
val outputs: List<ObservableValue<out OutputResolution>>) {
|
||||||
val id = transaction.id
|
val id = transaction.id
|
||||||
|
|
||||||
sealed class InputResolution {
|
sealed class InputResolution {
|
||||||
@ -39,21 +41,49 @@ data class PartiallyResolvedTransaction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class OutputResolution {
|
||||||
|
abstract val stateRef: StateRef
|
||||||
|
|
||||||
|
data class Unresolved(override val stateRef: StateRef) : OutputResolution()
|
||||||
|
data class Resolved(val stateAndRef: StateAndRef<ContractState>) : OutputResolution() {
|
||||||
|
override val stateRef: StateRef get() = stateAndRef.ref
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromSignedTransaction(
|
fun fromSignedTransaction(
|
||||||
transaction: SignedTransaction,
|
transaction: SignedTransaction,
|
||||||
transactions: ObservableMap<SecureHash, SignedTransaction>
|
stateMap: ObservableMap<StateRef, StateAndRef<ContractState>>
|
||||||
) = PartiallyResolvedTransaction(
|
) = PartiallyResolvedTransaction(
|
||||||
transaction = transaction,
|
transaction = transaction,
|
||||||
inputs = transaction.tx.inputs.map { stateRef ->
|
inputs = transaction.inputs.map { stateRef ->
|
||||||
EasyBind.map(transactions.getObservableValue(stateRef.txhash)) {
|
EasyBind.map(stateMap.getObservableValue(stateRef)) {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
InputResolution.Unresolved(stateRef)
|
InputResolution.Unresolved(stateRef)
|
||||||
} else {
|
} else {
|
||||||
InputResolution.Resolved(it.tx.outRef(stateRef.index))
|
InputResolution.Resolved(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outputs = if (transaction.coreTransaction is WireTransaction) {
|
||||||
|
transaction.tx.outRefsOfType<ContractState>().map {
|
||||||
|
OutputResolution.Resolved(it).lift()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Transaction will have the same number of outputs as inputs
|
||||||
|
val outputCount = transaction.coreTransaction.inputs.size
|
||||||
|
val stateRefs = (0 until outputCount).map { StateRef(transaction.id, it) }
|
||||||
|
stateRefs.map { stateRef ->
|
||||||
|
EasyBind.map(stateMap.getObservableValue(stateRef)) {
|
||||||
|
if (it == null) {
|
||||||
|
OutputResolution.Unresolved(stateRef)
|
||||||
|
} else {
|
||||||
|
OutputResolution.Resolved(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,9 +94,13 @@ data class PartiallyResolvedTransaction(
|
|||||||
class TransactionDataModel {
|
class TransactionDataModel {
|
||||||
private val transactions by observable(NodeMonitorModel::transactions)
|
private val transactions by observable(NodeMonitorModel::transactions)
|
||||||
private val collectedTransactions = transactions.recordInSequence()
|
private val collectedTransactions = transactions.recordInSequence()
|
||||||
private val transactionMap = transactions.recordAsAssociation(SignedTransaction::id)
|
private val vaultUpdates by observable(NodeMonitorModel::vaultUpdates)
|
||||||
|
private val stateMap = vaultUpdates.fold(FXCollections.observableHashMap<StateRef, StateAndRef<ContractState>>()) { map, update ->
|
||||||
|
val states = update.consumed + update.produced
|
||||||
|
states.forEach { map[it.ref] = it }
|
||||||
|
}
|
||||||
|
|
||||||
val partiallyResolvedTransactions = collectedTransactions.map {
|
val partiallyResolvedTransactions = collectedTransactions.map {
|
||||||
PartiallyResolvedTransaction.fromSignedTransaction(it, transactionMap)
|
PartiallyResolvedTransaction.fromSignedTransaction(it, stateMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,13 +96,31 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A SHA-256 hash value consisting of 32 0x00 bytes.
|
* A SHA-256 hash value consisting of 32 0x00 bytes.
|
||||||
|
* This field provides more intuitive access from Java.
|
||||||
*/
|
*/
|
||||||
val zeroHash = SecureHash.SHA256(ByteArray(32, { 0.toByte() }))
|
@JvmField
|
||||||
|
val zeroHash: SHA256 = SecureHash.SHA256(ByteArray(32, { 0.toByte() }))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A SHA-256 hash value consisting of 32 0x00 bytes.
|
||||||
|
* This function is provided for API stability.
|
||||||
|
*/
|
||||||
|
@Suppress("Unused")
|
||||||
|
fun getZeroHash(): SHA256 = zeroHash
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SHA-256 hash value consisting of 32 0xFF bytes.
|
* A SHA-256 hash value consisting of 32 0xFF bytes.
|
||||||
|
* This field provides more intuitive access from Java.
|
||||||
*/
|
*/
|
||||||
val allOnesHash = SecureHash.SHA256(ByteArray(32, { 255.toByte() }))
|
@JvmField
|
||||||
|
val allOnesHash: SHA256 = SecureHash.SHA256(ByteArray(32, { 255.toByte() }))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A SHA-256 hash value consisting of 32 0xFF bytes.
|
||||||
|
* This function is provided for API stability.
|
||||||
|
*/
|
||||||
|
@Suppress("Unused")
|
||||||
|
fun getAllOnesHash(): SHA256 = allOnesHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// In future, maybe SHA3, truncated hashes etc.
|
// In future, maybe SHA3, truncated hashes etc.
|
||||||
|
@ -40,6 +40,7 @@ import net.corda.client.jfx.utils.*
|
|||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.utilities.toBase58String
|
import net.corda.core.utilities.toBase58String
|
||||||
import net.corda.explorer.formatters.PartyNameFormatter
|
import net.corda.explorer.formatters.PartyNameFormatter
|
||||||
import net.corda.explorer.model.CordaView
|
import net.corda.explorer.model.CordaView
|
||||||
@ -91,7 +92,11 @@ class Network : CordaView() {
|
|||||||
.map { it as? PartiallyResolvedTransaction.InputResolution.Resolved }
|
.map { it as? PartiallyResolvedTransaction.InputResolution.Resolved }
|
||||||
.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.coreTransaction.let {
|
||||||
|
if (it is WireTransaction) it.outputStates.observable().getParties()
|
||||||
|
// For ContractUpgradeWireTransaction and NotaryChangeWireTransaction the output parties are the same as input parties
|
||||||
|
else inputParties
|
||||||
|
}
|
||||||
val signingParties = it.transaction.sigs.map { it.by.toKnownParty() }
|
val signingParties = it.transaction.sigs.map { it.by.toKnownParty() }
|
||||||
// Input parties fire a bullets to all output parties, then to the signing parties and then signing parties to output parties.
|
// Input parties fire a bullets to all output parties, then to the signing parties and then signing parties to output parties.
|
||||||
// !! This is a rough guess of how the message moves in the network.
|
// !! This is a rough guess of how the message moves in the network.
|
||||||
|
@ -37,6 +37,8 @@ import net.corda.core.crypto.toStringShort
|
|||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
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.core.utilities.toBase58String
|
||||||
import net.corda.sample.businessnetwork.iou.IOUState
|
import net.corda.sample.businessnetwork.iou.IOUState
|
||||||
import net.corda.explorer.AmountDiff
|
import net.corda.explorer.AmountDiff
|
||||||
@ -82,7 +84,7 @@ class TransactionViewer : CordaView("Transactions") {
|
|||||||
val tx: PartiallyResolvedTransaction,
|
val tx: PartiallyResolvedTransaction,
|
||||||
val id: SecureHash,
|
val id: SecureHash,
|
||||||
val inputs: Inputs,
|
val inputs: Inputs,
|
||||||
val outputs: ObservableList<StateAndRef<ContractState>>,
|
val outputs: Outputs,
|
||||||
val inputParties: ObservableList<List<ObservableValue<Party?>>>,
|
val inputParties: ObservableList<List<ObservableValue<Party?>>>,
|
||||||
val outputParties: ObservableList<List<ObservableValue<Party?>>>,
|
val outputParties: ObservableList<List<ObservableValue<Party?>>>,
|
||||||
val commandTypes: List<Class<CommandData>>,
|
val commandTypes: List<Class<CommandData>>,
|
||||||
@ -90,6 +92,7 @@ class TransactionViewer : CordaView("Transactions") {
|
|||||||
)
|
)
|
||||||
|
|
||||||
data class Inputs(val resolved: ObservableList<StateAndRef<ContractState>>, val unresolved: ObservableList<StateRef>)
|
data class Inputs(val resolved: ObservableList<StateAndRef<ContractState>>, val unresolved: ObservableList<StateRef>)
|
||||||
|
data class Outputs(val resolved: ObservableList<StateAndRef<ContractState>>, val unresolved: ObservableList<StateRef>)
|
||||||
|
|
||||||
override fun onDock() {
|
override fun onDock() {
|
||||||
txIdToScroll?.let {
|
txIdToScroll?.let {
|
||||||
@ -116,38 +119,42 @@ class TransactionViewer : CordaView("Transactions") {
|
|||||||
*/
|
*/
|
||||||
init {
|
init {
|
||||||
val transactions = transactions.map {
|
val transactions = transactions.map {
|
||||||
val resolved = it.inputs.sequence()
|
val resolvedInputs = it.inputs.sequence()
|
||||||
.map { it as? PartiallyResolvedTransaction.InputResolution.Resolved }
|
.map { it as? PartiallyResolvedTransaction.InputResolution.Resolved }
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.map { it.stateAndRef }
|
.map { it.stateAndRef }
|
||||||
val unresolved = it.inputs.sequence()
|
val unresolvedInputs = it.inputs.sequence()
|
||||||
.map { it as? PartiallyResolvedTransaction.InputResolution.Unresolved }
|
.map { it as? PartiallyResolvedTransaction.InputResolution.Unresolved }
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.map { it.stateRef }
|
.map { it.stateRef }
|
||||||
val outputs = it.transaction.tx.outputs
|
val resolvedOutputs = it.outputs.sequence()
|
||||||
.mapIndexed { index, transactionState ->
|
.map { it as? PartiallyResolvedTransaction.OutputResolution.Resolved }
|
||||||
val stateRef = StateRef(it.id, index)
|
.filterNotNull()
|
||||||
StateAndRef(transactionState, stateRef)
|
.map { it.stateAndRef }
|
||||||
}.observable()
|
val unresolvedOutputs = it.inputs.sequence()
|
||||||
|
.map { it as? PartiallyResolvedTransaction.InputResolution.Unresolved }
|
||||||
|
.filterNotNull()
|
||||||
|
.map { it.stateRef }
|
||||||
|
val commands = if (it.transaction.coreTransaction is WireTransaction) it.transaction.tx.commands else emptyList()
|
||||||
Transaction(
|
Transaction(
|
||||||
tx = it,
|
tx = it,
|
||||||
id = it.id,
|
id = it.id,
|
||||||
inputs = Inputs(resolved, unresolved),
|
inputs = Inputs(resolvedInputs, unresolvedInputs),
|
||||||
outputs = outputs,
|
outputs = Outputs(resolvedOutputs, unresolvedOutputs),
|
||||||
inputParties = resolved.getParties(),
|
inputParties = resolvedInputs.getParties(),
|
||||||
outputParties = outputs.getParties(),
|
outputParties = resolvedOutputs.getParties(),
|
||||||
commandTypes = it.transaction.tx.commands.map { it.value.javaClass },
|
commandTypes = commands.map { it.value.javaClass },
|
||||||
totalValueEquiv = ::calculateTotalEquiv.lift(myIdentity,
|
totalValueEquiv = ::calculateTotalEquiv.lift(myIdentity,
|
||||||
reportingExchange,
|
reportingExchange,
|
||||||
resolved.map { it.state.data }.lift(),
|
resolvedInputs.map { it.state.data }.lift(),
|
||||||
it.transaction.tx.outputStates.lift())
|
resolvedOutputs.map { it.state.data }.lift())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val searchField = SearchField(transactions,
|
val searchField = SearchField(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.contract.contains(s, true) } },
|
"Input" to { tx, s -> tx.inputs.resolved.any { it.state.contract.contains(s, true) } },
|
||||||
"Output" to { tx, s -> tx.outputs.any { it.state.contract.contains(s, true) } },
|
"Output" to { tx, s -> tx.outputs.resolved.any { it.state.contract.contains(s, true) } },
|
||||||
"Input Party" to { tx, s -> tx.inputParties.any { it.any { it.value?.name?.organisation?.contains(s, true) == true } } },
|
"Input Party" to { tx, s -> tx.inputParties.any { it.any { it.value?.name?.organisation?.contains(s, true) == true } } },
|
||||||
"Output Party" to { tx, s -> tx.outputParties.any { it.any { it.value?.name?.organisation?.contains(s, true) == true } } },
|
"Output Party" to { tx, s -> tx.outputParties.any { it.any { it.value?.name?.organisation?.contains(s, true) == true } } },
|
||||||
"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) } }
|
||||||
@ -174,7 +181,15 @@ class TransactionViewer : CordaView("Transactions") {
|
|||||||
text += "Unresolved(${it.unresolved.size})"
|
text += "Unresolved(${it.unresolved.size})"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
column("Output", Transaction::outputs).cellFormat { text = it.toText() }
|
column("Output", Transaction::outputs).cellFormat {
|
||||||
|
text = it.resolved.toText()
|
||||||
|
if (!it.unresolved.isEmpty()) {
|
||||||
|
if (!text.isBlank()) {
|
||||||
|
text += ", "
|
||||||
|
}
|
||||||
|
text += "Unresolved(${it.unresolved.size})"
|
||||||
|
}
|
||||||
|
}
|
||||||
column("Input Party", Transaction::inputParties).setCustomCellFactory {
|
column("Input Party", Transaction::inputParties).setCustomCellFactory {
|
||||||
label {
|
label {
|
||||||
text = it.formatJoinPartyNames(formatter = PartyNameFormatter.short)
|
text = it.formatJoinPartyNames(formatter = PartyNameFormatter.short)
|
||||||
@ -251,14 +266,14 @@ class TransactionViewer : CordaView("Transactions") {
|
|||||||
val signatureData = transaction.tx.transaction.sigs.map { it.by }
|
val signatureData = transaction.tx.transaction.sigs.map { it.by }
|
||||||
// Bind count to TitlePane
|
// Bind count to TitlePane
|
||||||
inputPane.text = "Input (${transaction.inputs.resolved.count()})"
|
inputPane.text = "Input (${transaction.inputs.resolved.count()})"
|
||||||
outputPane.text = "Output (${transaction.outputs.count()})"
|
outputPane.text = "Output (${transaction.outputs.resolved.count()})"
|
||||||
signaturesPane.text = "Signatures (${signatureData.count()})"
|
signaturesPane.text = "Signatures (${signatureData.count()})"
|
||||||
|
|
||||||
inputs.cellCache { getCell(it) }
|
inputs.cellCache { getCell(it) }
|
||||||
outputs.cellCache { getCell(it) }
|
outputs.cellCache { getCell(it) }
|
||||||
|
|
||||||
inputs.items = transaction.inputs.resolved
|
inputs.items = transaction.inputs.resolved
|
||||||
outputs.items = transaction.outputs.observable()
|
outputs.items = transaction.outputs.resolved
|
||||||
|
|
||||||
signatures.children.addAll(signatureData.map { signature ->
|
signatures.children.addAll(signatureData.map { signature ->
|
||||||
val party = signature.toKnownParty()
|
val party = signature.toKnownParty()
|
||||||
|
Loading…
Reference in New Issue
Block a user