mirror of
https://github.com/corda/corda.git
synced 2024-12-19 21:17:58 +00:00
CORDA-3133 (Version 2) (#5428)
* CORDA-3133 [v1] This proposal is a little more flexible by design. It adds a property to the state pointer class, allowing them to be referenced in transactions on a per-state-pointer basis. * CORDA-3133 - Updated `resolveAtTransaction` to `isResolved`. - Moved `isResolved` out of the constructor into an abstract property. - Added deprecation constructor declaration for backwards compatibility. * CORDA-3133 (version 2) - Added required changes to state pointers as per PR comments. - Added unit tests to ensure isResolved can be used to configure when state pointers should be resolved to reference inputs. * CORDA-3133 (version 2) - fixed unit tests. - added comment to changelog.rst. - added helper functions to `StatePointer` to allow easier creation of static and linear pointers.
This commit is contained in:
parent
33055702a8
commit
4e6edd012a
@ -1,12 +1,14 @@
|
||||
package net.corda.core.contracts
|
||||
|
||||
import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.node.services.queryBy
|
||||
import net.corda.core.node.services.vault.QueryCriteria
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
|
||||
/**
|
||||
@ -17,7 +19,36 @@ import net.corda.core.transactions.LedgerTransaction
|
||||
*/
|
||||
@CordaSerializable
|
||||
@KeepForDJVM
|
||||
@DoNotImplement
|
||||
sealed class StatePointer<T : ContractState> {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Creates a [StaticPointer] to the specified contract state.
|
||||
*
|
||||
* @param stateAndRef The [StateAndRef] instance from which to construct a static pointer.
|
||||
* @param isResolved Determines whether the state pointer should be resolved to a reference input when included in a transaction.
|
||||
* @return Returns a [StaticPointer] to the specified contract state.
|
||||
*/
|
||||
inline fun <reified T : ContractState> staticPointer(
|
||||
stateAndRef: StateAndRef<T>,
|
||||
isResolved: Boolean = false
|
||||
) = StaticPointer(stateAndRef.ref, T::class.java, isResolved)
|
||||
|
||||
/**
|
||||
* Creates a [LinearPointer] to the specified linear state.
|
||||
*
|
||||
* @param state The [LinearState] instance from which to construct a linear pointer.
|
||||
* @param isResolved Determines whether the state pointer should be resolved to a reference input when included in a transaction.
|
||||
* @return Returns a [LinearPointer] to the specified linear state.
|
||||
*/
|
||||
inline fun <reified T : LinearState> linearPointer(
|
||||
state: T,
|
||||
isResolved: Boolean = true
|
||||
) = LinearPointer(state.linearId, T::class.java, isResolved)
|
||||
}
|
||||
|
||||
/**
|
||||
* An identifier for the [ContractState] that this [StatePointer] points to.
|
||||
*/
|
||||
@ -28,6 +59,11 @@ sealed class StatePointer<T : ContractState> {
|
||||
*/
|
||||
abstract val type: Class<T>
|
||||
|
||||
/**
|
||||
* Determines whether the state pointer should be resolved to a reference input when used in a transaction.
|
||||
*/
|
||||
abstract val isResolved: Boolean
|
||||
|
||||
/**
|
||||
* Resolves a [StatePointer] to a [StateAndRef] via a vault query. This method will either return a [StateAndRef]
|
||||
* or return an exception.
|
||||
@ -54,7 +90,21 @@ sealed class StatePointer<T : ContractState> {
|
||||
* throw a [TransactionResolutionException]
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class StaticPointer<T : ContractState>(override val pointer: StateRef, override val type: Class<T>) : StatePointer<T>() {
|
||||
class StaticPointer<T : ContractState>(
|
||||
override val pointer: StateRef,
|
||||
override val type: Class<T>,
|
||||
override val isResolved: Boolean = false
|
||||
) : StatePointer<T>() {
|
||||
|
||||
/**
|
||||
* Allows this class to be evolved through backwards compatibility with version 1 of this class.
|
||||
*
|
||||
* @param pointer The state reference that this points to.
|
||||
* @param type The underlying [LinearState] type that this points to.
|
||||
*/
|
||||
@DeprecatedConstructorForDeserialization(version = 1)
|
||||
constructor(pointer: StateRef, type: Class<T>) : this(pointer, type, false)
|
||||
|
||||
/**
|
||||
* Resolves a [StaticPointer] to a [StateAndRef] via a [StateRef] look-up.
|
||||
*/
|
||||
@ -99,7 +149,21 @@ class StaticPointer<T : ContractState>(override val pointer: StateRef, override
|
||||
* of the [LinearState] is available. See reference states documentation on docs.corda.net for more info.
|
||||
*/
|
||||
@KeepForDJVM
|
||||
class LinearPointer<T : LinearState>(override val pointer: UniqueIdentifier, override val type: Class<T>) : StatePointer<T>() {
|
||||
class LinearPointer<T : LinearState>(
|
||||
override val pointer: UniqueIdentifier,
|
||||
override val type: Class<T>,
|
||||
override val isResolved: Boolean = true
|
||||
) : StatePointer<T>() {
|
||||
|
||||
/**
|
||||
* Allows this class to be evolved through backwards compatibility with version 1 of this class.
|
||||
*
|
||||
* @param pointer The unique identifier that this points to.
|
||||
* @param type The underlying [LinearState] type that this points to.
|
||||
*/
|
||||
@DeprecatedConstructorForDeserialization(version = 1)
|
||||
constructor(pointer: UniqueIdentifier, type: Class<T>) : this(pointer, type, true)
|
||||
|
||||
/**
|
||||
* Resolves a [LinearPointer] using the [UniqueIdentifier] contained in the [pointer] property. Returns a
|
||||
* [StateAndRef] containing the latest version of the [LinearState] that the node calling [resolve] is aware of.
|
||||
|
@ -593,7 +593,7 @@ open class TransactionBuilder(
|
||||
while (statePointerQueue.isNotEmpty()) {
|
||||
val nextStatePointer = statePointerQueue.pop()
|
||||
val hub = serviceHub
|
||||
if (hub != null) {
|
||||
if (hub != null && nextStatePointer.isResolved) {
|
||||
val resolvedStateAndRef = nextStatePointer.resolve(hub)
|
||||
// Don't add dupe reference states because CoreTransaction doesn't allow it.
|
||||
if (resolvedStateAndRef.ref !in referenceStates()) {
|
||||
|
@ -15,6 +15,8 @@ Unreleased
|
||||
|
||||
* Introduced a new API on ``KeyManagementService`` which facilitates lookups of ``PublicKey`` s to ``externalId`` s (Account IDs).
|
||||
|
||||
* ``StatePointer`` has been marked as ```@DoNotImplement``, which was an omission in the original release.
|
||||
|
||||
* Introduced a new low level flow diagnostics tool: checkpoint agent (that can be used standalone or in conjunction with the ``checkpoints dump`` shell command).
|
||||
See :doc:`checkpoint-tooling` for more information.
|
||||
|
||||
|
@ -38,7 +38,7 @@ class ResolveStatePointersTest {
|
||||
) : LinearState
|
||||
|
||||
@BelongsToContract(DummyContract::class)
|
||||
private data class Foo<T : LinearState>(val baz: LinearPointer<T>, override val participants: List<AbstractParty>) : ContractState
|
||||
private data class Foo<T : ContractState>(val baz: StatePointer<T>, override val participants: List<AbstractParty>) : ContractState
|
||||
|
||||
private val barOne = Bar(listOf(myself.party), 1)
|
||||
private val barTwo = Bar(listOf(myself.party), 2, LinearPointer(barOne.linearId, barOne::class.java))
|
||||
@ -67,18 +67,78 @@ class ResolveStatePointersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolve state pointers and check reference state is added to transaction`() {
|
||||
fun `resolve linear pointers and check reference state is not added to transaction when isResolved is false`() {
|
||||
val stateAndRef = createPointedToState(barOne)
|
||||
val linearId = stateAndRef.state.data.linearId
|
||||
|
||||
// Add a new state containing a linear pointer.
|
||||
val tx = TransactionBuilder(notary = notary.party, serviceHub = services).apply {
|
||||
val pointer = LinearPointer(linearId, barOne::class.java)
|
||||
val pointer = LinearPointer(linearId, barOne::class.java, false)
|
||||
addOutputState(Foo(pointer, listOf(myself.party)), DummyContract.PROGRAM_ID)
|
||||
addCommand(Command(DummyContract.Commands.Create(), myself.party.owningKey))
|
||||
}
|
||||
|
||||
// Check the StateRef for the pointed-to state is added as a reference.
|
||||
// Check the StateRef for the pointed-to state is not added as a reference.
|
||||
assert(tx.referenceStates().none { it == stateAndRef.ref })
|
||||
|
||||
// Resolve the StateRef to the actual state.
|
||||
val ltx = tx.toLedgerTransaction(services)
|
||||
assertEquals(emptyList(), ltx.referenceStates)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolve linear pointers and check reference state is added to transaction when isResolved is true`() {
|
||||
val stateAndRef = createPointedToState(barOne)
|
||||
val linearId = stateAndRef.state.data.linearId
|
||||
|
||||
// Add a new state containing a linear pointer.
|
||||
val tx = TransactionBuilder(notary = notary.party, serviceHub = services).apply {
|
||||
val pointer = LinearPointer(linearId, barOne::class.java, true)
|
||||
addOutputState(Foo(pointer, listOf(myself.party)), DummyContract.PROGRAM_ID)
|
||||
addCommand(Command(DummyContract.Commands.Create(), myself.party.owningKey))
|
||||
}
|
||||
|
||||
// Check the StateRef for the pointed-to state is not added as a reference.
|
||||
assertEquals(stateAndRef.ref, tx.referenceStates().single())
|
||||
|
||||
// Resolve the StateRef to the actual state.
|
||||
val ltx = tx.toLedgerTransaction(services)
|
||||
assertEquals(barOne, ltx.referenceStates.single())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolve static pointers and check reference state is not added to transaction when isResolved is false`() {
|
||||
val stateAndRef = createPointedToState(barOne)
|
||||
val stateRef = stateAndRef.ref
|
||||
|
||||
// Add a new state containing a linear pointer.
|
||||
val tx = TransactionBuilder(notary = notary.party, serviceHub = services).apply {
|
||||
val pointer = StaticPointer(stateRef, barOne::class.java, false)
|
||||
addOutputState(Foo(pointer, listOf(myself.party)), DummyContract.PROGRAM_ID)
|
||||
addCommand(Command(DummyContract.Commands.Create(), myself.party.owningKey))
|
||||
}
|
||||
|
||||
// Check the StateRef for the pointed-to state is not added as a reference.
|
||||
assert(tx.referenceStates().none { it == stateAndRef.ref })
|
||||
|
||||
// Resolve the StateRef to the actual state.
|
||||
val ltx = tx.toLedgerTransaction(services)
|
||||
assertEquals(emptyList(), ltx.referenceStates)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `resolve static pointers and check reference state is added to transaction when isResolved is true`() {
|
||||
val stateAndRef = createPointedToState(barOne)
|
||||
val stateRef = stateAndRef.ref
|
||||
|
||||
// Add a new state containing a linear pointer.
|
||||
val tx = TransactionBuilder(notary = notary.party, serviceHub = services).apply {
|
||||
val pointer = StaticPointer(stateRef, barOne::class.java, true)
|
||||
addOutputState(Foo(pointer, listOf(myself.party)), DummyContract.PROGRAM_ID)
|
||||
addCommand(Command(DummyContract.Commands.Create(), myself.party.owningKey))
|
||||
}
|
||||
|
||||
// Check the StateRef for the pointed-to state is not added as a reference.
|
||||
assertEquals(stateAndRef.ref, tx.referenceStates().single())
|
||||
|
||||
// Resolve the StateRef to the actual state.
|
||||
|
Loading…
Reference in New Issue
Block a user