Crest Model
A structured identity system for post-quantum blockchains
What is the Crest Model?
Crest is Sahyadri's native decentralized identity model. It defines how Decentralized Identifiers (DIDs) are created, controlled, updated, resolved, and deactivated at the protocol level.
The Account Model handles monetary state such as balances, nonces, transactions, and fees. The Crest Model handles decentralized identity state and identity-related operations.
Unlike traditional flat DID documents where keys are stored as simple string arrays without any semantic meaning or purpose classification, the Crest Model introduces a typed identity system where every cryptographic key carries an explicit intent. When a verification method is defined within a Crest document, it must declare its specific purpose through the CrestPurpose enum, which includes Authentication for login and session establishment, KeyAgreement for encrypted communication channels, CapabilityInvocation for authorizing blockchain actions and transactions, CapabilityDelegation for granting temporary or permanent authority to external entities such as devices or agents, and Assertion for signing claims and issuing verifiable credentials about the identity holder.
This explicit purpose system solves a fundamental problem that has plagued decentralized identity implementations since their inception. In standard DID documents using JSON-LD formats, a signing key appears identical to an encryption key or a delegation key at the data structure level, forcing applications to implement inconsistent validation logic that varies between implementations and creates security vulnerabilities when an application mistakenly uses an authentication key for signing assertions or delegates a capability invocation key to an untrusted third party. The Crest Model eliminates this ambiguity by encoding purpose directly into the protocol layer, ensuring that every key usage can be validated against its declared intention before any cryptographic operation executes.
The Crest Model is an identity-state model that operates alongside monetary state models such as UTXO and Account models. Rather than replacing the way Sahyadri manages monetary state, Crest provides a dedicated protocol-level structure for decentralized identity, including identity controllers, verification methods, services, and versioned identity state.
At its core, a Crest functions as a self-describing identity document that answers four fundamental questions which any decentralized identity system must address. First, it establishes globally unique identification through the did:sahyadri naming scheme combined with cryptographic hashes derived from the initial creation transaction, ensuring no two entities can ever claim the same identifier through collision or malicious registration. Second, it defines controllership by binding each Crest to a specific Sahyadri address formatted as csm1s prefix followed by bech32-encoded bytes, creating an unambiguous ownership chain where only the private key holder controlling that address can authorize modifications to the identity document. Third, it enumerates the cryptographic capabilities available to the identity holder through verification methods that specify not only the public key material but also the algorithm type, currently restricted to CRYSTALS-Dilithium3 for quantum resistance, and the permitted usage contexts through the purpose enum. Fourth, it declares service endpoints where this identity can be contacted or where additional identity-related data resides, such as Decentralized Web Node hubs for storing verifiable credentials, discovery endpoints for finding other identities within the Sahyadri network, or custom application-specific URLs.
The relationship between the Crest Model and Verifiable Credentials forms a layered architecture where Crests establish the foundational identity anchor while credentials represent signed statements issued about that identity by itself or by third-party attesters. When an entity wishes to issue a Verifiable Credential asserting some claim such as educational attainment, professional certification, or access authorization, it uses the assertion-purpose verification method from its Crest to sign the credential payload. Recipients of such credentials can then resolve the issuer's Crest from the blockchain, verify the signature against the registered public key, confirm that the key possesses assertion purpose, and establish trust in the credential's authenticity without relying on any centralized certificate authority or intermediary service.
Implementation-wise, the Crest Model persists identity data through a dual-store architecture built on RocksDB that enables bidirectional lookups in constant time regardless of query direction. The primary store uses the hexadecimal prefix 0x646964732d73746f7265 representing the ASCII string "dids-store" as a namespace organizer, mapping each DID's hash component to its serialized CrestDocument containing all current identity information. Complementing this forward lookup path, the reverse index store under prefix 0x646964732d616464722d696e646578 maps controller addresses back to their associated DID references, allowing the network to answer questions like "which identity controls this address" without scanning all existing DIDs. This dual-indexing strategy proves essential for practical applications where a user presents an address from their wallet and the application needs to discover and display the corresponding identity profile, or when auditing systems must trace transactions back to their originating identity controllers.
Every mutation to a Crest follows strict validation rules enforced at the consensus layer rather than left to application discretion. Creation requires the requesting address to sign the initial document and pay the associated identity registration fee, preventing spam identity flooding while establishing clear economic stakes in identity ownership. Updates must reference the expected current version number, implementing optimistic concurrency control that prevents lost update problems when two modifications race against each other, with only the first submission succeeding and subsequent attempts failing with version mismatch errors that prompt clients to fetch the latest state before retrying. Key rotation preserves the purpose attribute of the replaced method while updating the public key material, maintaining the semantic contract that an authentication key remains an authentication key even after the underlying cryptographic material changes due to compromise suspicion or regular security hygiene practices. Deactivation sets a boolean flag marking the Crest as permanently immutable without actually deleting historical data, preserving the complete record for regulatory compliance, forensic analysis, and proving what the identity looked like at any point in its lifetime.
Account Model vs Crest Model
Sahyadri separates monetary state from decentralized identity state. The Account Model is responsible for value transfer, while Crest is responsible for decentralized identity.
This separation exists because financial transactions and identity management have completely different needs. Money moves fast and needs simple state. Identity persists for years and needs rich metadata. Mixing them creates problems: updating a service endpoint shouldn't touch your balance, rotating authentication keys shouldn't expose your CSM holdings, and regulatory history preservation shouldn't bloat every node's financial database.
The Account Model handles balances, nonces, CSM transfers, and fees. When you send tokens, it debits sender, credits recipient, increments nonce, and records the state change. This layer optimizes for speed and minimal storage because it runs millions of times daily.
The Crest Model handles DIDs, identity documents, verification methods, and all identity operations. When you create a Crest, it generates a DID from your address but keeps it as an independent entity. You can share that DID publicly without revealing your balance. You can rotate keys without affecting your funds. You can register service endpoints that only identity queries ever see.
| Account Model | Crest Model |
|---|---|
| Balance | DID |
| Nonce | DID Document |
| CSM transactions | Identity operations |
| Fees | Verification methods |
| Value state | Identity state |
Each row shows how the two models handle similar concerns differently. A balance is just a number. A DID is a structured identifier resolving to rich data. A nonce prevents replay attacks. A DID Document contains nested objects with keys, services, and metadata. Fees are per-transaction costs. Verification methods are typed cryptographic keys declaring their specific purpose. Value state tracks token ownership. Identity tracks who can prove what claims using which keys.
This separation lets each layer optimize independently. Identity documents can grow large with metadata because only identity queries pay that cost. Account state stays tiny because financial validation never inspects service endpoints or key purposes. Post-quantum cryptography is mandatory for identity signatures while faster classical algorithms work fine for routine payments during the quantum transition period.
Crest Architecture
The Crest Model sits alongside the Account Model within the Sahyadri protocol. Both share the same blockchain foundation but operate on completely separate state partitions, communicating only when an identity operation needs to verify controllership against an address balance or when a transaction needs to resolve a DID for identity-based access control.
┌─────────────────────────────────────────────────────┐
│ SAHYADRI L1 │
│ (Consensus + Cryptography) │
└───────────────────────┬─────────────────────────────┘
│
┌───────────────┴───────────────┐
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│ ACCOUNT │ │ CREST │
│ MODEL │ │ MODEL │
├───────────────┤ ├───────────────┤
│ Balance │ │ DID │
│ Nonce │ │ Controller │
│ CSM Transfer │─── separate ──┤ Verification │
│ Fees │ state │ Methods │
│ Value State │ │ Services │
└───────────────┘ │ Version │
│ Identity State│
└───────────────┘The diagram shows two parallel branches under the same Sahyadri root. The left branch handles everything monetary: your CSM balance, transaction nonce, token transfers to other addresses, and overall value state that every validator must track. Fees associated with CSM transactions and applicable protocol operations. The right branch handles everything identity-related: your globally unique DID, the controller address that owns it, typed verification methods with explicit purposes, registered service endpoints, monotonically increasing version numbers for audit trails, and the complete identity state document.
Inside the Crest Model itself, each component serves a specific purpose. The DID is the public identifier you share with others, formatted as did:sahyadri followed by a hash derived from the creation transaction. The Controller is the csm1s address that proves ownership and authorizes any changes to the Crest. Verification Methods are cryptographic keys where each key declares whether it handles authentication, key agreement, capability invocation, capability delegation, or assertion through the CrestPurpose enum. Services are URLs pointing to external endpoints like Decentralized Web Node hubs where verifiable credentials get stored or discovery services where other identities can find you. Version is a counter incrementing on every update, enabling optimistic concurrency control and preventing lost modifications from racing updates. Identity State wraps all of this into a single coherent document that gets persisted in the dual-store architecture and resolved by any participant on the network.
When a user interacts with Sahyadri, they typically touch both models but through different operations. Sending CSM touches only the Account Model. Creating or updating a Crest touches only the Crest Model. The rare crossover happens during identity creation when the system verifies the controller address has sufficient balance to pay registration fees, or during credential issuance when the issuer's Crest verification method signs a payload that later gets anchored to an account transaction for immutability proof. This minimal coupling keeps both models independently optimizable while maintaining protocol-level consistency guarantees.
Crest Operation Lifecycle
Every Crest operation follows a validated execution path from client submission to finalized state. This lifecycle ensures that only authorized, well-formed mutations reach the canonical identity store.
┌──────────┐
│ Client │ Wallet / SDK / CLI initiates operation
└─────┬────┘
│
▼
┌──────────────┐
│ Crest │ Create / Update / Rotate / Deactivate
│ Operation │ Request struct built with payload
│ Request │
└─────┬────────┘
│
▼
┌──────────────────┐
│ Cryptographic │ Controller signs with Dilithium3
│ Signature │ Purpose-matched key verification
│ Verification │
└─────┬────────────┘
│
▼
┌──────────────────┐
│ Node │ validate_proof() checks signature
│ Validation │ Version check (optimistic concurrency)
│ │ Purpose enum validation
│ │ Controller ownership confirmation
└─────┬────────────┘
│
▼
┌──────────────────┐
│ State │ Dual-store RocksDB write
│ Transition │ Primary: dids-store || did_hash → document
│ │ Reverse: dids-addr-index || address → DID ref
│ │ Version incremented atomically
└─────┬────────────┘
│
▼
┌──────────────────┐
│ Consensus │ PoW + DAG orders operation
│ │ Block inclusion confirms finality
└─────┬────────────┘
│
▼
┌──────────────────┐
│ Finalized │ CrestDocument now immutable at new version
│ Crest State │ Resolvable by any node via DID or address
└──────────────────┘A Crest operation is validated by the node before it can modify the canonical identity state. This validation happens in multiple stages that together prevent unauthorized modifications, reject malformed requests, and maintain consistency across the distributed network.
At the client layer, the wallet or SDK constructs a typed request object matching the intended operation. For creation, this includes the desired verification methods and services. For updates, it specifies which fields to modify along with the expected current version number. For key rotation, it identifies the method to replace and provides the new public key material while preserving the existing purpose attribute. For deactivation, it includes only the target DID since no additional parameters are needed beyond authorization.
Signature generation uses the controller's private key through CRYSTALS-Dilithium3, producing a 2465-byte signature that covers the complete request payload. The signing key must match the operation type: authentication-purpose keys sign create and update requests, capability-invocation keys sign delegation operations, assertion keys sign credential issuance payloads. This purpose matching happens before the request leaves the client, catching obvious mismatches early.
Upon reaching a validating node, signature verification confirms the signer actually controls the claimed controller address. The node retrieves the current Crest state if it exists, extracts the public key corresponding to the declared purpose, and validates the Dilithium3 signature against the request bytes. Any mismatch between the signed payload and the serialized request, any corruption in the public key bytes, or any usage of an incorrect purpose key causes immediate rejection with a specific error code indicating what went wrong.
For update operations specifically, the node performs an optimistic concurrency check by comparing the expected version in the request against the actual stored version. If another update committed between when the client read the current state and when this request arrived, the versions will not match and the operation fails with a version conflict error. The client must then re-resolve the latest state, apply its changes on top of the new version, increment accordingly, and retry. This prevents silent lost updates where two concurrent modifications overwrite each other.
Once validation passes, the state transition writes to both stores in the dual-store architecture atomically. The primary store under the dids-store prefix records the updated or newly created CrestDocument with its incremented version number and fresh timestamp. Simultaneously, the reverse index under the dids-addr-index prefix maps the controller address to the DID reference, enabling future address-to-DID lookups without scanning. These writes use RocksDB batch operations ensuring either both succeed or neither persists, maintaining index consistency even if the node crashes mid-write.
Finally, consensus incorporation places the operation into a block through Sahyadri's Proof-of-Work plus DAG hybrid mechanism. Once sufficient work proves the block and DAG ordering establishes its position relative to concurrent blocks, the Crest state transition achieves finality. Other nodes receiving this block can independently verify the signature, replay the state transition, and arrive at identical Crest state without trusting the original proposing node. At this point, the operation is irreversible and globally visible to any participant resolving the affected DID.
Crest State Transition
┌─────────────────────────┐
│ Crest v1 │ Version: 1
│ │ Controller: csm1sabc...
│ verification_methods: │ Keys: [#key-auth, #key-agree]
│ #key-auth (Auth) │ Services: [#hub]
│ #key-agree (KeyAg) │ Status: Active
│ services: │ Updated: 1704000000
│ #hub │
└──────────┬──────────────┘
│
▼
┌─────────────────────────┐
│ Update Operation │ Request: Add #key-delegate
│ │ Expected Version: 1
│ { │ New Verification Method:
│ crest_id: "...", │ id: "#key-delegate"
│ add_verification: [ │ type: "Dilithium3"
│ { │ purpose: CapabilityDelegation
│ id: "#key-del", │ public_key: "<1952 bytes>"
│ purpose: CapDel │ }
│ } │ Signature: controller signs payload
│ ], │
│ expected_version: 1 │
│ } │
└──────────┬──────────────┘
│
▼
┌─────────────────────────┐
│ Validation │ ✓ Signature valid?
│ │ ✓ Controller matches signer?
│ Checks: │ ✓ Purpose enum valid?
│ • Signature check │ ✓ expected_version == stored_version?
│ • Ownership check │ ✓ New key format correct?
│ • Purpose validation │ ✓ Not deactivated?
│ • Version match │
│ • Active status │
└──────────┬──────────────┘
│
│ All pass → SUCCESS
│ Any fail → ERROR
▼
┌─────────────────────────┐
│ Crest v2 │ Version: 2 ← Auto-incremented
│ │ Controller: csm1sabc... (unchanged)
│ verification_methods: │ Keys: [#key-auth, #key-agree, #key-delegate] ← NEW
│ #key-auth (Auth) │ Services: [#hub] (unchanged)
│ #key-agree (KeyAg) │ Status: Active (unchanged)
│ #key-del (CapDel) ◄──│ Updated: 1704086400 ← New timestamp
│ services: │
│ #hub │
└─────────────────────────┘What Gets Validated
Before any state transition occurs, the node runs validate_proof() which checks six conditions in sequence. First, it verifies the cryptographic signature using the controller's Dilithium3 public key from the current stored state, rejecting any request where the signature fails or was produced by a different key. Second, it confirms the signing address actually matches the controller field of the existing Crest, preventing unauthorized parties from submitting modifications even if they somehow obtain a valid-looking signature. Third, it validates that every purpose value in new verification methods corresponds to a recognized variant in the CrestPurpose enum, catching typos like "Authenticationn" or invented purposes before they reach storage. Fourth, it compares the expected_version in the request against the actual stored version number, detecting concurrent modifications that would cause lost updates. Fifth, it confirms the target Crest has not been previously deactivated since deactivated documents reject all further operations by design. Sixth, for update operations specifically, it verifies that no duplicate method IDs would result from applying the changes, maintaining uniqueness within each document.
What Actually Changes
A successful transition modifies only the fields specified in the operation request while preserving everything else unchanged. Adding a verification method appends it to the array without touching existing entries. Removing a service deletes that single entry while leaving all other services intact. Rotating a key replaces the public_key_base64 bytes for the targeted method ID while keeping its purpose, ID, and position identical. The controller address never changes through updates because transferring identity ownership requires deactivation followed by fresh creation under a new controller rather than an in-place modification. Timestamp fields update automatically to reflect when the transition committed, not when the client constructed the request. The dual-store architecture receives both writes atomically: the primary store records the complete new document under the DID hash, and the reverse index refreshes the address-to-DID mapping if the controller reference changed, which it typically does not during normal operations.
How Version Increments
Version numbers are monotonically increasing unsigned 64-bit integers starting at one for newly created Crests. Each successful write operation, whether it adds a single verification method or modifies multiple fields simultaneously, increments the version by exactly one. The system does not use fractional versions, semantic versioning schemes, or branch identifiers. If Crest currently sits at version forty-two and an approved update adds two services and rotates one key, the resulting document becomes version forty-three, not forty-four or forty-two-point-one. This simple incrementing scheme enables the optimistic concurrency control mechanism: clients read the current version, include it as expected_version in their modification requests, and the node rejects the operation if the stored version has advanced past what the client observed. Upon rejection, the client re-resolves the latest state, applies changes atop the newer version, and retries. No locking, no transactions spanning multiple blocks, no coordination between competing updaters beyond reading and comparing version numbers.
What Happens On Invalid Operations
When validation fails, the node returns a specific error variant from the CrestOpError enum and zero state changes occur. The original Crest remains at its current version with all fields untouched. Different failure modes produce different error responses so clients can handle each case appropriately.
For signature failures, the error indicates which verification step failed, allowing the client to distinguish between corrupted signatures, wrong keys used for signing, and malformed public key bytes in the stored document. The typical fix is re-signing with the correct controller key.
For version conflicts, meaning another update committed after the client last read the state, the error includes the current version number so the client can immediately construct a retry without an extra round-trip to fetch the latest state. The client bumps its expected_version to the reported value and resubmits.
For ownership mismatches, where the signing address does not match the stored controller, the error makes clear that the requester lacks authorization. This typically indicates configuration errors in wallet software attempting operations with the wrong key pair.
For deactivated target errors, meaning the Crest was already deactivated before this operation arrived, the response informs the client that no further modifications are possible. The only recourse is creating a fresh Crest under a new DID if identity re-establishment is needed.
For invalid purpose or duplicate method errors, the specific problematic field is identified so the client can fix the request payload before retrying. These represent client-side bugs rather than transient conditions and typically require code fixes rather than automatic retries.
In all error cases, the dual-store database receives zero writes. No partial updates, no orphaned index entries, no inconsistent state visible to other nodes or subsequent queries. The validation gate either passes completely and transitions occur atomically, or fails completely and nothing touches persistent storage.
Overview
The Crest Model is a typed identity framework designed for the Sahyadri blockchain. It provides structured, validated identity documents with native post-quantum cryptographic support through Dilithium3.
Unlike flat string-based DID documents, the Crest Model enforces type safety at the protocol level. Every verification method has an explicit purpose, every service endpoint is validated, and all state changes are versioned for auditability.
The model addresses three limitations in existing approaches:
- No key purpose differentiation: Traditional DIDs represent keys as string arrays with no semantic meaning. A signing key looks identical to an encryption key.
- No post-quantum support: ECDSA and Ed25519 will become vulnerable to quantum computers. Adding post-quantum cryptography as an afterthought creates integration complexity.
- No enforced validation: Flat JSON-LD documents rely on application-level validation, which may be inconsistent or absent.
Core Concepts
What is a Crest?
A Crest is a structured identity document representing an entity on the Sahyadri network. Entities can be individuals, organizations, devices, or autonomous agents.
Every Crest contains:
- Globally unique identifier: A DID in the format
did:sahyadri:{hash} - Controller address: A Sahyadri address (
csm1s...) that owns and controls the identity - Typed verification methods: Cryptographic keys with explicit purposes
- Validated service endpoints: Verified external service references
- Version number: Monotonically increasing counter tracking modifications
- Lifecycle state: Active or deactivated (tombstone pattern)
Data Model Comparison
| Aspect | Traditional W3C DID | Crest Model |
|---|---|---|
| Key representation | String references in arrays | Typed structs with purpose enum |
| Purpose enforcement | Application-level interpretation | Protocol-level enum values |
| Cryptographic algorithm | External/optional specification | Native Dilithium3 requirement |
| Validation | Not enforced by protocol | Mandatory on every operation |
| State management | Immutable (new DID per change) | Mutable with version tracking |
| Address binding | Optional or weak | Required with reverse index |
| Deactivation | Delete and forget | Tombstone preserving history |
Data Types
CrestDocument
The primary identity structure. All identities on Sahyadri are represented as CrestDocument instances.
pub struct CrestDocument {
// Identifiers
pub id: String,
// Format: "did:sahyadri:{sha256(controller + created_at)}"
pub context: String,
// Default: "https://w3id.org/crest/v1"
pub controller: String,
// Sahyadri address: "csm1s..."
// Immutable once set
// Verification Methods
pub authentication: Vec<CrestVerificationMethod>,
// Keys for signing transactions
pub key_agreement: Vec<CrestVerificationMethod>,
// Keys for encryption/key exchange
// Services
pub services: Vec<CrestService>,
// Validated external endpoints
// State
pub version: u64,
// Starts at 1, increments on each mutation
pub deactivated: bool,
// false = active, true = tombstone (irreversible)
// Timestamps
pub created_at: u64,
// Unix timestamp at creation (immutable)
pub updated_at: u64,
// Unix timestamp of last modification
}CrestVerificationMethod
A cryptographic key with explicit purpose typing. This is the primary innovation over traditional DID key representations.
pub struct CrestVerificationMethod {
pub id: String,
// Format: "{controller}#key-{n}"
// Example: "csm1sxq9...#key-1"
pub key_type: String,
// Currently supported: "Dilithium3"
// Future: algorithm migration path
pub controller: String,
// Must match parent CrestDocument.controller
pub public_key_bytes: Vec<u8>,
// Raw key bytes
// Dilithium3: exactly 1952 bytes
pub purpose: CrestPurpose,
// Typed purpose enumeration
pub created_at: u64,
// Key addition timestamp
pub expires_at: Option<u64>,
// Optional expiration for time-limited credentials
}CrestPurpose Enumeration
The purpose enum defines what operations a key can perform. This prevents accidental misuse of keys across different security domains.
| Variant | Permitted Operations | Security Domain | Compromise Impact |
|---|---|---|---|
Authentication | Sign transactions, prove ownership | Identity verification | Attacker can sign transactions as controller |
KeyAgreement | Encrypt/decrypt messages, key exchange | Confidentiality | Attacker can decrypt intercepted communications |
CapabilityInvocation | Authorize delegated actions on behalf of controller | Delegation | Attacker can exercise delegated permissions |
CapabilityDelegation | Verify and attest to others' capabilities | Trust anchoring | Attacker can issue false attestations |
Assertion | Make verifiable claims about self | Credential issuance | Attacker can issue false claims under this identity |
CrestService
A validated service endpoint attached to the identity. Services undergo protocol-level validation before inclusion.
pub struct CrestService {
pub id: String,
// Format: "{controller}#svc-{type}"
pub service_type: String,
// Standard types:
// - "DIDComm": Decentralized messaging
// - "Web5DWN": Web5 Data Network node
// - "HubService": Universal resolver endpoint
// - "LinkedDomains": Domain ownership verification
// - "CredentialService": Verifiable credential issuance
pub service_endpoint: String,
// URL or DID reference where service operates
pub validated: bool,
// Protocol has verified endpoint accessibility
}Storage Architecture
Dual-Store Design
The Crest Model uses two RocksDB stores to enable bidirectional lookups:
Store 1: Primary Identity Store
───────────────────────────────
Prefix: "dids-store" (0x646964732d73746f7265)
Key: DidKey(did_bytes)
Value: DidDocument (JSON serialization)
Operation: resolve_crest(did) → CrestDocument
Lookup: O(1) hash access
Store 2: Reverse Address Index
───────────────────────────────
Prefix: "dids-addr-index" (0x646964732d616464722d696e646578)
Key: DidKey(address_bytes)
Value: DidIndexEntry(did_string)
Operation: resolve_by_address(address) → CrestDocument
Lookup: O(1) hash access (two-hop: address → did → document)Why Dual Stores?
| Query Pattern | Single Store | Dual Store |
|---|---|---|
| Resolve by DID | O(1) | O(1) |
| Resolve by address | O(n) scan or impossible | O(1) |
| Check address-DID uniqueness | O(n) scan | O(1) |
| Duplicate prevention on create | Requires full scan | O(1) index check |
Cache Layer
Both stores use CachedDbAccess providing LRU caching:
pub struct DbDidStore {
pub did_access: CachedDbAccess<DidKey, DidDocument>,
// Primary store with LRU cache
pub address_index: CachedDbAccess<DidKey, DidIndexEntry>,
// Reverse index with LRU cache
}
// Cache characteristics:
// - Configurable maximum size (default: 100MB)
// - Least-recently-used eviction policy
// - Thread-safe via RwLock
// - Write-through on mutations
// - Expected hit rate: >95% for active identitiesIdentity Lifecycle
State Machine
┌──────────────┐
│ CREATED │
│ v=1, active │
└──────┬───────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌────────────┐ ┌──────────┐ ┌────────────┐
│ RESOLVE │ │ UPDATE │ │ ROTATE │
│ (read-only)│ │ v=2,3,...│ │ KEY │
└────────────┘ └────┬─────┘ └────────────┘
│
┌─────────┴─────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ ADD SERVICE │ │ ADD METHOD │
│REMOVE SERVICE│ │ROTATE METHOD │
└──────────────┘ └──────────────┘
│
▼
┌──────────────┐
│ DEACTIVATE │
│ (tombstone) │
└──────────────┘
IRREVERSIBLEPhase Descriptions
Creation
Creating a new Crest identity performs these validations:
- Controller address format validation (must be valid
csm1s...address) - Public key size validation (must equal 1952 bytes for Dilithium3)
- Uniqueness check (address must not have existing DID via reverse index)
- Initial verification method construction with
purpose: Authentication - DID generation from controller + timestamp hash
- Dual-store write (primary + reverse index)
// Creation request structure
pub struct CreateCrestRequest {
pub controller: String, // "csm1s..."
pub public_key: Vec<u8>, // [u8; 1952] Dilithium3
pub services: Option<Vec<CrestService>>,
pub additional_keys: Option<Vec<CrestVerificationMethod>>,
}
// Response
pub struct CreateCrestResponse {
pub did: String, // Generated DID
pub document: CrestDocument, // Complete document
pub created_at: u64, // Timestamp
}Resolution
Two resolution methods exist:
| Method | Input | Store Used | Complexity |
|---|---|---|---|
resolve_crest(did) | DID string | Primary store | O(1) |
resolve_by_address(address) | Sahyadri address | Reverse index → Primary | O(1) |
Both methods return CrestDocument. Resolution fails if the identity is deactivated.
Update
Allowed mutations during update:
- Add service (endpoint validated before inclusion)
- Remove service (by ID, must exist)
- Add verification method (with required purpose)
Each update increments the version field. Concurrent updates are detected via version conflict errors requiring client retry.
Key Rotation
Key rotation replaces an existing verification method with a new cryptographic key. Use cases include:
- Compromised key replacement (urgent)
- Scheduled cryptographic refresh (compliance)
- Algorithm migration (Dilithium3 → future standard)
Rotation requires proof of ownership via Dilithium3 signature over the rotation request.
Deactivation
Deactivation sets deactivated = true. This operation:
- Is irreversible
- Preserves complete history for audit
- Blocks all future operations
- Maintains address-DID binding (prevents re-registration)
Deactivation Semantics
┌─────────────────────────┐
│ ACTIVE │ Status: active = true
│ │ Operations: ALL ALLOWED
│ • Create │ create ✓
│ • Update │ update ✓
│ • Resolve │ rotate_key ✓
│ • Rotate Key │ resolve ✓
│ • Deactivate │ deactivate ✓
└───────────┬─────────────┘
│
│ deactivate() called
│ Controller signs request
▼
┌─────────────────────────┐
│ DEACTIVATING │ Transitional state
│ │ Validation runs:
│ • Signature check │ ✓ Controller valid?
│ • Active status check │ ✓ Currently active?
│ • Ownership check │ ✓ Signer = controller?
└───────────┬─────────────┘
│
│ All checks pass
▼
┌─────────────────────────┐
│ DEACTIVATED │ Status: deactivated = true
│ │ Operations: SEVERELY RESTRICTED
│ • Resolve ✓ YES │ resolve ✓ (read-only)
│ • Update ✗ NO │ update ✗ BLOCKED
│ • Rotate Key ✗ NO │ rotate_key ✗ BLOCKED
│ • Deactivate ✗ NO │ deactivate ✗ ALREADY DONE
│ • Reactivate ✗ NO │ reactivation NOT POSSIBLE
│ • Reuse DID ✗ NO │ reuse permanently BLOCKED
└─────────────────────────┘Can a Deactivated DID Still Be Resolved?
Yes. Deactivated Crests remain fully resolvable through both lookup paths in the dual-store architecture. When a node receives a resolve_crest request for a deactivated DID, it fetches the document from the primary store under the dids-store prefix exactly as it would for an active Crest. The returned CrestDocument contains all fields at their final state: verification methods as they existed before deactivation, service endpoints as last configured, version number at the final value, timestamps showing when the document was created and last updated, and the deactivated boolean flag set to true.
This permanent resolvability serves important practical purposes. Auditors examining historical identity records can verify what keys a entity used at any point during its active lifetime. Applications that previously verified credentials issued by this identity can still confirm those credentials were signed by valid verification methods at issuance time. Legal and regulatory requirements often mandate preserving identity records for specified periods after closure, and immediate deletion would violate such mandates. Other entities referencing this DID in their own documents or credentials need the resolution to succeed so they can display meaningful information rather than broken references.
Are Updates Allowed After Deactivation?
No. Once the deactivated flag transitions from false to true, all mutation operations are permanently blocked for that DID. The implementation enforces this through an early return check in every write operation path.
When update_crest receives a request targeting a deactivated Crest, it checks the stored document's deactivated field before performing any signature validation, version comparison, or state modification. If deactivated equals true, the function immediately returns the error variant AlreadyDeactivated containing the DID string, and zero database writes occur. The same guard exists in rotate_key, which returns the same AlreadyDeactivated error before attempting any key replacement logic.
This design choice is intentional rather than technical limitation. Allowing updates to deactivated identities would undermine the semantic meaning of deactivation itself. If an entity publishes a statement that their identity has been retired, subsequent modifications to that identity would contradict the retirement announcement. Observers could not distinguish between legitimate pre-deactivation state and unauthorized post-deactivation tampering without complex version-tracking logic that would complicate every consumer of identity data.
Is Reactivation Possible?
No. Sahyadri's Crest Model does not support reactivation of deactivated identities. There is no reactivate_crest operation, no undelete mechanism, no administrative override that flips the deactivated flag back to false.
This permanence reflects the security model where deactivation represents an explicit, intentional, irreversible decision by the controller. Common scenarios triggering deactivation include suspected key compromise where the controller wants to permanently retire the identity rather than risk continued use of potentially exposed keys, organizational dissolution where legal entities cease operations and must terminate their digital presence, or user-initiated account closure where individuals exercise their right to be forgotten or simply no longer wish to maintain the identity on-chain.
If a controller who deactivated an identity later decides they need an active identity again, the only option is creating an entirely new Crest with a freshly generated DID derived from the new creation transaction hash. This new Crest has no connection to the old one, receives a new version starting at one, and requires registering fresh verification methods and services. Any external references to the old DID remain pointing to the deactivated document, which continues resolving correctly but shows its deactivated status.
Can the Same DID Be Reused After Deactivation?
No. DIDs are globally unique identifiers derived cryptographically from the creation transaction, and this derivation is deterministic and irreversible. The format did:sahyadri:{hash} uses a content-based hash that depends on the specific transaction bytes, controller address, timestamp, and initial payload at creation time. Recreating identical conditions would require producing a transaction with the exact same bytes at the exact same position in the blockchain, which the consensus mechanism prevents through standard double-spend protection and transaction uniqueness constraints.
Furthermore, even if cryptographic reuse were somehow possible, the protocol explicitly prevents it through the primary store structure. The key "dids-store" || did_hash maps to exactly one CrestDocument. If a deactivated document occupies that slot, attempting to store a new document at the same key would overwrite the historical record, which the implementation does not allow. The create_crest operation generates a fresh DID hash for each invocation regardless of whether previous DIDs exist or their current status.
From a practical standpoint, DID reuse would confuse any system that has ever resolved the original identity. Verifiable credentials signed by the original Crest's assertion keys would become ambiguous if the same DID pointed to a completely different entity with different keys. Service endpoints cached by applications would suddenly route to unintended destinations. The security implications of DID reuse are sufficiently severe that preventing it is considered a fundamental invariant rather than a policy choice.
In summary, deactivation is a one-way transition with well-defined semantics: the identity becomes immutable but remains readable, all mutations are permanently blocked, no reversal mechanism exists, and the identifier can never be recycled for a different purpose. These guarantees are enforced at the implementation level through conditional checks that reject disallowed operations before they can affect persistent state.
Operations API
| Operation | Signature | Description |
|---|---|---|
| Create | create_crest(ctx, req) | New identity creation with dual-store write |
| Resolve by DID | resolve_crest(ctx, did) | Primary store lookup |
| Resolve by Address | resolve_by_address(ctx, addr) | Reverse index lookup |
| Update | update_crest(ctx, req) | Add/remove services and methods |
| Rotate Key | rotate_key(ctx, req) | Replace verification method |
| Deactivate | deactivate_crest(ctx, did) | Permanent tombstone |
| Migrate Legacy | migrate_legacy_did(ctx, did) | W3C DID → Crest conversion |
| Validate Proof | validate_proof(ctx, proof) | Dilithium3 signature verification |
Error Handling
All operations return typed errors via CrestOpError:
| Variant | Condition | HTTP Equivalent |
|---|---|---|
NotFound(String) | DID/address does not exist | 404 Not Found |
AlreadyExists(String) | Duplicate registration attempt | 409 Conflict |
Validation(String) | Invalid input data | 422 Unprocessable Entity |
InvalidSignature(String) | Dilithium3 signature verification failed | 403 Forbidden |
VersionConflict | Concurrent modification detected | 409 Conflict |
Deactivated(String) | Operation on tombstoned identity | 410 Gone |
NotAuthorized(String) | Caller is not controller | 403 Forbidden |
StorageError(String) | Database operation failure | 500 Internal Server Error |
TransactionError(String) | Transaction construction failed | 422 Unprocessable Entity |
Cryptographic Foundation
Dilithium3 Integration
The Crest Model uses Dilithium3 (NIST FIPS 204) as the mandatory signature algorithm. Selection rationale:
- NIST Standardization: Winner of Post-Quantum Cryptography Standardization Project (2022)
- Lattice-based Security: Resistant to Shor's algorithm and quantum attacks
- 128-bit Quantum Security Level: Sufficient for all practical threat models
- Practical Performance: Reasonable operation speed compared to alternatives
Key Size Specifications
| Parameter | Dilithium3 | Dilithium2 | ECDSA (secp256k1) |
|---|---|---|---|
| Public Key Size | 1,952 bytes | 1,704 bytes | 32 bytes |
| Signature Size | 2,465 bytes | 2,427 bytes | 64 bytes |
| Classical Security | 192-bit | 143-bit | 128-bit |
| Quantum Security | 128-bit | 128-bit | 0-bit (vulnerable) |
Transaction Format
struct SahyadriTransaction {
// Header
version: u16,
chain_id: u32,
// Identity (Crest-bound)
public_key: [u8; 1952], // Dilithium3 public key
nonce: u64, // Account nonce (replay prevention)
// Payload
payload: TransactionPayload, // DCRT transfer, DID operation, etc.
// Signature
signature: [u8; 2465], // Dilithium3 signature
}
// Cryptographic overhead per transaction: ~4.4 KBLegacy Migration
Conversion Process
Existing W3C-compliant DID documents convert to CrestDocuments via from_legacy():
| Legacy Field | Crest Field | Transformation |
|---|---|---|
@context | context | Stringify array or default to "https://w3id.org/crest/v1" |
id | id | Direct copy |
authentication[] | authentication[] | String references to CrestVerificationMethod |
keyAgreement[] | key_agreement[] | String references to CrestVerificationMethod |
service[] | services[] | Generic objects to CrestService |
| (missing) | version | Initialize to 1 |
| (missing) | deactivated | Initialize to false |
Reverse conversion via to_legacy() enables interoperability with legacy tooling at the cost of type information loss.
Integration With Account Model
Sahyadri operates both models concurrently:
ACCOUNT MODEL CREST MODEL
────────────── ════════════
Balance (DCRT tokens) Typed identity document
Nonce (transaction counter) Verification methods (by purpose)
ScriptPublicKey Service endpoints
Version history
Connection: Account.address == Crest.controller
(same csm1s... address)
Workflow:
1. Create account (balance=0, nonce=0)
2. Create Crest (bound to account.address)
3. Sign transaction with Crest authentication key
4. Account nonce increments, balance changes
5. Crest remains unchanged (or updates independently)The Account Model tracks ownership state. The Crest Model tracks identity state. They share addressing but serve orthogonal purposes.
Security Properties
Guarantees
| Property | Mechanism |
|---|---|
| Uniqueness | Reverse index prevents duplicate address registrations |
| Integrity | All mutations require valid Dilithium3 signatures |
| Audit Trail | Version increments preserve complete modification history |
| Non-repudiation | Signatures bind operations to controllers |
| Confidentiality Separation | Key agreement keys isolated from authentication keys |
| Availability | Dual indexes enable O(1) bidirectional lookup |
| Post-Quantum Security | All cryptography uses lattice-based Dilithium3 |
Threat Mitigations
| Threat | Mitigation |
|---|---|
| Private key compromise | Immediate key rotation; old version invalidated |
| Identity hijacking | Controller binding + mandatory signatures |
| Transaction replay | Account nonce tracking rejects duplicates |
| Quantum computer attack | Lattice-based crypto resists Shor's algorithm |
| Front-running | DAG ordering + deterministic block processing |
Performance Characteristics
Operation Latency
| Operation | Latency | Notes |
|---|---|---|
create_crest | ~5ms | Dual-store write |
resolve_crest | ~0.1ms | Cache hit typical |
resolve_by_address | ~0.1ms | Cache hit typical |
update_crest | ~3ms | Validation + single write |
rotate_key | ~10ms | Includes signature verification |
migrate_legacy | ~2ms | Parse + transform only |
Storage Estimates
Per Crest Document:
Base overhead: ~500 bytes (JSON metadata)
Per auth key: ~2,000 bytes (includes 1952-byte pubkey)
Per agreement key: ~2,000 bytes (includes 1952-byte pubkey)
Per service: ~200 bytes (URL + type + validation flag)
Typical user identity (1 auth + 1 agreement + 2 services):
Total: ~4,700 bytes (~4.6 KB)
Scaling:
1,000 identities: ~4.7 MB
100,000 identities: ~470 MB
1,000,000 identities: ~4.7 GB raw (~1.5-2 GB compressed)
Index overhead: ~50 MB per million identities
Cache memory: Configurable LRU (default 100 MB)Implementation Status
| Component | Status | Location |
|---|---|---|
| CrestDocument struct | Complete | consensus/src/model/stores/crest_model.rs |
| CrestPurpose enum | Complete | consensus/src/model/stores/crest_model.rs |
| CrestBuilder pattern | Complete | consensus/src/model/stores/crest_model.rs |
| Operations layer | Complete | consensus/src/model/stores/crest_operations.rs |
| Dual-store architecture | Complete | consensus/src/model/stores/did_store.rs |
| Reverse index lookup | Complete | consensus/src/model/stores/did_store.rs |
| Legacy migration helpers | Complete | consensus/src/model/stores/crest_model.rs |
Build status: Compiling successfully with cargo build --release -p sahyadri-consensus.
Reference
File Structure
consensus/src/model/stores/
├── crest_model.rs # Data types, builder, migration
├── crest_operations.rs # Business logic layer
├── did_store.rs # Storage layer with dual indexes
└── mod.rs # Module exportsDependencies
- RocksDB — embedded key-value store
- serde / serde_json — serialization
- thiserror — error handling
- tokio — async runtime