Merge pull request #558 from corda/chrisr3-os-merge

Merge from OS
This commit is contained in:
Chris Rankin 2018-03-15 10:06:36 +00:00 committed by GitHub
commit 5360709917
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 108 additions and 37 deletions

View File

@ -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

View File

@ -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)
} }
} }

View File

@ -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.

View File

@ -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.

View File

@ -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()