Skip to content

Commit 773375f

Browse files
Corrected the behaviour of linear attestation pointers.
1 parent 9fa4903 commit 773375f

File tree

28 files changed

+198
-281
lines changed

28 files changed

+198
-281
lines changed

onixlabs-corda-identity-framework-contract/src/main/kotlin/io/onixlabs/corda/identityframework/contract/Attestation.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import net.corda.core.identity.AbstractParty
2626
import net.corda.core.schemas.MappedSchema
2727
import net.corda.core.schemas.PersistentState
2828
import net.corda.core.schemas.QueryableState
29+
import net.corda.core.serialization.serialize
2930
import java.util.*
3031

3132
/**
@@ -94,10 +95,8 @@ open class Attestation<T : ContractState>(
9495
linearId = linearId.id,
9596
externalId = linearId.externalId,
9697
attestor = attestor,
97-
pointerStateRef = pointer.stateRef.toString(),
98+
pointer = pointer.statePointer.toString(),
9899
pointerStateType = pointer.stateType.canonicalName,
99-
pointerStateLinearId = pointer.getLinearId()?.id,
100-
pointerStateExternalId = pointer.getLinearId()?.externalId,
101100
pointerHash = pointer.hash.toString(),
102101
status = status,
103102
previousStateRef = previousStateRef?.toString(),

onixlabs-corda-identity-framework-contract/src/main/kotlin/io/onixlabs/corda/identityframework/contract/AttestationPointer.kt

Lines changed: 78 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -34,33 +34,61 @@ import java.util.*
3434
* Represents the base class for attestation pointer implementations.
3535
*
3636
* @param T The underlying [ContractState] type.
37-
* @property stateRef The [StateRef] of the witnessed state being attested.
3837
* @property stateType The [Class] of the witnessed state being attested.
39-
* @property hash The unique hash of the attestation pointer.
38+
* @property statePointer The pointer to the witnessed state being attested.
39+
* @property hash The hash of the attestation pointer.
40+
*
41+
* Note that attestation pointer hashes should be unique for static attestation pointers since they point to the
42+
* attested state's [StateRef], however attestation pointer hashes for linear attestation pointers will not be unique
43+
* since they point to the attested state's [UniqueIdentifier].
4044
*/
4145
@CordaSerializable
4246
sealed class AttestationPointer<T : ContractState> : SingularResolvable<T>, Hashable {
4347
abstract val stateType: Class<T>
44-
abstract val stateRef: StateRef
48+
abstract val statePointer: Any
49+
50+
final override val hash: SecureHash
51+
get() = SecureHash.sha256("$stateType$statePointer")
4552

4653
/**
47-
* Determines whether any immutable properties of this object have changed.
54+
* Determines whether the specified object is equal to the current object.
4855
*
49-
* @param other The pointer to compare to this one.
50-
* @return Returns true if the immutable properties remain unchanged; otherwise, false.
56+
* @param other The object to compare with the current object.
57+
* @return Returns true if the specified object is equal to the current object; otherwise, false.
5158
*/
52-
internal abstract fun immutableEquals(other: AttestationPointer<T>): Boolean
59+
final override fun equals(other: Any?): Boolean {
60+
return this === other || (other is AttestationPointer<*>
61+
&& other.javaClass == javaClass
62+
&& other.stateType == stateType
63+
&& other.statePointer == statePointer)
64+
}
5365

5466
/**
55-
* Determines whether this [SingularResolvable] is pointing to the specified [StateAndRef] instance.
67+
* Serves as the default hash function.
5668
*
57-
* @param stateAndRef The [StateAndRef] to determine being pointed to.
58-
* @return Returns true if this [SingularResolvable] is pointing to the specified [StateAndRef]; otherwise, false.
69+
* @return Returns a hash code for the current object.
5970
*/
60-
override fun isPointingTo(stateAndRef: StateAndRef<T>): Boolean {
61-
return stateAndRef.ref == stateRef
71+
final override fun hashCode(): Int {
72+
return Objects.hash(stateType, statePointer)
73+
}
74+
75+
/**
76+
* Serves as the default hash function.
77+
*
78+
* @return Returns a hash code for the current object.
79+
*/
80+
final override fun toString(): String {
81+
return toDataClassString()
6282
}
6383

84+
/**
85+
* Determines whether any immutable properties of this object have changed.
86+
*
87+
* @param other The pointer to compare to this one.
88+
* @return Returns true if the immutable properties remain unchanged; otherwise, false.
89+
*/
90+
internal abstract fun immutableEquals(other: AttestationPointer<T>): Boolean
91+
6492
/**
6593
* Checks the claim and value class types of the specified state to ensure they match the expected types.
6694
*
@@ -75,77 +103,46 @@ sealed class AttestationPointer<T : ContractState> : SingularResolvable<T>, Hash
75103
}
76104
}
77105
}
78-
79-
/**
80-
* Gets the state linearId if this [AttestationPointer] is a [LinearAttestationPointer]; otherwise, null.
81-
* @return Returns the state linearId if this [AttestationPointer] is a [LinearAttestationPointer]; otherwise, null.
82-
*/
83-
internal fun getLinearId(): UniqueIdentifier? {
84-
return if (this is LinearAttestationPointer<*>) stateLinearId else null
85-
}
86106
}
87107

88108
/**
89109
* Represents a linear attestation pointer to a [LinearState].
90110
*
91-
* @param T The underlying [LinearState] type.
111+
* The intention of a linear attestation pointer is to evolve with the linear state that they point to. In this case
112+
* the linear state being witnessed and attested is free to evolve without losing attestation, since the attestation
113+
* points to the state by it's linear ID. Whilst this behavior is deliberate, it might incur some security concerns;
114+
* for example, the state being witnessed and attested may evolve to contain erroneous data, however its attestation
115+
* will remain unchanged until the attestor amends it.
92116
*
117+
* To mitigate these security concerns, the intention is that developers will derive a custom attestation and decide
118+
* on the attestation's validity based on some other factor about the underlying state.
119+
*
120+
* @param T The underlying [LinearState] type.
93121
* @property stateType The [Class] of the witnessed state being attested.
94-
* @property stateRef The [StateRef] of the witnessed state being attested.
95-
* @property stateLinearId The [UniqueIdentifier] of the witnessed state being attested.
96-
* @property hash The unique hash of the attestation pointer.
122+
* @property statePointer The pointer to the witnessed state being attested.
123+
* @property hash The hash of the attestation pointer.
97124
*/
98125
class LinearAttestationPointer<T : LinearState> internal constructor(
99126
override val stateType: Class<T>,
100-
override val stateRef: StateRef,
101-
val stateLinearId: UniqueIdentifier
127+
override val statePointer: UniqueIdentifier
102128
) : AttestationPointer<T>() {
103129

104-
constructor(stateAndRef: StateAndRef<T>) : this(
105-
stateType = stateAndRef.state.data.javaClass,
106-
stateRef = stateAndRef.ref,
107-
stateLinearId = stateAndRef.state.data.linearId
108-
)
130+
constructor(stateAndRef: StateAndRef<T>) : this(stateAndRef.state.data.javaClass, stateAndRef.state.data.linearId)
109131

110132
private val criteria: QueryCriteria = vaultQuery(stateType) {
111-
stateStatus(Vault.StateStatus.ALL)
133+
stateStatus(Vault.StateStatus.UNCONSUMED)
112134
relevancyStatus(Vault.RelevancyStatus.ALL)
113-
stateRefs(stateRef)
114-
linearIds(stateLinearId)
115-
}
116-
117-
override val hash: SecureHash
118-
get() = SecureHash.sha256("$stateType$stateRef$stateLinearId")
119-
120-
/**
121-
* Determines whether the specified object is equal to the current object.
122-
*
123-
* @param other The object to compare with the current object.
124-
* @return Returns true if the specified object is equal to the current object; otherwise, false.
125-
*/
126-
override fun equals(other: Any?): Boolean {
127-
return this === other || (other is LinearAttestationPointer<*>
128-
&& other.stateType == stateType
129-
&& other.stateRef == stateRef
130-
&& other.stateLinearId == stateLinearId)
131-
}
132-
133-
/**
134-
* Serves as the default hash function.
135-
*
136-
* @return Returns a hash code for the current object.
137-
*/
138-
override fun hashCode(): Int {
139-
return Objects.hash(stateType, stateRef, stateLinearId)
135+
linearIds(statePointer)
140136
}
141137

142138
/**
143-
* Serves as the default hash function.
139+
* Determines whether this [SingularResolvable] is pointing to the specified [StateAndRef] instance.
144140
*
145-
* @return Returns a hash code for the current object.
141+
* @param stateAndRef The [StateAndRef] to determine being pointed to.
142+
* @return Returns true if this [SingularResolvable] is pointing to the specified [StateAndRef]; otherwise, false.
146143
*/
147-
override fun toString(): String {
148-
return toDataClassString()
144+
override fun isPointingTo(stateAndRef: StateAndRef<T>): Boolean {
145+
return statePointer == stateAndRef.state.data.linearId
149146
}
150147

151148
/**
@@ -194,66 +191,46 @@ class LinearAttestationPointer<T : LinearState> internal constructor(
194191
override fun immutableEquals(other: AttestationPointer<T>): Boolean {
195192
return other is LinearAttestationPointer<*>
196193
&& other.stateType == stateType
197-
&& other.stateLinearId == stateLinearId
194+
&& other.statePointer == statePointer
198195
}
199196
}
200197

201198
/**
202199
* Represents a static attestation pointer to a [ContractState].
203200
*
204-
* @param T The underlying [LinearState] type.
201+
* The intention of a static attestation pointer is to point specifically to a version of a state by its [StateRef].
202+
* Any evolution of the state being witnessed and attested therefore renders existing attestations useless since
203+
* they no longer point to a relevant state on the ledger, and must be amended to point to the latest state version.
204+
*
205+
* In most cases, static attestation pointers may be considered safer than linear attestation pointers, since they
206+
* do not permit state evolution.
205207
*
208+
* @param T The underlying [LinearState] type.
206209
* @property stateType The [Class] of the witnessed state being attested.
207-
* @property stateRef The [StateRef] of the witnessed state being attested.
208-
* @property hash The unique hash of the attestation pointer.
210+
* @property statePointer The pointer to the witnessed state being attested.
211+
* @property hash The hash of the attestation pointer.
209212
*/
210213
class StaticAttestationPointer<T : ContractState> internal constructor(
211214
override val stateType: Class<T>,
212-
override val stateRef: StateRef
215+
override val statePointer: StateRef
213216
) : AttestationPointer<T>() {
214217

215-
constructor(stateAndRef: StateAndRef<T>) : this(
216-
stateType = stateAndRef.state.data.javaClass,
217-
stateRef = stateAndRef.ref
218-
)
218+
constructor(stateAndRef: StateAndRef<T>) : this(stateAndRef.state.data.javaClass, stateAndRef.ref)
219219

220220
private val criteria: QueryCriteria = vaultQuery(stateType) {
221221
stateStatus(Vault.StateStatus.ALL)
222222
relevancyStatus(Vault.RelevancyStatus.ALL)
223-
stateRefs(stateRef)
224-
}
225-
226-
override val hash: SecureHash
227-
get() = SecureHash.sha256("$stateType$stateRef")
228-
229-
/**
230-
* Determines whether the specified object is equal to the current object.
231-
*
232-
* @param other The object to compare with the current object.
233-
* @return Returns true if the specified object is equal to the current object; otherwise, false.
234-
*/
235-
override fun equals(other: Any?): Boolean {
236-
return this === other || (other is StaticAttestationPointer<*>
237-
&& other.stateType == stateType
238-
&& other.stateRef == stateRef)
223+
stateRefs(statePointer)
239224
}
240225

241226
/**
242-
* Serves as the default hash function.
243-
*
244-
* @return Returns a hash code for the current object.
245-
*/
246-
override fun hashCode(): Int {
247-
return Objects.hash(stateType, stateRef)
248-
}
249-
250-
/**
251-
* Serves as the default hash function.
227+
* Determines whether this [SingularResolvable] is pointing to the specified [StateAndRef] instance.
252228
*
253-
* @return Returns a hash code for the current object.
229+
* @param stateAndRef The [StateAndRef] to determine being pointed to.
230+
* @return Returns true if this [SingularResolvable] is pointing to the specified [StateAndRef]; otherwise, false.
254231
*/
255-
override fun toString(): String {
256-
return toDataClassString()
232+
override fun isPointingTo(stateAndRef: StateAndRef<T>): Boolean {
233+
return statePointer == stateAndRef.ref
257234
}
258235

259236
/**

onixlabs-corda-identity-framework-contract/src/main/kotlin/io/onixlabs/corda/identityframework/contract/AttestationSchema.kt

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,12 @@ object AttestationSchema {
4141
@Column(name = "attestor", nullable = false)
4242
val attestor: AbstractParty = NULL_PARTY,
4343

44-
@Column(name = "pointer_state_ref", nullable = false)
45-
val pointerStateRef: String = "",
44+
@Column(name = "pointer_state_pointer", nullable = false)
45+
val pointer: String = "",
4646

4747
@Column(name = "pointer_state_type", nullable = false)
4848
val pointerStateType: String = "",
4949

50-
@Column(name = "pointer_state_linear_id", nullable = true)
51-
val pointerStateLinearId: UUID? = null,
52-
53-
@Column(name = "pointer_state_external_id", nullable = true)
54-
val pointerStateExternalId: String? = null,
55-
5650
@Column(name = "pointer_hash", nullable = false)
5751
val pointerHash: String = "",
5852

0 commit comments

Comments
 (0)