mirror of
https://github.com/corda/corda.git
synced 2025-02-18 16:40:55 +00:00
Improvements to Vault Query Service soft locked state querying (#1174)
* Improvements to Vault Query Service soft locked state querying (removed old mechanism from VaultService). * Fixed rst document formatting. * Added additional soft locking criteria mode: all unlocked plus those locked as specified by lockId(s). * Addressed comments from PR feedback.
This commit is contained in:
parent
67361188e5
commit
bef8630fe0
@ -274,12 +274,6 @@ interface VaultService {
|
||||
*/
|
||||
fun softLockRelease(lockId: UUID, stateRefs: NonEmptySet<StateRef>? = null)
|
||||
|
||||
/**
|
||||
* Retrieve softLockStates for a given [UUID] or return all softLockStates in vault for a given
|
||||
* [ContractState] type
|
||||
*/
|
||||
fun <T : ContractState> softLockedStates(lockId: UUID? = null): List<StateAndRef<T>>
|
||||
|
||||
// DOCEND SoftLockAPI
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,19 @@ sealed class QueryCriteria {
|
||||
@CordaSerializable
|
||||
data class TimeCondition(val type: TimeInstantType, val predicate: ColumnPredicate<Instant>)
|
||||
|
||||
// DOCSTART VaultQuerySoftLockingCriteria
|
||||
@CordaSerializable
|
||||
data class SoftLockingCondition(val type: SoftLockingType, val lockIds: List<UUID> = emptyList())
|
||||
|
||||
@CordaSerializable
|
||||
enum class SoftLockingType {
|
||||
UNLOCKED_ONLY, // only unlocked states
|
||||
LOCKED_ONLY, // only soft locked states
|
||||
SPECIFIED, // only those soft locked states specified by lock id(s)
|
||||
UNLOCKED_AND_SPECIFIED // all unlocked states plus those soft locked states specified by lock id(s)
|
||||
}
|
||||
// DOCEND VaultQuerySoftLockingCriteria
|
||||
|
||||
abstract class CommonQueryCriteria : QueryCriteria() {
|
||||
abstract val status: Vault.StateStatus
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
@ -40,7 +53,7 @@ sealed class QueryCriteria {
|
||||
val contractStateTypes: Set<Class<out ContractState>>? = null,
|
||||
val stateRefs: List<StateRef>? = null,
|
||||
val notaryName: List<X500Name>? = null,
|
||||
val includeSoftlockedStates: Boolean = true,
|
||||
val softLockingCondition: SoftLockingCondition? = null,
|
||||
val timeCondition: TimeCondition? = null) : CommonQueryCriteria() {
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
return parser.parseCriteria(this as CommonQueryCriteria).plus(parser.parseCriteria(this))
|
||||
|
@ -56,7 +56,7 @@ There are four implementations of this interface which can be chained together t
|
||||
|
||||
1. ``VaultQueryCriteria`` provides filterable criteria on attributes within the Vault states table: status (UNCONSUMED, CONSUMED), state reference(s), contract state type(s), notaries, soft locked states, timestamps (RECORDED, CONSUMED).
|
||||
|
||||
.. note:: Sensible defaults are defined for frequently used attributes (status = UNCONSUMED, includeSoftlockedStates = true).
|
||||
.. note:: Sensible defaults are defined for frequently used attributes (status = UNCONSUMED, always include soft locked states).
|
||||
|
||||
2. ``FungibleAssetQueryCriteria`` provides filterable criteria on attributes defined in the Corda Core ``FungibleAsset`` contract state interface, used to represent assets that are fungible, countable and issued by a specific party (eg. ``Cash.State`` and ``CommodityContract.State`` in the Corda finance module). Filterable attributes include: participants(s), owner(s), quantity, issuer party(s) and issuer reference(s).
|
||||
|
||||
|
@ -81,6 +81,12 @@ UNRELEASED
|
||||
dealing with ``StateAndRef``s.
|
||||
|
||||
|
||||
* Vault query soft locking enhancements and deprecations
|
||||
* removed original ``VaultService`` ``softLockedStates` query mechanism.
|
||||
* introduced improved ``SoftLockingCondition`` filterable attribute in ``VaultQueryCriteria`` to enable specification
|
||||
of different soft locking retrieval behaviours (exclusive of soft locked states, soft locked states only, specified
|
||||
by set of lock ids)
|
||||
|
||||
Milestone 13
|
||||
------------
|
||||
|
||||
|
@ -22,13 +22,15 @@ query soft locks associated with states as required by their CorDapp application
|
||||
:start-after: DOCSTART SoftLockAPI
|
||||
:end-before: DOCEND SoftLockAPI
|
||||
|
||||
You can also control whether soft locked states are retrieved in general vault queries by setting an optional boolean
|
||||
`includeSoftLockedStates` flag (which is set to *true* by default)
|
||||
Query
|
||||
-----
|
||||
By default vault queries will always include locked states in its result sets.
|
||||
Custom filterable criteria can be specified using the ``SoftLockingCondition`` attribute of ``VaultQueryCriteria``:
|
||||
|
||||
.. literalinclude:: ../../core/src/main/kotlin/net/corda/core/node/services/VaultService.kt
|
||||
:language: kotlin
|
||||
:start-after: DOCSTART VaultStatesQuery
|
||||
:end-before: DOCEND VaultStatesQuery
|
||||
.. literalinclude:: ../../core/src/main/kotlin/net/corda/core/node/services/vault/QueryCriteria.kt
|
||||
:language: kotlin
|
||||
:start-after: DOCSTART VaultQuerySoftLockingCriteria
|
||||
:end-before: DOCEND VaultQuerySoftLockingCriteria
|
||||
|
||||
Explicit Usage
|
||||
--------------
|
||||
|
@ -48,8 +48,25 @@ class HibernateQueryCriteriaParser(val contractType: Class<out ContractState>,
|
||||
predicateSet.add(criteriaBuilder.and(vaultStates.get<String>("contractStateClassName").`in`(contractTypes)))
|
||||
|
||||
// soft locking
|
||||
if (!criteria.includeSoftlockedStates)
|
||||
predicateSet.add(criteriaBuilder.and(vaultStates.get<String>("lockId").isNull))
|
||||
criteria.softLockingCondition?.let {
|
||||
val softLocking = criteria.softLockingCondition
|
||||
val type = softLocking!!.type
|
||||
when(type) {
|
||||
QueryCriteria.SoftLockingType.UNLOCKED_ONLY ->
|
||||
predicateSet.add(criteriaBuilder.and(vaultStates.get<String>("lockId").isNull))
|
||||
QueryCriteria.SoftLockingType.LOCKED_ONLY ->
|
||||
predicateSet.add(criteriaBuilder.and(vaultStates.get<String>("lockId").isNotNull))
|
||||
QueryCriteria.SoftLockingType.UNLOCKED_AND_SPECIFIED -> {
|
||||
require(softLocking.lockIds.isNotEmpty()) { "Must specify one or more lockIds" }
|
||||
predicateSet.add(criteriaBuilder.or(vaultStates.get<String>("lockId").isNull,
|
||||
vaultStates.get<String>("lockId").`in`(softLocking.lockIds.map { it.toString() })))
|
||||
}
|
||||
QueryCriteria.SoftLockingType.SPECIFIED -> {
|
||||
require(softLocking.lockIds.isNotEmpty()) { "Must specify one or more lockIds" }
|
||||
predicateSet.add(criteriaBuilder.and(vaultStates.get<String>("lockId").`in`(softLocking.lockIds.map { it.toString() })))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// notary names
|
||||
criteria.notaryName?.let {
|
||||
|
@ -430,26 +430,6 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
||||
return stateAndRefs
|
||||
}
|
||||
|
||||
override fun <T : ContractState> softLockedStates(lockId: UUID?): List<StateAndRef<T>> {
|
||||
val stateAndRefs =
|
||||
session.withTransaction(transactionIsolationLevel) {
|
||||
val query = select(VaultSchema.VaultStates::class)
|
||||
.where(VaultSchema.VaultStates::stateStatus eq Vault.StateStatus.UNCONSUMED)
|
||||
.and(VaultSchema.VaultStates::contractStateClassName eq Cash.State::class.java.name)
|
||||
if (lockId != null)
|
||||
query.and(VaultSchema.VaultStates::lockId eq lockId)
|
||||
else
|
||||
query.and(VaultSchema.VaultStates::lockId.notNull())
|
||||
query.get()
|
||||
.map { it ->
|
||||
val stateRef = StateRef(SecureHash.parse(it.txId), it.index)
|
||||
val state = it.contractState.deserialize<TransactionState<T>>(context = STORAGE_CONTEXT)
|
||||
StateAndRef(state, stateRef)
|
||||
}.toList()
|
||||
}
|
||||
return stateAndRefs
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a transaction that moves an amount of currency to the given pubkey.
|
||||
*
|
||||
|
@ -12,6 +12,7 @@ import net.corda.core.node.services.Vault
|
||||
import net.corda.core.node.services.VaultQueryService
|
||||
import net.corda.core.node.services.VaultService
|
||||
import net.corda.core.node.services.queryBy
|
||||
import net.corda.core.node.services.vault.QueryCriteria.*
|
||||
import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria
|
||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
@ -132,22 +133,24 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
vaultSvc.softLockReserve(softLockId, stateRefsToSoftLock)
|
||||
|
||||
// all softlocked states
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>()).hasSize(2)
|
||||
val criteriaLocked = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.LOCKED_ONLY))
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaLocked).states).hasSize(2)
|
||||
// my softlocked states
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId)).hasSize(2)
|
||||
val criteriaByLockId = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.SPECIFIED, listOf(softLockId)))
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaByLockId).states).hasSize(2)
|
||||
|
||||
// excluding softlocked states
|
||||
val unlockedStates1 = vaultQuery.queryBy<Cash.State>(VaultQueryCriteria(includeSoftlockedStates = false)).states
|
||||
val unlockedStates1 = vaultQuery.queryBy<Cash.State>(VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.UNLOCKED_ONLY))).states
|
||||
assertThat(unlockedStates1).hasSize(1)
|
||||
|
||||
// soft lock release one of the states explicitly
|
||||
vaultSvc.softLockRelease(softLockId, NonEmptySet.of(unconsumedStates[1].ref))
|
||||
val unlockedStates2 = vaultQuery.queryBy<Cash.State>(VaultQueryCriteria(includeSoftlockedStates = false)).states
|
||||
val unlockedStates2 = vaultQuery.queryBy<Cash.State>(VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.UNLOCKED_ONLY))).states
|
||||
assertThat(unlockedStates2).hasSize(2)
|
||||
|
||||
// soft lock release the rest by id
|
||||
vaultSvc.softLockRelease(softLockId)
|
||||
val unlockedStates = vaultQuery.queryBy<Cash.State>(VaultQueryCriteria(includeSoftlockedStates = false)).states
|
||||
val unlockedStates = vaultQuery.queryBy<Cash.State>(VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.UNLOCKED_ONLY))).states
|
||||
assertThat(unlockedStates).hasSize(3)
|
||||
|
||||
// should be back to original states
|
||||
@ -157,13 +160,15 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
|
||||
@Test
|
||||
fun `soft locking attempt concurrent reserve`() {
|
||||
|
||||
val backgroundExecutor = Executors.newFixedThreadPool(2)
|
||||
val countDown = CountDownLatch(2)
|
||||
|
||||
val softLockId1 = UUID.randomUUID()
|
||||
val softLockId2 = UUID.randomUUID()
|
||||
|
||||
val criteriaByLockId1 = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.SPECIFIED, listOf(softLockId1)))
|
||||
val criteriaByLockId2 = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.SPECIFIED, listOf(softLockId2)))
|
||||
|
||||
val vaultStates =
|
||||
database.transaction {
|
||||
assertEquals(0.DOLLARS, services.getCashBalance(USD))
|
||||
@ -177,7 +182,7 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
try {
|
||||
database.transaction {
|
||||
vaultSvc.softLockReserve(softLockId1, stateRefsToSoftLock)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId1)).hasSize(3)
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaByLockId1).states).hasSize(3)
|
||||
}
|
||||
println("SOFT LOCK STATES #1 succeeded")
|
||||
} catch(e: Throwable) {
|
||||
@ -193,7 +198,7 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
Thread.sleep(100) // let 1st thread soft lock them 1st
|
||||
database.transaction {
|
||||
vaultSvc.softLockReserve(softLockId2, stateRefsToSoftLock)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId2)).hasSize(3)
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaByLockId2).states).hasSize(3)
|
||||
}
|
||||
println("SOFT LOCK STATES #2 succeeded")
|
||||
} catch(e: Throwable) {
|
||||
@ -205,10 +210,10 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
|
||||
countDown.await()
|
||||
database.transaction {
|
||||
val lockStatesId1 = vaultSvc.softLockedStates<Cash.State>(softLockId1)
|
||||
val lockStatesId1 = vaultQuery.queryBy<Cash.State>(criteriaByLockId1).states
|
||||
println("SOFT LOCK #1 final states: $lockStatesId1")
|
||||
assertThat(lockStatesId1.size).isIn(0, 3)
|
||||
val lockStatesId2 = vaultSvc.softLockedStates<Cash.State>(softLockId2)
|
||||
val lockStatesId2 = vaultQuery.queryBy<Cash.State>(criteriaByLockId2).states
|
||||
println("SOFT LOCK #2 final states: $lockStatesId2")
|
||||
assertThat(lockStatesId2.size).isIn(0, 3)
|
||||
}
|
||||
@ -216,7 +221,6 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
|
||||
@Test
|
||||
fun `soft locking partial reserve states fails`() {
|
||||
|
||||
val softLockId1 = UUID.randomUUID()
|
||||
val softLockId2 = UUID.randomUUID()
|
||||
|
||||
@ -231,7 +235,8 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
// lock 1st state with LockId1
|
||||
database.transaction {
|
||||
vaultSvc.softLockReserve(softLockId1, NonEmptySet.of(stateRefsToSoftLock.first()))
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId1)).hasSize(1)
|
||||
val criteriaByLockId1 = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.SPECIFIED, listOf(softLockId1)))
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaByLockId1).states).hasSize(1)
|
||||
}
|
||||
|
||||
// attempt to lock all 3 states with LockId2
|
||||
@ -244,8 +249,8 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
|
||||
@Test
|
||||
fun `attempt to lock states already soft locked by me`() {
|
||||
|
||||
val softLockId1 = UUID.randomUUID()
|
||||
val criteriaByLockId1 = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.SPECIFIED, listOf(softLockId1)))
|
||||
|
||||
val vaultStates =
|
||||
database.transaction {
|
||||
@ -258,13 +263,13 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
// lock states with LockId1
|
||||
database.transaction {
|
||||
vaultSvc.softLockReserve(softLockId1, stateRefsToSoftLock)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId1)).hasSize(3)
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaByLockId1).states).hasSize(3)
|
||||
}
|
||||
|
||||
// attempt to relock same states with LockId1
|
||||
database.transaction {
|
||||
vaultSvc.softLockReserve(softLockId1, stateRefsToSoftLock)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId1)).hasSize(3)
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaByLockId1).states).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,6 +277,7 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
fun `lock additional states to some already soft locked by me`() {
|
||||
|
||||
val softLockId1 = UUID.randomUUID()
|
||||
val criteriaByLockId1 = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.SPECIFIED, listOf(softLockId1)))
|
||||
|
||||
val vaultStates =
|
||||
database.transaction {
|
||||
@ -284,13 +290,13 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
// lock states with LockId1
|
||||
database.transaction {
|
||||
vaultSvc.softLockReserve(softLockId1, NonEmptySet.of(stateRefsToSoftLock.first()))
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId1)).hasSize(1)
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaByLockId1).states).hasSize(1)
|
||||
}
|
||||
|
||||
// attempt to lock all states with LockId1 (including previously already locked one)
|
||||
database.transaction {
|
||||
vaultSvc.softLockReserve(softLockId1, stateRefsToSoftLock.toNonEmptySet())
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId1)).hasSize(3)
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaByLockId1).states).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,7 +313,8 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
spendableStatesUSD.forEach(::println)
|
||||
assertThat(spendableStatesUSD).hasSize(1)
|
||||
assertThat(spendableStatesUSD[0].state.data.amount.quantity).isEqualTo(100L * 100)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>()).hasSize(1)
|
||||
val criteriaLocked = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.LOCKED_ONLY))
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaLocked).states).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,7 +367,8 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
val spendableStatesUSD = (vaultSvc as NodeVaultService).unconsumedStatesForSpending<Cash.State>(110.DOLLARS, lockId = UUID.randomUUID())
|
||||
spendableStatesUSD.forEach(::println)
|
||||
assertThat(spendableStatesUSD).hasSize(1)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>()).hasSize(0)
|
||||
val criteriaLocked = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.LOCKED_ONLY))
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaLocked).states).hasSize(0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -377,7 +385,8 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
spendableStatesUSD.forEach(::println)
|
||||
assertThat(spendableStatesUSD).hasSize(1)
|
||||
assertThat(spendableStatesUSD[0].state.data.amount.quantity).isGreaterThanOrEqualTo(100L)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>()).hasSize(1)
|
||||
val criteriaLocked = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.LOCKED_ONLY))
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaLocked).states).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -397,7 +406,8 @@ class NodeVaultServiceTest : TestDependencyInjectionBase() {
|
||||
spendableStatesUSD.forEach(::println)
|
||||
}
|
||||
// note only 3 spend attempts succeed with a total of 8 states
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>()).hasSize(8)
|
||||
val criteriaLocked = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.LOCKED_ONLY))
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaLocked).states).hasSize(8)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,6 +127,9 @@ class VaultQueryTests : TestDependencyInjectionBase() {
|
||||
return props
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val expectedEx = ExpectedException.none()!!
|
||||
|
||||
/**
|
||||
* Query API tests
|
||||
*/
|
||||
@ -460,15 +463,46 @@ class VaultQueryTests : TestDependencyInjectionBase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed states excluding soft locks`() {
|
||||
fun `unconsumed states with soft locking`() {
|
||||
database.transaction {
|
||||
|
||||
val issuedStates = services.fillWithSomeTestCash(100.DOLLARS, CASH_NOTARY, 3, 3, Random(0L))
|
||||
vaultSvc.softLockReserve(UUID.randomUUID(), NonEmptySet.of(issuedStates.states.first().ref, issuedStates.states.last().ref))
|
||||
val issuedStates = services.fillWithSomeTestCash(100.DOLLARS, CASH_NOTARY, 10, 10, Random(0L)).states.toList()
|
||||
vaultSvc.softLockReserve(UUID.randomUUID(), NonEmptySet.of(issuedStates[1].ref, issuedStates[2].ref, issuedStates[3].ref))
|
||||
val lockId1 = UUID.randomUUID()
|
||||
vaultSvc.softLockReserve(lockId1, NonEmptySet.of(issuedStates[4].ref, issuedStates[5].ref))
|
||||
val lockId2 = UUID.randomUUID()
|
||||
vaultSvc.softLockReserve(lockId2, NonEmptySet.of(issuedStates[6].ref))
|
||||
|
||||
val criteria = VaultQueryCriteria(includeSoftlockedStates = false)
|
||||
val results = vaultQuerySvc.queryBy<ContractState>(criteria)
|
||||
assertThat(results.states).hasSize(1)
|
||||
// excluding soft locked states
|
||||
val criteriaExclusive = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.UNLOCKED_ONLY))
|
||||
val resultsExclusive = vaultQuerySvc.queryBy<ContractState>(criteriaExclusive)
|
||||
assertThat(resultsExclusive.states).hasSize(4)
|
||||
|
||||
// only soft locked states
|
||||
val criteriaLockedOnly = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.LOCKED_ONLY))
|
||||
val resultsLockedOnly = vaultQuerySvc.queryBy<ContractState>(criteriaLockedOnly)
|
||||
assertThat(resultsLockedOnly.states).hasSize(6)
|
||||
|
||||
// soft locked states by single lock id
|
||||
val criteriaByLockId = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.SPECIFIED, listOf(lockId1)))
|
||||
val resultsByLockId = vaultQuerySvc.queryBy<ContractState>(criteriaByLockId)
|
||||
assertThat(resultsByLockId.states).hasSize(2)
|
||||
|
||||
// soft locked states by multiple lock ids
|
||||
val criteriaByLockIds = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.SPECIFIED, listOf(lockId1, lockId2)))
|
||||
val resultsByLockIds = vaultQuerySvc.queryBy<ContractState>(criteriaByLockIds)
|
||||
assertThat(resultsByLockIds.states).hasSize(3)
|
||||
|
||||
// unlocked and locked by `lockId2`
|
||||
val criteriaUnlockedAndByLockId = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.UNLOCKED_AND_SPECIFIED, listOf(lockId2)))
|
||||
val resultsUnlockedAndByLockIds = vaultQuerySvc.queryBy<ContractState>(criteriaUnlockedAndByLockId)
|
||||
assertThat(resultsUnlockedAndByLockIds.states).hasSize(5)
|
||||
|
||||
// missing lockId
|
||||
expectedEx.expect(IllegalArgumentException::class.java)
|
||||
expectedEx.expectMessage("Must specify one or more lockIds")
|
||||
val criteriaMissingLockId = VaultQueryCriteria(softLockingCondition = SoftLockingCondition(SoftLockingType.UNLOCKED_AND_SPECIFIED))
|
||||
vaultQuerySvc.queryBy<ContractState>(criteriaMissingLockId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -980,9 +1014,6 @@ class VaultQueryTests : TestDependencyInjectionBase() {
|
||||
}
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val expectedEx = ExpectedException.none()!!
|
||||
|
||||
// pagination: invalid page number
|
||||
@Test
|
||||
fun `invalid page number`() {
|
||||
|
@ -9,6 +9,7 @@ import net.corda.core.node.services.Vault
|
||||
import net.corda.core.node.services.VaultQueryService
|
||||
import net.corda.core.node.services.VaultService
|
||||
import net.corda.core.node.services.queryBy
|
||||
import net.corda.core.node.services.vault.QueryCriteria
|
||||
import net.corda.core.node.services.vault.QueryCriteria.VaultQueryCriteria
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
@ -128,6 +129,7 @@ class VaultWithCashTest : TestDependencyInjectionBase() {
|
||||
@Test
|
||||
fun `issue and attempt double spend`() {
|
||||
val freshKey = services.keyManagementService.freshKey()
|
||||
val criteriaLocked = VaultQueryCriteria(softLockingCondition = QueryCriteria.SoftLockingCondition(QueryCriteria.SoftLockingType.LOCKED_ONLY))
|
||||
|
||||
database.transaction {
|
||||
// A tx that sends us money.
|
||||
@ -138,11 +140,12 @@ class VaultWithCashTest : TestDependencyInjectionBase() {
|
||||
println("Cash balance: ${services.getCashBalance(USD)}")
|
||||
|
||||
assertThat(vaultQuery.queryBy<Cash.State>().states).hasSize(10)
|
||||
assertThat(vault.softLockedStates<Cash.State>()).hasSize(0)
|
||||
assertThat(vaultQuery.queryBy<Cash.State>(criteriaLocked).states).hasSize(0)
|
||||
}
|
||||
|
||||
val backgroundExecutor = Executors.newFixedThreadPool(2)
|
||||
val countDown = CountDownLatch(2)
|
||||
|
||||
// 1st tx that spends our money.
|
||||
backgroundExecutor.submit {
|
||||
database.transaction {
|
||||
@ -154,19 +157,21 @@ class VaultWithCashTest : TestDependencyInjectionBase() {
|
||||
println("txn1: ${txn1.id} spent ${((txn1.tx.outputs[0].data) as Cash.State).amount}")
|
||||
val unconsumedStates1 = vaultQuery.queryBy<Cash.State>()
|
||||
val consumedStates1 = vaultQuery.queryBy<Cash.State>(VaultQueryCriteria(status = Vault.StateStatus.CONSUMED))
|
||||
val lockedStates1 = vaultQuery.queryBy<Cash.State>(criteriaLocked).states
|
||||
println("""txn1 states:
|
||||
UNCONSUMED: ${unconsumedStates1.totalStatesAvailable} : $unconsumedStates1,
|
||||
CONSUMED: ${consumedStates1.totalStatesAvailable} : $consumedStates1,
|
||||
LOCKED: ${vault.softLockedStates<Cash.State>().count()} : ${vault.softLockedStates<Cash.State>()}
|
||||
LOCKED: ${lockedStates1.count()} : $lockedStates1
|
||||
""")
|
||||
services.recordTransactions(txn1)
|
||||
println("txn1: Cash balance: ${services.getCashBalance(USD)}")
|
||||
val unconsumedStates2 = vaultQuery.queryBy<Cash.State>()
|
||||
val consumedStates2 = vaultQuery.queryBy<Cash.State>(VaultQueryCriteria(status = Vault.StateStatus.CONSUMED))
|
||||
val lockedStates2 = vaultQuery.queryBy<Cash.State>(criteriaLocked).states
|
||||
println("""txn1 states:
|
||||
UNCONSUMED: ${unconsumedStates2.totalStatesAvailable} : $unconsumedStates2,
|
||||
CONSUMED: ${consumedStates2.totalStatesAvailable} : $consumedStates2,
|
||||
LOCKED: ${vault.softLockedStates<Cash.State>().count()} : ${vault.softLockedStates<Cash.State>()}
|
||||
LOCKED: ${lockedStates2.count()} : $lockedStates2
|
||||
""")
|
||||
txn1
|
||||
} catch(e: Exception) {
|
||||
@ -188,19 +193,21 @@ class VaultWithCashTest : TestDependencyInjectionBase() {
|
||||
println("txn2: ${txn2.id} spent ${((txn2.tx.outputs[0].data) as Cash.State).amount}")
|
||||
val unconsumedStates1 = vaultQuery.queryBy<Cash.State>()
|
||||
val consumedStates1 = vaultQuery.queryBy<Cash.State>(VaultQueryCriteria(status = Vault.StateStatus.CONSUMED))
|
||||
val lockedStates1 = vaultQuery.queryBy<Cash.State>(criteriaLocked).states
|
||||
println("""txn2 states:
|
||||
UNCONSUMED: ${unconsumedStates1.totalStatesAvailable} : $unconsumedStates1,
|
||||
CONSUMED: ${consumedStates1.totalStatesAvailable} : $consumedStates1,
|
||||
LOCKED: ${vault.softLockedStates<Cash.State>().count()} : ${vault.softLockedStates<Cash.State>()}
|
||||
LOCKED: ${lockedStates1.count()} : $lockedStates1
|
||||
""")
|
||||
services.recordTransactions(txn2)
|
||||
println("txn2: Cash balance: ${services.getCashBalance(USD)}")
|
||||
val unconsumedStates2 = vaultQuery.queryBy<Cash.State>()
|
||||
val consumedStates2 = vaultQuery.queryBy<Cash.State>()
|
||||
val lockedStates2 = vaultQuery.queryBy<Cash.State>(criteriaLocked).states
|
||||
println("""txn2 states:
|
||||
UNCONSUMED: ${unconsumedStates2.totalStatesAvailable} : $unconsumedStates2,
|
||||
CONSUMED: ${consumedStates2.totalStatesAvailable} : $consumedStates2,
|
||||
LOCKED: ${vault.softLockedStates<Cash.State>().count()} : ${vault.softLockedStates<Cash.State>()}
|
||||
LOCKED: ${lockedStates2.count()} : $lockedStates2
|
||||
""")
|
||||
txn2
|
||||
} catch(e: Exception) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user