CORDA-3184 - Vault Query API enhancement: strict participants matching (#5524)

* Implementation of exact participant parsing for common query criteria.

* Added more unit tests + fixed a minor bug.

* Additional clean-up.

* Add documentation and changelog entry.

* Revert debug level SQL logging.

* Fix detekt errors.

* Fix docs formatting in code samples.

* Updating baseline (false positives being reported).

* Fix "Name shadowed: subRoot" code quality check.

* Fix API stability checker failures.

* Update baseline.
This commit is contained in:
josecoll 2019-09-27 15:44:04 +01:00 committed by GitHub
parent 6ca4b589e2
commit 7dd6ecf96e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 272 additions and 16 deletions

View File

@ -8,6 +8,7 @@ import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.services.Vault
import net.corda.core.schemas.StatePersistable
import net.corda.core.serialization.CordaSerializable
@ -82,6 +83,7 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
open val participants: List<AbstractParty>? = null
abstract val contractStateTypes: Set<Class<out ContractState>>?
open val externalIds: List<UUID> = emptyList()
open val exactParticipants: List<AbstractParty>? = null
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
return parser.parseCriteria(this)
}
@ -101,8 +103,23 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
override val constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet(),
override val constraints: Set<Vault.ConstraintInfo> = emptySet(),
override val participants: List<AbstractParty>? = null,
override val externalIds: List<UUID> = emptyList()
override val externalIds: List<UUID> = emptyList(),
override val exactParticipants: List<AbstractParty>? = null
) : CommonQueryCriteria() {
@DeprecatedConstructorForDeserialization(version = 7)
constructor(
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
contractStateTypes: Set<Class<out ContractState>>? = null,
stateRefs: List<StateRef>? = null,
notary: List<AbstractParty>? = null,
softLockingCondition: SoftLockingCondition? = null,
timeCondition: TimeCondition? = null,
relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL,
constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet(),
constraints: Set<Vault.ConstraintInfo> = emptySet(),
participants: List<AbstractParty>? = null,
externalIds: List<UUID> = emptyList()
) : this(status, contractStateTypes, stateRefs, notary, softLockingCondition, timeCondition, relevancyStatus, constraintTypes, constraints, participants, externalIds, null)
// V4 constructors.
@DeprecatedConstructorForDeserialization(version = 7)
constructor(
@ -162,6 +179,36 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
fun withConstraints(constraints: Set<Vault.ConstraintInfo>): VaultQueryCriteria = copy(constraints = constraints)
fun withParticipants(participants: List<AbstractParty>): VaultQueryCriteria = copy(participants = participants)
fun withExternalIds(externalIds: List<UUID>): VaultQueryCriteria = copy(externalIds = externalIds)
fun withExactParticipants(exactParticipants: List<AbstractParty>): VaultQueryCriteria = copy(exactParticipants = exactParticipants)
fun copy(
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
contractStateTypes: Set<Class<out ContractState>>? = null,
stateRefs: List<StateRef>? = null,
notary: List<AbstractParty>? = null,
softLockingCondition: SoftLockingCondition? = null,
timeCondition: TimeCondition? = null,
relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL,
constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet(),
constraints: Set<Vault.ConstraintInfo> = emptySet(),
participants: List<AbstractParty>? = null,
externalIds: List<UUID> = emptyList()
): VaultQueryCriteria {
return VaultQueryCriteria(
status,
contractStateTypes,
stateRefs,
notary,
softLockingCondition,
timeCondition,
relevancyStatus,
constraintTypes,
constraints,
participants,
externalIds,
exactParticipants
)
}
fun copy(
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
@ -218,13 +265,24 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
* LinearStateQueryCriteria: provides query by attributes defined in [VaultSchema.VaultLinearState]
*/
data class LinearStateQueryCriteria(
override val participants: List<AbstractParty>?,
override val participants: List<AbstractParty>? = null,
val uuid: List<UUID>? = null,
val externalId: List<String>? = null,
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
override val contractStateTypes: Set<Class<out ContractState>>? = null,
override val relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL
override val relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL,
override val exactParticipants: List<AbstractParty>?
) : CommonQueryCriteria() {
// V4 c'tor
@DeprecatedConstructorForDeserialization(version = 4)
constructor(
participants: List<AbstractParty>? = null,
uuid: List<UUID>? = null,
externalId: List<String>? = null,
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
contractStateTypes: Set<Class<out ContractState>>? = null,
relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL
) : this(participants, uuid, externalId, status, contractStateTypes, relevancyStatus, null)
// V3 c'tor
@JvmOverloads
@DeprecatedConstructorForDeserialization(version = 2)
@ -265,6 +323,8 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
fun withStatus(status: Vault.StateStatus): LinearStateQueryCriteria = copy(status = status)
fun withContractStateTypes(contractStateTypes: Set<Class<out ContractState>>): LinearStateQueryCriteria = copy(contractStateTypes = contractStateTypes)
fun withRelevancyStatus(relevancyStatus: Vault.RelevancyStatus): LinearStateQueryCriteria = copy(relevancyStatus = relevancyStatus)
fun withExactParticipants(exactParticipants: List<AbstractParty>): LinearStateQueryCriteria =
copy(exactParticipants = exactParticipants)
fun copy(
participants: List<AbstractParty>? = this.participants,
@ -272,6 +332,23 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
externalId: List<String>? = this.externalId,
status: Vault.StateStatus = this.status,
contractStateTypes: Set<Class<out ContractState>>? = this.contractStateTypes
): LinearStateQueryCriteria {
return LinearStateQueryCriteria(
participants,
uuid,
externalId,
status,
contractStateTypes
)
}
fun copy(
participants: List<AbstractParty>? = this.participants,
uuid: List<UUID>? = this.uuid,
externalId: List<String>? = this.externalId,
status: Vault.StateStatus = this.status,
contractStateTypes: Set<Class<out ContractState>>? = this.contractStateTypes,
relevancyStatus: Vault.RelevancyStatus = this.relevancyStatus
): LinearStateQueryCriteria {
return LinearStateQueryCriteria(
participants,
@ -317,8 +394,21 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
val issuerRef: List<OpaqueBytes>? = null,
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
override val contractStateTypes: Set<Class<out ContractState>>? = null,
override val relevancyStatus: Vault.RelevancyStatus
override val relevancyStatus: Vault.RelevancyStatus,
override val exactParticipants: List<AbstractParty>? = null
) : CommonQueryCriteria() {
// V4 c'tor
@DeprecatedConstructorForDeserialization(version = 1)
constructor(
participants: List<AbstractParty>? = null,
owner: List<AbstractParty>? = null,
quantity: ColumnPredicate<Long>? = null,
issuer: List<AbstractParty>? = null,
issuerRef: List<OpaqueBytes>? = null,
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
contractStateTypes: Set<Class<out ContractState>>? = null,
relevancyStatus: Vault.RelevancyStatus
) : this(participants, owner, quantity, issuer, issuerRef, status, contractStateTypes, relevancyStatus, null)
@JvmOverloads
@DeprecatedConstructorForDeserialization(version = 1)
constructor(
@ -344,6 +434,30 @@ sealed class QueryCriteria : GenericQueryCriteria<QueryCriteria, IQueryCriteriaP
fun withStatus(status: Vault.StateStatus): FungibleAssetQueryCriteria = copy(status = status)
fun withContractStateTypes(contractStateTypes: Set<Class<out ContractState>>): FungibleAssetQueryCriteria = copy(contractStateTypes = contractStateTypes)
fun withRelevancyStatus(relevancyStatus: Vault.RelevancyStatus): FungibleAssetQueryCriteria = copy(relevancyStatus = relevancyStatus)
fun withExactParticipants(exactParticipants: List<AbstractParty>): FungibleAssetQueryCriteria
= copy(exactParticipants = exactParticipants)
fun copy(
participants: List<AbstractParty>? = this.participants,
owner: List<AbstractParty>? = this.owner,
quantity: ColumnPredicate<Long>? = this.quantity,
issuer: List<AbstractParty>? = this.issuer,
issuerRef: List<OpaqueBytes>? = this.issuerRef,
status: Vault.StateStatus = this.status,
contractStateTypes: Set<Class<out ContractState>>? = this.contractStateTypes,
relevancyStatus: Vault.RelevancyStatus = this.relevancyStatus
): FungibleAssetQueryCriteria {
return FungibleAssetQueryCriteria(
participants,
owner,
quantity,
issuer,
issuerRef,
status,
contractStateTypes,
relevancyStatus
)
}
fun copy(
participants: List<AbstractParty>? = this.participants,

View File

@ -755,6 +755,7 @@
<ID>LargeClass:PropertyTest.kt$PropertyTest</ID>
<ID>LargeClass:PropertyValidationTest.kt$PropertyValidationTest</ID>
<ID>LargeClass:ProtonWrapperTests.kt$ProtonWrapperTests</ID>
<ID>LargeClass:QueryCriteria.kt$QueryCriteria$VaultQueryCriteria : CommonQueryCriteria</ID>
<ID>LargeClass:QueryCriteriaUtils.kt$Builder</ID>
<ID>LargeClass:RPCClientProxyHandler.kt$RPCClientProxyHandler : InvocationHandler</ID>
<ID>LargeClass:RPCDriver.kt$RPCDriverDSL : InternalDriverDSL</ID>
@ -790,6 +791,7 @@
<ID>LargeClass:X509UtilitiesTest.kt$X509UtilitiesTest</ID>
<ID>LongMethod:EdDSATests.kt$EdDSATests$@Test fun `PureEdDSA Ed25519 test vectors`()</ID>
<ID>LongMethod:FlowCookbook.kt$InitiatorFlow$@Suppress("RemoveExplicitTypeArguments") @Suspendable override fun call()</ID>
<ID>LongMethod:HibernateQueryCriteriaParser.kt$HibernateQueryCriteriaParser$override fun parseCriteria(criteria: CommonQueryCriteria): Collection&lt;Predicate&gt;</ID>
<ID>LongMethod:IRSTests.kt$fun createDummyIRS(irsSelect: Int): InterestRateSwap.State</ID>
<ID>LongMethod:RpcReconnectTests.kt$RpcReconnectTests$ @Test fun `test that the RPC client is able to reconnect and proceed after node failure, restart, or connection reset`()</ID>
<ID>LongParameterList:AMQPSerializer.kt$AMQPSerializer$(obj: Any, data: Data, type: Type, output: SerializationOutput, context: SerializationContext, debugIndent: Int = 0)</ID>
@ -870,7 +872,10 @@
<ID>LongParameterList:ProcessUtilities.kt$ProcessUtilities$( arguments: List&lt;String&gt;, classPath: List&lt;String&gt; = defaultClassPath, workingDirectory: Path? = null, jdwpPort: Int? = null, extraJvmArguments: List&lt;String&gt; = emptyList(), maximumHeapSize: String? = null )</ID>
<ID>LongParameterList:ProcessUtilities.kt$ProcessUtilities$( className: String, arguments: List&lt;String&gt;, classPath: List&lt;String&gt; = defaultClassPath, workingDirectory: Path? = null, jdwpPort: Int? = null, extraJvmArguments: List&lt;String&gt; = emptyList(), maximumHeapSize: String? = null )</ID>
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.FungibleAssetQueryCriteria$( participants: List&lt;AbstractParty&gt;? = this.participants, owner: List&lt;AbstractParty&gt;? = this.owner, quantity: ColumnPredicate&lt;Long&gt;? = this.quantity, issuer: List&lt;AbstractParty&gt;? = this.issuer, issuerRef: List&lt;OpaqueBytes&gt;? = this.issuerRef, status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes )</ID>
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.FungibleAssetQueryCriteria$( participants: List&lt;AbstractParty&gt;? = this.participants, owner: List&lt;AbstractParty&gt;? = this.owner, quantity: ColumnPredicate&lt;Long&gt;? = this.quantity, issuer: List&lt;AbstractParty&gt;? = this.issuer, issuerRef: List&lt;OpaqueBytes&gt;? = this.issuerRef, status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes, relevancyStatus: Vault.RelevancyStatus = this.relevancyStatus )</ID>
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.LinearStateQueryCriteria$( participants: List&lt;AbstractParty&gt;? = this.participants, uuid: List&lt;UUID&gt;? = this.uuid, externalId: List&lt;String&gt;? = this.externalId, status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes, relevancyStatus: Vault.RelevancyStatus = this.relevancyStatus )</ID>
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$( status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = null, stateRefs: List&lt;StateRef&gt;? = null, notary: List&lt;AbstractParty&gt;? = null, softLockingCondition: SoftLockingCondition? = null, timeCondition: TimeCondition? = null, relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL, constraintTypes: Set&lt;Vault.ConstraintInfo.Type&gt; = emptySet(), constraints: Set&lt;Vault.ConstraintInfo&gt; = emptySet(), participants: List&lt;AbstractParty&gt;? = null )</ID>
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$( status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = null, stateRefs: List&lt;StateRef&gt;? = null, notary: List&lt;AbstractParty&gt;? = null, softLockingCondition: SoftLockingCondition? = null, timeCondition: TimeCondition? = null, relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL, constraintTypes: Set&lt;Vault.ConstraintInfo.Type&gt; = emptySet(), constraints: Set&lt;Vault.ConstraintInfo&gt; = emptySet(), participants: List&lt;AbstractParty&gt;? = null, externalIds: List&lt;UUID&gt; = emptyList() )</ID>
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$( status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes, stateRefs: List&lt;StateRef&gt;? = this.stateRefs, notary: List&lt;AbstractParty&gt;? = this.notary, softLockingCondition: SoftLockingCondition? = this.softLockingCondition, timeCondition: TimeCondition? = this.timeCondition )</ID>
<ID>LongParameterList:RPCClient.kt$RPCClient$( rpcOpsClass: Class&lt;I&gt;, username: String, password: String, externalTrace: Trace? = null, impersonatedActor: Actor? = null, targetLegalIdentity: CordaX500Name? = null )</ID>
<ID>LongParameterList:RPCDriver.kt$( isDebug: Boolean = false, driverDirectory: Path = Paths.get("build") / "rpc-driver" / getTimestampAsDirectoryName(), portAllocation: PortAllocation = globalPortAllocation, debugPortAllocation: PortAllocation = globalDebugPortAllocation, systemProperties: Map&lt;String, String&gt; = emptyMap(), useTestClock: Boolean = false, startNodesInProcess: Boolean = false, waitForNodesToFinish: Boolean = false, extraCordappPackagesToScan: List&lt;String&gt; = emptyList(), notarySpecs: List&lt;NotarySpec&gt; = emptyList(), externalTrace: Trace? = null, @Suppress("DEPRECATION") jmxPolicy: JmxPolicy = JmxPolicy(), networkParameters: NetworkParameters = testNetworkParameters(), notaryCustomOverrides: Map&lt;String, Any?&gt; = emptyMap(), inMemoryDB: Boolean = true, cordappsForAllNodes: Collection&lt;TestCordappInternal&gt;? = null, dsl: RPCDriverDSL.() -&gt; A )</ID>
@ -4459,6 +4464,7 @@
<ID>MaxLineLength:QueryCriteria.kt$QueryCriteria.VaultCustomQueryCriteria$fun withContractStateTypes(contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;): VaultCustomQueryCriteria&lt;L&gt;</ID>
<ID>MaxLineLength:QueryCriteria.kt$QueryCriteria.VaultCustomQueryCriteria$fun withRelevancyStatus(relevancyStatus: Vault.RelevancyStatus): VaultCustomQueryCriteria&lt;L&gt;</ID>
<ID>MaxLineLength:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$( status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = null, stateRefs: List&lt;StateRef&gt;? = null, notary: List&lt;AbstractParty&gt;? = null, softLockingCondition: SoftLockingCondition? = null, timeCondition: TimeCondition? = null, relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL, constraintTypes: Set&lt;Vault.ConstraintInfo.Type&gt; = emptySet(), constraints: Set&lt;Vault.ConstraintInfo&gt; = emptySet(), participants: List&lt;AbstractParty&gt;? = null )</ID>
<ID>MaxLineLength:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$( status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = null, stateRefs: List&lt;StateRef&gt;? = null, notary: List&lt;AbstractParty&gt;? = null, softLockingCondition: SoftLockingCondition? = null, timeCondition: TimeCondition? = null, relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL, constraintTypes: Set&lt;Vault.ConstraintInfo.Type&gt; = emptySet(), constraints: Set&lt;Vault.ConstraintInfo&gt; = emptySet(), participants: List&lt;AbstractParty&gt;? = null, externalIds: List&lt;UUID&gt; = emptyList() )</ID>
<ID>MaxLineLength:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$@DeprecatedConstructorForDeserialization(version = 2) constructor(status: Vault.StateStatus, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;?) : this(status, contractStateTypes, participants = null)</ID>
<ID>MaxLineLength:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$@DeprecatedConstructorForDeserialization(version = 4) constructor(status: Vault.StateStatus, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;?, stateRefs: List&lt;StateRef&gt;?, notary: List&lt;AbstractParty&gt;?) : this( status, contractStateTypes, stateRefs, notary, participants = null )</ID>
<ID>MaxLineLength:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$@DeprecatedConstructorForDeserialization(version = 5) constructor(status: Vault.StateStatus, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;?, stateRefs: List&lt;StateRef&gt;?, notary: List&lt;AbstractParty&gt;?, softLockingCondition: SoftLockingCondition?) : this( status, contractStateTypes, stateRefs, notary, softLockingCondition, participants = null )</ID>
@ -5839,6 +5845,7 @@
<ID>TooManyFunctions:PropertyValidationTest.kt$PropertyValidationTest</ID>
<ID>TooManyFunctions:ProtonWrapperTests.kt$ProtonWrapperTests</ID>
<ID>TooManyFunctions:PublicKeyToOwningIdentityCacheImplTest.kt$PublicKeyToOwningIdentityCacheImplTest</ID>
<ID>TooManyFunctions:QueryCriteria.kt$QueryCriteria$FungibleAssetQueryCriteria : CommonQueryCriteria</ID>
<ID>TooManyFunctions:QueryCriteria.kt$QueryCriteria$VaultQueryCriteria : CommonQueryCriteria</ID>
<ID>TooManyFunctions:QueryCriteriaUtils.kt$Builder</ID>
<ID>TooManyFunctions:RPCApi.kt$net.corda.nodeapi.RPCApi.kt</ID>

View File

@ -86,7 +86,8 @@ 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),
state constraints (see :ref:`Constraint Types <implicit_constraint_types>`), relevancy (ALL, RELEVANT, NON_RELEVANT).
state constraints (see :ref:`Constraint Types <implicit_constraint_types>`), relevancy (ALL, RELEVANT, NON_RELEVANT),
participants (exact or any match).
.. note:: Sensible defaults are defined for frequently used attributes (status = UNCONSUMED, always include soft
locked states).
@ -94,7 +95,7 @@ There are four implementations of this interface which can be chained together t
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).
attributes include: participants (exact or any match), owner(s), quantity, issuer party(s) and issuer reference(s).
.. note:: All contract states that extend the ``FungibleAsset`` now automatically persist that interfaces common
state attributes to the **vault_fungible_states** table.
@ -102,7 +103,7 @@ There are four implementations of this interface which can be chained together t
3. ``LinearStateQueryCriteria`` provides filterable criteria on attributes defined in the Corda Core ``LinearState``
and ``DealState`` contract state interfaces, used to represent entities that continuously supersede themselves, all
of which share the same ``linearId`` (e.g. trade entity states such as the ``IRSState`` defined in the SIMM
valuation demo). Filterable attributes include: participant(s), linearId(s), uuid(s), and externalId(s).
valuation demo). Filterable attributes include: participants (exact or any match), linearId(s), uuid(s), and externalId(s).
.. note:: All contract states that extend ``LinearState`` or ``DealState`` now automatically persist those
interfaces common state attributes to the **vault_linear_states** table.
@ -292,7 +293,7 @@ Query for unconsumed states for a given notary:
:end-before: DOCEND VaultQueryExample4
:dedent: 12
Query for unconsumed states for a given set of participants:
Query for unconsumed states for a given set of participants (matches any state that contains at least one of the specified participants):
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt
:language: kotlin
@ -300,6 +301,14 @@ Query for unconsumed states for a given set of participants:
:end-before: DOCEND VaultQueryExample5
:dedent: 12
Query for unconsumed states for a given set of participants (exactly matches only states that contain all specified participants):
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt
:language: kotlin
:start-after: DOCSTART VaultQueryExample51
:end-before: DOCEND VaultQueryExample51
:dedent: 12
Query for unconsumed states recorded between two time intervals:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt
@ -364,7 +373,7 @@ Query for unconsumed deal states with deals references:
:end-before: DOCEND VaultQueryExample10
:dedent: 12
Query for unconsumed deal states with deals parties:
Query for unconsumed deal states with deals parties (any match):
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt
:language: kotlin
@ -372,6 +381,14 @@ Query for unconsumed deal states with deals parties:
:end-before: DOCEND VaultQueryExample11
:dedent: 12
Query for unconsumed deal states with deals parties (exact match):
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt
:language: kotlin
:start-after: DOCSTART VaultQueryExample52
:end-before: DOCEND VaultQueryExample52
:dedent: 12
Query for only relevant linear states in the vault:
.. literalinclude:: ../../node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt

View File

@ -8,6 +8,8 @@ Unreleased
----------
* Moved and renamed the testing web server to the ``testing`` subproject. Also renamed the published artifact to ``corda-testserver.jar``.
* New Vault Query criteria to specify exact matches for specified participants.
* Support for Java 11 (compatibility mode). Please read https://github.com/corda/corda/pull/5356.
* Removed the RPC exception privacy feature. Previously, in production mode, the exceptions thrown on the node were stripped of all content

View File

@ -471,8 +471,12 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
predicateSet.add(criteriaBuilder.and(vaultFungibleStatesRoot.get<ByteArray>("issuerRef").`in`(issuerRefs)))
}
if (criteria.participants != null && criteria.exactParticipants != null)
throw VaultQueryException("Cannot specify both participants (${criteria.participants}) and exactParticipants " +
"(${criteria.exactParticipants}).")
// Participants.
criteria.participants?.let {
if (criteria.participants != null || criteria.exactParticipants != null) {
// Join VaultFungibleState and PersistentParty tables (participant values are added to the common query criteria predicate)
val statePartyToFungibleStatesJoin = criteriaBuilder.and(
criteriaBuilder.equal(vaultFungibleStatesRoot.get<VaultSchemaV1.VaultFungibleStates>("stateRef"),
@ -508,8 +512,12 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
predicateSet.add(criteriaBuilder.and(vaultLinearStatesRoot.get<String>("externalId").`in`(externalIds)))
}
if (criteria.participants != null && criteria.exactParticipants != null)
throw VaultQueryException("Cannot specify both participants (${criteria.participants}) " +
"and exactParticipants (${criteria.exactParticipants}).")
// Participants.
criteria.participants?.let {
if (criteria.participants != null || criteria.exactParticipants != null) {
// Join VaultLinearState and PersistentParty tables (participant values are added to the common query criteria predicate)
val statePartyToLinearStatesJoin = criteriaBuilder.and(
criteriaBuilder.equal(vaultLinearStatesRoot.get<VaultSchemaV1.VaultLinearStates>("stateRef"),
@ -702,6 +710,38 @@ class HibernateQueryCriteriaParser(val contractStateType: Class<out ContractStat
}
}
// Exact participants
// Requires a tricky SQL query to ensure *only* exact matches are selected (eg. a transaction cannot have more nor less than the
// exact participants specified in the query criteria).
criteria.exactParticipants?.let {
val exactParticipants = criteria.exactParticipants!!
// obtain all transactions where other participants are not present
val subQueryNotExists = criteriaQuery.subquery(Tuple::class.java)
val subRoot = subQueryNotExists.from(VaultSchemaV1.PersistentParty::class.java)
subQueryNotExists.select(subRoot.get("x500Name"))
subQueryNotExists.where(criteriaBuilder.and(
criteriaBuilder.equal(vaultStates.get<VaultSchemaV1.VaultStates>("stateRef"),
subRoot.get<VaultSchemaV1.PersistentParty>("compositeKey").get<PersistentStateRef>("stateRef"))),
criteriaBuilder.not(subRoot.get<VaultSchemaV1.PersistentParty>("x500Name").`in`(exactParticipants)))
val subQueryNotExistsPredicate = criteriaBuilder.and(criteriaBuilder.not(criteriaBuilder.exists(subQueryNotExists)))
constraintPredicates.add(subQueryNotExistsPredicate)
// join with transactions for each matching participant (only required where more than one)
if (exactParticipants.size > 1)
exactParticipants.forEach { participant ->
val subQueryExists = criteriaQuery.subquery(Tuple::class.java)
val subRootExists = subQueryExists.from(VaultSchemaV1.PersistentParty::class.java)
subQueryExists.select(subRootExists.get("x500Name"))
subQueryExists.where(criteriaBuilder.and(
criteriaBuilder.equal(vaultStates.get<VaultSchemaV1.VaultStates>("stateRef"),
subRootExists.get<VaultSchemaV1.PersistentParty>("compositeKey").get<PersistentStateRef>("stateRef"))),
criteriaBuilder.equal(subRootExists.get<VaultSchemaV1.PersistentParty>("x500Name"), participant))
val subQueryExistsPredicate = criteriaBuilder.and(criteriaBuilder.exists(subQueryExists))
constraintPredicates.add(subQueryExistsPredicate)
}
}
return emptySet()
}

View File

@ -241,6 +241,11 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
val criteria = VaultQueryCriteria(participants = listOf(BIG_CORP))
val results = vaultService.queryBy<ContractState>(criteria)
assertThat(results.states).hasSize(1)
// same query using strict participant matching
val strictCriteria = VaultQueryCriteria().withExactParticipants(listOf(BIG_CORP))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(0) // all states include node identity (MEGA_CORP)
}
}
@ -254,6 +259,11 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
val criteria = VaultQueryCriteria(participants = listOf(MINI_CORP, BIG_CORP))
val results = vaultService.queryBy<ContractState>(criteria)
assertThat(results.states).hasSize(2)
// same query using strict participant matching
val strictCriteria = VaultQueryCriteria().withExactParticipants(listOf(MEGA_CORP, BIG_CORP))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(1)
}
}
@ -795,10 +805,48 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
identitySvc.verifyAndRegisterIdentity(BIG_CORP_IDENTITY)
vaultFiller.fillWithSomeTestLinearStates(2, "TEST", participants = listOf(MEGA_CORP, MINI_CORP))
vaultFiller.fillWithSomeTestDeals(listOf("456"), participants = listOf(MEGA_CORP, BIG_CORP))
vaultFiller.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(BIG_CORP))
val criteria = LinearStateQueryCriteria(participants = listOf(BIG_CORP))
vaultFiller.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(MEGA_CORP))
val criteria = LinearStateQueryCriteria(participants = listOf(MEGA_CORP))
val results = vaultService.queryBy<ContractState>(criteria)
assertThat(results.states).hasSize(3)
assertThat(results.states).hasSize(5)
// same query using strict participant matching
val strictCriteria = LinearStateQueryCriteria().withExactParticipants(listOf(MEGA_CORP))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(2)
}
}
@Test
fun `unconsumed dummy states for exact single participant`() {
database.transaction {
identitySvc.verifyAndRegisterIdentity(BIG_CORP_IDENTITY)
vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP, MINI_CORP))
vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP, BIG_CORP))
vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP)) // exact match
val strictCriteria = VaultQueryCriteria(exactParticipants = listOf(MEGA_CORP))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(1)
}
}
@Test
fun `unconsumed dummy states for exact two participants`() {
database.transaction {
identitySvc.verifyAndRegisterIdentity(BIG_CORP_IDENTITY)
vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP, MINI_CORP))
vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP, BIG_CORP)) // exact match
vaultFiller.fillWithDummyState(participants = listOf(MEGA_CORP))
val strictCriteria = VaultQueryCriteria(exactParticipants = listOf(MEGA_CORP, BIG_CORP))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(1)
// same query using strict participant matching (unordered list of participants)
val strictCriteriaUnordered = VaultQueryCriteria(exactParticipants = listOf(BIG_CORP, MEGA_CORP))
val strictResultsUnordered = vaultService.queryBy<ContractState>(strictCriteriaUnordered)
assertThat(strictResultsUnordered.states).hasSize(1)
}
}
@ -813,8 +861,19 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
val criteria = LinearStateQueryCriteria(participants = listOf(BIG_CORP, MINI_CORP))
val results = vaultService.queryBy<ContractState>(criteria)
// DOCEND VaultQueryExample5
assertThat(results.states).hasSize(3)
// same query using strict participant matching
// DOCSTART VaultQueryExample51
val strictCriteria = LinearStateQueryCriteria(exactParticipants = listOf(MEGA_CORP, BIG_CORP))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
// DOCEND VaultQueryExample51
assertThat(strictResults.states).hasSize(1)
// same query using strict participant matching (unordered list of participants)
val strictCriteriaUnordered = LinearStateQueryCriteria(exactParticipants = listOf(BIG_CORP, MEGA_CORP))
val strictResultsUnordered = vaultService.queryBy<ContractState>(strictCriteriaUnordered)
assertThat(strictResultsUnordered.states).hasSize(1)
}
}
@ -1959,6 +2018,13 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
// DOCEND VaultQueryExample11
assertThat(results.states).hasSize(1)
// same query using strict participant matching
// DOCSTART VaultQueryExample52
val strictCriteria = LinearStateQueryCriteria().withExactParticipants(parties)
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
// DOCEND VaultQueryExample52
assertThat(strictResults.states).hasSize(0) // node identity included (MEGA_CORP)
}
}
@ -2352,6 +2418,11 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
assertThat(results.states).hasSize(1)
assertThat(results.states[0].state.data.linearId.externalId).isEqualTo("TEST1")
// same query using strict participant matching
val strictCriteria = LinearStateQueryCriteria().withExactParticipants(listOf(ALICE))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(0) // all states include node identity (MEGA_CORP)
}
}
@ -2369,6 +2440,11 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
assertThat(results.states).hasSize(1)
assertThat(results.states[0].state.data.linearId.externalId).isEqualTo("TEST1")
// same query using strict participant matching
val strictCriteria = LinearStateQueryCriteria().withExactParticipants(listOf(MEGA_CORP, ALICE, BOB, CHARLIE))
val strictResults = vaultService.queryBy<ContractState>(strictCriteria)
assertThat(strictResults.states).hasSize(1)
}
}
@ -2401,7 +2477,7 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
}
@Test
fun `composite query for fungible and linear states for multiple participants`() {
fun `composite query for fungible, linear and dummy states for multiple participants`() {
database.transaction {
identitySvc.verifyAndRegisterIdentity(ALICE_IDENTITY)
identitySvc.verifyAndRegisterIdentity(BOB_IDENTITY)