Skip to main content

Right

Struct Right 

Source
pub struct Right {
    pub id: RightId,
    pub commitment: Hash,
    pub owner: OwnershipProof,
    pub salt: Vec<u8>,
    pub nullifier: Option<Hash>,
    pub state_root: Option<Hash>,
    pub execution_proof: Option<Vec<u8>>,
}
Expand description

A consumable Right in the USP system.

Every chain enforces single-use of Rights, but at different enforcement levels (L1 Structural → L2 Type-Enforced → L3 Cryptographic).

The chain provides the minimum guarantee (single-use enforcement). Clients verify everything else.

Fields§

§id: RightId

Unique identifier: H(commitment || salt)

§commitment: Hash

Encodes the state + rules of this Right

§owner: OwnershipProof

Proof of ownership

§salt: Vec<u8>

Salt used to compute the Right ID. Stored to enable ID recomputation during deserialization.

§nullifier: Option<Hash>

One-time consumption marker (L3+ only)

L1 (Bitcoin/Sui): None — chain enforces structurally. L2 (Aptos): None — Move VM enforces non-duplication. L3 (Ethereum): Some — nullifier registered in contract.

§state_root: Option<Hash>

Off-chain state commitment root

Commits to the full state history for this Right. Clients use this to verify state transitions without fetching the entire history on every validation.

§execution_proof: Option<Vec<u8>>

Optional execution proof (ZK, fraud proof, etc.)

For advanced use cases where the Right’s execution needs to be proven without revealing its contents.

Implementations§

Source§

impl Right

Source

pub fn new(commitment: Hash, owner: OwnershipProof, salt: &[u8]) -> Self

Create a new Right with the given parameters.

The Right ID is deterministically computed from the commitment and salt, ensuring uniqueness even for duplicate commitments.

Source

pub fn consume( &mut self, secret: Option<&[u8]>, chain_context: Option<&[u8; 32]>, ) -> Option<Hash>

Mark this Right as consumed by setting the nullifier.

§Enforcement Level
  • L1 (Bitcoin/Sui): This method is a local marker only. The actual single-use enforcement is done by the chain (UTXO spending / Object deletion).

  • L2 (Aptos): This method is a local marker only. The Move VM enforces non-duplication of resources.

  • L3 (Ethereum): The nullifier MUST be registered on-chain. The contract’s nullifiers[id] = true is what enforces single-use.

§Nullifier Construction

nullifier = tagged_hash("csv-nullifier", right_id || secret || context)

Where context = H(chain_id || domain_separator) binds the nullifier to a specific chain context, preventing cross-chain replay attacks even if the same secret is reused.

§Arguments
  • secret — The user’s secret (prevents front-running)
  • chain_context — Pre-computed context hash: H(chain_id || domain_separator)
§Returns

The nullifier hash, or None for L1/L2 chains where the nullifier is not needed (but returned for local tracking).

Source

pub fn transfer(&self, new_owner: OwnershipProof, transfer_salt: &[u8]) -> Right

Transfer this Right to a new owner.

Creates a new Right with the same commitment and state but different ownership. The original Right remains valid until explicitly consumed.

§Arguments
  • new_owner - The new owner’s ownership proof
  • transfer_salt - A unique salt for the transfer to ensure unique ID
§Returns

A new Right instance with the new owner and a fresh ID

Source

pub fn verify(&self) -> Result<(), RightError>

Verify this Right’s ownership and validity.

This is the core client-side validation function. It checks:

  1. The ownership proof is cryptographically valid
  2. The Right ID is correctly derived from commitment || salt
  3. The commitment is well-formed
  4. The Right has not been consumed (nullifier not set)

For full consignment validation, use the client-side validation engine (Sprint 2).

Source

pub fn to_canonical_bytes(&self) -> Vec<u8>

Serialize this Right to canonical bytes.

Used for hashing, signing, and transmission.

Source

pub fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, RightError>

Deserialize a Right from canonical bytes.

§Errors

Returns RightError::InvalidEncoding if the bytes are malformed.

Source

pub fn is_consumed(&self) -> bool

Check if this Right has been consumed.

Source

pub fn requires_nullifier(&self) -> bool

Get the chain enforcement level indicator.

Returns true if this is an L3 (cryptographic) Right that requires nullifier tracking.

Trait Implementations§

Source§

impl Clone for Right

Source§

fn clone(&self) -> Right

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Right

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Right

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for Right

Source§

fn eq(&self, other: &Right) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for Right

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl Eq for Right

Source§

impl StructuralPartialEq for Right

Auto Trait Implementations§

§

impl Freeze for Right

§

impl RefUnwindSafe for Right

§

impl Send for Right

§

impl Sync for Right

§

impl Unpin for Right

§

impl UnsafeUnpin for Right

§

impl UnwindSafe for Right

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,