mirror of
https://github.com/corda/corda.git
synced 2025-06-16 06:08:13 +00:00
Vault Query API design (#522)
* Added queryBy(QueryCriteria) Vault API and Junit tests. * Minor fix following rebase. * Spit out Vault Query tests into separate source file. * WIP * Enable composition of QueryCriteria specifications. Additional JUnit test cases to validate API. * Added Deprecating annotations. Added QueryCriteria for set of contractStateTypes * Minor tweaks and additional JUnit test cases (chain of linear id) * Added Java Junit tests and QueryCriteria builder support. * Added API documentation (including coding snippets and examples). * Added @JvmOverloads to QueryCriteria classes for easy of use from Java. * Refactored QueryCriteria API to use composition via sealed data classes. * Enable infix notation. * Fixed typo. * Clarified future work to enforce DB level permissioning. * Moved PageSpec and Order from QueryCriteria to become parameters of Query itself. * Moved PageSpec and Order from QueryCriteria to become parameters of Query itself. * TokenType now specified as set of <Class> (was non extensible enum). * Exposed new Vault Query API functions via RPC. * Fixed compiler error in java test. * Addressed a couple of minor PR review scomments from MH. * Major updates following PR discussion and recommendations. * All pagination and sorting arguments are optional (and constructed with sensible defaults). Added Java helper functions for queryBy and trackBy interfaces. Added Java trackBy unit tests. Miscellaneous cleanup. * Added Generic Index schema mapping and query support. * Query criteria referencing Party now references a String (until Identity framework built out). Added participants attribute to general query criteria. * Fleshed our IndexCriteria including PR recommendation to define column aliases for index mappings. * Removed all directly exposed API dependencies on requery. * Updated documentation. * Provide sensible defaults for all Query arguments. Add RPC Java helpers and increase range of Vault Service helpers. * Further improvements (upgrading notes) and updates to documentation. * RST documentation updates. * Updates to address RP latest set of review comments. * Updates to address MH latest set of review comments. * Updated to highlight use of VaultIndexQueryCriteria to directly reference a JPA-annotated entity (versus the indirect, explicitly mapped attribute to GenericIndexSchema approach) * Aesthetic updates requested by MH * Reverted Indexing approach: removed all references to VaultIndexedQueryCriteria and GenericVaultIndexSchemaV1 scheme. * Final clean-up and minor updates prior to merge. * Fixed compiler warnings (except deprecation warnings) * Reverted all changes to Vault Schemas (except simple illustrative VaultLinearState used in VaultQueryTests) * Reverted all changes to Vault Schemas (except simple illustrative VaultLinearState used in VaultQueryTests) * Commented out @Deprecated annotations (as a hedge against us releasing M12 with the work half-done) * Renamed RPC JavaHelper functions as RPCDispatcher does not allow more than one method with same name.
This commit is contained in:
@ -15,6 +15,9 @@ import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.NetworkMapCache
|
||||
import net.corda.core.node.services.StateMachineTransactionMapping
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.node.services.vault.PageSpecification
|
||||
import net.corda.core.node.services.vault.QueryCriteria
|
||||
import net.corda.core.node.services.vault.Sort
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
@ -55,6 +58,23 @@ class CordaRPCOpsImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : ContractState> vaultQueryBy(criteria: QueryCriteria,
|
||||
paging: PageSpecification,
|
||||
sorting: Sort): Vault.Page<T> {
|
||||
return database.transaction {
|
||||
services.vaultService.queryBy<T>(criteria, paging, sorting)
|
||||
}
|
||||
}
|
||||
|
||||
@RPCReturnsObservables
|
||||
override fun <T : ContractState> vaultTrackBy(criteria: QueryCriteria,
|
||||
paging: PageSpecification,
|
||||
sorting: Sort): Vault.PageAndUpdates<T> {
|
||||
return database.transaction {
|
||||
services.vaultService.trackBy<T>(criteria, paging, sorting)
|
||||
}
|
||||
}
|
||||
|
||||
override fun verifiedTransactions(): Pair<List<SignedTransaction>, Observable<SignedTransaction>> {
|
||||
return database.transaction {
|
||||
services.storageService.validatedTransactions.track()
|
||||
|
@ -15,18 +15,15 @@ import net.corda.contracts.clause.AbstractConserveAmount
|
||||
import net.corda.core.ThreadBox
|
||||
import net.corda.core.bufferUntilSubscribed
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.AbstractParty
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.crypto.Party
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.containsAny
|
||||
import net.corda.core.crypto.toBase58String
|
||||
import net.corda.core.flows.FlowStateMachine
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.services.StatesNotAvailableException
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.node.services.VaultService
|
||||
import net.corda.core.node.services.unconsumedStates
|
||||
import net.corda.core.node.services.vault.PageSpecification
|
||||
import net.corda.core.node.services.vault.QueryCriteria
|
||||
import net.corda.core.node.services.vault.Sort
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.tee
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
@ -194,6 +191,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
||||
.map { it ->
|
||||
val stateRef = StateRef(SecureHash.parse(it.txId), it.index)
|
||||
val state = it.contractState.deserialize<TransactionState<T>>(storageKryo())
|
||||
Vault.StateMetadata(stateRef, it.contractStateClassName, it.recordedTime, it.consumedTime, it.stateStatus, it.notaryName, it.notaryKey, it.lockId, it.lockUpdateTime)
|
||||
StateAndRef(state, stateRef)
|
||||
}
|
||||
}
|
||||
@ -221,6 +219,26 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
||||
return stateAndRefs.associateBy({ it.ref }, { it.state })
|
||||
}
|
||||
|
||||
override fun <T : ContractState> queryBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort): Vault.Page<T> {
|
||||
|
||||
TODO("Under construction")
|
||||
|
||||
// If [VaultQueryCriteria.PageSpecification] specified
|
||||
// must return (CloseableIterator) result.get().iterator(skip, take)
|
||||
// where
|
||||
// skip = Max[(pageNumber - 1),0] * pageSize
|
||||
// take = pageSize
|
||||
}
|
||||
|
||||
override fun <T : ContractState> trackBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort): Vault.PageAndUpdates<T> {
|
||||
TODO("Under construction")
|
||||
|
||||
// return mutex.locked {
|
||||
// Vault.PageAndUpdates(queryBy(criteria),
|
||||
// _updatesPublisher.bufferUntilSubscribed().wrapWithDatabaseTransaction())
|
||||
// }
|
||||
}
|
||||
|
||||
override fun notifyAll(txns: Iterable<WireTransaction>) {
|
||||
val ourKeys = services.keyManagementService.keys.keys
|
||||
val netDelta = txns.fold(Vault.NoUpdate) { netDelta, txn -> netDelta + makeUpdate(txn, ourKeys) }
|
||||
@ -520,4 +538,4 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
||||
private fun stateRefArgs(stateRefs: Iterable<StateRef>): List<List<Any>> {
|
||||
return stateRefs.map { listOf("'${it.txhash}'", it.index) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,280 @@
|
||||
package net.corda.node.services.vault;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import kotlin.*;
|
||||
import net.corda.contracts.asset.*;
|
||||
import net.corda.core.contracts.*;
|
||||
import net.corda.core.crypto.*;
|
||||
import net.corda.core.node.services.*;
|
||||
import net.corda.core.node.services.vault.*;
|
||||
import net.corda.core.node.services.vault.QueryCriteria.*;
|
||||
import net.corda.core.serialization.*;
|
||||
import net.corda.core.transactions.*;
|
||||
import net.corda.node.services.vault.schemas.*;
|
||||
import net.corda.testing.node.*;
|
||||
import org.jetbrains.annotations.*;
|
||||
import org.jetbrains.exposed.sql.*;
|
||||
import org.junit.*;
|
||||
import rx.Observable;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
import static net.corda.contracts.asset.CashKt.*;
|
||||
import static net.corda.contracts.testing.VaultFiller.*;
|
||||
import static net.corda.core.node.services.vault.QueryCriteriaKt.*;
|
||||
import static net.corda.core.node.services.vault.QueryCriteriaUtilsKt.*;
|
||||
import static net.corda.core.utilities.TestConstants.*;
|
||||
import static net.corda.node.utilities.DatabaseSupportKt.*;
|
||||
import static net.corda.node.utilities.DatabaseSupportKt.transaction;
|
||||
import static net.corda.testing.CoreTestUtils.*;
|
||||
import static net.corda.testing.node.MockServicesKt.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
@Ignore
|
||||
public class VaultQueryJavaTests {
|
||||
|
||||
private MockServices services;
|
||||
private VaultService vaultSvc;
|
||||
private Closeable dataSource;
|
||||
private Database database;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
Properties dataSourceProps = makeTestDataSourceProperties(SecureHash.randomSHA256().toString());
|
||||
Pair<Closeable, Database> dataSourceAndDatabase = configureDatabase(dataSourceProps);
|
||||
dataSource = dataSourceAndDatabase.getFirst();
|
||||
database = dataSourceAndDatabase.getSecond();
|
||||
|
||||
transaction(database, statement -> services = new MockServices() {
|
||||
@NotNull
|
||||
@Override
|
||||
public VaultService getVaultService() {
|
||||
return makeVaultService(dataSourceProps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordTransactions(@NotNull Iterable<SignedTransaction> txs) {
|
||||
for (SignedTransaction stx : txs ) {
|
||||
getStorageService().getValidatedTransactions().addTransaction(stx);
|
||||
}
|
||||
|
||||
Stream<WireTransaction> wtxn = StreamSupport.stream(txs.spliterator(), false).map(txn -> txn.getTx());
|
||||
getVaultService().notifyAll(wtxn.collect(Collectors.toList()));
|
||||
}
|
||||
});
|
||||
|
||||
vaultSvc = services.getVaultService();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() throws IOException {
|
||||
dataSource.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample Vault Query API tests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Static queryBy() tests
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void consumedStates() {
|
||||
transaction(database, tx -> {
|
||||
fillWithSomeTestCash(services,
|
||||
new Amount<>(100, Currency.getInstance("USD")),
|
||||
getDUMMY_NOTARY(),
|
||||
3,
|
||||
3,
|
||||
new Random(),
|
||||
new OpaqueBytes("1".getBytes()),
|
||||
null,
|
||||
getDUMMY_CASH_ISSUER(),
|
||||
getDUMMY_CASH_ISSUER_KEY() );
|
||||
|
||||
// DOCSTART VaultJavaQueryExample1
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<Class<ContractState>> contractStateTypes = new HashSet(Collections.singletonList(Cash.State.class));
|
||||
Vault.StateStatus status = Vault.StateStatus.CONSUMED;
|
||||
|
||||
VaultQueryCriteria criteria = new VaultQueryCriteria(status, null, contractStateTypes);
|
||||
Vault.Page<ContractState> results = vaultSvc.queryBy(criteria);
|
||||
// DOCEND VaultJavaQueryExample1
|
||||
|
||||
assertThat(results.getStates()).hasSize(3);
|
||||
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumedDealStatesPagedSorted() {
|
||||
transaction(database, tx -> {
|
||||
|
||||
UniqueIdentifier uid = new UniqueIdentifier();
|
||||
fillWithSomeTestLinearStates(services, 10, uid);
|
||||
|
||||
List<String> dealIds = Arrays.asList("123", "456", "789");
|
||||
fillWithSomeTestDeals(services, dealIds, 0);
|
||||
|
||||
// DOCSTART VaultJavaQueryExample2
|
||||
Vault.StateStatus status = Vault.StateStatus.CONSUMED;
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<Class<ContractState>> contractStateTypes = new HashSet(Collections.singletonList(Cash.State.class));
|
||||
|
||||
QueryCriteria vaultCriteria = new VaultQueryCriteria(status, null, contractStateTypes);
|
||||
|
||||
List<UniqueIdentifier> linearIds = Arrays.asList(uid);
|
||||
List<String> dealPartyNames = Arrays.asList(getMEGA_CORP().getName());
|
||||
QueryCriteria dealCriteriaAll = new LinearStateQueryCriteria(linearIds, false, dealIds, dealPartyNames);
|
||||
|
||||
QueryCriteria compositeCriteria = and(dealCriteriaAll, vaultCriteria);
|
||||
|
||||
PageSpecification pageSpec = new PageSpecification(0, getMAX_PAGE_SIZE());
|
||||
Sort.SortColumn sortByUid = new Sort.SortColumn(VaultLinearStateEntity.UUID.getName(), Sort.Direction.DESC, Sort.NullHandling.NULLS_LAST);
|
||||
Sort sorting = new Sort(ImmutableSet.of(sortByUid));
|
||||
Vault.Page<ContractState> results = vaultSvc.queryBy(compositeCriteria, pageSpec, sorting);
|
||||
// DOCEND VaultJavaQueryExample2
|
||||
|
||||
assertThat(results.getStates()).hasSize(4);
|
||||
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamic trackBy() tests
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void trackCashStates() {
|
||||
|
||||
transaction(database, tx -> {
|
||||
fillWithSomeTestCash(services,
|
||||
new Amount<>(100, Currency.getInstance("USD")),
|
||||
getDUMMY_NOTARY(),
|
||||
3,
|
||||
3,
|
||||
new Random(),
|
||||
new OpaqueBytes("1".getBytes()),
|
||||
null,
|
||||
getDUMMY_CASH_ISSUER(),
|
||||
getDUMMY_CASH_ISSUER_KEY() );
|
||||
|
||||
// DOCSTART VaultJavaQueryExample1
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<Class<ContractState>> contractStateTypes = new HashSet(Collections.singletonList(Cash.State.class));
|
||||
|
||||
VaultQueryCriteria criteria = new VaultQueryCriteria(Vault.StateStatus.UNCONSUMED, null, contractStateTypes);
|
||||
Vault.PageAndUpdates<ContractState> results = vaultSvc.trackBy(criteria);
|
||||
|
||||
Vault.Page<ContractState> snapshot = results.getCurrent();
|
||||
Observable<Vault.Update> updates = results.getFuture();
|
||||
|
||||
// DOCEND VaultJavaQueryExample1
|
||||
assertThat(snapshot.getStates()).hasSize(3);
|
||||
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trackDealStatesPagedSorted() {
|
||||
transaction(database, tx -> {
|
||||
|
||||
UniqueIdentifier uid = new UniqueIdentifier();
|
||||
fillWithSomeTestLinearStates(services, 10, uid);
|
||||
|
||||
List<String> dealIds = Arrays.asList("123", "456", "789");
|
||||
fillWithSomeTestDeals(services, dealIds, 0);
|
||||
|
||||
// DOCSTART VaultJavaQueryExample2
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<Class<ContractState>> contractStateTypes = new HashSet(Collections.singletonList(DealState.class));
|
||||
QueryCriteria vaultCriteria = new VaultQueryCriteria(Vault.StateStatus.UNCONSUMED, null, contractStateTypes);
|
||||
|
||||
List<UniqueIdentifier> linearIds = Arrays.asList(uid);
|
||||
List<String> dealPartyNames = Arrays.asList(getMEGA_CORP().getName());
|
||||
QueryCriteria dealCriteriaAll = new LinearStateQueryCriteria(linearIds, false, dealIds, dealPartyNames);
|
||||
|
||||
QueryCriteria compositeCriteria = and(dealCriteriaAll, vaultCriteria);
|
||||
|
||||
PageSpecification pageSpec = new PageSpecification(0, getMAX_PAGE_SIZE());
|
||||
Sort.SortColumn sortByUid = new Sort.SortColumn(VaultLinearStateEntity.UUID.getName(), Sort.Direction.DESC, Sort.NullHandling.NULLS_LAST);
|
||||
Sort sorting = new Sort(ImmutableSet.of(sortByUid));
|
||||
Vault.PageAndUpdates<ContractState> results = vaultSvc.trackBy(compositeCriteria, pageSpec, sorting);
|
||||
|
||||
Vault.Page<ContractState> snapshot = results.getCurrent();
|
||||
Observable<Vault.Update> updates = results.getFuture();
|
||||
// DOCEND VaultJavaQueryExample2
|
||||
|
||||
assertThat(snapshot.getStates()).hasSize(4);
|
||||
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated usage
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void consumedStatesDeprecated() {
|
||||
transaction(database, tx -> {
|
||||
fillWithSomeTestCash(services,
|
||||
new Amount<>(100, Currency.getInstance("USD")),
|
||||
getDUMMY_NOTARY(),
|
||||
3,
|
||||
3,
|
||||
new Random(),
|
||||
new OpaqueBytes("1".getBytes()),
|
||||
null,
|
||||
getDUMMY_CASH_ISSUER(),
|
||||
getDUMMY_CASH_ISSUER_KEY() );
|
||||
|
||||
// DOCSTART VaultDeprecatedJavaQueryExample1
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<Class<ContractState>> contractStateTypes = new HashSet(Collections.singletonList(Cash.State.class));
|
||||
EnumSet<Vault.StateStatus> status = EnumSet.of(Vault.StateStatus.CONSUMED);
|
||||
|
||||
// WARNING! unfortunately cannot use inlined reified Kotlin extension methods.
|
||||
Iterable<StateAndRef<ContractState>> results = vaultSvc.states(contractStateTypes, status, true);
|
||||
// DOCEND VaultDeprecatedJavaQueryExample1
|
||||
|
||||
assertThat(results).hasSize(3);
|
||||
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumedStatesForLinearIdDeprecated() {
|
||||
transaction(database, tx -> {
|
||||
|
||||
UniqueIdentifier trackUid = new UniqueIdentifier();
|
||||
fillWithSomeTestLinearStates(services, 1, trackUid);
|
||||
fillWithSomeTestLinearStates(services, 4, new UniqueIdentifier());
|
||||
|
||||
// DOCSTART VaultDeprecatedJavaQueryExample2
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<Class<ContractState>> contractStateTypes = new HashSet(Collections.singletonList(LinearState.class));
|
||||
EnumSet<Vault.StateStatus> status = EnumSet.of(Vault.StateStatus.CONSUMED);
|
||||
|
||||
// WARNING! unfortunately cannot use inlined reified Kotlin extension methods.
|
||||
Iterable<StateAndRef<ContractState>> results = vaultSvc.states(contractStateTypes, status, true);
|
||||
|
||||
Stream<StateAndRef<ContractState>> trackedLinearState = StreamSupport.stream(results.spliterator(), false).filter(
|
||||
state -> ((LinearState) state.component1().getData()).getLinearId() == trackUid);
|
||||
// DOCEND VaultDeprecatedJavaQueryExample2
|
||||
|
||||
assertThat(results).hasSize(4);
|
||||
assertThat(trackedLinearState).hasSize(1);
|
||||
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
}
|
@ -35,7 +35,7 @@ import kotlin.test.assertNull
|
||||
|
||||
class NodeVaultServiceTest {
|
||||
lateinit var services: MockServices
|
||||
val vault: VaultService get() = services.vaultService
|
||||
val vaultSvc: VaultService get() = services.vaultService
|
||||
lateinit var dataSource: Closeable
|
||||
lateinit var database: Database
|
||||
|
||||
@ -73,11 +73,11 @@ class NodeVaultServiceTest {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
|
||||
val w1 = services.vaultService.unconsumedStates<Cash.State>()
|
||||
val w1 = vaultSvc.unconsumedStates<Cash.State>()
|
||||
assertThat(w1).hasSize(3)
|
||||
|
||||
val originalStorage = services.storageService
|
||||
val originalVault = services.vaultService
|
||||
val originalVault = vaultSvc
|
||||
val services2 = object : MockServices() {
|
||||
override val vaultService: VaultService get() = originalVault
|
||||
|
||||
@ -103,11 +103,11 @@ class NodeVaultServiceTest {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
|
||||
val w1 = services.vaultService.unconsumedStates<Cash.State>().toList()
|
||||
val w1 = vaultSvc.unconsumedStates<Cash.State>().toList()
|
||||
assertThat(w1).hasSize(3)
|
||||
|
||||
val stateRefs = listOf(w1[1].ref, w1[2].ref)
|
||||
val states = services.vaultService.statesForRefs(stateRefs)
|
||||
val states = vaultSvc.statesForRefs(stateRefs)
|
||||
assertThat(states).hasSize(2)
|
||||
}
|
||||
}
|
||||
@ -118,32 +118,32 @@ class NodeVaultServiceTest {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
|
||||
val unconsumedStates = services.vaultService.unconsumedStates<Cash.State>().toList()
|
||||
val unconsumedStates = vaultSvc.unconsumedStates<Cash.State>().toList()
|
||||
assertThat(unconsumedStates).hasSize(3)
|
||||
|
||||
val stateRefsToSoftLock = setOf(unconsumedStates[1].ref, unconsumedStates[2].ref)
|
||||
|
||||
// soft lock two of the three states
|
||||
val softLockId = UUID.randomUUID()
|
||||
services.vaultService.softLockReserve(softLockId, stateRefsToSoftLock)
|
||||
vaultSvc.softLockReserve(softLockId, stateRefsToSoftLock)
|
||||
|
||||
// all softlocked states
|
||||
assertThat(services.vaultService.softLockedStates<Cash.State>()).hasSize(2)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>()).hasSize(2)
|
||||
// my softlocked states
|
||||
assertThat(services.vaultService.softLockedStates<Cash.State>(softLockId)).hasSize(2)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId)).hasSize(2)
|
||||
|
||||
// excluding softlocked states
|
||||
val unlockedStates1 = services.vaultService.unconsumedStates<Cash.State>(includeSoftLockedStates = false).toList()
|
||||
val unlockedStates1 = vaultSvc.unconsumedStates<Cash.State>(includeSoftLockedStates = false).toList()
|
||||
assertThat(unlockedStates1).hasSize(1)
|
||||
|
||||
// soft lock release one of the states explicitly
|
||||
services.vaultService.softLockRelease(softLockId, setOf(unconsumedStates[1].ref))
|
||||
val unlockedStates2 = services.vaultService.unconsumedStates<Cash.State>(includeSoftLockedStates = false).toList()
|
||||
vaultSvc.softLockRelease(softLockId, setOf(unconsumedStates[1].ref))
|
||||
val unlockedStates2 = vaultSvc.unconsumedStates<Cash.State>(includeSoftLockedStates = false).toList()
|
||||
assertThat(unlockedStates2).hasSize(2)
|
||||
|
||||
// soft lock release the rest by id
|
||||
services.vaultService.softLockRelease(softLockId)
|
||||
val unlockedStates = services.vaultService.unconsumedStates<Cash.State>(includeSoftLockedStates = false).toList()
|
||||
vaultSvc.softLockRelease(softLockId)
|
||||
val unlockedStates = vaultSvc.unconsumedStates<Cash.State>(includeSoftLockedStates = false).toList()
|
||||
assertThat(unlockedStates).hasSize(3)
|
||||
|
||||
// should be back to original states
|
||||
@ -162,7 +162,7 @@ class NodeVaultServiceTest {
|
||||
|
||||
val vaultStates =
|
||||
database.transaction {
|
||||
assertNull(vault.cashBalances[USD])
|
||||
assertNull(vaultSvc.cashBalances[USD])
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
}
|
||||
val stateRefsToSoftLock = vaultStates.states.map { it.ref }.toSet()
|
||||
@ -172,8 +172,8 @@ class NodeVaultServiceTest {
|
||||
backgroundExecutor.submit {
|
||||
try {
|
||||
database.transaction {
|
||||
vault.softLockReserve(softLockId1, stateRefsToSoftLock)
|
||||
assertThat(vault.softLockedStates<Cash.State>(softLockId1)).hasSize(3)
|
||||
vaultSvc.softLockReserve(softLockId1, stateRefsToSoftLock)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId1)).hasSize(3)
|
||||
}
|
||||
println("SOFT LOCK STATES #1 succeeded")
|
||||
} catch(e: Throwable) {
|
||||
@ -188,8 +188,8 @@ class NodeVaultServiceTest {
|
||||
try {
|
||||
Thread.sleep(100) // let 1st thread soft lock them 1st
|
||||
database.transaction {
|
||||
vault.softLockReserve(softLockId2, stateRefsToSoftLock)
|
||||
assertThat(vault.softLockedStates<Cash.State>(softLockId2)).hasSize(3)
|
||||
vaultSvc.softLockReserve(softLockId2, stateRefsToSoftLock)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId2)).hasSize(3)
|
||||
}
|
||||
println("SOFT LOCK STATES #2 succeeded")
|
||||
} catch(e: Throwable) {
|
||||
@ -201,10 +201,10 @@ class NodeVaultServiceTest {
|
||||
|
||||
countDown.await()
|
||||
database.transaction {
|
||||
val lockStatesId1 = vault.softLockedStates<Cash.State>(softLockId1)
|
||||
val lockStatesId1 = vaultSvc.softLockedStates<Cash.State>(softLockId1)
|
||||
println("SOFT LOCK #1 final states: $lockStatesId1")
|
||||
assertThat(lockStatesId1.size).isIn(0, 3)
|
||||
val lockStatesId2 = vault.softLockedStates<Cash.State>(softLockId2)
|
||||
val lockStatesId2 = vaultSvc.softLockedStates<Cash.State>(softLockId2)
|
||||
println("SOFT LOCK #2 final states: $lockStatesId2")
|
||||
assertThat(lockStatesId2.size).isIn(0, 3)
|
||||
}
|
||||
@ -218,7 +218,7 @@ class NodeVaultServiceTest {
|
||||
|
||||
val vaultStates =
|
||||
database.transaction {
|
||||
assertNull(vault.cashBalances[USD])
|
||||
assertNull(vaultSvc.cashBalances[USD])
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
}
|
||||
val stateRefsToSoftLock = vaultStates.states.map { it.ref }.toSet()
|
||||
@ -226,14 +226,14 @@ class NodeVaultServiceTest {
|
||||
|
||||
// lock 1st state with LockId1
|
||||
database.transaction {
|
||||
vault.softLockReserve(softLockId1, setOf(stateRefsToSoftLock.first()))
|
||||
assertThat(vault.softLockedStates<Cash.State>(softLockId1)).hasSize(1)
|
||||
vaultSvc.softLockReserve(softLockId1, setOf(stateRefsToSoftLock.first()))
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId1)).hasSize(1)
|
||||
}
|
||||
|
||||
// attempt to lock all 3 states with LockId2
|
||||
database.transaction {
|
||||
assertThatExceptionOfType(StatesNotAvailableException::class.java).isThrownBy(
|
||||
{ vault.softLockReserve(softLockId2, stateRefsToSoftLock) }
|
||||
{ vaultSvc.softLockReserve(softLockId2, stateRefsToSoftLock) }
|
||||
).withMessageContaining("only 2 rows available").withNoCause()
|
||||
}
|
||||
}
|
||||
@ -245,7 +245,7 @@ class NodeVaultServiceTest {
|
||||
|
||||
val vaultStates =
|
||||
database.transaction {
|
||||
assertNull(vault.cashBalances[USD])
|
||||
assertNull(vaultSvc.cashBalances[USD])
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
}
|
||||
val stateRefsToSoftLock = vaultStates.states.map { it.ref }.toSet()
|
||||
@ -253,14 +253,14 @@ class NodeVaultServiceTest {
|
||||
|
||||
// lock states with LockId1
|
||||
database.transaction {
|
||||
vault.softLockReserve(softLockId1, stateRefsToSoftLock)
|
||||
assertThat(vault.softLockedStates<Cash.State>(softLockId1)).hasSize(3)
|
||||
vaultSvc.softLockReserve(softLockId1, stateRefsToSoftLock)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId1)).hasSize(3)
|
||||
}
|
||||
|
||||
// attempt to relock same states with LockId1
|
||||
database.transaction {
|
||||
vault.softLockReserve(softLockId1, stateRefsToSoftLock)
|
||||
assertThat(vault.softLockedStates<Cash.State>(softLockId1)).hasSize(3)
|
||||
vaultSvc.softLockReserve(softLockId1, stateRefsToSoftLock)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId1)).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,7 +271,7 @@ class NodeVaultServiceTest {
|
||||
|
||||
val vaultStates =
|
||||
database.transaction {
|
||||
assertNull(vault.cashBalances[USD])
|
||||
assertNull(vaultSvc.cashBalances[USD])
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
}
|
||||
val stateRefsToSoftLock = vaultStates.states.map { it.ref }.toSet()
|
||||
@ -279,14 +279,14 @@ class NodeVaultServiceTest {
|
||||
|
||||
// lock states with LockId1
|
||||
database.transaction {
|
||||
vault.softLockReserve(softLockId1, setOf(stateRefsToSoftLock.first()))
|
||||
assertThat(vault.softLockedStates<Cash.State>(softLockId1)).hasSize(1)
|
||||
vaultSvc.softLockReserve(softLockId1, setOf(stateRefsToSoftLock.first()))
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId1)).hasSize(1)
|
||||
}
|
||||
|
||||
// attempt to lock all states with LockId1 (including previously already locked one)
|
||||
database.transaction {
|
||||
vault.softLockReserve(softLockId1, stateRefsToSoftLock)
|
||||
assertThat(vault.softLockedStates<Cash.State>(softLockId1)).hasSize(3)
|
||||
vaultSvc.softLockReserve(softLockId1, stateRefsToSoftLock)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>(softLockId1)).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,14 +296,14 @@ class NodeVaultServiceTest {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
|
||||
val unconsumedStates = services.vaultService.unconsumedStates<Cash.State>().toList()
|
||||
val unconsumedStates = vaultSvc.unconsumedStates<Cash.State>().toList()
|
||||
assertThat(unconsumedStates).hasSize(1)
|
||||
|
||||
val spendableStatesUSD = (services.vaultService as NodeVaultService).unconsumedStatesForSpending<Cash.State>(100.DOLLARS, lockId = UUID.randomUUID())
|
||||
val spendableStatesUSD = (vaultSvc as NodeVaultService).unconsumedStatesForSpending<Cash.State>(100.DOLLARS, lockId = UUID.randomUUID())
|
||||
spendableStatesUSD.forEach(::println)
|
||||
assertThat(spendableStatesUSD).hasSize(1)
|
||||
assertThat(spendableStatesUSD[0].state.data.amount.quantity).isEqualTo(100L * 100)
|
||||
assertThat(services.vaultService.softLockedStates<Cash.State>()).hasSize(1)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>()).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,7 +314,7 @@ class NodeVaultServiceTest {
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (DUMMY_CASH_ISSUER))
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (BOC.ref(1)), issuerKey = BOC_KEY)
|
||||
|
||||
val spendableStatesUSD = services.vaultService.unconsumedStatesForSpending<Cash.State>(200.DOLLARS, lockId = UUID.randomUUID(),
|
||||
val spendableStatesUSD = vaultSvc.unconsumedStatesForSpending<Cash.State>(200.DOLLARS, lockId = UUID.randomUUID(),
|
||||
onlyFromIssuerParties = setOf(DUMMY_CASH_ISSUER.party, BOC)).toList()
|
||||
spendableStatesUSD.forEach(::println)
|
||||
assertThat(spendableStatesUSD).hasSize(2)
|
||||
@ -332,10 +332,10 @@ class NodeVaultServiceTest {
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (BOC.ref(2)), issuerKey = BOC_KEY, ref = OpaqueBytes.of(2))
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (BOC.ref(3)), issuerKey = BOC_KEY, ref = OpaqueBytes.of(3))
|
||||
|
||||
val unconsumedStates = services.vaultService.unconsumedStates<Cash.State>().toList()
|
||||
val unconsumedStates = vaultSvc.unconsumedStates<Cash.State>().toList()
|
||||
assertThat(unconsumedStates).hasSize(4)
|
||||
|
||||
val spendableStatesUSD = services.vaultService.unconsumedStatesForSpending<Cash.State>(200.DOLLARS, lockId = UUID.randomUUID(),
|
||||
val spendableStatesUSD = vaultSvc.unconsumedStatesForSpending<Cash.State>(200.DOLLARS, lockId = UUID.randomUUID(),
|
||||
onlyFromIssuerParties = setOf(BOC), withIssuerRefs = setOf(OpaqueBytes.of(1), OpaqueBytes.of(2))).toList()
|
||||
assertThat(spendableStatesUSD).hasSize(2)
|
||||
assertThat(spendableStatesUSD[0].state.data.amount.token.issuer.party).isEqualTo(BOC)
|
||||
@ -350,13 +350,13 @@ class NodeVaultServiceTest {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
|
||||
val unconsumedStates = services.vaultService.unconsumedStates<Cash.State>().toList()
|
||||
val unconsumedStates = vaultSvc.unconsumedStates<Cash.State>().toList()
|
||||
assertThat(unconsumedStates).hasSize(1)
|
||||
|
||||
val spendableStatesUSD = (services.vaultService as NodeVaultService).unconsumedStatesForSpending<Cash.State>(110.DOLLARS, lockId = UUID.randomUUID())
|
||||
val spendableStatesUSD = (vaultSvc as NodeVaultService).unconsumedStatesForSpending<Cash.State>(110.DOLLARS, lockId = UUID.randomUUID())
|
||||
spendableStatesUSD.forEach(::println)
|
||||
assertThat(spendableStatesUSD).hasSize(1)
|
||||
assertThat(services.vaultService.softLockedStates<Cash.State>()).hasSize(0)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>()).hasSize(0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,14 +366,14 @@ class NodeVaultServiceTest {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 2, 2, Random(0L))
|
||||
|
||||
val unconsumedStates = services.vaultService.unconsumedStates<Cash.State>().toList()
|
||||
val unconsumedStates = vaultSvc.unconsumedStates<Cash.State>().toList()
|
||||
assertThat(unconsumedStates).hasSize(2)
|
||||
|
||||
val spendableStatesUSD = (services.vaultService as NodeVaultService).unconsumedStatesForSpending<Cash.State>(1.DOLLARS, lockId = UUID.randomUUID())
|
||||
val spendableStatesUSD = (vaultSvc as NodeVaultService).unconsumedStatesForSpending<Cash.State>(1.DOLLARS, lockId = UUID.randomUUID())
|
||||
spendableStatesUSD.forEach(::println)
|
||||
assertThat(spendableStatesUSD).hasSize(1)
|
||||
assertThat(spendableStatesUSD[0].state.data.amount.quantity).isGreaterThanOrEqualTo(100L)
|
||||
assertThat(services.vaultService.softLockedStates<Cash.State>()).hasSize(1)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>()).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,15 +385,15 @@ class NodeVaultServiceTest {
|
||||
services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 10, 10, Random(0L))
|
||||
services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 10, 10, Random(0L))
|
||||
|
||||
val allStates = services.vaultService.unconsumedStates<Cash.State>()
|
||||
val allStates = vaultSvc.unconsumedStates<Cash.State>()
|
||||
assertThat(allStates).hasSize(30)
|
||||
|
||||
for (i in 1..5) {
|
||||
val spendableStatesUSD = (services.vaultService as NodeVaultService).unconsumedStatesForSpending<Cash.State>(20.DOLLARS, lockId = UUID.randomUUID())
|
||||
val spendableStatesUSD = (vaultSvc as NodeVaultService).unconsumedStatesForSpending<Cash.State>(20.DOLLARS, lockId = UUID.randomUUID())
|
||||
spendableStatesUSD.forEach(::println)
|
||||
}
|
||||
// note only 3 spend attempts succeed with a total of 8 states
|
||||
assertThat(services.vaultService.softLockedStates<Cash.State>()).hasSize(8)
|
||||
assertThat(vaultSvc.softLockedStates<Cash.State>()).hasSize(8)
|
||||
}
|
||||
}
|
||||
|
||||
@ -411,10 +411,10 @@ class NodeVaultServiceTest {
|
||||
|
||||
services.recordTransactions(listOf(usefulTX))
|
||||
|
||||
services.vaultService.addNoteToTransaction(usefulTX.id, "USD Sample Note 1")
|
||||
services.vaultService.addNoteToTransaction(usefulTX.id, "USD Sample Note 2")
|
||||
services.vaultService.addNoteToTransaction(usefulTX.id, "USD Sample Note 3")
|
||||
assertEquals(3, services.vaultService.getTransactionNotes(usefulTX.id).count())
|
||||
vaultSvc.addNoteToTransaction(usefulTX.id, "USD Sample Note 1")
|
||||
vaultSvc.addNoteToTransaction(usefulTX.id, "USD Sample Note 2")
|
||||
vaultSvc.addNoteToTransaction(usefulTX.id, "USD Sample Note 3")
|
||||
assertEquals(3, vaultSvc.getTransactionNotes(usefulTX.id).count())
|
||||
|
||||
// Issue more Money (GBP)
|
||||
val anotherTX = TransactionType.General.Builder(null).apply {
|
||||
@ -424,8 +424,8 @@ class NodeVaultServiceTest {
|
||||
|
||||
services.recordTransactions(listOf(anotherTX))
|
||||
|
||||
services.vaultService.addNoteToTransaction(anotherTX.id, "GPB Sample Note 1")
|
||||
assertEquals(1, services.vaultService.getTransactionNotes(anotherTX.id).count())
|
||||
vaultSvc.addNoteToTransaction(anotherTX.id, "GPB Sample Note 1")
|
||||
assertEquals(1, vaultSvc.getTransactionNotes(anotherTX.id).count())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,782 @@
|
||||
package net.corda.node.services.vault
|
||||
|
||||
import net.corda.contracts.CommercialPaper
|
||||
import net.corda.contracts.asset.Cash
|
||||
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
||||
import net.corda.contracts.testing.fillWithSomeTestCash
|
||||
import net.corda.contracts.testing.fillWithSomeTestDeals
|
||||
import net.corda.contracts.testing.fillWithSomeTestLinearStates
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.Party
|
||||
import net.corda.core.crypto.entropyToKeyPair
|
||||
import net.corda.core.days
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.node.services.VaultService
|
||||
import net.corda.core.node.services.linearHeadsOfType
|
||||
import net.corda.core.node.services.vault.*
|
||||
import net.corda.core.node.services.vault.QueryCriteria.*
|
||||
import net.corda.core.seconds
|
||||
import net.corda.core.serialization.OpaqueBytes
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.*
|
||||
import net.corda.node.services.vault.schemas.VaultLinearStateEntity
|
||||
import net.corda.node.services.vault.schemas.VaultSchema
|
||||
import net.corda.node.utilities.configureDatabase
|
||||
import net.corda.node.utilities.transaction
|
||||
import net.corda.schemas.CashSchemaV1.PersistentCashState
|
||||
import net.corda.schemas.CommercialPaperSchemaV1.PersistentCommercialPaperState
|
||||
import net.corda.testing.*
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.makeTestDataSourceProperties
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.io.Closeable
|
||||
import java.math.BigInteger
|
||||
import java.security.KeyPair
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
|
||||
@Ignore
|
||||
class VaultQueryTests {
|
||||
lateinit var services: MockServices
|
||||
val vaultSvc: VaultService get() = services.vaultService
|
||||
lateinit var dataSource: Closeable
|
||||
lateinit var database: Database
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val dataSourceProps = makeTestDataSourceProperties()
|
||||
val dataSourceAndDatabase = configureDatabase(dataSourceProps)
|
||||
dataSource = dataSourceAndDatabase.first
|
||||
database = dataSourceAndDatabase.second
|
||||
database.transaction {
|
||||
services = object : MockServices() {
|
||||
override val vaultService: VaultService = makeVaultService(dataSourceProps)
|
||||
|
||||
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
|
||||
for (stx in txs) {
|
||||
storageService.validatedTransactions.addTransaction(stx)
|
||||
}
|
||||
// Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions.
|
||||
vaultService.notifyAll(txs.map { it.tx })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
dataSource.close()
|
||||
}
|
||||
|
||||
/**
|
||||
* Query API tests
|
||||
*/
|
||||
|
||||
/** Generic Query tests
|
||||
(combining both FungibleState and LinearState contract types) */
|
||||
|
||||
@Test
|
||||
fun `unconsumed states`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
|
||||
|
||||
// DOCSTART VaultQueryExample1
|
||||
val criteria = VaultQueryCriteria() // default is UNCONSUMED
|
||||
val result = vaultSvc.queryBy<ContractState>(criteria)
|
||||
|
||||
/**
|
||||
* Query result returns a [Vault.Page] which contains:
|
||||
* 1) actual states as a list of [StateAndRef]
|
||||
* 2) state reference and associated vault metadata as a list of [Vault.StateMetadata]
|
||||
* 3) [PageSpecification] used to delimit the size of items returned in the result set (defaults to [DEFAULT_PAGE_SIZE])
|
||||
* 4) Total number of items available (to aid further pagination if required)
|
||||
*/
|
||||
val states = result.states
|
||||
val metadata = result.statesMetadata
|
||||
|
||||
// DOCEND VaultQueryExample1
|
||||
assertThat(states).hasSize(16)
|
||||
assertThat(metadata).hasSize(16)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed states for state refs`() {
|
||||
database.transaction {
|
||||
|
||||
val issuedStates = services.fillWithSomeTestLinearStates(2)
|
||||
val stateRefs = issuedStates.states.map { it.ref }.toList()
|
||||
services.fillWithSomeTestLinearStates(8)
|
||||
|
||||
// DOCSTART VaultQueryExample2
|
||||
val criteria = VaultQueryCriteria(stateRefs = listOf(stateRefs.first(), stateRefs.last()))
|
||||
val results = vaultSvc.queryBy<LinearState>(criteria)
|
||||
// DOCEND VaultQueryExample2
|
||||
|
||||
assertThat(results.states).hasSize(2)
|
||||
assertThat(results.states.first().ref).isEqualTo(issuedStates.states.first().ref)
|
||||
assertThat(results.states.last().ref).isEqualTo(issuedStates.states.last().ref)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed states for contract state types`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
|
||||
|
||||
// default State.Status is UNCONSUMED
|
||||
// DOCSTART VaultQueryExample3
|
||||
val criteria = VaultQueryCriteria(contractStateTypes = setOf(Cash.State::class.java, DealState::class.java))
|
||||
val results = vaultSvc.queryBy<ContractState>(criteria)
|
||||
// DOCEND VaultQueryExample3
|
||||
assertThat(results.states).hasSize(6)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `consumed states`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestLinearStates(2, UniqueIdentifier("TEST")) // create 2 states with same UID
|
||||
services.fillWithSomeTestLinearStates(8)
|
||||
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
|
||||
|
||||
// services.consumeLinearStates(UniqueIdentifier("TEST"))
|
||||
// services.consumeDeals("456")
|
||||
// services.consumeCash(80.DOLLARS)
|
||||
|
||||
val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED)
|
||||
val results = vaultSvc.queryBy<ContractState>(criteria)
|
||||
assertThat(results.states).hasSize(5)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `all states`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestLinearStates(2, UniqueIdentifier("TEST")) // create 2 results with same UID
|
||||
services.fillWithSomeTestLinearStates(8)
|
||||
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
|
||||
|
||||
// services.consumeLinearStates(UniqueIdentifier("TEST"))
|
||||
// services.consumeDeals("456")
|
||||
// services.consumeCash(80.DOLLARS)
|
||||
|
||||
val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
||||
val results = vaultSvc.queryBy<ContractState>(criteria)
|
||||
assertThat(results.states).hasSize(16)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val CASH_NOTARY_KEY: KeyPair by lazy { entropyToKeyPair(BigInteger.valueOf(20)) }
|
||||
val CASH_NOTARY: Party get() = Party("Notary Service", CASH_NOTARY_KEY.public)
|
||||
|
||||
@Test
|
||||
fun `unconsumed states by notary`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, CASH_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
|
||||
|
||||
// DOCSTART VaultQueryExample4
|
||||
val criteria = VaultQueryCriteria(notaryName = listOf(CASH_NOTARY.name))
|
||||
val results = vaultSvc.queryBy<ContractState>(criteria)
|
||||
// DOCEND VaultQueryExample4
|
||||
assertThat(results.states).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed states by participants`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestLinearStates(2, UniqueIdentifier("TEST"), participants = listOf(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY))
|
||||
services.fillWithSomeTestDeals(listOf("456"), 3, participants = listOf(MEGA_CORP_PUBKEY, BIG_CORP_PUBKEY))
|
||||
services.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(BIG_CORP_PUBKEY, MINI_CORP_PUBKEY))
|
||||
|
||||
// DOCSTART VaultQueryExample5
|
||||
val criteria = VaultQueryCriteria(participantIdentities = listOf(MEGA_CORP.name, MINI_CORP.name))
|
||||
val results = vaultSvc.queryBy<ContractState>(criteria)
|
||||
// DOCEND VaultQueryExample5
|
||||
|
||||
assertThat(results.states).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed states excluding soft locks`() {
|
||||
database.transaction {
|
||||
|
||||
val issuedStates = services.fillWithSomeTestCash(100.DOLLARS, CASH_NOTARY, 3, 3, Random(0L))
|
||||
vaultSvc.softLockReserve(UUID.randomUUID(), setOf(issuedStates.states.first().ref, issuedStates.states.last().ref))
|
||||
|
||||
val criteria = VaultQueryCriteria(includeSoftlockedStates = false)
|
||||
val results = vaultSvc.queryBy<ContractState>(criteria)
|
||||
assertThat(results.states).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed states recorded between two time intervals`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, CASH_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
|
||||
|
||||
// DOCSTART VaultQueryExample6
|
||||
val start = TEST_TX_TIME
|
||||
val end = TEST_TX_TIME.plus(30, ChronoUnit.DAYS)
|
||||
val recordedBetweenExpression = LogicalExpression(
|
||||
QueryCriteria.TimeInstantType.RECORDED, Operator.BETWEEN, arrayOf(start, end))
|
||||
val criteria = VaultQueryCriteria(timeCondition = recordedBetweenExpression)
|
||||
val results = vaultSvc.queryBy<ContractState>(criteria)
|
||||
// DOCEND VaultQueryExample6
|
||||
assertThat(results.states).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `states consumed after time`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, CASH_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
|
||||
|
||||
val asOfDateTime = TEST_TX_TIME
|
||||
val consumedAfterExpression = LogicalExpression(
|
||||
QueryCriteria.TimeInstantType.CONSUMED, Operator.GREATER_THAN, arrayOf(asOfDateTime))
|
||||
val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED,
|
||||
timeCondition = consumedAfterExpression)
|
||||
val results = vaultSvc.queryBy<ContractState>(criteria)
|
||||
|
||||
assertThat(results.states).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
// pagination: first page
|
||||
@Test
|
||||
fun `all states with paging specification - first page`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 100, 100, Random(0L))
|
||||
|
||||
// DOCSTART VaultQueryExample7
|
||||
val pagingSpec = PageSpecification(DEFAULT_PAGE_NUM, 10)
|
||||
val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
||||
val results = vaultSvc.queryBy<ContractState>(criteria, paging = pagingSpec)
|
||||
// DOCEND VaultQueryExample7
|
||||
assertThat(results.states).hasSize(10)
|
||||
}
|
||||
}
|
||||
|
||||
// pagination: last page
|
||||
@Test
|
||||
fun `all states with paging specification - last`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 100, 100, Random(0L))
|
||||
|
||||
// Last page implies we need to perform a row count for the Query first,
|
||||
// and then re-query for a given offset defined by (count - pageSize)
|
||||
val pagingSpec = PageSpecification(-1, 10)
|
||||
|
||||
val criteria = VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
||||
val results = vaultSvc.queryBy<ContractState>(criteria, paging = pagingSpec)
|
||||
assertThat(results.states).hasSize(10) // should retrieve states 90..99
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed fungible assets`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
// services.fillWithSomeTestCommodity()
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
|
||||
val criteria = VaultQueryCriteria(contractStateTypes = setOf(FungibleAsset::class.java)) // default is UNCONSUMED
|
||||
val results = vaultSvc.queryBy<FungibleAsset<*>>(criteria)
|
||||
assertThat(results.states).hasSize(4)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `consumed fungible assets`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
// services.consumeCash(2)
|
||||
// services.fillWithSomeTestCommodity()
|
||||
// services.consumeCommodity()
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
// services.consumeLinearStates(8)
|
||||
|
||||
val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED,
|
||||
contractStateTypes = setOf(FungibleAsset::class.java))
|
||||
val results = vaultSvc.queryBy<FungibleAsset<*>>(criteria)
|
||||
assertThat(results.states).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed cash fungible assets`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
|
||||
val criteria = VaultQueryCriteria(contractStateTypes = setOf(Cash.State::class.java)) // default is UNCONSUMED
|
||||
val results = vaultSvc.queryBy<Cash.State>(criteria)
|
||||
assertThat(results.states).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `consumed cash fungible assets`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
// services.consumeCash(2)
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
// services.consumeLinearStates(8)
|
||||
|
||||
val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED)
|
||||
val results = vaultSvc.queryBy<Cash.State>(criteria)
|
||||
assertThat(results.states).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed linear heads`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
|
||||
val criteria = VaultQueryCriteria(contractStateTypes = setOf(LinearState::class.java)) // default is UNCONSUMED
|
||||
val results = vaultSvc.queryBy<LinearState>(criteria)
|
||||
assertThat(results.states).hasSize(10)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `consumed linear heads`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
// services.consumeLinearStates(8)
|
||||
|
||||
val criteria = VaultQueryCriteria(status = Vault.StateStatus.CONSUMED,
|
||||
contractStateTypes = setOf(LinearState::class.java))
|
||||
val results = vaultSvc.queryBy<LinearState>(criteria)
|
||||
assertThat(results.states).hasSize(2)
|
||||
}
|
||||
}
|
||||
|
||||
/** LinearState tests */
|
||||
|
||||
@Test
|
||||
fun `unconsumed linear heads for linearId`() {
|
||||
database.transaction {
|
||||
|
||||
val issuedStates = services.fillWithSomeTestLinearStates(10)
|
||||
|
||||
// DOCSTART VaultQueryExample8
|
||||
val linearIds = issuedStates.states.map { it.state.data.linearId }.toList()
|
||||
val criteria = LinearStateQueryCriteria(linearId = listOf(linearIds.first(), linearIds.last()))
|
||||
val results = vaultSvc.queryBy<LinearState>(criteria)
|
||||
// DOCEND VaultQueryExample8
|
||||
assertThat(results.states).hasSize(2)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `latest unconsumed linear heads for linearId`() {
|
||||
database.transaction {
|
||||
|
||||
val issuedStates = services.fillWithSomeTestLinearStates(2, UniqueIdentifier("TEST")) // create 2 states with same UID
|
||||
services.fillWithSomeTestLinearStates(8)
|
||||
|
||||
val linearIds = issuedStates.states.map { it.state.data.linearId }.toList()
|
||||
val criteria = LinearStateQueryCriteria(linearId = listOf(linearIds.first()),
|
||||
latestOnly = true)
|
||||
val results = vaultSvc.queryBy<LinearState>(criteria)
|
||||
assertThat(results.states).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `return chain of linear state for a given id`() {
|
||||
database.transaction {
|
||||
|
||||
val id = UniqueIdentifier("TEST")
|
||||
services.fillWithSomeTestLinearStates(1, UniqueIdentifier("TEST"))
|
||||
// services.processLinearState(id) // consume current and produce new state reference
|
||||
// services.processLinearState(id) // consume current and produce new state reference
|
||||
// services.processLinearState(id) // consume current and produce new state reference
|
||||
|
||||
// should now have 1 UNCONSUMED & 3 CONSUMED state refs for Linear State with UniqueIdentifier("TEST")
|
||||
// DOCSTART VaultQueryExample9
|
||||
val linearStateCriteria = LinearStateQueryCriteria(linearId = listOf(id))
|
||||
val vaultCriteria = VaultQueryCriteria(status = Vault.StateStatus.ALL)
|
||||
val sorting = Sort(setOf(Sort.SortColumn(VaultSchema.VaultLinearState::uuid.name, Sort.Direction.DESC)))
|
||||
val results = vaultSvc.queryBy<LinearState>(linearStateCriteria.and(vaultCriteria), sorting)
|
||||
// DOCEND VaultQueryExample9
|
||||
assertThat(results.states).hasSize(4)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DEPRECATED return linear states for a given id`() {
|
||||
database.transaction {
|
||||
|
||||
val linearUid = UniqueIdentifier("TEST")
|
||||
services.fillWithSomeTestLinearStates(1, UniqueIdentifier("TEST"))
|
||||
// services.processLinearState(id) // consume current and produce new state reference
|
||||
// services.processLinearState(id) // consume current and produce new state reference
|
||||
// services.processLinearState(id) // consume current and produce new state reference
|
||||
|
||||
// should now have 1 UNCONSUMED & 3 CONSUMED state refs for Linear State with UniqueIdentifier("TEST")
|
||||
|
||||
// DOCSTART VaultDeprecatedQueryExample1
|
||||
val states = vaultSvc.linearHeadsOfType<LinearState>().filter { it.key == linearUid }
|
||||
// DOCEND VaultDeprecatedQueryExample1
|
||||
|
||||
assertThat(states).hasSize(4)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DEPRECATED return consumed linear states for a given id`() {
|
||||
database.transaction {
|
||||
|
||||
val linearUid = UniqueIdentifier("TEST")
|
||||
services.fillWithSomeTestLinearStates(1, UniqueIdentifier("TEST"))
|
||||
// services.processLinearState(id) // consume current and produce new state reference
|
||||
// services.processLinearState(id) // consume current and produce new state reference
|
||||
// services.processLinearState(id) // consume current and produce new state reference
|
||||
|
||||
// should now have 1 UNCONSUMED & 3 CONSUMED state refs for Linear State with UniqueIdentifier("TEST")
|
||||
|
||||
// DOCSTART VaultDeprecatedQueryExample2
|
||||
val states = vaultSvc.states(setOf(LinearState::class.java),
|
||||
EnumSet.of(Vault.StateStatus.CONSUMED)).filter { it.state.data.linearId == linearUid }
|
||||
// DOCEND VaultDeprecatedQueryExample2
|
||||
|
||||
assertThat(states).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `latest unconsumed linear heads for state refs`() {
|
||||
database.transaction {
|
||||
|
||||
val issuedStates = services.fillWithSomeTestLinearStates(2, UniqueIdentifier("TEST")) // create 2 states with same UID
|
||||
services.fillWithSomeTestLinearStates(8)
|
||||
val stateRefs = issuedStates.states.map { it.ref }.toList()
|
||||
|
||||
val vaultCriteria = VaultQueryCriteria(stateRefs = listOf(stateRefs.first(), stateRefs.last()))
|
||||
val linearStateCriteria = LinearStateQueryCriteria(latestOnly = true)
|
||||
val results = vaultSvc.queryBy<LinearState>(vaultCriteria.and(linearStateCriteria))
|
||||
assertThat(results.states).hasSize(2)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed deals`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
|
||||
|
||||
val criteria = LinearStateQueryCriteria()
|
||||
val results = vaultSvc.queryBy<DealState>(criteria)
|
||||
assertThat(results.states).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed deals for ref`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
|
||||
|
||||
// DOCSTART VaultQueryExample10
|
||||
val criteria = LinearStateQueryCriteria(dealRef = listOf("456", "789"))
|
||||
val results = vaultSvc.queryBy<DealState>(criteria)
|
||||
// DOCEND VaultQueryExample10
|
||||
|
||||
assertThat(results.states).hasSize(2)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `latest unconsumed deals for ref`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestLinearStates(2, UniqueIdentifier("TEST"))
|
||||
services.fillWithSomeTestDeals(listOf("456"), 3) // create 3 revisions with same ID
|
||||
services.fillWithSomeTestDeals(listOf("123", "789"))
|
||||
|
||||
val criteria = LinearStateQueryCriteria(dealRef = listOf("456"), latestOnly = true)
|
||||
val results = vaultSvc.queryBy<DealState>(criteria)
|
||||
assertThat(results.states).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `latest unconsumed deals with party`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestLinearStates(2, UniqueIdentifier("TEST"))
|
||||
services.fillWithSomeTestDeals(listOf("456"), 3) // specify party
|
||||
services.fillWithSomeTestDeals(listOf("123", "789"))
|
||||
|
||||
// DOCSTART VaultQueryExample11
|
||||
val criteria = LinearStateQueryCriteria(dealPartyName = listOf(MEGA_CORP.name, MINI_CORP.name))
|
||||
val results = vaultSvc.queryBy<DealState>(criteria)
|
||||
// DOCEND VaultQueryExample11
|
||||
|
||||
assertThat(results.states).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
/** FungibleAsset tests */
|
||||
@Test
|
||||
fun `unconsumed fungible assets of token type`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
|
||||
val criteria = FungibleAssetQueryCriteria(tokenType = listOf(Currency::class.java))
|
||||
val results = vaultSvc.queryBy<FungibleAsset<*>>(criteria)
|
||||
assertThat(results.states).hasSize(9)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed fungible assets for single currency`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
|
||||
// DOCSTART VaultQueryExample12
|
||||
val criteria = FungibleAssetQueryCriteria(tokenValue = listOf(USD.currencyCode))
|
||||
val results = vaultSvc.queryBy<FungibleAsset<*>>(criteria)
|
||||
// DOCEND VaultQueryExample12
|
||||
|
||||
assertThat(results.states).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed fungible assets for single currency and quantity greater than`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestLinearStates(10)
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
services.fillWithSomeTestCash(50.POUNDS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
|
||||
// DOCSTART VaultQueryExample13
|
||||
val criteria = FungibleAssetQueryCriteria(tokenValue = listOf(GBP.currencyCode),
|
||||
quantity = LogicalExpression(this, Operator.GREATER_THAN, 50))
|
||||
val results = vaultSvc.queryBy<Cash.State>(criteria)
|
||||
// DOCEND VaultQueryExample13
|
||||
|
||||
assertThat(results.states).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed fungible assets for several currencies`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 3, 3, Random(0L))
|
||||
|
||||
val criteria = FungibleAssetQueryCriteria(tokenValue = listOf(CHF.currencyCode, GBP.currencyCode))
|
||||
val results = vaultSvc.queryBy<FungibleAsset<*>>(criteria)
|
||||
assertThat(results.states).hasSize(3)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed fungible assets for issuer party`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (DUMMY_CASH_ISSUER))
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (BOC.ref(1)), issuerKey = BOC_KEY)
|
||||
|
||||
// DOCSTART VaultQueryExample14
|
||||
val criteria = FungibleAssetQueryCriteria(issuerPartyName = listOf(BOC.name))
|
||||
val results = vaultSvc.queryBy<FungibleAsset<*>>(criteria)
|
||||
// DOCEND VaultQueryExample14
|
||||
|
||||
assertThat(results.states).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed fungible assets for specific issuer party and refs`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (DUMMY_CASH_ISSUER))
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (BOC.ref(1)), issuerKey = BOC_KEY, ref = OpaqueBytes.of(1))
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (BOC.ref(2)), issuerKey = BOC_KEY, ref = OpaqueBytes.of(2))
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (BOC.ref(3)), issuerKey = BOC_KEY, ref = OpaqueBytes.of(3))
|
||||
|
||||
val criteria = FungibleAssetQueryCriteria(issuerPartyName = listOf(BOC.name),
|
||||
issuerRef = listOf(BOC.ref(1).reference, BOC.ref(2).reference))
|
||||
val results = vaultSvc.queryBy<FungibleAsset<*>>(criteria)
|
||||
assertThat(results.states).hasSize(2)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed fungible assets with exit keys`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (DUMMY_CASH_ISSUER))
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L), issuedBy = (BOC.ref(1)), issuerKey = BOC_KEY)
|
||||
|
||||
// DOCSTART VaultQueryExample15
|
||||
val criteria = FungibleAssetQueryCriteria(exitKeyIdentity = listOf(DUMMY_CASH_ISSUER.party.toString()))
|
||||
val results = vaultSvc.queryBy<FungibleAsset<*>>(criteria)
|
||||
// DOCEND VaultQueryExample15
|
||||
|
||||
assertThat(results.states).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unconsumed fungible assets by owner`() {
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 2, 2, Random(0L), issuedBy = (DUMMY_CASH_ISSUER))
|
||||
// issue some cash to BOB
|
||||
// issue some cash to ALICE
|
||||
|
||||
val criteria = FungibleAssetQueryCriteria(ownerIdentity = listOf(BOB.name, ALICE.name))
|
||||
val results = vaultSvc.queryBy<FungibleAsset<*>>(criteria)
|
||||
assertThat(results.states).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
/** Vault Custom Query tests */
|
||||
|
||||
// specifying Query on Commercial Paper contract state attributes
|
||||
@Test
|
||||
fun `commercial paper custom query`() {
|
||||
database.transaction {
|
||||
|
||||
// MegaCorp™ issues $10,000 of commercial paper, to mature in 30 days, owned by itself.
|
||||
val faceValue = 10000.DOLLARS `issued by` DUMMY_CASH_ISSUER
|
||||
val issuance = MEGA_CORP.ref(1)
|
||||
val commercialPaper =
|
||||
CommercialPaper().generateIssue(issuance, faceValue, TEST_TX_TIME + 30.days, DUMMY_NOTARY).apply {
|
||||
setTime(TEST_TX_TIME, 30.seconds)
|
||||
signWith(MEGA_CORP_KEY)
|
||||
signWith(DUMMY_NOTARY_KEY)
|
||||
}.toSignedTransaction()
|
||||
services.recordTransactions(commercialPaper)
|
||||
|
||||
val ccyIndex = LogicalExpression(PersistentCommercialPaperState::currency, Operator.EQUAL, USD.currencyCode)
|
||||
val maturityIndex = LogicalExpression(PersistentCommercialPaperState::maturity, Operator.GREATER_THAN_OR_EQUAL, TEST_TX_TIME + 30.days)
|
||||
val faceValueIndex = LogicalExpression(PersistentCommercialPaperState::faceValue, Operator.GREATER_THAN_OR_EQUAL, 10000)
|
||||
|
||||
val criteria = VaultCustomQueryCriteria(maturityIndex.and(faceValueIndex).or(ccyIndex))
|
||||
val result = vaultSvc.queryBy<CommercialPaper.State>(criteria)
|
||||
|
||||
assertThat(result.states).hasSize(1)
|
||||
assertThat(result.statesMetadata).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
/** Chaining together different Query Criteria tests**/
|
||||
|
||||
// specifying Query on Cash contract state attributes
|
||||
@Test
|
||||
fun `all cash states with amount of currency greater or equal than`() {
|
||||
|
||||
database.transaction {
|
||||
|
||||
services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
services.fillWithSomeTestCash(10.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
services.fillWithSomeTestCash(1.DOLLARS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||
|
||||
// DOCSTART VaultQueryExample16
|
||||
val generalCriteria = VaultQueryCriteria(Vault.StateStatus.ALL)
|
||||
|
||||
val currencyIndex = LogicalExpression(PersistentCashState::currency, Operator.EQUAL, USD.currencyCode)
|
||||
val quantityIndex = LogicalExpression(PersistentCashState::pennies, Operator.GREATER_THAN_OR_EQUAL, 10)
|
||||
val customCriteria = VaultCustomQueryCriteria(currencyIndex.and(quantityIndex))
|
||||
|
||||
val criteria = generalCriteria.and(customCriteria)
|
||||
val results = vaultSvc.queryBy<Cash.State>(criteria)
|
||||
// DOCEND VaultQueryExample16
|
||||
|
||||
assertThat(results.states).hasSize(2)
|
||||
}
|
||||
}
|
||||
|
||||
// specifying Query on Linear state attributes
|
||||
@Test
|
||||
fun `consumed linear heads for linearId between two timestamps`() {
|
||||
database.transaction {
|
||||
val issuedStates = services.fillWithSomeTestLinearStates(10)
|
||||
val externalIds = issuedStates.states.map { it.state.data.linearId }.map { it.externalId }[0]
|
||||
val uuids = issuedStates.states.map { it.state.data.linearId }.map { it.id }[1]
|
||||
|
||||
val start = TEST_TX_TIME
|
||||
val end = TEST_TX_TIME.plus(30, ChronoUnit.DAYS)
|
||||
val recordedBetweenExpression = LogicalExpression(TimeInstantType.RECORDED, Operator.BETWEEN, arrayOf(start, end))
|
||||
val basicCriteria = VaultQueryCriteria(timeCondition = recordedBetweenExpression)
|
||||
|
||||
val linearIdsExpression = LogicalExpression(VaultLinearStateEntity::externalId, Operator.IN, externalIds)
|
||||
val linearIdCondition = LogicalExpression(VaultLinearStateEntity::uuid, Operator.EQUAL, uuids)
|
||||
val customIndexCriteria = VaultCustomQueryCriteria(linearIdsExpression.or(linearIdCondition))
|
||||
|
||||
val criteria = basicCriteria.and(customIndexCriteria)
|
||||
val results = vaultSvc.queryBy<LinearState>(criteria)
|
||||
|
||||
assertThat(results.states).hasSize(2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* USE CASE demonstrations (outside of mainline Corda)
|
||||
*
|
||||
* 1) Template / Tutorial CorDapp service using Vault API Custom Query to access attributes of IOU State
|
||||
* 2) Template / Tutorial Flow using a JDBC session to execute a custom query
|
||||
* 3) Template / Tutorial CorDapp service query extension executing Named Queries via JPA
|
||||
* 4) Advanced pagination queries using Spring Data (and/or Hibernate/JPQL)
|
||||
*/
|
||||
}
|
Reference in New Issue
Block a user