diff --git a/client/src/integration-test/kotlin/net/corda/client/CordaRPCClientTest.kt b/client/src/integration-test/kotlin/net/corda/client/CordaRPCClientTest.kt index 8ce0228f31..153fb670fd 100644 --- a/client/src/integration-test/kotlin/net/corda/client/CordaRPCClientTest.kt +++ b/client/src/integration-test/kotlin/net/corda/client/CordaRPCClientTest.kt @@ -1,7 +1,6 @@ package net.corda.client import net.corda.core.contracts.DOLLARS -import net.corda.core.contracts.issuedBy import net.corda.core.flows.FlowException import net.corda.core.getOrThrow import net.corda.core.messaging.startFlow @@ -85,10 +84,7 @@ class CordaRPCClientTest : NodeBasedTest() { fun `FlowException thrown by flow`() { client.start(rpcUser.username, rpcUser.password) val proxy = client.proxy() - val handle = proxy.startFlow(::CashPaymentFlow, - 100.DOLLARS.issuedBy(node.info.legalIdentity.ref(1)), - node.info.legalIdentity - ) + val handle = proxy.startFlow(::CashPaymentFlow, 100.DOLLARS, node.info.legalIdentity) // TODO Restrict this to CashException once RPC serialisation has been fixed assertThatExceptionOfType(FlowException::class.java).isThrownBy { handle.returnValue.getOrThrow() diff --git a/client/src/integration-test/kotlin/net/corda/client/NodeMonitorModelTest.kt b/client/src/integration-test/kotlin/net/corda/client/NodeMonitorModelTest.kt index b6adeb68eb..54b0678774 100644 --- a/client/src/integration-test/kotlin/net/corda/client/NodeMonitorModelTest.kt +++ b/client/src/integration-test/kotlin/net/corda/client/NodeMonitorModelTest.kt @@ -4,8 +4,7 @@ import net.corda.client.model.NodeMonitorModel import net.corda.client.model.ProgressTrackingEvent import net.corda.core.bufferUntilSubscribed import net.corda.core.contracts.Amount -import net.corda.core.contracts.Issued -import net.corda.core.contracts.PartyAndReference +import net.corda.core.contracts.DOLLARS import net.corda.core.contracts.USD import net.corda.core.flows.StateMachineRunId import net.corda.core.getOrThrow @@ -123,17 +122,8 @@ class NodeMonitorModelTest : DriverBasedTest() { @Test fun `cash issue and move`() { - rpc.startFlow(::CashIssueFlow, - Amount(100, USD), - OpaqueBytes(ByteArray(1, { 1 })), - aliceNode.legalIdentity, - notaryNode.notaryIdentity - ).returnValue.getOrThrow() - - rpc.startFlow(::CashPaymentFlow, - Amount(100, Issued(PartyAndReference(aliceNode.legalIdentity, OpaqueBytes(ByteArray(1, { 1 }))), USD)), - aliceNode.legalIdentity - ) + rpc.startFlow(::CashIssueFlow, 100.DOLLARS, OpaqueBytes.of(1), aliceNode.legalIdentity, notaryNode.notaryIdentity).returnValue.getOrThrow() + rpc.startFlow(::CashPaymentFlow, 100.DOLLARS, aliceNode.legalIdentity).returnValue.getOrThrow() var issueSmId: StateMachineRunId? = null var moveSmId: StateMachineRunId? = null diff --git a/client/src/main/kotlin/net/corda/client/mock/EventGenerator.kt b/client/src/main/kotlin/net/corda/client/mock/EventGenerator.kt index c343878cc4..24ea8a09f6 100644 --- a/client/src/main/kotlin/net/corda/client/mock/EventGenerator.kt +++ b/client/src/main/kotlin/net/corda/client/mock/EventGenerator.kt @@ -73,11 +73,9 @@ class EventGenerator( } val moveCashGenerator = - amountIssuedGenerator.combine( - partyGenerator - ) { amountIssued, recipient -> + amountIssuedGenerator.combine(partyGenerator) { amountIssued, recipient -> CashFlowCommand.PayCash( - amount = amountIssued, + amount = amountIssued.withoutIssuer(), recipient = recipient ) } diff --git a/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt b/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt index 50651b1ea0..8b346bb185 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt @@ -3,9 +3,7 @@ package net.corda.core.contracts import net.corda.core.crypto.CompositeKey import net.corda.core.flows.FlowException -class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException() { - override fun toString() = "Insufficient balance, missing $amountMissing" -} +class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException("Insufficient balance, missing $amountMissing") /** * Interface for contract states representing assets which are fungible, countable and issued by a diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt index a5dd4b4640..2be6b47c58 100644 --- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt +++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/IntegrationTestingTutorial.kt @@ -4,13 +4,11 @@ import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture import net.corda.contracts.asset.Cash import net.corda.core.contracts.DOLLARS -import net.corda.core.contracts.issuedBy import net.corda.core.getOrThrow import net.corda.core.messaging.startFlow import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.Vault import net.corda.core.serialization.OpaqueBytes -import net.corda.core.toFuture import net.corda.flows.CashIssueFlow import net.corda.flows.CashPaymentFlow import net.corda.node.driver.driver @@ -92,10 +90,7 @@ class IntegrationTestingTutorial { // START 5 for (i in 1 .. 10) { - bobProxy.startFlow(::CashPaymentFlow, - i.DOLLARS.issuedBy(alice.nodeInfo.legalIdentity.ref(issueRef)), - alice.nodeInfo.legalIdentity - ).returnValue.getOrThrow() + bobProxy.startFlow(::CashPaymentFlow, i.DOLLARS, alice.nodeInfo.legalIdentity).returnValue.getOrThrow() } aliceVaultUpdates.expectEvents { diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt index 2e923502f1..2f26d671ee 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt @@ -122,7 +122,7 @@ fun generateTransactions(proxy: CordaRPCOps) { ownedQuantity -= quantity } else if (ownedQuantity > 1000 && n < 0.7) { val quantity = Math.abs(random.nextLong() % Math.min(ownedQuantity, 2000)) - proxy.startFlow(::CashPaymentFlow, Amount(quantity, Issued(meAndRef, USD)), me) + proxy.startFlow(::CashPaymentFlow, Amount(quantity, USD), me) } else { val quantity = Math.abs(random.nextLong() % 1000) proxy.startFlow(::CashIssueFlow, Amount(quantity, USD), issueRef, me, notary) diff --git a/finance/src/main/kotlin/net/corda/flows/CashFlowCommand.kt b/finance/src/main/kotlin/net/corda/flows/CashFlowCommand.kt index 067d2f991d..bbb4788011 100644 --- a/finance/src/main/kotlin/net/corda/flows/CashFlowCommand.kt +++ b/finance/src/main/kotlin/net/corda/flows/CashFlowCommand.kt @@ -1,7 +1,6 @@ package net.corda.flows import net.corda.core.contracts.Amount -import net.corda.core.contracts.Issued import net.corda.core.crypto.Party import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.FlowHandle @@ -11,7 +10,7 @@ import net.corda.core.transactions.SignedTransaction import java.util.* /** - * A command to initiate the Cash flow with. + * A command to initiate the cash flow with. */ sealed class CashFlowCommand { abstract fun startFlow(proxy: CordaRPCOps): FlowHandle @@ -32,7 +31,7 @@ sealed class CashFlowCommand { * @param amount the amount of currency to issue on to the ledger. * @param recipient the party to issue the cash to. */ - class PayCash(val amount: Amount>, val recipient: Party) : CashFlowCommand() { + class PayCash(val amount: Amount, val recipient: Party, val issuerConstraint: Party? = null) : CashFlowCommand() { override fun startFlow(proxy: CordaRPCOps) = proxy.startFlow(::CashPaymentFlow, amount, recipient) } diff --git a/finance/src/main/kotlin/net/corda/flows/CashPaymentFlow.kt b/finance/src/main/kotlin/net/corda/flows/CashPaymentFlow.kt index d78f310177..5ec7052c07 100644 --- a/finance/src/main/kotlin/net/corda/flows/CashPaymentFlow.kt +++ b/finance/src/main/kotlin/net/corda/flows/CashPaymentFlow.kt @@ -1,7 +1,9 @@ package net.corda.flows import co.paralleluniverse.fibers.Suspendable -import net.corda.core.contracts.* +import net.corda.core.contracts.Amount +import net.corda.core.contracts.InsufficientBalanceException +import net.corda.core.contracts.TransactionType import net.corda.core.crypto.Party import net.corda.core.crypto.keys import net.corda.core.crypto.toStringShort @@ -12,13 +14,19 @@ import java.security.KeyPair import java.util.* /** - * Initiates a flow that produces an cash move transaction. + * Initiates a flow that sends cash to a recipient. * * @param amount the amount of a currency to pay to the recipient. * @param recipient the party to pay the currency to. + * @param issuerConstraint if specified, the payment will be made using only cash issued by the given parties. */ -open class CashPaymentFlow(val amount: Amount>, val recipient: Party, progressTracker: ProgressTracker) : AbstractCashFlow(progressTracker) { - constructor(amount: Amount>, recipient: Party) : this(amount, recipient, tracker()) +open class CashPaymentFlow( + val amount: Amount, + val recipient: Party, + progressTracker: ProgressTracker, + val issuerConstraint: Set? = null) : AbstractCashFlow(progressTracker) { + /** A straightforward constructor that constructs spends using cash states of any issuer. */ + constructor(amount: Amount, recipient: Party) : this(amount, recipient, tracker()) @Suspendable override fun call(): SignedTransaction { @@ -28,11 +36,11 @@ open class CashPaymentFlow(val amount: Amount>, val recipient: val (spendTX, keysForSigning) = try { serviceHub.vaultService.generateSpend( builder, - amount.withoutIssuer(), + amount, recipient.owningKey, - setOf(amount.token.issuer.party)) + issuerConstraint) } catch (e: InsufficientBalanceException) { - throw CashException("Insufficient cash for spend", e) + throw CashException("Insufficient cash for spend: ${e.message}", e) } progressTracker.currentStep = SIGNING_TX diff --git a/finance/src/main/kotlin/net/corda/flows/IssuerFlow.kt b/finance/src/main/kotlin/net/corda/flows/IssuerFlow.kt index 61662a9c20..be160dc427 100644 --- a/finance/src/main/kotlin/net/corda/flows/IssuerFlow.kt +++ b/finance/src/main/kotlin/net/corda/flows/IssuerFlow.kt @@ -89,7 +89,7 @@ object IssuerFlow { return issueTx // now invoke Cash subflow to Move issued assetType to issue requester progressTracker.currentStep = TRANSFERRING - val moveCashFlow = CashPaymentFlow(amount.issuedBy(bankOfCordaParty.ref(issuerPartyRef)), issueTo) + val moveCashFlow = CashPaymentFlow(amount, issueTo) val moveTx = subFlow(moveCashFlow) // NOTE: CashFlow PayCash calls FinalityFlow which performs a Broadcast (which stores a local copy of the txn to the ledger) return moveTx diff --git a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt index 181e3ea6e1..5dbd4cf616 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/DistributedServiceTests.kt @@ -144,9 +144,7 @@ class DistributedServiceTests : DriverBasedTest() { } private fun paySelf(amount: Amount) { - val payHandle = aliceProxy.startFlow( - ::CashPaymentFlow, - amount.issuedBy(alice.nodeInfo.legalIdentity.ref(0)), alice.nodeInfo.legalIdentity) + val payHandle = aliceProxy.startFlow(::CashPaymentFlow, amount, alice.nodeInfo.legalIdentity) payHandle.returnValue.getOrThrow() } } diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index 8f0df1e30b..13de1d560c 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -126,10 +126,7 @@ class CordaRPCOpsImplTest { network.runNetwork() - rpc.startFlow(::CashPaymentFlow, - Amount(100, Issued(PartyAndReference(aliceNode.info.legalIdentity, OpaqueBytes(ByteArray(1, { 1 }))), USD)), - aliceNode.info.legalIdentity - ) + rpc.startFlow(::CashPaymentFlow, Amount(100, USD), aliceNode.info.legalIdentity) network.runNetwork() diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/StateMachineManagerTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/StateMachineManagerTests.kt index 4c9916bb91..0909c6ac83 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/StateMachineManagerTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/StateMachineManagerTests.kt @@ -6,7 +6,6 @@ import com.google.common.util.concurrent.ListenableFuture import net.corda.core.* import net.corda.core.contracts.DOLLARS import net.corda.core.contracts.DummyState -import net.corda.core.contracts.issuedBy import net.corda.core.crypto.Party import net.corda.core.crypto.generateKeyPair import net.corda.core.flows.FlowException @@ -326,9 +325,7 @@ class StateMachineManagerTests { notary1.info.notaryIdentity)) // We pay a couple of times, the notary picking should go round robin for (i in 1 .. 3) { - node1.services.startFlow(CashPaymentFlow( - 500.DOLLARS.issuedBy(node1.info.legalIdentity.ref(0x01)), - node2.info.legalIdentity)) + node1.services.startFlow(CashPaymentFlow(500.DOLLARS, node2.info.legalIdentity)) net.runNetwork() } val endpoint = net.messagingNetwork.endpoint(notary1.net.myAddress as InMemoryMessagingNetwork.PeerHandle)!! diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt index 31cbcf5ca1..feb6fe2794 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/cordapps/cash/NewTransaction.kt @@ -18,8 +18,6 @@ import net.corda.client.fxutils.map import net.corda.client.fxutils.unique import net.corda.client.model.* import net.corda.core.contracts.Amount -import net.corda.core.contracts.Issued -import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.withoutIssuer import net.corda.core.crypto.AbstractParty import net.corda.core.crypto.Party @@ -148,7 +146,7 @@ class NewTransaction : Fragment() { CashTransaction.Issue -> { CashFlowCommand.IssueCash(Amount(amount.value, currencyChoiceBox.value), issueRef, partyBChoiceBox.value.legalIdentity, notaries.first().notaryIdentity) } - CashTransaction.Pay -> CashFlowCommand.PayCash(Amount(amount.value, Issued(PartyAndReference(issuerChoiceBox.value, issueRef), currencyChoiceBox.value)), partyBChoiceBox.value.legalIdentity) + CashTransaction.Pay -> CashFlowCommand.PayCash(Amount(amount.value, currencyChoiceBox.value), partyBChoiceBox.value.legalIdentity) CashTransaction.Exit -> CashFlowCommand.ExitCash(Amount(amount.value, currencyChoiceBox.value), issueRef) else -> null } diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt index d397985b79..4f7e375314 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/CrossCashTest.kt @@ -3,16 +3,13 @@ package net.corda.loadtest.tests import net.corda.client.mock.Generator import net.corda.client.mock.pickN import net.corda.contracts.asset.Cash -import net.corda.core.* import net.corda.core.contracts.Issued import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.USD import net.corda.core.crypto.AbstractParty -import net.corda.core.crypto.AnonymousParty -import net.corda.core.flows.FlowException -import net.corda.core.messaging.startFlow +import net.corda.core.failure import net.corda.core.serialization.OpaqueBytes -import net.corda.flows.CashException +import net.corda.core.success import net.corda.flows.CashFlowCommand import net.corda.loadtest.LoadTest import net.corda.loadtest.NodeHandle @@ -125,7 +122,7 @@ val crossCashTest = LoadTest( val quantities = state.nodeVaults[node.info.legalIdentity] ?: mapOf() val possibleRecipients = nodeMap.keys.toList() val moves = quantities.map { - it.value.toDouble() / 1000 to generateMove(it.value, USD, it.key.toAnonymous(), possibleRecipients) + it.value.toDouble() / 1000 to generateMove(it.value, USD, node.info.legalIdentity, possibleRecipients) } val exits = quantities.mapNotNull { if (it.key == node.info.legalIdentity) { @@ -160,26 +157,26 @@ val crossCashTest = LoadTest( val newDiffQueues = state.copyQueues() val recipientOriginators = newDiffQueues.getOrPut(command.command.recipient, { HashMap() }) val senderQuantities = newNodeVaults[command.node.info.legalIdentity]!! - val quantity = command.command.amount.quantity - val issuer = command.command.amount.token.issuer.party + val amount = command.command.amount + val issuer = command.command.issuerConstraint!! val originator = command.node.info.legalIdentity val senderQuantity = senderQuantities[issuer] ?: throw Exception( "Generated payment of ${command.command.amount} from ${command.node.info.legalIdentity}, " + "however there is no cash from $issuer!" ) - if (senderQuantity < quantity) { + if (senderQuantity < amount.quantity) { throw Exception( "Generated payment of ${command.command.amount} from ${command.node.info.legalIdentity}, " + "however they only have $senderQuantity!" ) } - if (senderQuantity == quantity) { + if (senderQuantity == amount.quantity) { senderQuantities.remove(issuer) } else { - senderQuantities.put(issuer, senderQuantity - quantity) + senderQuantities.put(issuer, senderQuantity - amount.quantity) } val recipientQueue = recipientOriginators.getOrPut(originator, { ArrayList() }) - recipientQueue.add(Pair(issuer, quantity)) + recipientQueue.add(Pair(issuer, amount.quantity)) CrossCashState(newNodeVaults, newDiffQueues) } is CashFlowCommand.ExitCash -> { diff --git a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/GenerateHelpers.kt b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/GenerateHelpers.kt index 34a324b95a..38c0d33639 100644 --- a/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/GenerateHelpers.kt +++ b/tools/loadtest/src/main/kotlin/net/corda/loadtest/tests/GenerateHelpers.kt @@ -5,7 +5,7 @@ import net.corda.client.mock.generateAmount import net.corda.client.mock.pickOne import net.corda.core.contracts.Issued import net.corda.core.contracts.PartyAndReference -import net.corda.core.crypto.AnonymousParty +import net.corda.core.contracts.withoutIssuer import net.corda.core.crypto.Party import net.corda.core.serialization.OpaqueBytes import net.corda.flows.CashFlowCommand @@ -28,13 +28,13 @@ fun generateIssue( fun generateMove( max: Long, currency: Currency, - issuer: AnonymousParty, + issuer: Party, possibleRecipients: List ): Generator { return generateAmount(1, max, Generator.pure(Issued(PartyAndReference(issuer, OpaqueBytes.of(0)), currency))).combine( Generator.pickOne(possibleRecipients) ) { amount, recipient -> - CashFlowCommand.PayCash(amount, recipient) + CashFlowCommand.PayCash(amount.withoutIssuer(), recipient, issuer) } }