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 14a6531d25..f16e4122c5 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,10 +1,18 @@ package net.corda.finance.flows +import net.corda.core.contracts.TransactionState +import net.corda.core.contracts.withoutIssuer +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.asset.cash.selection.AbstractCashSelection import net.corda.finance.contracts.getCashBalance +import net.corda.finance.issuedBy +import net.corda.testing.core.singleIdentity import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.driver import net.corda.testing.driver.internal.InProcessImpl @@ -34,4 +42,97 @@ class CashSelectionTest { assertThat(availableBalanceAfterExit).isEqualTo(issuedAmount - exitedAmount) } } + + @Test + fun `cash selection sees states added in the same transaction`() { + driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { + val node = startNode().getOrThrow() as InProcessImpl + val nodeIdentity = node.services.myInfo.singleIdentity() + val issuer = nodeIdentity.ref(1) + val coin = 1.DOLLARS.issuedBy(issuer) + val exitedAmount = 1.DOLLARS + val issuance = TransactionBuilder(null as Party?) + issuance.addOutputState(TransactionState(Cash.State(coin, nodeIdentity), Cash.PROGRAM_ID, defaultNotaryIdentity)) + issuance.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) + + //insert ans select in the same transaction + val exitStates = node.database.transaction { + + val transaction = node.services.signInitialTransaction(issuance, nodeIdentity.owningKey) + node.services.recordTransactions(transaction) + + val builder = TransactionBuilder(notary = null) + 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 `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() + + val issuer = nodeIdentity.ref(1) + + val exitStates = node.database.transaction { + //issue $1 coin twice + 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.services.recordTransactions(transaction) + }) + + val exitedAmount = 1.DOLLARS + val builder = TransactionBuilder(notary = null) + 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"))) { + 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/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt index 1f63424f3f..850436efa6 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt @@ -105,6 +105,7 @@ class CordaPersistence( fun createSession(): Connection { // We need to set the database for the current [Thread] or [Fiber] here as some tests share threads across databases. _contextDatabase.set(this) + currentDBSession().flush() return contextTransaction.connection }