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:
Ross Nicoll 2017-08-11 17:02:39 +01:00
parent 89476904fc
commit b76d036843
29 changed files with 111 additions and 168 deletions

View File

@ -108,14 +108,10 @@ class NodeMonitorModelTest : DriverBasedTest() {
@Test
fun `cash issue works end to end`() {
val anonymous = false
rpc.startFlow(::CashIssueFlow,
Amount(100, USD),
aliceNode.legalIdentity,
rpc.nodeIdentity().legalIdentity,
OpaqueBytes(ByteArray(1, { 1 })),
notaryNode.notaryIdentity,
anonymous
notaryNode.notaryIdentity
)
vaultUpdates.expectEvents(isStrict = false) {
@ -137,8 +133,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
@Test
fun `cash issue and move`() {
val anonymous = false
rpc.startFlow(::CashIssueFlow, 100.DOLLARS, aliceNode.legalIdentity, rpc.nodeIdentity().legalIdentity, OpaqueBytes.of(1),
notaryNode.notaryIdentity, anonymous).returnValue.getOrThrow()
rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), notaryNode.notaryIdentity).returnValue.getOrThrow()
rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, bobNode.legalIdentity, anonymous).returnValue.getOrThrow()
var issueSmId: StateMachineRunId? = null

View File

@ -71,7 +71,7 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
FlowHandle<AbstractCashFlow.Result> flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class,
DOLLARS(123), OpaqueBytes.of("1".getBytes()),
node.info.getLegalIdentity(), node.info.getLegalIdentity());
node.info.getLegalIdentity());
System.out.println("Started issuing cash, waiting on result");
flowHandle.getReturnValue().get();

View File

@ -78,8 +78,7 @@ class CordaRPCClientTest : NodeBasedTest() {
println("Creating proxy")
println("Starting flow")
val flowHandle = connection!!.proxy.startTrackedFlow(::CashIssueFlow,
20.DOLLARS, node.info.legalIdentity,
node.info.legalIdentity, OpaqueBytes.of(0), node.info.legalIdentity, true
20.DOLLARS, OpaqueBytes.of(0), node.info.legalIdentity
)
println("Started flow, waiting on result")
flowHandle.progress.subscribe {
@ -114,8 +113,7 @@ class CordaRPCClientTest : NodeBasedTest() {
assertTrue(startCash.isEmpty(), "Should not start with any cash")
val flowHandle = proxy.startFlow(::CashIssueFlow,
123.DOLLARS, node.info.legalIdentity,
node.info.legalIdentity, OpaqueBytes.of(0), node.info.legalIdentity, true
123.DOLLARS, OpaqueBytes.of(0), node.info.legalIdentity
)
println("Started issuing cash, waiting on result")
flowHandle.returnValue.get()
@ -141,15 +139,16 @@ class CordaRPCClientTest : NodeBasedTest() {
}
}
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,
123.DOLLARS, nodeIdentity,
nodeIdentity, OpaqueBytes.of(0), nodeIdentity,
true
123.DOLLARS,
OpaqueBytes.of(0),
nodeIdentity
).returnValue.getOrThrow()
proxy.startFlowDynamic(CashIssueFlow::class.java,
1000.DOLLARS, OpaqueBytes.of(0),
nodeIdentity, nodeIdentity).returnValue.getOrThrow()
1000.DOLLARS,
OpaqueBytes.of(0),
nodeIdentity).returnValue.getOrThrow()
assertEquals(2, countRpcFlows)
assertEquals(1, countShellFlows)
}

View File

@ -75,7 +75,7 @@ public class StandaloneCordaRPCJavaClientTest {
FlowHandle<AbstractCashFlow.Result> flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class,
dollars123, OpaqueBytes.of("1".getBytes()),
notaryNode.getLegalIdentity(), notaryNode.getLegalIdentity());
notaryNode.getLegalIdentity());
System.out.println("Started issuing cash, waiting on result");
flowHandle.getReturnValue().get();

View File

