From 9c368b14864dd7a1a9ab0c56e652233949e3f251 Mon Sep 17 00:00:00 2001 From: szymonsztuka Date: Tue, 29 May 2018 15:39:30 +0100 Subject: [PATCH] ENT-1956 Fix SQL query for cash selection for SQL Server/Azure and Oracle (#885) Bug in SQL code for cash selection in Azure/Sql Server/Oracle db was occurring for selection within outputs few outputs from a single transaction and requiring returning a change. --- .../corda/finance/flows/CashSelectionTest.kt | 43 +++++++++++++++++-- .../cash/selection/CashSelectionOracleImpl.kt | 2 +- .../selection/CashSelectionSQLServerImpl.kt | 2 +- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/finance/src/integration-test/kotlin/net/corda/finance/flows/CashSelectionTest.kt b/finance/src/integration-test/kotlin/net/corda/finance/flows/CashSelectionTest.kt index bf055a3fa4..5e36c4705a 100644 --- a/finance/src/integration-test/kotlin/net/corda/finance/flows/CashSelectionTest.kt +++ b/finance/src/integration-test/kotlin/net/corda/finance/flows/CashSelectionTest.kt @@ -1,14 +1,16 @@ package net.corda.finance.flows +import net.corda.core.contracts.* +import net.corda.core.identity.Party import net.corda.core.messaging.startFlow +import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS +import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.getCashBalance -import net.corda.testing.core.ALICE_NAME -import net.corda.testing.core.BOB_NAME -import net.corda.testing.core.DUMMY_BANK_A_NAME -import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.finance.issuedBy +import net.corda.testing.core.* import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.driver import net.corda.testing.driver.internal.InProcessImpl @@ -48,4 +50,37 @@ class CashSelectionTest : IntegrationTest() { assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount) } } + + @Test + fun `select cash states issued by single transaction and give change`() { + driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { + val node = startNode().getOrThrow() as InProcessImpl + val nodeIdentity = node.services.myInfo.singleIdentity() + + val coins = listOf(3.DOLLARS, 2.DOLLARS, 1.DOLLARS).map { it.issuedBy(nodeIdentity.ref(1)) } + + //create single transaction with 3 cash outputs + val issuance = TransactionBuilder(null as Party?) + coins.map { issuance.addOutputState(TransactionState(Cash.State(it, nodeIdentity), "net.corda.finance.contracts.asset.Cash", defaultNotaryIdentity)) } + issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) + + val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey) + node.database.transaction { + node.services.recordTransactions(transaction) + } + + val issuedAmount = coins.reduce { sum, element -> sum + element }.withoutIssuer() + + val availableBalance = node.rpc.getCashBalance(issuedAmount.token) + + assertThat(availableBalance).isEqualTo(issuedAmount) + + val exitedAmount = 3.01.DOLLARS + node.rpc.startFlow(::CashExitFlow, exitedAmount, OpaqueBytes.of(1)).returnValue.getOrThrow() + + val availableBalanceAfterExit = node.rpc.getCashBalance(issuedAmount.token) + + assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount) + } + } } \ No newline at end of file diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionOracleImpl.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionOracleImpl.kt index bf9e6170b4..99481385fd 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionOracleImpl.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionOracleImpl.kt @@ -40,7 +40,7 @@ class CashSelectionOracleImpl : AbstractCashSelection(maxRetries = 16, retrySlee WITH entry(transaction_id, output_index, pennies, total, lock_id) AS ( SELECT vs.transaction_id, vs.output_index, ccs.pennies, - SUM(ccs.pennies) OVER (ORDER BY ccs.transaction_id), vs.lock_id + SUM(ccs.pennies) OVER (ORDER BY ccs.transaction_id, ccs.output_index), vs.lock_id FROM contract_cash_states ccs, vault_states vs WHERE vs.transaction_id = ccs.transaction_id AND vs.output_index = ccs.output_index AND vs.state_status = 0 diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionSQLServerImpl.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionSQLServerImpl.kt index 20d0c5343b..609822073d 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionSQLServerImpl.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/CashSelectionSQLServerImpl.kt @@ -46,7 +46,7 @@ class CashSelectionSQLServerImpl : AbstractCashSelection(maxRetries = 16, retryS WITH row(transaction_id, output_index, pennies, total, lock_id) AS ( SELECT vs.transaction_id, vs.output_index, ccs.pennies, - SUM(ccs.pennies) OVER (ORDER BY ccs.transaction_id RANGE UNBOUNDED PRECEDING), vs.lock_id + SUM(ccs.pennies) OVER (ORDER BY ccs.transaction_id, ccs.output_index RANGE UNBOUNDED PRECEDING), vs.lock_id FROM contract_cash_states AS ccs, vault_states AS vs WHERE vs.transaction_id = ccs.transaction_id AND vs.output_index = ccs.output_index AND vs.state_status = 0