mirror of
https://github.com/corda/corda.git
synced 2025-02-06 11:09:18 +00:00
* filter by contract state in _trackBy * write tests to check that _trackBy is filtering the states correct and tidy up filtering functions * remove un needed function * add change log message for filtering unrelated ContractStates from trackBy
This commit is contained in:
parent
6cc08776b5
commit
5ceb61606a
@ -7,6 +7,9 @@ release, see :doc:`upgrade-notes`.
|
|||||||
Unreleased
|
Unreleased
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
* Fixed an issue where ``trackBy`` was returning ``ContractStates`` from a transaction that were not being tracked. The
|
||||||
|
unrelated ``ContractStates`` will now be filtered out from the returned ``Vault.Update``.
|
||||||
|
|
||||||
* Introducing the flow hospital - a component of the node that manages flows that have errored and whether they should
|
* Introducing the flow hospital - a component of the node that manages flows that have errored and whether they should
|
||||||
be retried from their previous checkpoints or have their errors propagate. Currently it will respond to any error that
|
be retried from their previous checkpoints or have their errors propagate. Currently it will respond to any error that
|
||||||
occurs during the resolution of a received transaction as part of ``FinalityFlow``. In such a scenerio the receiving
|
occurs during the resolution of a received transaction as part of ``FinalityFlow``. In such a scenerio the receiving
|
||||||
|
@ -489,12 +489,21 @@ class NodeVaultService(
|
|||||||
return database.transaction {
|
return database.transaction {
|
||||||
mutex.locked {
|
mutex.locked {
|
||||||
val snapshotResults = _queryBy(criteria, paging, sorting, contractStateType)
|
val snapshotResults = _queryBy(criteria, paging, sorting, contractStateType)
|
||||||
val updates: Observable<Vault.Update<T>> = uncheckedCast(_updatesPublisher.bufferUntilSubscribed().filter { it.containsType(contractStateType, snapshotResults.stateTypes) })
|
val updates: Observable<Vault.Update<T>> = uncheckedCast(_updatesPublisher.bufferUntilSubscribed()
|
||||||
|
.filter { it.containsType(contractStateType, snapshotResults.stateTypes) }
|
||||||
|
.map { filterContractStates(it, contractStateType) })
|
||||||
DataFeed(snapshotResults, updates)
|
DataFeed(snapshotResults, updates)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun <T : ContractState> filterContractStates(update: Vault.Update<T>, contractStateType: Class<out T>) =
|
||||||
|
update.copy(consumed = filterByContractState(contractStateType, update.consumed),
|
||||||
|
produced = filterByContractState(contractStateType, update.produced))
|
||||||
|
|
||||||
|
private fun <T : ContractState> filterByContractState(contractStateType: Class<out T>, stateAndRefs: Set<StateAndRef<T>>) =
|
||||||
|
stateAndRefs.filter { contractStateType.isAssignableFrom(it.state.data.javaClass) }.toSet()
|
||||||
|
|
||||||
private fun getSession() = database.currentOrNew().session
|
private fun getSession() = database.currentOrNew().session
|
||||||
/**
|
/**
|
||||||
* Derive list from existing vault states and then incrementally update using vault observables
|
* Derive list from existing vault states and then incrementally update using vault observables
|
||||||
|
@ -28,10 +28,7 @@ import net.corda.nodeapi.internal.persistence.DatabaseTransaction
|
|||||||
import net.corda.testing.core.*
|
import net.corda.testing.core.*
|
||||||
import net.corda.testing.internal.TEST_TX_TIME
|
import net.corda.testing.internal.TEST_TX_TIME
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
import net.corda.testing.internal.vault.DUMMY_LINEAR_CONTRACT_PROGRAM_ID
|
import net.corda.testing.internal.vault.*
|
||||||
import net.corda.testing.internal.vault.DummyLinearContract
|
|
||||||
import net.corda.testing.internal.vault.DummyLinearStateSchemaV1
|
|
||||||
import net.corda.testing.internal.vault.VaultFiller
|
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
|
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
|
||||||
import net.corda.testing.node.makeTestIdentityService
|
import net.corda.testing.node.makeTestIdentityService
|
||||||
@ -2282,4 +2279,73 @@ class VaultQueryTests : VaultQueryTestsBase(), VaultQueryParties by delegate {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `track by only returns updates of tracked type`() {
|
||||||
|
val updates = database.transaction {
|
||||||
|
val (snapshot, updates) = vaultService.trackBy<DummyDealContract.State>()
|
||||||
|
assertThat(snapshot.states).hasSize(0)
|
||||||
|
val states = vaultFiller.fillWithSomeTestLinearAndDealStates(10).states
|
||||||
|
this.session.flush()
|
||||||
|
vaultFiller.consumeLinearStates(states.toList())
|
||||||
|
updates
|
||||||
|
}
|
||||||
|
|
||||||
|
updates.expectEvents {
|
||||||
|
sequence(
|
||||||
|
expect { (consumed, produced, flowId) ->
|
||||||
|
require(flowId == null) {}
|
||||||
|
require(consumed.isEmpty()) {}
|
||||||
|
require(produced.size == 10) {}
|
||||||
|
require(produced.filter { DummyDealContract.State::class.java.isAssignableFrom(it.state.data::class.java) }.size == 10) {}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `track by of super class only returns updates of sub classes of tracked type`() {
|
||||||
|
val updates = database.transaction {
|
||||||
|
val (snapshot, updates) = vaultService.trackBy<DealState>()
|
||||||
|
assertThat(snapshot.states).hasSize(0)
|
||||||
|
val states = vaultFiller.fillWithSomeTestLinearAndDealStates(10).states
|
||||||
|
this.session.flush()
|
||||||
|
vaultFiller.consumeLinearStates(states.toList())
|
||||||
|
updates
|
||||||
|
}
|
||||||
|
|
||||||
|
updates.expectEvents {
|
||||||
|
sequence(
|
||||||
|
expect { (consumed, produced, flowId) ->
|
||||||
|
require(flowId == null) {}
|
||||||
|
require(consumed.isEmpty()) {}
|
||||||
|
require(produced.size == 10) {}
|
||||||
|
require(produced.filter { DealState::class.java.isAssignableFrom(it.state.data::class.java) }.size == 10) {}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `track by of contract state interface returns updates of all states`() {
|
||||||
|
val updates = database.transaction {
|
||||||
|
val (snapshot, updates) = vaultService.trackBy<ContractState>()
|
||||||
|
assertThat(snapshot.states).hasSize(0)
|
||||||
|
val states = vaultFiller.fillWithSomeTestLinearAndDealStates(10).states
|
||||||
|
this.session.flush()
|
||||||
|
vaultFiller.consumeLinearStates(states.toList())
|
||||||
|
updates
|
||||||
|
}
|
||||||
|
|
||||||
|
updates.expectEvents {
|
||||||
|
sequence(
|
||||||
|
expect { (consumed, produced, flowId) ->
|
||||||
|
require(flowId == null) {}
|
||||||
|
require(consumed.isEmpty()) {}
|
||||||
|
require(produced.size == 20) {}
|
||||||
|
require(produced.filter { ContractState::class.java.isAssignableFrom(it.state.data::class.java) }.size == 20) {}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -127,6 +127,42 @@ class VaultFiller @JvmOverloads constructor(
|
|||||||
return Vault(states)
|
return Vault(states)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
fun fillWithSomeTestLinearAndDealStates(numberToCreate: Int,
|
||||||
|
externalId: String? = null,
|
||||||
|
participants: List<AbstractParty> = emptyList(),
|
||||||
|
linearString: String = "",
|
||||||
|
linearNumber: Long = 0L,
|
||||||
|
linearBoolean: Boolean = false,
|
||||||
|
linearTimestamp: Instant = now()): Vault<LinearState> {
|
||||||
|
val myKey: PublicKey = services.myInfo.chooseIdentity().owningKey
|
||||||
|
val me = AnonymousParty(myKey)
|
||||||
|
val issuerKey = defaultNotary.keyPair
|
||||||
|
val signatureMetadata = SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(issuerKey.public).schemeNumberID)
|
||||||
|
val transactions: List<SignedTransaction> = (1..numberToCreate).map {
|
||||||
|
val dummyIssue = TransactionBuilder(notary = defaultNotary.party).apply {
|
||||||
|
// Issue a Linear state
|
||||||
|
addOutputState(DummyLinearContract.State(
|
||||||
|
linearId = UniqueIdentifier(externalId),
|
||||||
|
participants = participants.plus(me),
|
||||||
|
linearString = linearString,
|
||||||
|
linearNumber = linearNumber,
|
||||||
|
linearBoolean = linearBoolean,
|
||||||
|
linearTimestamp = linearTimestamp), DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
|
||||||
|
// Issue a Deal state
|
||||||
|
addOutputState(DummyDealContract.State(ref = "test ref", participants = participants.plus(me)), DUMMY_DEAL_PROGRAM_ID)
|
||||||
|
addCommand(dummyCommand())
|
||||||
|
}
|
||||||
|
return@map services.signInitialTransaction(dummyIssue).withAdditionalSignature(issuerKey, signatureMetadata)
|
||||||
|
}
|
||||||
|
services.recordTransactions(transactions)
|
||||||
|
// Get all the StateAndRefs of all the generated transactions.
|
||||||
|
val states = transactions.flatMap { stx ->
|
||||||
|
stx.tx.outputs.indices.map { i -> stx.tx.outRef<LinearState>(i) }
|
||||||
|
}
|
||||||
|
return Vault(states)
|
||||||
|
}
|
||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun fillWithSomeTestCash(howMuch: Amount<Currency>,
|
fun fillWithSomeTestCash(howMuch: Amount<Currency>,
|
||||||
issuerServices: ServiceHub,
|
issuerServices: ServiceHub,
|
||||||
@ -167,7 +203,6 @@ class VaultFiller @JvmOverloads constructor(
|
|||||||
return Vault(states)
|
return Vault(states)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
|
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user