Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ members = [
"contracts/governance",
"contracts/insurance",
"contracts/teachlink",
"contracts/documentation",
"contracts/identity_registry",
"contracts/credential_registry",
]

[workspace.package]
Expand Down
23 changes: 23 additions & 0 deletions contracts/IDENTITY_CREDENTIAL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
**Identity & Credential Contracts (Soroban)**

Overview
- `identity-registry`: on-chain DID registry. Stores DID -> controller, auth methods, recovery address.
- `credential-registry`: on-chain credential index. Stores credential hash -> (issuer DID, subject DID, metadata pointer, expires_at, status).

Key on-chain guarantees
- Deterministic verification can check presence and status of a credential on-chain.
- Full VC JSON and ZK proofs remain off-chain; only hashes/roots and status bits stored on-chain.

Next steps / integration notes
- Wire `credential-registry` to call `identity-registry` for authoritative issuer controller checks.
- Add Merkle/bitmap-based revocation root support for efficient revocation proofs.
- Implement cross-contract calls and auth to allow DID controllers (not raw addresses) to issue/revoke.
- Add off-chain ZK proof verifier support: store verification circuits' commitment roots on-chain and provide helper APIs for verifiers.
- Marketplace, federation, selective-disclosure circuits, and biometric-binding are implemented off-chain; contracts store anchors/roots.

Files added
- `contracts/identity_registry` β€” Cargo + src/lib.rs
- `contracts/credential_registry` β€” Cargo + src/lib.rs

Testing & build
- Use the workspace's soroban toolchain and existing patterns (see other `contracts/*` crates) to build and test.
22 changes: 22 additions & 0 deletions contracts/credential_registry/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "credential-registry"
version = "0.1.0"
edition.workspace = true
repository.workspace = true
license.workspace = true

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
soroban-sdk.workspace = true

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }

[features]
testutils = ["soroban-sdk/testutils"]

[lints]
workspace = true

123 changes: 123 additions & 0 deletions contracts/credential_registry/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#![no_std]

use soroban_sdk::{
contract, contractevent, contractimpl, symbol_short, Address, Bytes, BytesN, Env,
};

#[contractevent]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Crediss {
pub credential_hash: BytesN<32>,
pub issuer_did: Bytes,
pub subject_did: Bytes,
pub metadata_ptr: Bytes,
pub expires_at: i128,
}

#[contractevent]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Credrev {
pub credential_hash: BytesN<32>,
pub issuer_did: Bytes,
pub subject_did: Bytes,
}
#[contract]
pub struct CredentialRegistryContract;

#[derive(Clone)]
pub enum CredentialStatus {
Active,
Revoked,
Expired,
}

#[contractimpl]
impl CredentialRegistryContract {
// Issue a credential by storing its hash and metadata pointer.
// `credential_hash` should be a deterministic hash (e.g., SHA-256) of the full VC JSON.
pub fn issue_credential(
env: &Env,
credential_hash: BytesN<32>,
issuer: Address,
issuer_did: Bytes,
subject_did: Bytes,
metadata_ptr: Bytes,
expires_at: i128,
) {
issuer.require_auth();
let key = (symbol_short!("cred"), credential_hash.clone());
assert!(
!env.storage().persistent().has(&key),
"credential already exists"
);
let record: (Bytes, Bytes, Bytes, i128, i32) = (
issuer_did.clone(),
subject_did.clone(),
metadata_ptr.clone(),
expires_at,
0i32,
);
env.storage().persistent().set(&key, &record);
Crediss {
credential_hash,
issuer_did,
subject_did,
metadata_ptr,
expires_at,
}
.publish(env);
}

// Revoke a credential. Caller must be issuer (signed address)
pub fn revoke_credential(env: &Env, credential_hash: BytesN<32>, issuer: Address) {
issuer.require_auth();
let key = (symbol_short!("cred"), credential_hash.clone());
let opt: Option<(Bytes, Bytes, Bytes, i128, i32)> = env.storage().persistent().get(&key);
match opt {
Some((issuer_did, subject_did, metadata_ptr, expires_at, _status)) => {
let record: (Bytes, Bytes, Bytes, i128, i32) = (
issuer_did.clone(),
subject_did.clone(),
metadata_ptr.clone(),
expires_at,
1i32,
);
env.storage().persistent().set(&key, &record);
Credrev {
credential_hash,
issuer_did,
subject_did,
}
.publish(env);
}
None => panic!("credential not found"),
}
}

// Get credential record: returns (issuer_did, subject_did, metadata_ptr, expires_at, status)
pub fn get_credential(
env: &Env,
credential_hash: BytesN<32>,
) -> Option<(Bytes, Bytes, Bytes, i128, i32)> {
let key = (symbol_short!("cred"), credential_hash.clone());
env.storage().persistent().get(&key)
}

// Check if credential is active (not revoked and not expired)
pub fn is_active(env: &Env, credential_hash: BytesN<32>, now_ts: i128) -> bool {
match Self::get_credential(env, credential_hash.clone()) {
Some((_issuer, _subject, _meta, expires_at, status)) => {
if status == 1 {
return false;
}
if expires_at > 0 && now_ts > expires_at {
return false;
}
true
}
None => false,
}
}
}

fn main() {}
22 changes: 22 additions & 0 deletions contracts/identity_registry/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "identity-registry"
version = "0.1.0"
edition.workspace = true
repository.workspace = true
license.workspace = true

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
soroban-sdk.workspace = true

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }

[features]
testutils = ["soroban-sdk/testutils"]

[lints]
workspace = true

Loading