mirror of
https://github.com/corda/corda.git
synced 2025-01-15 17:30:02 +00:00
Wave 3 of Business Network changes. (#193)
* R3NET-546: Re-arrange independent flows into separate packages. Functionally this is a NOP change. * R3NET-546: Start BNO as a separate Corda node and improve GUI experience for IOU. * R3NET-546: Move all the membership checks to the Business Network Owner node side, creating "InitiatedBy" flows as necessary. * R3NET-546: Make MembershipViolationException AMQP serializable. * R3NET-546: Improve GUI error reporting in case of membership violation. * R3NET-546: Code changes following review by: @shamsasari * R3NET-546: Code changes following review by: @shamsasari * R3NET-546: Added a dedicated InvalidMembershipListNameException.
This commit is contained in:
parent
246142173d
commit
c0e997c1dd
@ -1,4 +1,4 @@
|
|||||||
package net.corda.sample.businessnetwork
|
package net.corda.sample.businessnetwork.iou
|
||||||
|
|
||||||
import net.corda.core.contracts.CommandData
|
import net.corda.core.contracts.CommandData
|
||||||
import net.corda.core.contracts.Contract
|
import net.corda.core.contracts.Contract
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.sample.businessnetwork
|
package net.corda.sample.businessnetwork.iou
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.contracts.Command
|
import net.corda.core.contracts.Command
|
||||||
@ -9,19 +9,19 @@ import net.corda.core.identity.Party
|
|||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.sample.businessnetwork.membership.MembershipAware
|
import net.corda.sample.businessnetwork.membership.flow.CheckMembershipFlow
|
||||||
import net.corda.sample.businessnetwork.membership.CheckMembershipFlow
|
import net.corda.sample.businessnetwork.membership.flow.CheckMembershipResult
|
||||||
import net.corda.sample.businessnetwork.membership.CheckMembershipResult
|
|
||||||
import kotlin.reflect.jvm.jvmName
|
import kotlin.reflect.jvm.jvmName
|
||||||
|
|
||||||
@InitiatingFlow
|
@InitiatingFlow
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class IOUFlow(val iouValue: Int,
|
class IOUFlow(val iouValue: Int,
|
||||||
val otherParty: Party) : FlowLogic<SignedTransaction>(), MembershipAware {
|
val otherParty: Party) : FlowLogic<SignedTransaction>() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
// TODO: Derive membership name from CorDapp config.
|
||||||
val allowedMembershipName =
|
val allowedMembershipName =
|
||||||
CordaX500Name("AliceBobMembershipList", "AliceBob", "Washington", "US")
|
CordaX500Name("AliceBobMembershipList", "Oslo", "NO")
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The progress tracker provides checkpoints indicating the progress of the flow to observers. */
|
/** The progress tracker provides checkpoints indicating the progress of the flow to observers. */
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.sample.businessnetwork
|
package net.corda.sample.businessnetwork.iou
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.contracts.requireThat
|
import net.corda.core.contracts.requireThat
|
||||||
@ -7,14 +7,14 @@ import net.corda.core.flows.FlowSession
|
|||||||
import net.corda.core.flows.InitiatedBy
|
import net.corda.core.flows.InitiatedBy
|
||||||
import net.corda.core.flows.SignTransactionFlow
|
import net.corda.core.flows.SignTransactionFlow
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.sample.businessnetwork.membership.MembershipAware
|
import net.corda.sample.businessnetwork.membership.flow.CheckMembershipFlow
|
||||||
|
import net.corda.sample.businessnetwork.membership.flow.CheckMembershipResult
|
||||||
|
|
||||||
@InitiatedBy(IOUFlow::class)
|
@InitiatedBy(IOUFlow::class)
|
||||||
class IOUFlowResponder(val otherPartySession: FlowSession) : FlowLogic<Unit>(), MembershipAware {
|
class IOUFlowResponder(val otherPartySession: FlowSession) : FlowLogic<Unit>() {
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call() {
|
override fun call() {
|
||||||
|
check(subFlow(CheckMembershipFlow(otherPartySession.counterparty, IOUFlow.allowedMembershipName)) == CheckMembershipResult.PASS)
|
||||||
otherPartySession.counterparty.checkMembership(IOUFlow.allowedMembershipName, this)
|
|
||||||
|
|
||||||
subFlow(object : SignTransactionFlow(otherPartySession, SignTransactionFlow.tracker()) {
|
subFlow(object : SignTransactionFlow(otherPartySession, SignTransactionFlow.tracker()) {
|
||||||
override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
override fun checkTransaction(stx: SignedTransaction) = requireThat {
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.sample.businessnetwork
|
package net.corda.sample.businessnetwork.iou
|
||||||
|
|
||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
@ -1,38 +0,0 @@
|
|||||||
package net.corda.sample.businessnetwork.membership
|
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
|
||||||
import net.corda.core.flows.FlowLogic
|
|
||||||
import net.corda.core.flows.FlowSession
|
|
||||||
import net.corda.core.flows.InitiatedBy
|
|
||||||
import net.corda.core.flows.InitiatingFlow
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.serialization.CordaSerializable
|
|
||||||
import net.corda.core.utilities.unwrap
|
|
||||||
|
|
||||||
@CordaSerializable
|
|
||||||
enum class CheckMembershipResult {
|
|
||||||
PASS,
|
|
||||||
FAIL
|
|
||||||
}
|
|
||||||
|
|
||||||
@InitiatingFlow
|
|
||||||
class CheckMembershipFlow(private val otherParty: Party, private val membershipName: CordaX500Name) : FlowLogic<CheckMembershipResult>(), MembershipAware {
|
|
||||||
@Suspendable
|
|
||||||
override fun call(): CheckMembershipResult {
|
|
||||||
otherParty.checkMembership(membershipName, this)
|
|
||||||
// This will trigger CounterpartyCheckMembershipFlow
|
|
||||||
val untrustworthyData = initiateFlow(otherParty).sendAndReceive<CheckMembershipResult>(membershipName)
|
|
||||||
return untrustworthyData.unwrap { it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@InitiatedBy(CheckMembershipFlow::class)
|
|
||||||
class CounterpartyCheckMembershipFlow(private val otherPartySession: FlowSession) : FlowLogic<Unit>(), MembershipAware {
|
|
||||||
@Suspendable
|
|
||||||
override fun call() {
|
|
||||||
val membershipName = otherPartySession.receive<CordaX500Name>().unwrap { it }
|
|
||||||
otherPartySession.counterparty.checkMembership(membershipName, this)
|
|
||||||
otherPartySession.send(CheckMembershipResult.PASS)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
package net.corda.sample.businessnetwork.membership
|
|
||||||
|
|
||||||
import net.corda.core.flows.FlowException
|
|
||||||
import net.corda.core.flows.FlowLogic
|
|
||||||
import net.corda.core.identity.AbstractParty
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
import net.corda.core.node.ServiceHub
|
|
||||||
import net.corda.sample.businessnetwork.membership.internal.MembershipListProvider
|
|
||||||
|
|
||||||
interface MembershipAware {
|
|
||||||
/**
|
|
||||||
* Checks that party has at least one common membership list with current node.
|
|
||||||
* TODO: This functionality ought to be moved into a dedicated CordaService.
|
|
||||||
*/
|
|
||||||
fun <T> AbstractParty.checkMembership(membershipName: CordaX500Name, initiatorFlow: FlowLogic<T>) {
|
|
||||||
val membershipList = getMembershipList(membershipName, initiatorFlow.serviceHub)
|
|
||||||
if (this !in membershipList) {
|
|
||||||
val msg = "'$this' doesn't belong to membership list: ${membershipName.commonName}"
|
|
||||||
throw MembershipViolationException(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getMembershipList(listName: CordaX500Name, serviceHub: ServiceHub): MembershipList = MembershipListProvider.obtainMembershipList(listName, serviceHub.networkMapCache)
|
|
||||||
}
|
|
||||||
|
|
||||||
class MembershipViolationException(msg: String) : FlowException(msg)
|
|
@ -1,16 +0,0 @@
|
|||||||
package net.corda.sample.businessnetwork.membership
|
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
|
||||||
import net.corda.core.flows.FlowLogic
|
|
||||||
import net.corda.core.flows.StartableByRPC
|
|
||||||
import net.corda.core.identity.AbstractParty
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flow to obtain content of the membership lists this node belongs to.
|
|
||||||
*/
|
|
||||||
@StartableByRPC
|
|
||||||
class ObtainMembershipListContentFlow(private val membershipListName: CordaX500Name) : FlowLogic<Set<AbstractParty>>(), MembershipAware {
|
|
||||||
@Suspendable
|
|
||||||
override fun call(): Set<AbstractParty> = getMembershipList(membershipListName, serviceHub).content()
|
|
||||||
}
|
|
@ -0,0 +1,42 @@
|
|||||||
|
package net.corda.sample.businessnetwork.membership.flow
|
||||||
|
|
||||||
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.flows.FlowSession
|
||||||
|
import net.corda.core.flows.InitiatedBy
|
||||||
|
import net.corda.core.flows.InitiatingFlow
|
||||||
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.serialization.CordaSerializable
|
||||||
|
import net.corda.core.utilities.unwrap
|
||||||
|
|
||||||
|
@CordaSerializable
|
||||||
|
enum class CheckMembershipResult {
|
||||||
|
PASS,
|
||||||
|
FAIL
|
||||||
|
}
|
||||||
|
|
||||||
|
@InitiatingFlow
|
||||||
|
class CheckMembershipFlow(private val otherParty: Party, private val membershipName: CordaX500Name) : FlowLogic<CheckMembershipResult>() {
|
||||||
|
@Suspendable
|
||||||
|
override fun call(): CheckMembershipResult {
|
||||||
|
val bnoParty = serviceHub.networkMapCache.getPeerByLegalName(membershipName)
|
||||||
|
return if (bnoParty != null) {
|
||||||
|
// This will trigger CounterpartyCheckMembershipFlow
|
||||||
|
val untrustworthyData = initiateFlow(bnoParty).sendAndReceive<CheckMembershipResult>(otherParty)
|
||||||
|
untrustworthyData.unwrap { it }
|
||||||
|
} else {
|
||||||
|
throw InvalidMembershipListNameException(membershipName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@InitiatedBy(CheckMembershipFlow::class)
|
||||||
|
class OwnerSideCheckMembershipFlow(private val initiatingPartySession: FlowSession) : FlowLogic<Unit>(), MembershipAware {
|
||||||
|
@Suspendable
|
||||||
|
override fun call() {
|
||||||
|
val partyToCheck = initiatingPartySession.receive<Party>().unwrap { it }
|
||||||
|
partyToCheck.checkMembership(ourIdentity.name, this)
|
||||||
|
initiatingPartySession.send(CheckMembershipResult.PASS)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package net.corda.sample.businessnetwork.membership.flow
|
||||||
|
|
||||||
|
import net.corda.core.flows.FlowException
|
||||||
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.node.ServiceHub
|
||||||
|
import net.corda.sample.businessnetwork.membership.internal.MembershipListProvider
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
interface MembershipAware {
|
||||||
|
/**
|
||||||
|
* Checks that party is included into the specified membership list.
|
||||||
|
*/
|
||||||
|
fun <T> AbstractParty.checkMembership(membershipName: CordaX500Name, initiatorFlow: FlowLogic<T>) {
|
||||||
|
LoggerFactory.getLogger(javaClass).debug("Checking membership of party '${this.nameOrNull()}' in membership list '$membershipName'")
|
||||||
|
val membershipList = getMembershipList(membershipName, initiatorFlow.serviceHub)
|
||||||
|
if (this !in membershipList) {
|
||||||
|
val msg = "'$this' doesn't belong to membership list: ${membershipName.organisation}"
|
||||||
|
throw MembershipViolationException(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMembershipList(listName: CordaX500Name, serviceHub: ServiceHub): MembershipList {
|
||||||
|
LoggerFactory.getLogger(javaClass).debug("Obtaining membership list for name '$listName'")
|
||||||
|
return MembershipListProvider.obtainMembershipList(listName, serviceHub.networkMapCache)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MembershipViolationException(val msg: String) : FlowException(msg)
|
||||||
|
|
||||||
|
class InvalidMembershipListNameException(val membershipListName: CordaX500Name) : FlowException("Business Network owner node not found for: $membershipListName")
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.sample.businessnetwork.membership
|
package net.corda.sample.businessnetwork.membership.flow
|
||||||
|
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
|
|
@ -0,0 +1,34 @@
|
|||||||
|
package net.corda.sample.businessnetwork.membership.flow
|
||||||
|
|
||||||
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
|
import net.corda.core.flows.*
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.utilities.unwrap
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flow to obtain content of the membership lists this node belongs to.
|
||||||
|
*/
|
||||||
|
@StartableByRPC
|
||||||
|
@InitiatingFlow
|
||||||
|
class ObtainMembershipListContentFlow(private val membershipListName: CordaX500Name) : FlowLogic<Set<AbstractParty>>() {
|
||||||
|
@Suspendable
|
||||||
|
override fun call(): Set<AbstractParty> {
|
||||||
|
val bnoParty = serviceHub.networkMapCache.getPeerByLegalName(membershipListName) ?:
|
||||||
|
throw InvalidMembershipListNameException(membershipListName)
|
||||||
|
val untrustworthyData = initiateFlow(bnoParty).receive<Set<AbstractParty>>()
|
||||||
|
return untrustworthyData.unwrap { it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@InitiatedBy(ObtainMembershipListContentFlow::class)
|
||||||
|
class OwnerSideObtainMembershipListContentFlow(private val initiatingPartySession: FlowSession) : FlowLogic<Unit>(), MembershipAware {
|
||||||
|
@Suspendable
|
||||||
|
override fun call() {
|
||||||
|
// Checking whether the calling party is a member. If not it is not even in position to enquire about membership list content.
|
||||||
|
initiatingPartySession.counterparty.checkMembership(ourIdentity.name, this)
|
||||||
|
|
||||||
|
val membershipListContent: Set<AbstractParty> = getMembershipList(ourIdentity.name, serviceHub).content()
|
||||||
|
initiatingPartySession.send(membershipListContent)
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ import com.opencsv.CSVReaderBuilder
|
|||||||
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.node.services.NetworkMapCache
|
import net.corda.core.node.services.NetworkMapCache
|
||||||
import net.corda.sample.businessnetwork.membership.MembershipList
|
import net.corda.sample.businessnetwork.membership.flow.MembershipList
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,9 +2,9 @@ package net.corda.sample.businessnetwork.membership.internal
|
|||||||
|
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.node.services.NetworkMapCache
|
import net.corda.core.node.services.NetworkMapCache
|
||||||
import net.corda.sample.businessnetwork.membership.MembershipList
|
import net.corda.sample.businessnetwork.membership.flow.MembershipList
|
||||||
|
|
||||||
object MembershipListProvider {
|
object MembershipListProvider {
|
||||||
fun obtainMembershipList(listName: CordaX500Name, networkMapCache: NetworkMapCache): MembershipList =
|
fun obtainMembershipList(listName: CordaX500Name, networkMapCache: NetworkMapCache): MembershipList =
|
||||||
CsvMembershipList(MembershipListProvider::class.java.getResourceAsStream("${listName.commonName}.csv"), networkMapCache)
|
CsvMembershipList(MembershipListProvider::class.java.getResourceAsStream("${listName.organisation}.csv"), networkMapCache)
|
||||||
}
|
}
|
@ -14,8 +14,8 @@ import net.corda.core.messaging.FlowHandle
|
|||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.sample.businessnetwork.IOUFlow
|
import net.corda.sample.businessnetwork.iou.IOUFlow
|
||||||
import net.corda.sample.businessnetwork.membership.ObtainMembershipListContentFlow
|
import net.corda.sample.businessnetwork.membership.flow.ObtainMembershipListContentFlow
|
||||||
import net.corda.finance.GBP
|
import net.corda.finance.GBP
|
||||||
import net.corda.finance.USD
|
import net.corda.finance.USD
|
||||||
import net.corda.finance.contracts.asset.Cash
|
import net.corda.finance.contracts.asset.Cash
|
||||||
@ -32,8 +32,14 @@ import net.corda.testing.driver.PortAllocation
|
|||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class ExplorerSimulation(private val options: OptionSet) {
|
class ExplorerSimulation(private val options: OptionSet) {
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
fun packagesOfClasses(vararg classes: KClass<*>): List<String> = classes.map { it.java.`package`.name }
|
||||||
|
}
|
||||||
|
|
||||||
private val user = User("user1", "test", permissions = setOf(
|
private val user = User("user1", "test", permissions = setOf(
|
||||||
startFlow<CashPaymentFlow>(),
|
startFlow<CashPaymentFlow>(),
|
||||||
startFlow<CashConfigDataFlow>(),
|
startFlow<CashConfigDataFlow>(),
|
||||||
@ -52,6 +58,7 @@ class ExplorerSimulation(private val options: OptionSet) {
|
|||||||
private lateinit var bobNode: NodeHandle
|
private lateinit var bobNode: NodeHandle
|
||||||
private lateinit var issuerNodeGBP: NodeHandle
|
private lateinit var issuerNodeGBP: NodeHandle
|
||||||
private lateinit var issuerNodeUSD: NodeHandle
|
private lateinit var issuerNodeUSD: NodeHandle
|
||||||
|
private lateinit var bnoNode: NodeHandle
|
||||||
private lateinit var notary: Party
|
private lateinit var notary: Party
|
||||||
|
|
||||||
private val RPCConnections = ArrayList<CordaRPCConnection>()
|
private val RPCConnections = ArrayList<CordaRPCConnection>()
|
||||||
@ -65,7 +72,8 @@ class ExplorerSimulation(private val options: OptionSet) {
|
|||||||
|
|
||||||
fun startDemoNodes() {
|
fun startDemoNodes() {
|
||||||
val portAllocation = PortAllocation.Incremental(20000)
|
val portAllocation = PortAllocation.Incremental(20000)
|
||||||
driver(portAllocation = portAllocation, extraCordappPackagesToScan = listOf("net.corda.finance", IOUFlow::class.java.`package`.name),
|
driver(portAllocation = portAllocation,
|
||||||
|
extraCordappPackagesToScan = packagesOfClasses(CashPaymentFlow::class, IOUFlow::class, ObtainMembershipListContentFlow::class),
|
||||||
isDebug = true, waitForAllNodesToFinish = true, jmxPolicy = JmxPolicy(true)) {
|
isDebug = true, waitForAllNodesToFinish = true, jmxPolicy = JmxPolicy(true)) {
|
||||||
// TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo.
|
// TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo.
|
||||||
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user))
|
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user))
|
||||||
@ -76,14 +84,16 @@ class ExplorerSimulation(private val options: OptionSet) {
|
|||||||
customOverrides = mapOf("issuableCurrencies" to listOf("GBP")))
|
customOverrides = mapOf("issuableCurrencies" to listOf("GBP")))
|
||||||
val issuerUSD = startNode(providedName = usaBankName, rpcUsers = listOf(manager),
|
val issuerUSD = startNode(providedName = usaBankName, rpcUsers = listOf(manager),
|
||||||
customOverrides = mapOf("issuableCurrencies" to listOf("USD")))
|
customOverrides = mapOf("issuableCurrencies" to listOf("USD")))
|
||||||
|
val bno = startNode(providedName = IOUFlow.allowedMembershipName, rpcUsers = listOf(user))
|
||||||
|
|
||||||
notaryNode = defaultNotaryNode.get()
|
notaryNode = defaultNotaryNode.get()
|
||||||
aliceNode = alice.get()
|
aliceNode = alice.get()
|
||||||
bobNode = bob.get()
|
bobNode = bob.get()
|
||||||
issuerNodeGBP = issuerGBP.get()
|
issuerNodeGBP = issuerGBP.get()
|
||||||
issuerNodeUSD = issuerUSD.get()
|
issuerNodeUSD = issuerUSD.get()
|
||||||
|
bnoNode = bno.get()
|
||||||
|
|
||||||
arrayOf(notaryNode, aliceNode, bobNode, issuerNodeGBP, issuerNodeUSD).forEach {
|
arrayOf(notaryNode, aliceNode, bobNode, issuerNodeGBP, issuerNodeUSD, bnoNode).forEach {
|
||||||
println("${it.nodeInfo.legalIdentities.first()} started on ${it.configuration.rpcAddress}")
|
println("${it.nodeInfo.legalIdentities.first()} started on ${it.configuration.rpcAddress}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ import net.corda.client.jfx.utils.map
|
|||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.sample.businessnetwork.IOUFlow
|
import net.corda.sample.businessnetwork.iou.IOUFlow
|
||||||
import net.corda.sample.businessnetwork.membership.ObtainMembershipListContentFlow
|
import net.corda.sample.businessnetwork.membership.flow.ObtainMembershipListContentFlow
|
||||||
|
|
||||||
class MembershipListModel {
|
class MembershipListModel {
|
||||||
private val proxy by observableValue(NodeMonitorModel::proxyObservable)
|
private val proxy by observableValue(NodeMonitorModel::proxyObservable)
|
||||||
|
@ -83,9 +83,10 @@ class Network : CordaView() {
|
|||||||
.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 { it.by.toKnownParty() }
|
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, 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.
|
||||||
// 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) + signingParties.cross(outputParties)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ 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.utilities.toBase58String
|
import net.corda.core.utilities.toBase58String
|
||||||
import net.corda.sample.businessnetwork.IOUState
|
import net.corda.sample.businessnetwork.iou.IOUState
|
||||||
import net.corda.explorer.AmountDiff
|
import net.corda.explorer.AmountDiff
|
||||||
import net.corda.explorer.formatters.AmountFormatter
|
import net.corda.explorer.formatters.AmountFormatter
|
||||||
import net.corda.explorer.formatters.Formatter
|
import net.corda.explorer.formatters.Formatter
|
||||||
|
@ -2,10 +2,17 @@ package net.corda.explorer.views.cordapps.iou
|
|||||||
|
|
||||||
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
|
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
|
||||||
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
|
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
|
||||||
|
import javafx.beans.binding.Bindings
|
||||||
|
import javafx.geometry.Pos
|
||||||
import javafx.scene.input.MouseButton
|
import javafx.scene.input.MouseButton
|
||||||
import javafx.scene.layout.BorderPane
|
import javafx.scene.layout.BorderPane
|
||||||
|
import net.corda.client.jfx.model.TransactionDataModel
|
||||||
|
import net.corda.client.jfx.model.observableList
|
||||||
|
import net.corda.client.jfx.utils.map
|
||||||
import net.corda.core.utilities.Try
|
import net.corda.core.utilities.Try
|
||||||
import net.corda.explorer.model.CordaView
|
import net.corda.explorer.model.CordaView
|
||||||
|
import net.corda.explorer.model.CordaWidget
|
||||||
|
import net.corda.sample.businessnetwork.iou.IOUState
|
||||||
import net.corda.explorer.model.MembershipListModel
|
import net.corda.explorer.model.MembershipListModel
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
@ -13,6 +20,7 @@ class IOUViewer : CordaView("IOU") {
|
|||||||
// Inject UI elements.
|
// Inject UI elements.
|
||||||
override val root: BorderPane by fxml()
|
override val root: BorderPane by fxml()
|
||||||
override val icon: FontAwesomeIcon = FontAwesomeIcon.CHEVRON_CIRCLE_RIGHT
|
override val icon: FontAwesomeIcon = FontAwesomeIcon.CHEVRON_CIRCLE_RIGHT
|
||||||
|
override val widgets = listOf(CordaWidget(title, IOUWidget(), icon)).observable()
|
||||||
|
|
||||||
// Wire up UI
|
// Wire up UI
|
||||||
init {
|
init {
|
||||||
@ -32,4 +40,18 @@ class IOUViewer : CordaView("IOU") {
|
|||||||
val allParties = MembershipListModel().allParties
|
val allParties = MembershipListModel().allParties
|
||||||
allParties[0]
|
allParties[0]
|
||||||
}.isSuccess
|
}.isSuccess
|
||||||
|
|
||||||
|
private class IOUWidget : BorderPane() {
|
||||||
|
private val partiallyResolvedTransactions by observableList(TransactionDataModel::partiallyResolvedTransactions)
|
||||||
|
private val iouTransactions = partiallyResolvedTransactions.filtered { t -> t.transaction.tx.outputs.any({ ts -> ts.data is IOUState }) }
|
||||||
|
|
||||||
|
init {
|
||||||
|
right {
|
||||||
|
label {
|
||||||
|
textProperty().bind(Bindings.size(iouTransactions).map(Number::toString))
|
||||||
|
BorderPane.setAlignment(this, Pos.BOTTOM_RIGHT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.explorer.views.cordapps.iou
|
package net.corda.explorer.views.cordapps.iou
|
||||||
|
|
||||||
import com.google.common.base.Splitter
|
import com.google.common.base.Splitter
|
||||||
|
import com.sun.javafx.collections.ImmutableObservableList
|
||||||
import javafx.beans.binding.Bindings
|
import javafx.beans.binding.Bindings
|
||||||
import javafx.beans.binding.BooleanBinding
|
import javafx.beans.binding.BooleanBinding
|
||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
@ -15,13 +16,15 @@ import net.corda.client.jfx.model.*
|
|||||||
import net.corda.client.jfx.utils.isNotNull
|
import net.corda.client.jfx.utils.isNotNull
|
||||||
import net.corda.client.jfx.utils.map
|
import net.corda.client.jfx.utils.map
|
||||||
import net.corda.core.flows.FlowException
|
import net.corda.core.flows.FlowException
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.messaging.FlowHandle
|
import net.corda.core.messaging.FlowHandle
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.sample.businessnetwork.IOUFlow
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import net.corda.sample.businessnetwork.iou.IOUFlow
|
||||||
import net.corda.explorer.formatters.PartyNameFormatter
|
import net.corda.explorer.formatters.PartyNameFormatter
|
||||||
import net.corda.explorer.model.MembershipListModel
|
import net.corda.explorer.model.MembershipListModel
|
||||||
import net.corda.explorer.views.bigDecimalFormatter
|
import net.corda.explorer.views.bigDecimalFormatter
|
||||||
@ -46,7 +49,12 @@ class NewTransaction : Fragment() {
|
|||||||
fun show(window: Window) {
|
fun show(window: Window) {
|
||||||
|
|
||||||
// Every time re-query from the server side
|
// Every time re-query from the server side
|
||||||
val elementsFromServer = MembershipListModel().allParties
|
val elementsFromServer = try {
|
||||||
|
MembershipListModel().allParties
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
loggerFor<NewTransaction>().error("Unexpected error fetching membership list content", ex)
|
||||||
|
ImmutableObservableList<AbstractParty>()
|
||||||
|
}
|
||||||
|
|
||||||
partyBChoiceBox.apply {
|
partyBChoiceBox.apply {
|
||||||
items = FXCollections.observableList(parties.map { it.chooseIdentityAndCert() }).filtered { elementsFromServer.contains(it.party) }.sorted()
|
items = FXCollections.observableList(parties.map { it.chooseIdentityAndCert() }).filtered { elementsFromServer.contains(it.party) }.sorted()
|
||||||
|
Loading…
Reference in New Issue
Block a user