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 5e36c4705a..d813df59fa 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 @@ -8,6 +8,7 @@ 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.asset.cash.selection.AbstractCashSelection import net.corda.finance.contracts.getCashBalance import net.corda.finance.issuedBy import net.corda.testing.core.* @@ -51,6 +52,39 @@ class CashSelectionTest : IntegrationTest() { } } + @Test + fun `dont return extra coins if the selected amount has been reached`() { + driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { + val node = startNode().getOrThrow() as InProcessImpl + val nodeIdentity = node.services.myInfo.singleIdentity() + + //issue $1 coin twice + val issuer = nodeIdentity.ref(1) + repeat(2, { + val coin = 1.DOLLARS.issuedBy(issuer) + val issuance = TransactionBuilder(null as Party?) + issuance.addOutputState(TransactionState(Cash.State(coin, nodeIdentity), Cash.PROGRAM_ID, defaultNotaryIdentity)) + issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) + + val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey) + node.database.transaction { + node.services.recordTransactions(transaction) + } + }) + + val exitedAmount = 1.DOLLARS + + val builder = TransactionBuilder(notary = null) + val exitStates = node.database.transaction { + AbstractCashSelection + .getInstance { node.services.jdbcSession().metaData } + .unconsumedCashStatesForSpending(node.services, exitedAmount, setOf(issuer.party), builder.notary, builder.lockId, setOf(issuer.reference)) + } + val returnedCoinsNumber = 1 + assertThat(exitStates.size).isEqualTo(returnedCoinsNumber) + } + } + @Test fun `select cash states issued by single transaction and give change`() { driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { 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 99481385fd..c9c68f1413 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 @@ -63,7 +63,7 @@ class CashSelectionOracleImpl : AbstractCashSelection(maxRetries = 16, retrySlee } else { "" }) + """) SELECT transaction_id, output_index, pennies, total, lock_id - FROM entry where total <= ? + pennies""" + FROM entry where total < ? + pennies""" // Use prepared statement for protection against SQL Injection (http://www.h2database.com/html/advanced.html#sql_injection) connection.prepareStatement(selectJoin).use { statement -> 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 609822073d..fa2578c4c8 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 @@ -70,7 +70,7 @@ class CashSelectionSQLServerImpl : AbstractCashSelection(maxRetries = 16, retryS } else { "" }) + """) SELECT row.transaction_id, row.output_index, row.pennies, row.total, row.lock_id - FROM row where row.total <= ? + row.pennies""" + FROM row where row.total < ? + row.pennies""" // Use prepared statement for protection against SQL Injection connection.prepareStatement(selectJoin).use { statement ->