From 0a4d98161afc4f81348b164636b3ae55cf74dc0f Mon Sep 17 00:00:00 2001 From: szymonsztuka Date: Wed, 30 May 2018 10:49:44 +0100 Subject: [PATCH] CORDA-1266 When a cash output is identical only the fist output is saved. (#3244) --- .../node/services/vault/NodeVaultService.kt | 7 +-- .../services/vault/NodeVaultServiceTest.kt | 51 +++++++++++++++++-- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index c9a9ccbaa0..f2ada92b06 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -131,9 +131,10 @@ class NodeVaultService( fun makeUpdate(tx: WireTransaction): Vault.Update? { val ourNewStates = when (statesToRecord) { StatesToRecord.NONE -> throw AssertionError("Should not reach here") - StatesToRecord.ONLY_RELEVANT -> tx.outputs.filter { isRelevant(it.data, keyManagementService.filterMyKeys(tx.outputs.flatMap { it.data.participants.map { it.owningKey } }).toSet()) } - StatesToRecord.ALL_VISIBLE -> tx.outputs - }.map { tx.outRef(it.data) } + StatesToRecord.ONLY_RELEVANT -> tx.outputs.withIndex().filter { + isRelevant(it.value.data, keyManagementService.filterMyKeys(tx.outputs.flatMap { it.data.participants.map { it.owningKey } }).toSet()) } + StatesToRecord.ALL_VISIBLE -> tx.outputs.withIndex() + }.map { tx.outRef(it.index) } // Retrieve all unconsumed states for this transaction's inputs val consumedStates = loadStates(tx.inputs) diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index bd1904efc2..dce2e9bf50 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -4,10 +4,7 @@ import co.paralleluniverse.fibers.Suspendable import com.nhaarman.mockito_kotlin.argThat import com.nhaarman.mockito_kotlin.doNothing import com.nhaarman.mockito_kotlin.whenever -import net.corda.core.contracts.Amount -import net.corda.core.contracts.Issued -import net.corda.core.contracts.StateAndRef -import net.corda.core.contracts.StateRef +import net.corda.core.contracts.* import net.corda.core.crypto.NullKeys import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.* @@ -687,4 +684,50 @@ class NodeVaultServiceTest { } assertEquals(currentCashStates + 1, countCash()) } + + @Test + fun `insert equal cash states issued by single transaction`() { + val nodeIdentity = MEGA_CORP + val coins = listOf(1.DOLLARS, 1.DOLLARS).map { it.issuedBy(nodeIdentity.ref(1)) } + + //create single transaction with 2 'identical' cash outputs + val txb = TransactionBuilder(DUMMY_NOTARY) + coins.map { txb.addOutputState(TransactionState(Cash.State(it, nodeIdentity), Cash.PROGRAM_ID, DUMMY_NOTARY)) } + txb.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) + val issueTx = txb.toWireTransaction(services) + + // ensure transaction contract state is persisted in DBStorage + val signedIssuedTx = services.signInitialTransaction(txb) + (services.validatedTransactions as WritableTransactionStorage).addTransaction(signedIssuedTx) + + database.transaction { vaultService.notify(StatesToRecord.ONLY_RELEVANT, issueTx) } + + val recordedStates = database.transaction { + vaultService.queryBy().states.size + } + assertThat(recordedStates).isEqualTo(coins.size) + } + + @Test + fun `insert different cash states issued by single transaction`() { + val nodeIdentity = MEGA_CORP + val coins = listOf(2.DOLLARS, 1.DOLLARS).map { it.issuedBy(nodeIdentity.ref(1)) } + + //create single transaction with 2 'identical' cash outputs + val txb = TransactionBuilder(DUMMY_NOTARY) + coins.map { txb.addOutputState(TransactionState(Cash.State(it, nodeIdentity), Cash.PROGRAM_ID, DUMMY_NOTARY)) } + txb.addCommand(Cash.Commands.Issue(), nodeIdentity.owningKey) + val issueTx = txb.toWireTransaction(services) + + // ensure transaction contract state is persisted in DBStorage + val signedIssuedTx = services.signInitialTransaction(txb) + (services.validatedTransactions as WritableTransactionStorage).addTransaction(signedIssuedTx) + + database.transaction { vaultService.notify(StatesToRecord.ONLY_RELEVANT, issueTx) } + + val recordedStates = database.transaction { + vaultService.queryBy().states.size + } + assertThat(recordedStates).isEqualTo(coins.size) + } }