@ -3,7 +3,6 @@ package net.corda.kotlin.rpc
import com.google.common.hash.Hashing
import com.google.common.hash.HashingInputStream
import net.corda.client.rpc.CordaRPCConnection
import net.corda.client.rpc.notUsed
import net.corda.contracts.asset.Cash
import net.corda.contracts.getCashBalance
import net.corda.contracts.getCashBalances
@ -96,7 +95,7 @@ class StandaloneCordaRPClientTest {
@Test
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)
}
@ -104,7 +103,7 @@ class StandaloneCordaRPClientTest {
fun `test starting tracked flow`() {
var trackCount = 0
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 ->
log.info("Flow>> $msg")
@ -133,7 +132,7 @@ class StandaloneCordaRPClientTest {
}
// 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)
assertEquals(1, updateCount.get())
}
@ -150,7 +149,7 @@ class StandaloneCordaRPClientTest {
}
// 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)
assertNotEquals(0, updateCount.get())
@ -164,7 +163,7 @@ class StandaloneCordaRPClientTest {
@Test
fun `test vault query by`() {
// 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)
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
@ -192,10 +191,7 @@ class StandaloneCordaRPClientTest {
val startCash = rpcProxy.getCashBalances()
assertTrue(startCash.isEmpty(), "Should not start with any cash")
val flowHandle = rpcProxy.startFlow(::CashIssueFlow,
629.DOLLARS, OpaqueBytes.of(0),
notaryNode.legalIdentity, notaryNode.legalIdentity
)
val flowHandle = rpcProxy.startFlow(::CashIssueFlow, 629.DOLLARS, OpaqueBytes.of(0), notaryNode.legalIdentity)
println("Started issuing cash, waiting on result")
flowHandle.returnValue.get()

View File

@ -175,8 +175,7 @@ class ContractUpgradeFlowTest {
@Test
fun `upgrade Cash to v2`() {
// Create some cash.
val anonymous = false
val result = a.services.startFlow(CashIssueFlow(Amount(1000, USD), a.info.legalIdentity, a.info.legalIdentity, OpaqueBytes.of(1), notary, anonymous)).resultFuture
val result = a.services.startFlow(CashIssueFlow(Amount(1000, USD), OpaqueBytes.of(1), notary)).resultFuture
mockNet.runNetwork()
val stx = result.getOrThrow().stx
val stateAndRef = stx.tx.outRef<Cash.State>(0)

View File

@ -1,8 +1,6 @@
package net.corda.docs
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.messaging.startFlow
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.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.finance.DOLLARS
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
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.testing.*
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 java.util.*
import kotlin.concurrent.thread
import kotlin.test.assertEquals
class IntegrationTestingTutorial {
@ -32,7 +25,8 @@ class IntegrationTestingTutorial {
// START 1
driver {
val aliceUser = User("aliceUser", "testPassword1", permissions = setOf(
startFlowPermission<CashIssueFlow>()
startFlowPermission<CashIssueFlow>(),
startFlowPermission<CashPaymentFlow>()
))
val bobUser = User("bobUser", "testPassword2", permissions = setOf(
startFlowPermission<CashPaymentFlow>()
@ -62,20 +56,21 @@ class IntegrationTestingTutorial {
// START 4
val issueRef = OpaqueBytes.of(0)
val futures = Stack<CordaFuture<*>>()
(1..10).map { i ->
thread {
futures.push(aliceProxy.startFlow(::CashIssueFlow,
i.DOLLARS,
bob.nodeInfo.legalIdentity,
alice.nodeInfo.legalIdentity,
issueRef,
notary.nodeInfo.notaryIdentity,
true
).returnValue)
}
}.forEach(Thread::join) // Ensure the stack of futures is populated.
futures.forEach { it.getOrThrow() }
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,
bob.nodeInfo.legalIdentity,
true
).returnValue
}.transpose().getOrThrow()
bobVaultUpdates.expectEvents {
parallel(

View File

@ -128,7 +128,7 @@ fun generateTransactions(proxy: CordaRPCOps) {
proxy.startFlow(::CashPaymentFlow, Amount(quantity, USD), me)
} else {
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
}
}

View File

@ -133,8 +133,7 @@ object TopupIssuerFlow {
// invoke Cash subflow to issue Asset
progressTracker.currentStep = ISSUING
val issuer = serviceHub.myInfo.legalIdentity
val issueRecipient = serviceHub.myInfo.legalIdentity
val issueCashFlow = CashIssueFlow(amount, issueRecipient, issuer, issuerPartyRef, notaryParty, anonymous = false)
val issueCashFlow = CashIssueFlow(amount, issuerPartyRef, notaryParty)
val issueTx = subFlow(issueCashFlow)
// NOTE: issueCashFlow performs a Broadcast (which stores a local copy of the txn to the ledger)
// short-circuit when issuing to self

View File

@ -65,11 +65,8 @@ class CustomVaultQueryTest {
private fun issueCashForCurrency(amountToIssue: Amount<Currency>) {
// Use NodeA as issuer and create some dollars
val flowHandle1 = nodeA.services.startFlow(CashIssueFlow(amountToIssue,
nodeA.info.legalIdentity,
nodeA.info.legalIdentity,
OpaqueBytes.of(0x01),
notaryNode.info.notaryIdentity,
anonymous = false))
notaryNode.info.notaryIdentity))
// Wait for the flow to stop and print
flowHandle1.resultFuture.getOrThrow()
}

View File

@ -45,22 +45,16 @@ class FxTransactionBuildTutorialTest {
fun `Run ForeignExchangeFlow to completion`() {
// Use NodeA as issuer and create some dollars
val flowHandle1 = nodeA.services.startFlow(CashIssueFlow(DOLLARS(1000),
nodeA.info.legalIdentity,
nodeA.info.legalIdentity,
OpaqueBytes.of(0x01),
notaryNode.info.notaryIdentity,
false))
notaryNode.info.notaryIdentity))
// Wait for the flow to stop and print
flowHandle1.resultFuture.getOrThrow()
printBalances()
// Using NodeB as Issuer create some pounds.
val flowHandle2 = nodeB.services.startFlow(CashIssueFlow(POUNDS(1000),
nodeB.info.legalIdentity,
nodeB.info.legalIdentity,
OpaqueBytes.of(0x01),
notaryNode.info.notaryIdentity,
false))
notaryNode.info.notaryIdentity))
// Wait for flow to come to an end and print
flowHandle2.resultFuture.getOrThrow()
printBalances()

View File

@ -6,11 +6,13 @@ import net.corda.core.messaging.CordaRPCOps
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 java.util.*
/**
* 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 {
abstract fun startFlow(proxy: CordaRPCOps): FlowHandle<AbstractCashFlow.Result>
@ -22,7 +24,10 @@ sealed class CashFlowCommand {
val recipient: Party,
val notary: Party,
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)
}
}
/**

View File

@ -15,51 +15,37 @@ import net.corda.core.utilities.ProgressTracker
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 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.
*/
@StartableByRPC
class CashIssueFlow(val amount: Amount<Currency>,
val issueTo: Party,
val issuerBankParty
: Party,
val issuerBankPartyRef: OpaqueBytes,
val notary: Party,
val anonymous: Boolean,
progressTracker: ProgressTracker) : AbstractCashFlow<AbstractCashFlow.Result>(progressTracker) {
constructor(amount: Amount<Currency>,
issuerBankPartyRef: OpaqueBytes,
issuerBankParty: Party,
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())
notary: Party) : this(amount, issuerBankPartyRef, notary, tracker())
@Suspendable
override fun call(): AbstractCashFlow.Result {
progressTracker.currentStep = GENERATING_ID
val txIdentities = if (anonymous) {
subFlow(TransactionKeyFlow(issueTo))
} else {
emptyMap<Party, AnonymousParty>()
}
val anonymousRecipient = txIdentities[issueTo] ?: issueTo
val issuerCert = serviceHub.myInfo.legalIdentityAndCert
progressTracker.currentStep = GENERATING_TX
val builder: TransactionBuilder = TransactionBuilder(notary)
val issuer = issuerBankParty.ref(issuerBankPartyRef)
val signers = Cash().generateIssue(builder, amount.issuedBy(issuer), anonymousRecipient, notary)
val issuer = issuerCert.party.ref(issuerBankPartyRef)
val signers = Cash().generateIssue(builder, amount.issuedBy(issuer), issuerCert.party, notary)
progressTracker.currentStep = SIGNING_TX
val tx = serviceHub.signInitialTransaction(builder, signers)
progressTracker.currentStep = FINALISING_TX
subFlow(FinalityFlow(tx))
return Result(tx, anonymousRecipient)
return Result(tx, issuerCert.party)
}
}

View File

@ -33,11 +33,7 @@ class CashExitFlowTests {
bankOfCorda = bankOfCordaNode.info.legalIdentity
mockNet.runNetwork()
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance,
bankOfCorda,
bankOfCorda, ref,
notary,
anonymous = true)).resultFuture
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture
mockNet.runNetwork()
future.getOrThrow()
}
@ -50,8 +46,7 @@ class CashExitFlowTests {
@Test
fun `exit some cash`() {
val exitAmount = 500.DOLLARS
val future = bankOfCordaNode.services.startFlow(CashExitFlow(exitAmount,
ref)).resultFuture
val future = bankOfCordaNode.services.startFlow(CashExitFlow(exitAmount, ref)).resultFuture
mockNet.runNetwork()
val exitTx = future.getOrThrow().stx.tx
val expected = (initialBalance - exitAmount).`issued by`(bankOfCorda.ref(ref))
@ -64,8 +59,7 @@ class CashExitFlowTests {
@Test
fun `exit zero cash`() {
val expected = 0.DOLLARS
val future = bankOfCordaNode.services.startFlow(CashExitFlow(expected,
ref)).resultFuture
val future = bankOfCordaNode.services.startFlow(CashExitFlow(expected, ref)).resultFuture
mockNet.runNetwork()
assertFailsWith<CashException> {
future.getOrThrow()

View File

@ -42,12 +42,7 @@ class CashIssueFlowTests {
fun `issue some cash`() {
val expected = 500.DOLLARS
val ref = OpaqueBytes.of(0x01)
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(expected,
bankOfCorda,
bankOfCorda,
ref,
notary,
anonymous = true)).resultFuture
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(expected, ref, notary)).resultFuture
mockNet.runNetwork()
val issueTx = future.getOrThrow().stx
val output = issueTx.tx.outputsOfType<Cash.State>().single()
@ -58,12 +53,7 @@ class CashIssueFlowTests {
fun `issue zero cash`() {
val expected = 0.DOLLARS
val ref = OpaqueBytes.of(0x01)
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(expected,
bankOfCorda,
bankOfCorda,
ref,
notary,
anonymous = true)).resultFuture
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(expected, ref, notary)).resultFuture
mockNet.runNetwork()
assertFailsWith<IllegalArgumentException> {
future.getOrThrow()

View File

@ -37,12 +37,7 @@ class CashPaymentFlowTests {
notary = notaryNode.info.notaryIdentity
bankOfCorda = bankOfCordaNode.info.legalIdentity
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance,
bankOfCorda,
bankOfCorda,
ref,
notary,
true)).resultFuture
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref, notary)).resultFuture
mockNet.runNetwork()
future.getOrThrow()
}

View File

@ -111,7 +111,7 @@ class NodePerformanceTests {
a.rpcClientToNode().use("A", "A") { connection ->
println("ISSUING")
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()
doneFutures.transpose().get()
println("STARTING PAYMENT")

View File

@ -134,14 +134,10 @@ class DistributedServiceTests : DriverBasedTest() {
}
private fun issueCash(amount: Amount<Currency>) {
val issueHandle = aliceProxy.startFlow(
::CashIssueFlow,
amount, OpaqueBytes.of(0), alice.nodeInfo.legalIdentity, raftNotaryIdentity)
issueHandle.returnValue.getOrThrow()
aliceProxy.startFlow(::CashIssueFlow, amount, OpaqueBytes.of(0), raftNotaryIdentity).returnValue.getOrThrow()
}
private fun paySelf(amount: Amount<Currency>) {
val payHandle = aliceProxy.startFlow(::CashPaymentFlow, amount, alice.nodeInfo.legalIdentity)
payHandle.returnValue.getOrThrow()
aliceProxy.startFlow(::CashPaymentFlow, amount, alice.nodeInfo.legalIdentity).returnValue.getOrThrow()
}
}

View File

@ -93,9 +93,8 @@ class CordaRPCOpsImplTest {
}
// Tell the monitoring service node to issue some cash
val anonymous = false
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()
var issueSmId: StateMachineRunId? = null
@ -133,11 +132,8 @@ class CordaRPCOpsImplTest {
val anonymous = false
val result = rpc.startFlow(::CashIssueFlow,
100.DOLLARS,
aliceNode.info.legalIdentity,
rpc.nodeIdentity().legalIdentity,
OpaqueBytes(ByteArray(1, { 1 })),
notaryNode.info.notaryIdentity,
false
notaryNode.info.notaryIdentity
)
mockNet.runNetwork()
@ -212,14 +208,7 @@ class CordaRPCOpsImplTest {
fun `cash command by user not permissioned for cash`() {
CURRENT_RPC_CONTEXT.set(RpcContext(User("user", "pwd", permissions = emptySet())))
assertThatExceptionOfType(PermissionException::class.java).isThrownBy {
rpc.startFlow(::CashIssueFlow,
Amount(100, USD),
aliceNode.info.legalIdentity,
rpc.nodeIdentity().legalIdentity,
OpaqueBytes(ByteArray(1, { 1 })),
notaryNode.info.notaryIdentity,
false
)
rpc.startFlow(::CashIssueFlow, Amount(100, USD), OpaqueBytes(ByteArray(1, { 1 })), notaryNode.info.notaryIdentity)
}
}

View File

@ -330,11 +330,8 @@ class FlowFrameworkTests {
assertEquals(notary1.info.notaryIdentity, notary2.info.notaryIdentity)
node1.services.startFlow(CashIssueFlow(
2000.DOLLARS,
node1.info.legalIdentity,
node1.info.legalIdentity,
OpaqueBytes.of(0x01),
notary1.info.notaryIdentity,
anonymous = false))
notary1.info.notaryIdentity))
// We pay a couple of times, the notary picking should go round robin
for (i in 1..3) {
val flow = node1.services.startFlow(CashPaymentFlow(500.DOLLARS, node2.info.legalIdentity, anonymous = false))

View File

@ -9,6 +9,7 @@ import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.utilities.getOrThrow
import net.corda.finance.DOLLARS
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.nodeapi.User
@ -20,7 +21,9 @@ class BankOfCordaRPCClientTest {
@Test
fun `issuer flow via RPC`() {
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 (nodeBankOfCorda, nodeBigCorporation) = listOf(
startNode(BOC.name, setOf(ServiceInfo(SimpleNotaryService.type)), listOf(bocManager)),
@ -47,10 +50,12 @@ class BankOfCordaRPCClientTest {
bocProxy.startFlow(
::CashIssueFlow,
1000.DOLLARS,
nodeBigCorporation.nodeInfo.legalIdentity,
nodeBankOfCorda.nodeInfo.legalIdentity,
BIG_CORP_PARTY_REF,
nodeBankOfCorda.nodeInfo.notaryIdentity,
nodeBankOfCorda.nodeInfo.notaryIdentity).returnValue.getOrThrow()
bocProxy.startFlow(
::CashPaymentFlow,
1000.DOLLARS,
nodeBigCorporation.nodeInfo.legalIdentity,
anonymous).returnValue.getOrThrow()
// Check Bank of Corda Vault Updates

View File

@ -70,7 +70,9 @@ private class BankOfCordaDriver {
startFlowPermission<CashPaymentFlow>(),
startFlowPermission<CashIssueFlow>(),
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)))
val bankOfCorda = startNode(
BOC.name,

View File

@ -9,6 +9,7 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.testing.http.HttpApi
import java.util.*
@ -28,6 +29,8 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) {
/**
* RPC API
*
* @return a pair of the issuing and payment transactions.
*/
fun requestRPCIssue(params: IssueRequestParams): SignedTransaction {
val client = CordaRPCClient(hostAndPort)
@ -38,8 +41,6 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) {
// Resolve parties via RPC
val issueToParty = rpc.partyFromX500Name(params.issueToPartyName)
?: 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)
?: throw IllegalStateException("Unable to locate ${params.notaryName} in Network Map Service")
val notaryNode = rpc.nodeIdentityFromParty(notaryLegalIdentity)
@ -48,7 +49,9 @@ class BankOfCordaClientApi(val hostAndPort: NetworkHostAndPort) {
val amount = Amount(params.amount, Currency.getInstance(params.currency))
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
}
}

View File

@ -7,6 +7,7 @@ import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import org.bouncycastle.asn1.x500.X500Name
import java.time.LocalDateTime
import java.util.*
@ -53,12 +54,13 @@ class BankOfCordaWebApi(val rpc: CordaRPCOps) {
val amount = Amount(params.amount, Currency.getInstance(params.currency))
val issuerBankPartyRef = OpaqueBytes.of(params.issuerBankPartyRef.toByte())
val anonymous = params.anonymous
// invoke client side of Issuer Flow: IssuanceRequester
// The line below blocks and waits for the future to resolve.
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")
Response.status(Response.Status.CREATED).build()
} catch (e: Exception) {

View File

@ -7,6 +7,7 @@ import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.millis
import net.corda.finance.DOLLARS
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.nodeapi.User
@ -27,7 +28,9 @@ class TraderDemoTest : NodeBasedTest() {
@Test
fun `runs trader demo`() {
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>()))
val (nodeA, nodeB, bankNode) = listOf(
startNode(DUMMY_BANK_A.name, rpcUsers = listOf(demoUser)),

View File

@ -16,6 +16,7 @@ import net.corda.core.utilities.getOrThrow
import net.corda.finance.DOLLARS
import net.corda.finance.USD
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.node.services.vault.VaultSchemaV1
import net.corda.testing.DUMMY_NOTARY
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")
val amounts = calculateRandomlySizedAmounts(amount, 3, 10, Random())
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 ->
rpc.startFlow(::CashIssueFlow, amount.copy(quantity = pennies), buyer, rpc.nodeIdentity().legalIdentity,
OpaqueBytes.of(1), notaryNode.notaryIdentity, anonymous).returnValue
rpc.startFlow(::CashPaymentFlow, amount.copy(quantity = pennies), buyer, anonymous).returnValue
}
resultFutures.transpose().getOrThrow()

View File

@ -38,8 +38,7 @@ class ExplorerSimulation(val options: OptionSet) {
val manager = User("manager", "test", permissions = setOf(
startFlowPermission<CashIssueFlow>(),
startFlowPermission<CashPaymentFlow>(),
startFlowPermission<CashExitFlow>(),
startFlowPermission<IssuerFlow.IssuanceRequester>())
startFlowPermission<CashExitFlow>())
)
lateinit var notaryNode: NodeHandle

View File

@ -36,8 +36,9 @@ import net.corda.explorer.views.bigDecimalFormatter
import net.corda.explorer.views.byteFormatter
import net.corda.explorer.views.stringConverter
import net.corda.flows.AbstractCashFlow
import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow
import net.corda.flows.CashFlowCommand
import net.corda.flows.IssuerFlow.IssuanceRequester
import org.controlsfx.dialog.ExceptionDialog
import tornadofx.*
import java.math.BigDecimal
@ -94,12 +95,13 @@ class NewTransaction : Fragment() {
show()
}
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.recipient,
command.issueRef,
myIdentity.value!!.legalIdentity,
command.notary,
command.anonymous)
} else {
command.startFlow(rpcProxy.value!!)

View File

@ -118,7 +118,7 @@ class VerifierTests {
val alice = aliceFuture.get()
val notary = notaryFuture.get()
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)
for (i in 1..10) {
alice.rpc.startFlow(::CashPaymentFlow, 10.DOLLARS, alice.nodeInfo.legalIdentity).returnValue.get()