mirror of
https://github.com/corda/corda.git
synced 2024-12-20 21:43:14 +00:00
Change CashIssueFlow to always issue to ourselves
Change CashIssueFlow to always issue to ourselves, and require the cash is then moved in a separate payment operation. This more closely models actual operation inside banks, and is a step towards making all move-like operations go through a uniform verification process.
This commit is contained in:
parent
89476904fc
commit
b76d036843
@ -108,14 +108,10 @@ class NodeMonitorModelTest : DriverBasedTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `cash issue works end to end`() {
|
fun `cash issue works end to end`() {
|
||||||
val anonymous = false
|
|
||||||
rpc.startFlow(::CashIssueFlow,
|
rpc.startFlow(::CashIssueFlow,
|
||||||
Amount(100, USD),
|
Amount(100, USD),
|
||||||
aliceNode.legalIdentity,
|
|
||||||
rpc.nodeIdentity().legalIdentity,
|
|
||||||
OpaqueBytes(ByteArray(1, { 1 })),
|
OpaqueBytes(ByteArray(1, { 1 })),
|
||||||
notaryNode.notaryIdentity,
|
notaryNode.notaryIdentity
|
||||||
anonymous
|
|
||||||
)
|
)
|
||||||
|
|
||||||
vaultUpdates.expectEvents(isStrict = false) {
|
vaultUpdates.expectEvents(isStrict = false) {
|
||||||
@ -137,8 +133,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun `cash issue and move`() {
|
fun `cash issue and move`() {
|
||||||
val anonymous = false
|
val anonymous = false
|
||||||
rpc.startFlow(::CashIssueFlow, 100.DOLLARS, aliceNode.legalIdentity, rpc.nodeIdentity().legalIdentity, OpaqueBytes.of(1),
|
rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryNode.notaryIdentity).returnValue.getOrThrow()
|
||||||
notaryNode.notaryIdentity, anonymous).returnValue.getOrThrow()
|
|
||||||
rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.legalIdentity, anonymous).returnValue.getOrThrow()
|
rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.legalIdentity, anonymous).returnValue.getOrThrow()
|
||||||
|
|
||||||
var issueSmId: StateMachineRunId? = null
|
var issueSmId: StateMachineRunId? = null
|
||||||
|
@ -71,7 +71,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
|
|||||||
|
|
||||||
FlowHandle<AbstractCashFlow.Result> flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class,
|
FlowHandle<AbstractCashFlow.Result> flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class,
|
||||||
DOLLARS(123), OpaqueBytes.of("1".getBytes()),
|
DOLLARS(123), OpaqueBytes.of("1".getBytes()),
|
||||||
node.info.getLegalIdentity(), node.info.getLegalIdentity());
|
node.info.getLegalIdentity());
|
||||||
System.out.println("Started issuing cash, waiting on result");
|
System.out.println("Started issuing cash, waiting on result");
|
||||||
flowHandle.getReturnValue().get();
|
flowHandle.getReturnValue().get();
|
||||||
|
|
||||||
|
@ -78,8 +78,7 @@ class CordaRPCClientTest : NodeBasedTest() {
|
|||||||
println("Creating proxy")
|
println("Creating proxy")
|
||||||
println("Starting flow")
|
println("Starting flow")
|
||||||
val flowHandle = connection!!.proxy.startTrackedFlow(::CashIssueFlow,
|
val flowHandle = connection!!.proxy.startTrackedFlow(::CashIssueFlow,
|
||||||
20.DOLLARS, node.info.legalIdentity,
|
20.DOLLARS, OpaqueBytes.of(0), node.info.legalIdentity
|
||||||
node.info.legalIdentity, OpaqueBytes.of(0), node.info.legalIdentity, true
|
|
||||||
)
|
)
|
||||||
println("Started flow, waiting on result")
|
println("Started flow, waiting on result")
|
||||||
flowHandle.progress.subscribe {
|
flowHandle.progress.subscribe {
|
||||||
@ -114,8 +113,7 @@ class CordaRPCClientTest : NodeBasedTest() {
|
|||||||
assertTrue(startCash.isEmpty(), "Should not start with any cash")
|
assertTrue(startCash.isEmpty(), "Should not start with any cash")
|
||||||
|
|
||||||
val flowHandle = proxy.startFlow(::CashIssueFlow,
|
val flowHandle = proxy.startFlow(::CashIssueFlow,
|
||||||
123.DOLLARS, node.info.legalIdentity,
|
123.DOLLARS, OpaqueBytes.of(0), node.info.legalIdentity
|
||||||
node.info.legalIdentity, OpaqueBytes.of(0), node.info.legalIdentity, true
|
|
||||||
)
|
)
|
||||||
println("Started issuing cash, waiting on result")
|
println("Started issuing cash, waiting on result")
|
||||||
flowHandle.returnValue.get()
|
flowHandle.returnValue.get()
|
||||||
@ -141,15 +139,16 @@ class CordaRPCClientTest : NodeBasedTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val nodeIdentity = node.info.legalIdentity
|
val nodeIdentity = node.info.legalIdentity
|
||||||
node.services.startFlow(CashIssueFlow(2000.DOLLARS, nodeIdentity, nodeIdentity, OpaqueBytes.of(0), nodeIdentity, true), FlowInitiator.Shell).resultFuture.getOrThrow()
|
node.services.startFlow(CashIssueFlow(2000.DOLLARS, OpaqueBytes.of(0), nodeIdentity), FlowInitiator.Shell).resultFuture.getOrThrow()
|
||||||
proxy.startFlow(::CashIssueFlow,
|
proxy.startFlow(::CashIssueFlow,
|
||||||
123.DOLLARS, nodeIdentity,
|
123.DOLLARS,
|
||||||
nodeIdentity, OpaqueBytes.of(0), nodeIdentity,
|
OpaqueBytes.of(0),
|
||||||
true
|
nodeIdentity
|
||||||
).returnValue.getOrThrow()
|
).returnValue.getOrThrow()
|
||||||
proxy.startFlowDynamic(CashIssueFlow::class.java,
|
proxy.startFlowDynamic(CashIssueFlow::class.java,
|
||||||
1000.DOLLARS, OpaqueBytes.of(0),
|
1000.DOLLARS,
|
||||||
nodeIdentity, nodeIdentity).returnValue.getOrThrow()
|
OpaqueBytes.of(0),
|
||||||
|
nodeIdentity).returnValue.getOrThrow()
|
||||||
assertEquals(2, countRpcFlows)
|
assertEquals(2, countRpcFlows)
|
||||||
assertEquals(1, countShellFlows)
|
assertEquals(1, countShellFlows)
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ public class StandaloneCordaRPCJavaClientTest {
|
|||||||
|
|
||||||
FlowHandle<AbstractCashFlow.Result> flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class,
|
FlowHandle<AbstractCashFlow.Result> flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class,
|
||||||
dollars123, OpaqueBytes.of("1".getBytes()),
|
dollars123, OpaqueBytes.of("1".getBytes()),
|
||||||
notaryNode.getLegalIdentity(), notaryNode.getLegalIdentity());
|
notaryNode.getLegalIdentity());
|
||||||
System.out.println("Started issuing cash, waiting on result");
|
System.out.println("Started issuing cash, waiting on result");
|
||||||
flowHandle.getReturnValue().get();
|
flowHandle.getReturnValue().get();
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ package net.corda.kotlin.rpc
|
|||||||
import com.google.common.hash.Hashing
|
import com.google.common.hash.Hashing
|
||||||
import com.google.common.hash.HashingInputStream
|
import com.google.common.hash.HashingInputStream
|
||||||
import net.corda.client.rpc.CordaRPCConnection
|
import net.corda.client.rpc.CordaRPCConnection
|
||||||
import net.corda.client.rpc.notUsed
|
|
||||||
import net.corda.contracts.asset.Cash
|
import net.corda.contracts.asset.Cash
|
||||||
import net.corda.contracts.getCashBalance
|
import net.corda.contracts.getCashBalance
|
||||||
import net.corda.contracts.getCashBalances
|
import net.corda.contracts.getCashBalances
|
||||||
@ -96,7 +95,7 @@ class StandaloneCordaRPClientTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test starting flow`() {
|
fun `test starting flow`() {
|
||||||
rpcProxy.startFlow(::CashIssueFlow, 127.POUNDS, OpaqueBytes.of(0), notaryNode.legalIdentity, notaryNode.notaryIdentity)
|
rpcProxy.startFlow(::CashIssueFlow, 127.POUNDS, OpaqueBytes.of(0), notaryNode.notaryIdentity)
|
||||||
.returnValue.getOrThrow(timeout)
|
.returnValue.getOrThrow(timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +103,7 @@ class StandaloneCordaRPClientTest {
|
|||||||
fun `test starting tracked flow`() {
|
fun `test starting tracked flow`() {
|
||||||
var trackCount = 0
|
var trackCount = 0
|
||||||
val handle = rpcProxy.startTrackedFlow(
|
val handle = rpcProxy.startTrackedFlow(
|
||||||
::CashIssueFlow, 429.DOLLARS, OpaqueBytes.of(0), notaryNode.legalIdentity, notaryNode.notaryIdentity
|
::CashIssueFlow, 429.DOLLARS, OpaqueBytes.of(0), notaryNode.notaryIdentity
|
||||||
)
|
)
|
||||||
handle.progress.subscribe { msg ->
|
handle.progress.subscribe { msg ->
|
||||||
log.info("Flow>> $msg")
|
log.info("Flow>> $msg")
|
||||||
@ -133,7 +132,7 @@ class StandaloneCordaRPClientTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now issue some cash
|
// Now issue some cash
|
||||||
rpcProxy.startFlow(::CashIssueFlow, 513.SWISS_FRANCS, OpaqueBytes.of(0), notaryNode.legalIdentity, notaryNode.notaryIdentity)
|
rpcProxy.startFlow(::CashIssueFlow, 513.SWISS_FRANCS, OpaqueBytes.of(0), notaryNode.notaryIdentity)
|
||||||
.returnValue.getOrThrow(timeout)
|
.returnValue.getOrThrow(timeout)
|
||||||
assertEquals(1, updateCount.get())
|
assertEquals(1, updateCount.get())
|
||||||
}
|
}
|
||||||
@ -150,7 +149,7 @@ class StandaloneCordaRPClientTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now issue some cash
|
// Now issue some cash
|
||||||
rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNode.legalIdentity, notaryNode.notaryIdentity)
|
rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNode.notaryIdentity)
|
||||||
.returnValue.getOrThrow(timeout)
|
.returnValue.getOrThrow(timeout)
|
||||||
assertNotEquals(0, updateCount.get())
|
assertNotEquals(0, updateCount.get())
|
||||||
|
|
||||||
@ -164,7 +163,7 @@ class StandaloneCordaRPClientTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `test vault query by`() {
|
fun `test vault query by`() {
|
||||||
// Now issue some cash
|
// Now issue some cash
|
||||||
rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNode.legalIdentity, notaryNode.notaryIdentity)
|
rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNode.notaryIdentity)
|
||||||
.returnValue.getOrThrow(timeout)
|
.returnValue.getOrThrow(timeout)
|
||||||
|
|
||||||
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
||||||
@ -192,10 +191,7 @@ class StandaloneCordaRPClientTest {
|
|||||||
val startCash = rpcProxy.getCashBalances()
|
val startCash = rpcProxy.getCashBalances()
|
||||||
assertTrue(startCash.isEmpty(), "Should not start with any cash")
|
assertTrue(startCash.isEmpty(), "Should not start with any cash")
|
||||||
|
|
||||||
val flowHandle = rpcProxy.startFlow(::CashIssueFlow,
|
val flowHandle = rpcProxy.startFlow(::CashIssueFlow, 629.DOLLARS, OpaqueBytes.of(0), notaryNode.legalIdentity)
|
||||||
629.DOLLARS, OpaqueBytes.of(0),
|
|
||||||
notaryNode.legalIdentity, notaryNode.legalIdentity
|
|
||||||
)
|
|
||||||
println("Started issuing cash, waiting on result")
|
println("Started issuing cash, waiting on result")
|
||||||
flowHandle.returnValue.get()
|
flowHandle.returnValue.get()
|
||||||
|
|
||||||
|
@ -175,8 +175,7 @@ class ContractUpgradeFlowTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `upgrade Cash to v2`() {
|
fun `upgrade Cash to v2`() {
|
||||||
// Create some cash.
|
// Create some cash.
|
||||||
val anonymous = false
|
val result = a.services.startFlow(CashIssueFlow(Amount(1000, USD), OpaqueBytes.of(1), notary)).resultFuture
|
||||||
val result = a.services.startFlow(CashIssueFlow(Amount(1000, USD), a.info.legalIdentity, a.info.legalIdentity, OpaqueBytes.of(1), notary, anonymous)).resultFuture
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val stx = result.getOrThrow().stx
|
val stx = result.getOrThrow().stx
|
||||||
val stateAndRef = stx.tx.outRef<Cash.State>(0)
|
val stateAndRef = stx.tx.outRef<Cash.State>(0)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package net.corda.docs
|
package net.corda.docs
|
||||||
|
|
||||||
import net.corda.contracts.asset.Cash
|
import net.corda.contracts.asset.Cash
|
||||||
import net.corda.core.concurrent.CordaFuture
|
|
||||||
import net.corda.finance.DOLLARS
|
|
||||||
import net.corda.core.internal.concurrent.transpose
|
import net.corda.core.internal.concurrent.transpose
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.messaging.vaultTrackBy
|
import net.corda.core.messaging.vaultTrackBy
|
||||||
@ -10,6 +8,7 @@ import net.corda.core.node.services.ServiceInfo
|
|||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
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.finance.DOLLARS
|
||||||
import net.corda.flows.CashIssueFlow
|
import net.corda.flows.CashIssueFlow
|
||||||
import net.corda.flows.CashPaymentFlow
|
import net.corda.flows.CashPaymentFlow
|
||||||
import net.corda.node.services.startFlowPermission
|
import net.corda.node.services.startFlowPermission
|
||||||
@ -17,13 +16,7 @@ import net.corda.node.services.transactions.ValidatingNotaryService
|
|||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.expect
|
|
||||||
import net.corda.testing.expectEvents
|
|
||||||
import net.corda.testing.parallel
|
|
||||||
import net.corda.testing.sequence
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.*
|
|
||||||
import kotlin.concurrent.thread
|
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class IntegrationTestingTutorial {
|
class IntegrationTestingTutorial {
|
||||||
@ -32,7 +25,8 @@ class IntegrationTestingTutorial {
|
|||||||
// START 1
|
// START 1
|
||||||
driver {
|
driver {
|
||||||
val aliceUser = User("aliceUser", "testPassword1", permissions = setOf(
|
val aliceUser = User("aliceUser", "testPassword1", permissions = setOf(
|
||||||
startFlowPermission<CashIssueFlow>()
|
startFlowPermission<CashIssueFlow>(),
|
||||||
|
startFlowPermission<CashPaymentFlow>()
|
||||||
))
|
))
|
||||||
val bobUser = User("bobUser", "testPassword2", permissions = setOf(
|
val bobUser = User("bobUser", "testPassword2", permissions = setOf(
|
||||||
startFlowPermission<CashPaymentFlow>()
|
startFlowPermission<CashPaymentFlow>()
|
||||||
@ -62,20 +56,21 @@ class IntegrationTestingTutorial {
|
|||||||
|
|
||||||
// START 4
|
// START 4
|
||||||
val issueRef = OpaqueBytes.of(0)
|
val issueRef = OpaqueBytes.of(0)
|
||||||
val futures = Stack<CordaFuture<*>>()
|
|
||||||
(1..10).map { i ->
|
(1..10).map { i ->
|
||||||
thread {
|
aliceProxy.startFlow(::CashIssueFlow,
|
||||||
futures.push(aliceProxy.startFlow(::CashIssueFlow,
|
i.DOLLARS,
|
||||||
|
issueRef,
|
||||||
|
notary.nodeInfo.notaryIdentity
|
||||||
|
).returnValue
|
||||||
|
}.transpose().getOrThrow()
|
||||||
|
// We wait for all of the issuances to run before we start making payments
|
||||||
|
(1..10).map { i ->
|
||||||
|
aliceProxy.startFlow(::CashPaymentFlow,
|
||||||
i.DOLLARS,
|
i.DOLLARS,
|
||||||
bob.nodeInfo.legalIdentity,
|
bob.nodeInfo.legalIdentity,
|
||||||
alice.nodeInfo.legalIdentity,
|
|
||||||
issueRef,
|
|
||||||
notary.nodeInfo.notaryIdentity,
|
|
||||||
true
|
true
|
||||||
).returnValue)
|
).returnValue
|
||||||
}
|
}.transpose().getOrThrow()
|
||||||
}.forEach(Thread::join) // Ensure the stack of futures is populated.
|
|
||||||
futures.forEach { it.getOrThrow() }
|
|
||||||
|
|
||||||
bobVaultUpdates.expectEvents {
|
bobVaultUpdates.expectEvents {
|
||||||
parallel(
|
parallel(
|
||||||
|
@ -128,7 +128,7 @@ fun generateTransactions(proxy: CordaRPCOps) {
|
|||||||
proxy.startFlow(::CashPaymentFlow, Amount(quantity, USD), me)
|
proxy.startFlow(::CashPaymentFlow, Amount(quantity, USD), me)
|
||||||
} else {
|
} else {
|
||||||
val quantity = Math.abs(random.nextLong() % 1000)
|
val quantity = Math.abs(random.nextLong() % 1000)
|
||||||
proxy.startFlow(::CashIssueFlow, Amount(quantity, USD), me, me, issueRef, notary, true)
|
proxy.startFlow(::CashIssueFlow, Amount(quantity, USD), issueRef, notary)
|
||||||
ownedQuantity += quantity
|
ownedQuantity += quantity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,8 +133,7 @@ object TopupIssuerFlow {
|
|||||||
// invoke Cash subflow to issue Asset
|
// invoke Cash subflow to issue Asset
|
||||||
progressTracker.currentStep = ISSUING
|
progressTracker.currentStep = ISSUING
|
||||||
val issuer = serviceHub.myInfo.legalIdentity
|
val issuer = serviceHub.myInfo.legalIdentity
|
||||||
val issueRecipient = serviceHub.myInfo.legalIdentity
|
val issueCashFlow = CashIssueFlow(amount, issuerPartyRef, notaryParty)
|
||||||
val issueCashFlow = CashIssueFlow(amount, issueRecipient, issuer, issuerPartyRef, notaryParty, anonymous = false)
|
|
||||||
val issueTx = subFlow(issueCashFlow)
|
val issueTx = subFlow(issueCashFlow)
|
||||||
// NOTE: issueCashFlow performs a Broadcast (which stores a local copy of the txn to the ledger)
|
// NOTE: issueCashFlow performs a Broadcast (which stores a local copy of the txn to the ledger)
|
||||||
// short-circuit when issuing to self
|
// short-circuit when issuing to self
|
||||||
|
@ -65,11 +65,8 @@ class CustomVaultQueryTest {
|
|||||||
private fun issueCashForCurrency(amountToIssue: Amount<Currency>) {
|
private fun issueCashForCurrency(amountToIssue: Amount<Currency>) {
|
||||||
// Use NodeA as issuer and create some dollars
|
// Use NodeA as issuer and create some dollars
|
||||||
val flowHandle1 = nodeA.services.startFlow(CashIssueFlow(amountToIssue,
|
val flowHandle1 = nodeA.services.startFlow(CashIssueFlow(amountToIssue,
|
||||||
nodeA.info.legalIdentity,
|
|
||||||
nodeA.info.legalIdentity,
|
|
||||||
OpaqueBytes.of(0x01),
|
OpaqueBytes.of(0x01),
|
||||||
notaryNode.info.notaryIdentity,
|
notaryNode.info.notaryIdentity))
|
||||||
anonymous = false))
|
|
||||||
// Wait for the flow to stop and print
|
// Wait for the flow to stop and print
|
||||||
flowHandle1.resultFuture.getOrThrow()
|
flowHandle1.resultFuture.getOrThrow()
|
||||||
}
|
}
|
||||||
|
@ -45,22 +45,16 @@ class FxTransactionBuildTutorialTest {
|
|||||||
fun `Run ForeignExchangeFlow to completion`() {
|
fun `Run ForeignExchangeFlow to completion`() {
|
||||||
// Use NodeA as issuer and create some dollars
|
// Use NodeA as issuer and create some dollars
|
||||||
val flowHandle1 = nodeA.services.startFlow(CashIssueFlow(DOLLARS(1000),
|
val flowHandle1 = nodeA.services.startFlow(CashIssueFlow(DOLLARS(1000),
|
||||||
nodeA.info.legalIdentity,
|
|
||||||
nodeA.info.legalIdentity,
|
|
||||||
OpaqueBytes.of(0x01),
|
OpaqueBytes.of(0x01),
|
||||||
notaryNode.info.notaryIdentity,
|
notaryNode.info.notaryIdentity))
|
||||||
false))
|
|
||||||
// Wait for the flow to stop and print
|
// Wait for the flow to stop and print
|
||||||
flowHandle1.resultFuture.getOrThrow()
|
flowHandle1.resultFuture.getOrThrow()
|
||||||
printBalances()
|
printBalances()
|
||||||
|
|
||||||
// Using NodeB as Issuer create some pounds.
|
// Using NodeB as Issuer create some pounds.
|
||||||
val flowHandle2 = nodeB.services.startFlow(CashIssueFlow(POUNDS(1000),
|
val flowHandle2 = nodeB.services.startFlow(CashIssueFlow(POUNDS(1000),
|
||||||
nodeB.info.legalIdentity,
|
|
||||||
nodeB.info.legalIdentity,
|
|
||||||
OpaqueBytes.of(0x01),
|
OpaqueBytes.of(0x01),
|
||||||
notaryNode.info.notaryIdentity,
|
notaryNode.info.notaryIdentity))
|
||||||
false))
|
|
||||||
// Wait for flow to come to an end and print
|
// Wait for flow to come to an end and print
|
||||||
flowHandle2.resultFuture.getOrThrow()
|
flowHandle2.resultFuture.getOrThrow()
|
||||||
printBalances()
|
printBalances()
|
||||||
|
@ -6,11 +6,13 @@ import net.corda.core.messaging.CordaRPCOps
|
|||||||
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.utilities.OpaqueBytes
|
import net.corda.core.utilities.OpaqueBytes
|
||||||
|
import net.corda.core.utilities.getOrThrow
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A command to initiate the cash flow with.
|
* A command to initiate the cash flow with.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated("Please use the flows directly, these will be removed in a later release")
|
||||||
sealed class CashFlowCommand {
|
sealed class CashFlowCommand {
|
||||||
abstract fun startFlow(proxy: CordaRPCOps): FlowHandle<AbstractCashFlow.Result>
|
abstract fun startFlow(proxy: CordaRPCOps): FlowHandle<AbstractCashFlow.Result>
|
||||||
|
|
||||||
@ -22,7 +24,10 @@ sealed class CashFlowCommand {
|
|||||||
val recipient: Party,
|
val recipient: Party,
|
||||||
val notary: Party,
|
val notary: Party,
|
||||||
val anonymous: Boolean) : CashFlowCommand() {
|
val anonymous: Boolean) : CashFlowCommand() {
|
||||||
override fun startFlow(proxy: CordaRPCOps) = proxy.startFlow(::CashIssueFlow, amount, recipient, proxy.nodeIdentity().legalIdentity, issueRef, notary, anonymous)
|
override fun startFlow(proxy: CordaRPCOps): FlowHandle<AbstractCashFlow.Result> {
|
||||||
|
proxy.startFlow(::CashIssueFlow, amount, issueRef, notary).returnValue.getOrThrow()
|
||||||
|
return proxy.startFlow(::CashPaymentFlow, amount, recipient, anonymous)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,51 +15,37 @@ import net.corda.core.utilities.ProgressTracker
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiates a flow that produces cash issuance transaction.
|
* Initiates a flow that self-issues cash (which should then be sent to recipient(s) using a payment transaction).
|
||||||
|
*
|
||||||
|
* We issue cash only to ourselves so that all KYC/AML checks on payments are enforced consistently, rather than risk
|
||||||
|
* checks for issuance and payments differing. Outside of test scenarios it would be extremely unusual to issue cash
|
||||||
|
* and immediately transfer it, so impact of this limitation is considered minimal.
|
||||||
*
|
*
|
||||||
* @param amount the amount of currency to issue.
|
* @param amount the amount of currency to issue.
|
||||||
* @param issuerBankPartyRef a reference to put on the issued currency.
|
* @param issuerBankPartyRef a reference to put on the issued currency.
|
||||||
* @param issueTo the party who should own the currency after it is issued.
|
|
||||||
* @param notary the notary to set on the output states.
|
* @param notary the notary to set on the output states.
|
||||||
*/
|
*/
|
||||||
@StartableByRPC
|
@StartableByRPC
|
||||||
class CashIssueFlow(val amount: Amount<Currency>,
|
class CashIssueFlow(val amount: Amount<Currency>,
|
||||||
val issueTo: Party,
|
|
||||||
val issuerBankParty
|
|
||||||
: Party,
|
|
||||||
val issuerBankPartyRef: OpaqueBytes,
|
val issuerBankPartyRef: OpaqueBytes,
|
||||||
val notary: Party,
|
val notary: Party,
|
||||||
val anonymous: Boolean,
|
|
||||||
progressTracker: ProgressTracker) : AbstractCashFlow<AbstractCashFlow.Result>(progressTracker) {
|
progressTracker: ProgressTracker) : AbstractCashFlow<AbstractCashFlow.Result>(progressTracker) {
|
||||||
constructor(amount: Amount<Currency>,
|
constructor(amount: Amount<Currency>,
|
||||||
issuerBankPartyRef: OpaqueBytes,
|
issuerBankPartyRef: OpaqueBytes,
|
||||||
issuerBankParty: Party,
|
notary: Party) : this(amount, issuerBankPartyRef, notary, tracker())
|
||||||
issueTo: Party,
|
|
||||||
notary: Party) : this(amount, issueTo, issuerBankParty, issuerBankPartyRef, notary, true, tracker())
|
|
||||||
constructor(amount: Amount<Currency>,
|
|
||||||
issueTo: Party,
|
|
||||||
issuerBankParty: Party,
|
|
||||||
issuerBankPartyRef: OpaqueBytes,
|
|
||||||
notary: Party,
|
|
||||||
anonymous: Boolean) : this(amount, issueTo, issuerBankParty, issuerBankPartyRef, notary, anonymous, tracker())
|
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): AbstractCashFlow.Result {
|
override fun call(): AbstractCashFlow.Result {
|
||||||
progressTracker.currentStep = GENERATING_ID
|
val issuerCert = serviceHub.myInfo.legalIdentityAndCert
|
||||||
val txIdentities = if (anonymous) {
|
|
||||||
subFlow(TransactionKeyFlow(issueTo))
|
|
||||||
} else {
|
|
||||||
emptyMap<Party, AnonymousParty>()
|
|
||||||
}
|
|
||||||
val anonymousRecipient = txIdentities[issueTo] ?: issueTo
|
|
||||||
progressTracker.currentStep = GENERATING_TX
|
progressTracker.currentStep = GENERATING_TX
|
||||||
val builder: TransactionBuilder = TransactionBuilder(notary)
|
val builder: TransactionBuilder = TransactionBuilder(notary)
|
||||||
val issuer = issuerBankParty.ref(issuerBankPartyRef)
|
val issuer = issuerCert.party.ref(issuerBankPartyRef)
|
||||||
val signers = Cash().generateIssue(builder, amount.issuedBy(issuer), anonymousRecipient, notary)
|
val signers = Cash().generateIssue(builder, amount.issuedBy(issuer), issuerCert.party, notary)
|
||||||
progressTracker.currentStep = SIGNING_TX
|
progressTracker.currentStep = SIGNING_TX
|
||||||
val tx = serviceHub.signInitialTransaction(builder, signers)
|
val tx = serviceHub.signInitialTransaction(builder, signers)
|
||||||
progressTracker.currentStep = FINALISING_TX
|
progressTracker.currentStep = FINALISING_TX
|
||||||
subFlow(FinalityFlow(tx))
|
subFlow(FinalityFlow(tx))
|
||||||
return Result(tx, anonymousRecipient)
|
return Result(tx, issuerCert.party)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,11 +33,7 @@ class CashExitFlowTests {
|
|||||||
bankOfCorda = bankOfCordaNode.info.legalIdentity
|
bankOfCorda = bankOfCordaNode.info.legalIdentity
|
||||||
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance,
|
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture
|
||||||
bankOfCorda,
|
|
||||||
bankOfCorda, ref,
|
|
||||||
notary,
|
|
||||||
anonymous = true)).resultFuture
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
future.getOrThrow()
|
future.getOrThrow()
|
||||||
}
|
}
|
||||||
@ -50,8 +46,7 @@ class CashExitFlowTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `exit some cash`() {
|
fun `exit some cash`() {
|
||||||
val exitAmount = 500.DOLLARS
|
val exitAmount = 500.DOLLARS
|
||||||
val future = bankOfCordaNode.services.startFlow(CashExitFlow(exitAmount,
|
val future = bankOfCordaNode.services.startFlow(CashExitFlow(exitAmount, ref)).resultFuture
|
||||||
ref)).resultFuture
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val exitTx = future.getOrThrow().stx.tx
|
val exitTx = future.getOrThrow().stx.tx
|
||||||
val expected = (initialBalance - exitAmount).`issued by`(bankOfCorda.ref(ref))
|
val expected = (initialBalance - exitAmount).`issued by`(bankOfCorda.ref(ref))
|
||||||
@ -64,8 +59,7 @@ class CashExitFlowTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `exit zero cash`() {
|
fun `exit zero cash`() {
|
||||||
val expected = 0.DOLLARS
|
val expected = 0.DOLLARS
|
||||||
val future = bankOfCordaNode.services.startFlow(CashExitFlow(expected,
|
val future = bankOfCordaNode.services.startFlow(CashExitFlow(expected, ref)).resultFuture
|
||||||
ref)).resultFuture
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
assertFailsWith<CashException> {
|
assertFailsWith<CashException> {
|
||||||
future.getOrThrow()
|
future.getOrThrow()
|
||||||
|
@ -42,12 +42,7 @@ class CashIssueFlowTests {
|
|||||||
fun `issue some cash`() {
|
fun `issue some cash`() {
|
||||||
val expected = 500.DOLLARS
|
val expected = 500.DOLLARS
|
||||||
val ref = OpaqueBytes.of(0x01)
|
val ref = OpaqueBytes.of(0x01)
|
||||||
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(expected,
|
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(expected, ref, notary)).resultFuture
|
||||||
bankOfCorda,
|
|
||||||
bankOfCorda,
|
|
||||||
ref,
|
|
||||||
notary,
|
|
||||||
anonymous = true)).resultFuture
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val issueTx = future.getOrThrow().stx
|
val issueTx = future.getOrThrow().stx
|
||||||
val output = issueTx.tx.outputsOfType<Cash.State>().single()
|
val output = issueTx.tx.outputsOfType<Cash.State>().single()
|
||||||
@ -58,12 +53,7 @@ class CashIssueFlowTests {
|
|||||||
fun `issue zero cash`() {
|
fun `issue zero cash`() {
|
||||||
val expected = 0.DOLLARS
|
val expected = 0.DOLLARS
|
||||||
val ref = OpaqueBytes.of(0x01)
|
val ref = OpaqueBytes.of(0x01)
|
||||||
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(expected,
|
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(expected, ref, notary)).resultFuture
|
||||||
bankOfCorda,
|
|
||||||
bankOfCorda,
|
|
||||||
ref,
|
|
||||||
notary,
|
|
||||||
anonymous = true)).resultFuture
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
assertFailsWith<IllegalArgumentException> {
|
assertFailsWith<IllegalArgumentException> {
|
||||||
future.getOrThrow()
|
future.getOrThrow()
|
||||||
|
@ -37,12 +37,7 @@ class CashPaymentFlowTests {
|
|||||||
notary = notaryNode.info.notaryIdentity
|
notary = notaryNode.info.notaryIdentity
|
||||||
bankOfCorda = bankOfCordaNode.info.legalIdentity
|
bankOfCorda = bankOfCordaNode.info.legalIdentity
|
||||||
|
|
||||||
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance,
|
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture
|
||||||
bankOfCorda,
|
|
||||||
bankOfCorda,
|
|
||||||
ref,
|
|
||||||
notary,
|
|
||||||
true)).resultFuture
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
future.getOrThrow()
|
future.getOrThrow()
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ class NodePerformanceTests {
|
|||||||
a.rpcClientToNode().use("A", "A") { connection ->
|
a.rpcClientToNode().use("A", "A") { connection ->
|
||||||
println("ISSUING")
|
println("ISSUING")
|
||||||
val doneFutures = (1..100).toList().parallelStream().map {
|
val doneFutures = (1..100).toList().parallelStream().map {
|
||||||
connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), a.nodeInfo.legalIdentity, a.nodeInfo.notaryIdentity).returnValue
|
connection.proxy.startFlow(::CashIssueFlow, 1.DOLLARS, OpaqueBytes.of(0), a.nodeInfo.notaryIdentity).returnValue
|
||||||
}.toList()
|
}.toList()
|
||||||
doneFutures.transpose().get()
|
doneFutures.transpose().get()
|
||||||
println("STARTING PAYMENT")
|
println("STARTING PAYMENT")
|
||||||
|
@ -134,14 +134,10 @@ class DistributedServiceTests : DriverBasedTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun issueCash(amount: Amount<Currency>) {
|
private fun issueCash(amount: Amount<Currency>) {
|
||||||
val issueHandle = aliceProxy.startFlow(
|
aliceProxy.startFlow(::CashIssueFlow, amount, OpaqueBytes.of(0), raftNotaryIdentity).returnValue.getOrThrow()
|
||||||
::CashIssueFlow,
|
|
||||||
amount, OpaqueBytes.of(0), alice.nodeInfo.legalIdentity, raftNotaryIdentity)
|
|
||||||
issueHandle.returnValue.getOrThrow()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun paySelf(amount: Amount<Currency>) {
|
private fun paySelf(amount: Amount<Currency>) {
|
||||||
val payHandle = aliceProxy.startFlow(::CashPaymentFlow, amount, alice.nodeInfo.legalIdentity)
|
aliceProxy.startFlow(::CashPaymentFlow, amount, alice.nodeInfo.legalIdentity).returnValue.getOrThrow()
|
||||||
payHandle.returnValue.getOrThrow()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,9 +93,8 @@ class CordaRPCOpsImplTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tell the monitoring service node to issue some cash
|
// Tell the monitoring service node to issue some cash
|
||||||
val anonymous = false
|
|
||||||
val recipient = aliceNode.info.legalIdentity
|
val recipient = aliceNode.info.legalIdentity
|
||||||
val result = rpc.startFlow(::CashIssueFlow, Amount(quantity, GBP), recipient, rpc.nodeIdentity().legalIdentity, ref, notaryNode.info.notaryIdentity, anonymous)
|
val result = rpc.startFlow(::CashIssueFlow, Amount(quantity, GBP), ref, notaryNode.info.notaryIdentity)
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
|
|
||||||
var issueSmId: StateMachineRunId? = null
|
var issueSmId: StateMachineRunId? = null
|
||||||
@ -133,11 +132,8 @@ class CordaRPCOpsImplTest {
|
|||||||
val anonymous = false
|
val anonymous = false
|
||||||
val result = rpc.startFlow(::CashIssueFlow,
|
val result = rpc.startFlow(::CashIssueFlow,
|
||||||
100.DOLLARS,
|
100.DOLLARS,
|
||||||
aliceNode.info.legalIdentity,
|
|
||||||
rpc.nodeIdentity().legalIdentity,
|
|
||||||
OpaqueBytes(ByteArray(1, { 1 })),
|
OpaqueBytes(ByteArray(1, { 1 })),
|
||||||
notaryNode.info.notaryIdentity,
|
notaryNode.info.notaryIdentity
|
||||||
false
|
|
||||||
)
|
)
|
||||||
|
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
@ -212,14 +208,7 @@ class CordaRPCOpsImplTest {
|
|||||||
fun `cash command by user not permissioned for cash`() {
|
fun `cash command by user not permissioned for cash`() {
|
||||||
CURRENT_RPC_CONTEXT.set(RpcContext(User("user", "pwd", permissions = emptySet())))
|
CURRENT_RPC_CONTEXT.set(RpcContext(User("user", "pwd", permissions = emptySet())))
|
||||||
assertThatExceptionOfType(PermissionException::class.java).isThrownBy {
|
assertThatExceptionOfType(PermissionException::class.java).isThrownBy {
|
||||||
rpc.startFlow(::CashIssueFlow,
|
rpc.startFlow(::CashIssueFlow, Amount(100, USD), OpaqueBytes(ByteArray(1, { 1 })), notaryNode.info.notaryIdentity)
|
||||||
Amount(100, USD),
|
|
||||||
aliceNode.info.legalIdentity,
|
|
||||||
rpc.nodeIdentity().legalIdentity,
|
|
||||||
OpaqueBytes(ByteArray(1, { 1 })),
|
|
||||||
notaryNode.info.notaryIdentity,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,11 +330,8 @@ class FlowFrameworkTests {
|
|||||||
assertEquals(notary1.info.notaryIdentity, notary2.info.notaryIdentity)
|
assertEquals(notary1.info.notaryIdentity, notary2.info.notaryIdentity)
|
||||||
node1.services.startFlow(CashIssueFlow(
|
node1.services.startFlow(CashIssueFlow(
|
||||||
2000.DOLLARS,
|
2000.DOLLARS,
|
||||||
node1.info.legalIdentity,
|
|
||||||
node1.info.legalIdentity,
|
|
||||||
OpaqueBytes.of(0x01),
|
OpaqueBytes.of(0x01),
|
||||||
notary1.info.notaryIdentity,
|
notary1.info.notaryIdentity))
|
||||||
anonymous = false))
|
|
||||||
// We pay a couple of times, the notary picking should go round robin
|
// We pay a couple of times, the notary picking should go round robin
|
||||||
for (i in 1..3) {
|
for (i in 1..3) {
|
||||||
val flow = node1.services.startFlow(CashPaymentFlow(500.DOLLARS, node2.info.legalIdentity, anonymous = false))
|
val flow = node1.services.startFlow(CashPaymentFlow(500.DOLLARS, node2.info.legalIdentity, anonymous = false))
|
||||||
|
@ -9,6 +9,7 @@ import net.corda.core.node.services.vault.QueryCriteria
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.flows.CashIssueFlow
|
import net.corda.flows.CashIssueFlow
|
||||||
|
import net.corda.flows.CashPaymentFlow
|
||||||
import net.corda.node.services.startFlowPermission
|
import net.corda.node.services.startFlowPermission
|
||||||
import net.corda.node.services.transactions.SimpleNotaryService
|
import net.corda.node.services.transactions.SimpleNotaryService
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
@ -20,7 +21,9 @@ class BankOfCordaRPCClientTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `issuer flow via RPC`() {
|
fun `issuer flow via RPC`() {
|
||||||
driver(dsl = {
|
driver(dsl = {
|
||||||
val bocManager = User("bocManager", "password1", permissions = setOf(startFlowPermission<CashIssueFlow>()))
|
val bocManager = User("bocManager", "password1", permissions = setOf(
|
||||||
|
startFlowPermission<CashIssueFlow>(),
|
||||||
|
startFlowPermission<CashPaymentFlow>()))
|
||||||
val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet())
|
val bigCorpCFO = User("bigCorpCFO", "password2", permissions = emptySet())
|
||||||
val (nodeBankOfCorda, nodeBigCorporation) = listOf(
|
val (nodeBankOfCorda, nodeBigCorporation) = listOf(
|
||||||
startNode(BOC.name, setOf(ServiceInfo(SimpleNotaryService.type)), listOf(bocManager)),
|
startNode(BOC.name, setOf(ServiceInfo(SimpleNotaryService.type)), listOf(bocManager)),
|
||||||
@ -47,10 +50,12 @@ class BankOfCordaRPCClientTest {
|
|||||||
bocProxy.startFlow(
|
bocProxy.startFlow(
|
||||||
::CashIssueFlow,
|
::CashIssueFlow,
|
||||||
1000.DOLLARS,
|
1000.DOLLARS,
|
||||||
nodeBigCorporation.nodeInfo.legalIdentity,
|
|
||||||
nodeBankOfCorda.nodeInfo.legalIdentity,
|
|
||||||
BIG_CORP_PARTY_REF,
|
BIG_CORP_PARTY_REF,
|
||||||
nodeBankOfCorda.nodeInfo.notaryIdentity,
|
nodeBankOfCorda.nodeInfo.notaryIdentity).returnValue.getOrThrow()
|
||||||
|
bocProxy.startFlow(
|
||||||
|
::CashPaymentFlow,
|
||||||
|
1000.DOLLARS,
|
||||||
|
nodeBigCorporation.nodeInfo.legalIdentity,
|
||||||
anonymous).returnValue.getOrThrow()
|
anonymous).returnValue.getOrThrow()
|
||||||
|
|
||||||
// Check Bank of Corda Vault Updates
|
// Check Bank of Corda Vault Updates
|
||||||
|
@ -70,7 +70,9 @@ private class BankOfCordaDriver {
|
|||||||
startFlowPermission<CashPaymentFlow>(),
|
startFlowPermission<CashPaymentFlow>(),
|
||||||
startFlowPermission<CashIssueFlow>(),
|
startFlowPermission<CashIssueFlow>(),
|
||||||
startFlowPermission<CashExitFlow>()))
|
startFlowPermission<CashExitFlow>()))
|
||||||
val bigCorpUser = User(BIGCORP_USERNAME, "test", permissions = setOf(startFlowPermission<CashPaymentFlow>()))
|
val bigCorpUser = User(BIGCORP_USERNAME, "test",
|
||||||
|
permissions = setOf(
|
||||||
|
startFlowPermission<CashPaymentFlow>()))
|
||||||
startNode(DUMMY_NOTARY.name, setOf(ServiceInfo(SimpleNotaryService.type)))
|
startNode(DUMMY_NOTARY.name, setOf(ServiceInfo(SimpleNotaryService.type)))
|
||||||
val bankOfCorda = startNode(
|
val bankOfCorda = startNode(
|
||||||
BOC.name,
|
BOC.name,
|
||||||
|
@ -9,6 +9,7 @@ import net.corda.core.utilities.NetworkHostAndPort
|
|||||||
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.flows.CashIssueFlow
|
import net.corda.flows.CashIssueFlow
|
||||||
|
import net.corda.flows.CashPaymentFlow
|
||||||
import net.corda.testing.http.HttpApi
|
import net.corda.testing.http.HttpApi
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -28,6 +29,8 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* RPC API
|
* RPC API
|
||||||
|
*
|
||||||
|
* @return a pair of the issuing and payment transactions.
|
||||||
*/
|
*/
|
||||||
fun requestRPCIssue(params: IssueRequestParams): SignedTransaction {
|
fun requestRPCIssue(params: IssueRequestParams): SignedTransaction {
|
||||||
val client = CordaRPCClient(hostAndPort)
|
val client = CordaRPCClient(hostAndPort)
|
||||||
@ -38,8 +41,6 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) {
|
|||||||
// Resolve parties via RPC
|
// Resolve parties via RPC
|
||||||
val issueToParty = rpc.partyFromX500Name(params.issueToPartyName)
|
val issueToParty = rpc.partyFromX500Name(params.issueToPartyName)
|
||||||
?: throw Exception("Unable to locate ${params.issueToPartyName} in Network Map Service")
|
?: throw Exception("Unable to locate ${params.issueToPartyName} in Network Map Service")
|
||||||
val issuerBankParty = rpc.partyFromX500Name(params.issuerBankName)
|
|
||||||
?: throw Exception("Unable to locate ${params.issuerBankName} in Network Map Service")
|
|
||||||
val notaryLegalIdentity = rpc.partyFromX500Name(params.notaryName)
|
val notaryLegalIdentity = rpc.partyFromX500Name(params.notaryName)
|
||||||
?: throw IllegalStateException("Unable to locate ${params.notaryName} in Network Map Service")
|
?: throw IllegalStateException("Unable to locate ${params.notaryName} in Network Map Service")
|
||||||
val notaryNode = rpc.nodeIdentityFromParty(notaryLegalIdentity)
|
val notaryNode = rpc.nodeIdentityFromParty(notaryLegalIdentity)
|
||||||
@ -48,7 +49,9 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) {
|
|||||||
val amount = Amount(params.amount, Currency.getInstance(params.currency))
|
val amount = Amount(params.amount, Currency.getInstance(params.currency))
|
||||||
val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte())
|
val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte())
|
||||||
|
|
||||||
return rpc.startFlow(::CashIssueFlow, amount, issueToParty, issuerBankParty, issuerBankPartyRef, notaryNode.notaryIdentity, params.anonymous)
|
rpc.startFlow(::CashIssueFlow, amount, issuerBankPartyRef, notaryNode.notaryIdentity)
|
||||||
|
.returnValue.getOrThrow().stx
|
||||||
|
return rpc.startFlow(::CashPaymentFlow, amount, issueToParty, params.anonymous)
|
||||||
.returnValue.getOrThrow().stx
|
.returnValue.getOrThrow().stx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import net.corda.core.utilities.OpaqueBytes
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.flows.CashIssueFlow
|
import net.corda.flows.CashIssueFlow
|
||||||
|
import net.corda.flows.CashPaymentFlow
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -53,12 +54,13 @@ class BankOfCordaWebApi(val rpc: CordaRPCOps) {
|
|||||||
|
|
||||||
val amount = Amount(params.amount, Currency.getInstance(params.currency))
|
val amount = Amount(params.amount, Currency.getInstance(params.currency))
|
||||||
val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte())
|
val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte())
|
||||||
val anonymous = params.anonymous
|
|
||||||
|
|
||||||
// invoke client side of Issuer Flow: IssuanceRequester
|
// invoke client side of Issuer Flow: IssuanceRequester
|
||||||
// The line below blocks and waits for the future to resolve.
|
// The line below blocks and waits for the future to resolve.
|
||||||
return try {
|
return try {
|
||||||
rpc.startFlow(::CashIssueFlow, amount, issueToParty, issuerBankParty, issuerBankPartyRef, notaryNode.notaryIdentity, anonymous).returnValue.getOrThrow()
|
rpc.startFlow(::CashIssueFlow, amount, issuerBankPartyRef, notaryNode.notaryIdentity).returnValue.getOrThrow()
|
||||||
|
rpc.startFlow(::CashPaymentFlow, amount, issueToParty, params.anonymous)
|
||||||
|
.returnValue.getOrThrow().stx
|
||||||
logger.info("Issue request completed successfully: $params")
|
logger.info("Issue request completed successfully: $params")
|
||||||
Response.status(Response.Status.CREATED).build()
|
Response.status(Response.Status.CREATED).build()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -7,6 +7,7 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.core.utilities.millis
|
import net.corda.core.utilities.millis
|
||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.flows.CashIssueFlow
|
import net.corda.flows.CashIssueFlow
|
||||||
|
import net.corda.flows.CashPaymentFlow
|
||||||
import net.corda.node.services.startFlowPermission
|
import net.corda.node.services.startFlowPermission
|
||||||
import net.corda.node.services.transactions.SimpleNotaryService
|
import net.corda.node.services.transactions.SimpleNotaryService
|
||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
@ -27,7 +28,9 @@ class TraderDemoTest : NodeBasedTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun `runs trader demo`() {
|
fun `runs trader demo`() {
|
||||||
val demoUser = User("demo", "demo", setOf(startFlowPermission<SellerFlow>()))
|
val demoUser = User("demo", "demo", setOf(startFlowPermission<SellerFlow>()))
|
||||||
val bankUser = User("user1", "test", permissions = setOf(startFlowPermission<CashIssueFlow>(),
|
val bankUser = User("user1", "test", permissions = setOf(
|
||||||
|
startFlowPermission<CashIssueFlow>(),
|
||||||
|
startFlowPermission<CashPaymentFlow>(),
|
||||||
startFlowPermission<CommercialPaperIssueFlow>()))
|
startFlowPermission<CommercialPaperIssueFlow>()))
|
||||||
val (nodeA, nodeB, bankNode) = listOf(
|
val (nodeA, nodeB, bankNode) = listOf(
|
||||||
startNode(DUMMY_BANK_A.name, rpcUsers = listOf(demoUser)),
|
startNode(DUMMY_BANK_A.name, rpcUsers = listOf(demoUser)),
|
||||||
|
@ -16,6 +16,7 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.finance.USD
|
import net.corda.finance.USD
|
||||||
import net.corda.flows.CashIssueFlow
|
import net.corda.flows.CashIssueFlow
|
||||||
|
import net.corda.flows.CashPaymentFlow
|
||||||
import net.corda.node.services.vault.VaultSchemaV1
|
import net.corda.node.services.vault.VaultSchemaV1
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
import net.corda.testing.contracts.calculateRandomlySizedAmounts
|
import net.corda.testing.contracts.calculateRandomlySizedAmounts
|
||||||
@ -52,10 +53,10 @@ class TraderDemoClientApi(val rpc: CordaRPCOps) {
|
|||||||
?: throw IllegalStateException("Unable to locate notary node in network map cache")
|
?: throw IllegalStateException("Unable to locate notary node in network map cache")
|
||||||
val amounts = calculateRandomlySizedAmounts(amount, 3, 10, Random())
|
val amounts = calculateRandomlySizedAmounts(amount, 3, 10, Random())
|
||||||
val anonymous = false
|
val anonymous = false
|
||||||
// issue random amounts of currency up to the requested amount, in parallel
|
rpc.startFlow(::CashIssueFlow, amount, OpaqueBytes.of(1), notaryNode.notaryIdentity).returnValue.getOrThrow()
|
||||||
|
// pay random amounts of currency up to the requested amount, in parallel
|
||||||
val resultFutures = amounts.map { pennies ->
|
val resultFutures = amounts.map { pennies ->
|
||||||
rpc.startFlow(::CashIssueFlow, amount.copy(quantity = pennies), buyer, rpc.nodeIdentity().legalIdentity,
|
rpc.startFlow(::CashPaymentFlow, amount.copy(quantity = pennies), buyer, anonymous).returnValue
|
||||||
OpaqueBytes.of(1), notaryNode.notaryIdentity, anonymous).returnValue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resultFutures.transpose().getOrThrow()
|
resultFutures.transpose().getOrThrow()
|
||||||
|
@ -38,8 +38,7 @@ class ExplorerSimulation(val options: OptionSet) {
|
|||||||
val manager = User("manager", "test", permissions = setOf(
|
val manager = User("manager", "test", permissions = setOf(
|
||||||
startFlowPermission<CashIssueFlow>(),
|
startFlowPermission<CashIssueFlow>(),
|
||||||
startFlowPermission<CashPaymentFlow>(),
|
startFlowPermission<CashPaymentFlow>(),
|
||||||
startFlowPermission<CashExitFlow>(),
|
startFlowPermission<CashExitFlow>())
|
||||||
startFlowPermission<IssuerFlow.IssuanceRequester>())
|
|
||||||
)
|
)
|
||||||
|
|
||||||
lateinit var notaryNode: NodeHandle
|
lateinit var notaryNode: NodeHandle
|
||||||
|
@ -36,8 +36,9 @@ import net.corda.explorer.views.bigDecimalFormatter
|
|||||||
import net.corda.explorer.views.byteFormatter
|
import net.corda.explorer.views.byteFormatter
|
||||||
import net.corda.explorer.views.stringConverter
|
import net.corda.explorer.views.stringConverter
|
||||||
import net.corda.flows.AbstractCashFlow
|
import net.corda.flows.AbstractCashFlow
|
||||||
|
import net.corda.flows.CashIssueFlow
|
||||||
|
import net.corda.flows.CashPaymentFlow
|
||||||
import net.corda.flows.CashFlowCommand
|
import net.corda.flows.CashFlowCommand
|
||||||
import net.corda.flows.IssuerFlow.IssuanceRequester
|
|
||||||
import org.controlsfx.dialog.ExceptionDialog
|
import org.controlsfx.dialog.ExceptionDialog
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
@ -94,12 +95,13 @@ class NewTransaction : Fragment() {
|
|||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
val handle: FlowHandle<AbstractCashFlow.Result> = if (command is CashFlowCommand.IssueCash) {
|
val handle: FlowHandle<AbstractCashFlow.Result> = if (command is CashFlowCommand.IssueCash) {
|
||||||
rpcProxy.value!!.startFlow(::IssuanceRequester,
|
rpcProxy.value!!.startFlow(::CashIssueFlow,
|
||||||
|
command.amount,
|
||||||
|
command.issueRef,
|
||||||
|
command.notary)
|
||||||
|
rpcProxy.value!!.startFlow(::CashPaymentFlow,
|
||||||
command.amount,
|
command.amount,
|
||||||
command.recipient,
|
command.recipient,
|
||||||
command.issueRef,
|
|
||||||
myIdentity.value!!.legalIdentity,
|
|
||||||
command.notary,
|
|
||||||
command.anonymous)
|
command.anonymous)
|
||||||
} else {
|
} else {
|
||||||
command.startFlow(rpcProxy.value!!)
|
command.startFlow(rpcProxy.value!!)
|
||||||
|
@ -118,7 +118,7 @@ class VerifierTests {
|
|||||||
val alice = aliceFuture.get()
|
val alice = aliceFuture.get()
|
||||||
val notary = notaryFuture.get()
|
val notary = notaryFuture.get()
|
||||||
startVerifier(notary)
|
startVerifier(notary)
|
||||||
alice.rpc.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), alice.nodeInfo.legalIdentity, notaryFuture.get().nodeInfo.notaryIdentity).returnValue.get()
|
alice.rpc.startFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), notaryFuture.get().nodeInfo.notaryIdentity).returnValue.get()
|
||||||
notary.waitUntilNumberOfVerifiers(1)
|
notary.waitUntilNumberOfVerifiers(1)
|
||||||
for (i in 1..10) {
|
for (i in 1..10) {
|
||||||
alice.rpc.startFlow(::CashPaymentFlow, 10.DOLLARS, alice.nodeInfo.legalIdentity).returnValue.get()
|
alice.rpc.startFlow(::CashPaymentFlow, 10.DOLLARS, alice.nodeInfo.legalIdentity).returnValue.get()
|
||||||
|
Loading…
Reference in New Issue
Block a user