diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/Contract.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/iou/IOUContract.kt similarity index 96% rename from samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/Contract.kt rename to samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/iou/IOUContract.kt index 3bafcf3a03..b4f880d36f 100644 --- a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/Contract.kt +++ b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/iou/IOUContract.kt @@ -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.Contract diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/Flow.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/iou/IOUFlow.kt similarity index 88% rename from samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/Flow.kt rename to samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/iou/IOUFlow.kt index 068a8f9161..8c8765b24e 100644 --- a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/Flow.kt +++ b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/iou/IOUFlow.kt @@ -1,4 +1,4 @@ -package net.corda.sample.businessnetwork +package net.corda.sample.businessnetwork.iou import co.paralleluniverse.fibers.Suspendable 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.TransactionBuilder import net.corda.core.utilities.ProgressTracker -import net.corda.sample.businessnetwork.membership.MembershipAware -import net.corda.sample.businessnetwork.membership.CheckMembershipFlow -import net.corda.sample.businessnetwork.membership.CheckMembershipResult +import net.corda.sample.businessnetwork.membership.flow.CheckMembershipFlow +import net.corda.sample.businessnetwork.membership.flow.CheckMembershipResult import kotlin.reflect.jvm.jvmName @InitiatingFlow @StartableByRPC class IOUFlow(val iouValue: Int, - val otherParty: Party) : FlowLogic(), MembershipAware { + val otherParty: Party) : FlowLogic() { companion object { + // TODO: Derive membership name from CorDapp config. val allowedMembershipName = - CordaX500Name("AliceBobMembershipList", "AliceBob", "Washington", "US") + CordaX500Name("AliceBobMembershipList", "Oslo", "NO") } /** The progress tracker provides checkpoints indicating the progress of the flow to observers. */ diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/FlowResponder.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/iou/IOUFlowResponder.kt similarity index 72% rename from samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/FlowResponder.kt rename to samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/iou/IOUFlowResponder.kt index b9e6a2ac06..8c59fcbecc 100644 --- a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/FlowResponder.kt +++ b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/iou/IOUFlowResponder.kt @@ -1,4 +1,4 @@ -package net.corda.sample.businessnetwork +package net.corda.sample.businessnetwork.iou import co.paralleluniverse.fibers.Suspendable 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.SignTransactionFlow 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) -class IOUFlowResponder(val otherPartySession: FlowSession) : FlowLogic(), MembershipAware { +class IOUFlowResponder(val otherPartySession: FlowSession) : FlowLogic() { @Suspendable override fun call() { - - otherPartySession.counterparty.checkMembership(IOUFlow.allowedMembershipName, this) + check(subFlow(CheckMembershipFlow(otherPartySession.counterparty, IOUFlow.allowedMembershipName)) == CheckMembershipResult.PASS) subFlow(object : SignTransactionFlow(otherPartySession, SignTransactionFlow.tracker()) { override fun checkTransaction(stx: SignedTransaction) = requireThat { diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/State.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/iou/IOUState.kt similarity index 85% rename from samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/State.kt rename to samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/iou/IOUState.kt index 9cd8b888e4..b5add03b77 100644 --- a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/State.kt +++ b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/iou/IOUState.kt @@ -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.identity.Party diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/CheckMembershipFlow.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/CheckMembershipFlow.kt deleted file mode 100644 index 0516a54628..0000000000 --- a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/CheckMembershipFlow.kt +++ /dev/null @@ -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(), MembershipAware { - @Suspendable - override fun call(): CheckMembershipResult { - otherParty.checkMembership(membershipName, this) - // This will trigger CounterpartyCheckMembershipFlow - val untrustworthyData = initiateFlow(otherParty).sendAndReceive(membershipName) - return untrustworthyData.unwrap { it } - } -} - -@InitiatedBy(CheckMembershipFlow::class) -class CounterpartyCheckMembershipFlow(private val otherPartySession: FlowSession) : FlowLogic(), MembershipAware { - @Suspendable - override fun call() { - val membershipName = otherPartySession.receive().unwrap { it } - otherPartySession.counterparty.checkMembership(membershipName, this) - otherPartySession.send(CheckMembershipResult.PASS) - } -} \ No newline at end of file diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/MembershipAware.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/MembershipAware.kt deleted file mode 100644 index dd436b6d68..0000000000 --- a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/MembershipAware.kt +++ /dev/null @@ -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 AbstractParty.checkMembership(membershipName: CordaX500Name, initiatorFlow: FlowLogic) { - 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) \ No newline at end of file diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/ObtainMembershipListContentFlow.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/ObtainMembershipListContentFlow.kt deleted file mode 100644 index d44e3865fc..0000000000 --- a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/ObtainMembershipListContentFlow.kt +++ /dev/null @@ -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>(), MembershipAware { - @Suspendable - override fun call(): Set = getMembershipList(membershipListName, serviceHub).content() -} \ No newline at end of file diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/flow/CheckMembershipFlow.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/flow/CheckMembershipFlow.kt new file mode 100644 index 0000000000..6f21a054d6 --- /dev/null +++ b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/flow/CheckMembershipFlow.kt @@ -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() { + @Suspendable + override fun call(): CheckMembershipResult { + val bnoParty = serviceHub.networkMapCache.getPeerByLegalName(membershipName) + return if (bnoParty != null) { + // This will trigger CounterpartyCheckMembershipFlow + val untrustworthyData = initiateFlow(bnoParty).sendAndReceive(otherParty) + untrustworthyData.unwrap { it } + } else { + throw InvalidMembershipListNameException(membershipName) + } + } +} + +@InitiatedBy(CheckMembershipFlow::class) +class OwnerSideCheckMembershipFlow(private val initiatingPartySession: FlowSession) : FlowLogic(), MembershipAware { + @Suspendable + override fun call() { + val partyToCheck = initiatingPartySession.receive().unwrap { it } + partyToCheck.checkMembership(ourIdentity.name, this) + initiatingPartySession.send(CheckMembershipResult.PASS) + } +} \ No newline at end of file diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/flow/MembershipAware.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/flow/MembershipAware.kt new file mode 100644 index 0000000000..b55b4b475b --- /dev/null +++ b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/flow/MembershipAware.kt @@ -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 AbstractParty.checkMembership(membershipName: CordaX500Name, initiatorFlow: FlowLogic) { + 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") \ No newline at end of file diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/MembershipList.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/flow/MembershipList.kt similarity index 90% rename from samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/MembershipList.kt rename to samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/flow/MembershipList.kt index a8cc101346..fa6253c8e4 100644 --- a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/MembershipList.kt +++ b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/flow/MembershipList.kt @@ -1,4 +1,4 @@ -package net.corda.sample.businessnetwork.membership +package net.corda.sample.businessnetwork.membership.flow import net.corda.core.identity.AbstractParty diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/flow/ObtainMembershipListContentFlow.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/flow/ObtainMembershipListContentFlow.kt new file mode 100644 index 0000000000..fba113c237 --- /dev/null +++ b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/flow/ObtainMembershipListContentFlow.kt @@ -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>() { + @Suspendable + override fun call(): Set { + val bnoParty = serviceHub.networkMapCache.getPeerByLegalName(membershipListName) ?: + throw InvalidMembershipListNameException(membershipListName) + val untrustworthyData = initiateFlow(bnoParty).receive>() + return untrustworthyData.unwrap { it } + } +} + +@InitiatedBy(ObtainMembershipListContentFlow::class) +class OwnerSideObtainMembershipListContentFlow(private val initiatingPartySession: FlowSession) : FlowLogic(), 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 = getMembershipList(ourIdentity.name, serviceHub).content() + initiatingPartySession.send(membershipListContent) + } +} \ No newline at end of file diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/internal/CsvMembershipList.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/internal/CsvMembershipList.kt index 5a98412c97..1e1186e552 100644 --- a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/internal/CsvMembershipList.kt +++ b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/internal/CsvMembershipList.kt @@ -4,7 +4,7 @@ import com.opencsv.CSVReaderBuilder import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name 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 /** diff --git a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/internal/MembershipListProvider.kt b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/internal/MembershipListProvider.kt index b9ad6e1f92..98e215bcd4 100644 --- a/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/internal/MembershipListProvider.kt +++ b/samples/business-network-demo/src/main/kotlin/net/corda/sample/businessnetwork/membership/internal/MembershipListProvider.kt @@ -2,9 +2,9 @@ package net.corda.sample.businessnetwork.membership.internal import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.NetworkMapCache -import net.corda.sample.businessnetwork.membership.MembershipList +import net.corda.sample.businessnetwork.membership.flow.MembershipList object MembershipListProvider { 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) } \ No newline at end of file diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt index 7c8ef402dc..9bc750a772 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ExplorerSimulation.kt @@ -14,8 +14,8 @@ import net.corda.core.messaging.FlowHandle import net.corda.core.messaging.startFlow import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow -import net.corda.sample.businessnetwork.IOUFlow -import net.corda.sample.businessnetwork.membership.ObtainMembershipListContentFlow +import net.corda.sample.businessnetwork.iou.IOUFlow +import net.corda.sample.businessnetwork.membership.flow.ObtainMembershipListContentFlow import net.corda.finance.GBP import net.corda.finance.USD import net.corda.finance.contracts.asset.Cash @@ -32,8 +32,14 @@ import net.corda.testing.driver.PortAllocation import net.corda.testing.driver.driver import java.time.Instant import java.util.* +import kotlin.reflect.KClass class ExplorerSimulation(private val options: OptionSet) { + + private companion object { + fun packagesOfClasses(vararg classes: KClass<*>): List = classes.map { it.java.`package`.name } + } + private val user = User("user1", "test", permissions = setOf( startFlow(), startFlow(), @@ -52,6 +58,7 @@ class ExplorerSimulation(private val options: OptionSet) { private lateinit var bobNode: NodeHandle private lateinit var issuerNodeGBP: NodeHandle private lateinit var issuerNodeUSD: NodeHandle + private lateinit var bnoNode: NodeHandle private lateinit var notary: Party private val RPCConnections = ArrayList() @@ -65,7 +72,8 @@ class ExplorerSimulation(private val options: OptionSet) { fun startDemoNodes() { 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)) { // TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo. 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"))) val issuerUSD = startNode(providedName = usaBankName, rpcUsers = listOf(manager), customOverrides = mapOf("issuableCurrencies" to listOf("USD"))) + val bno = startNode(providedName = IOUFlow.allowedMembershipName, rpcUsers = listOf(user)) notaryNode = defaultNotaryNode.get() aliceNode = alice.get() bobNode = bob.get() issuerNodeGBP = issuerGBP.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}") } diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/model/MembershipListModel.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/model/MembershipListModel.kt index 0c39bfe950..df0712a615 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/model/MembershipListModel.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/model/MembershipListModel.kt @@ -9,8 +9,8 @@ import net.corda.client.jfx.utils.map import net.corda.core.identity.AbstractParty import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow -import net.corda.sample.businessnetwork.IOUFlow -import net.corda.sample.businessnetwork.membership.ObtainMembershipListContentFlow +import net.corda.sample.businessnetwork.iou.IOUFlow +import net.corda.sample.businessnetwork.membership.flow.ObtainMembershipListContentFlow class MembershipListModel { private val proxy by observableValue(NodeMonitorModel::proxyObservable) diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt index 8970e7b1e1..9658b93a61 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/Network.kt @@ -83,9 +83,10 @@ class Network : CordaView() { .map { it.stateAndRef.state.data }.getParties() val outputParties = it.transaction.tx.outputStates.observable().getParties() 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. - inputParties.cross(outputParties) + inputParties.cross(signingParties) + inputParties.cross(outputParties) + inputParties.cross(signingParties) + signingParties.cross(outputParties) } } diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt index 8d75aec165..b4e2ef1b21 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt @@ -28,7 +28,7 @@ import net.corda.core.identity.AbstractParty import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party 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.formatters.AmountFormatter import net.corda.explorer.formatters.Formatter diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/iou/IOUViewer.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/iou/IOUViewer.kt index 3dd6afa82b..51ab1c2c58 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/iou/IOUViewer.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/iou/IOUViewer.kt @@ -2,10 +2,17 @@ package net.corda.explorer.views.cordapps.iou import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView +import javafx.beans.binding.Bindings +import javafx.geometry.Pos import javafx.scene.input.MouseButton 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.explorer.model.CordaView +import net.corda.explorer.model.CordaWidget +import net.corda.sample.businessnetwork.iou.IOUState import net.corda.explorer.model.MembershipListModel import tornadofx.* @@ -13,6 +20,7 @@ class IOUViewer : CordaView("IOU") { // Inject UI elements. override val root: BorderPane by fxml() override val icon: FontAwesomeIcon = FontAwesomeIcon.CHEVRON_CIRCLE_RIGHT + override val widgets = listOf(CordaWidget(title, IOUWidget(), icon)).observable() // Wire up UI init { @@ -28,8 +36,22 @@ class IOUViewer : CordaView("IOU") { } fun isEnabledForNode(): Boolean = Try.on { - // Assuming if the model can be initialized - the CorDapp is installed - val allParties = MembershipListModel().allParties - allParties[0] - }.isSuccess + // Assuming if the model can be initialized - the CorDapp is installed + val allParties = MembershipListModel().allParties + allParties[0] + }.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) + } + } + } + } } \ No newline at end of file diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/iou/NewTransaction.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/iou/NewTransaction.kt index b675b8d2a5..281eaa0c5c 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/iou/NewTransaction.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/iou/NewTransaction.kt @@ -1,6 +1,7 @@ package net.corda.explorer.views.cordapps.iou import com.google.common.base.Splitter +import com.sun.javafx.collections.ImmutableObservableList import javafx.beans.binding.Bindings import javafx.beans.binding.BooleanBinding 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.map import net.corda.core.flows.FlowException +import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.messaging.FlowHandle import net.corda.core.messaging.startFlow import net.corda.core.transactions.SignedTransaction 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.model.MembershipListModel import net.corda.explorer.views.bigDecimalFormatter @@ -46,7 +49,12 @@ class NewTransaction : Fragment() { fun show(window: Window) { // Every time re-query from the server side - val elementsFromServer = MembershipListModel().allParties + val elementsFromServer = try { + MembershipListModel().allParties + } catch (ex: Exception) { + loggerFor().error("Unexpected error fetching membership list content", ex) + ImmutableObservableList() + } partyBChoiceBox.apply { items = FXCollections.observableList(parties.map { it.chooseIdentityAndCert() }).filtered { elementsFromServer.contains(it.party) }.sorted()