[Docs] vc-revocation-registry README and API documentation#14
Conversation
|
@Just-Bamford Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
📝 WalkthroughWalkthroughAdds a new Changesvc-revocation-registry contract
Sequence Diagram(s)sequenceDiagram
participant AdminCaller as Admin
participant Contract as VcRevocationRegistryContract
participant InstanceStorage as Soroban instance storage
participant PersistentStorage as Soroban persistent storage
participant InitializedEvent as Initialized
participant CredentialRevokedEvent as CredentialRevoked
participant CredentialUnrevokedEvent as CredentialUnrevoked
AdminCaller->>Contract: initialize(admin)
Contract->>InstanceStorage: write_admin(admin)
Contract->>InstanceStorage: extend_instance_ttl()
Contract->>InitializedEvent: publish(admin)
AdminCaller->>Contract: revoke(issuer, credential_id)
Contract->>InstanceStorage: read_admin()
Contract->>PersistentStorage: write_revocation(issuer, credential_id, RevocationRecord)
Contract->>PersistentStorage: extend_revocation_ttl()
Contract->>CredentialRevokedEvent: publish(issuer, credential_id)
AdminCaller->>Contract: unrevoke(issuer, credential_id)
Contract->>InstanceStorage: read_admin()
Contract->>PersistentStorage: remove_revocation(issuer, credential_id)
Contract->>PersistentStorage: extend_revocation_ttl()
Contract->>CredentialUnrevokedEvent: publish(issuer, credential_id)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
contracts/vc-revocation-registry/src/test.rs (1)
34-37: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAssert the exact
ContractErrorvariants in these failure tests.
catch_unwind(...).is_err()accepts any panic, so these cases can still pass on the wrong failure mode. Use the client’s error-returning helper (or equivalent) and check the expected variants:AlreadyInitialized,CredentialAlreadyExists,CredentialNotFound,NotInitialized, andInvalidCredentialId.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@contracts/vc-revocation-registry/src/test.rs` around lines 34 - 37, The failure tests are too loose because catch_unwind(...).is_err() accepts any panic, so update the checks in the relevant client test cases to assert the exact ContractError variant returned by the error path instead. Use the client’s error-returning helper or equivalent around initialize and the other affected methods, and match the expected variants explicitly: AlreadyInitialized, CredentialAlreadyExists, CredentialNotFound, NotInitialized, and InvalidCredentialId.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@contracts/vc-revocation-registry/Cargo.toml`:
- Around line 8-9: The contract crate type for vc-revocation-registry is
incomplete: [lib] only declares rlib, so the Soroban build will not produce the
WASM artifact. Update the crate-type in Cargo.toml to include cdylib alongside
rlib, keeping the change scoped to the contract library configuration so stellar
contract build can emit the expected contract binary.
In `@contracts/vc-revocation-registry/src/contract.rs`:
- Around line 68-69: The revocation flow in contract::write_revocation currently
extends only the instance TTL, so the persistent record keyed by (issuer,
credential_id) can still expire. Update the write and successful read paths in
the contract logic to also call the storage TTL helper for DataKey::Revocation
after storage::write_revocation and after any successful revocation lookup, so
the revocation entry itself is kept alive alongside the instance.
In `@contracts/vc-revocation-registry/src/storage.rs`:
- Around line 94-98: The revocation TTL helper is missing the storage key
required by Persistent::extend_ttl, so update extend_revocation_ttl to accept
DataKey::Revocation(issuer, credential_id) and pass it through when extending
the record’s TTL. Then update the revoke and unrevoke call sites to supply that
key, and make sure unrevoke stops extending TTL after it removes the revocation
record.
In `@contracts/vc-revocation-registry/src/test.rs`:
- Around line 7-12: The test setup currently uses a random Address for
contract_id, so VcRevocationRegistryContractClient::new is not bound to a
deployed contract. Update setup() to register VcRevocationRegistryContract on
the Env and use the returned address for contract_id, keeping the existing
admin/address/auth setup intact.
---
Nitpick comments:
In `@contracts/vc-revocation-registry/src/test.rs`:
- Around line 34-37: The failure tests are too loose because
catch_unwind(...).is_err() accepts any panic, so update the checks in the
relevant client test cases to assert the exact ContractError variant returned by
the error path instead. Use the client’s error-returning helper or equivalent
around initialize and the other affected methods, and match the expected
variants explicitly: AlreadyInitialized, CredentialAlreadyExists,
CredentialNotFound, NotInitialized, and InvalidCredentialId.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c83df290-4bd3-4c1e-911e-253648cd7d14
📒 Files selected for processing (10)
Cargo.tomlREADME.mdcontracts/vc-revocation-registry/Cargo.tomlcontracts/vc-revocation-registry/README.mdcontracts/vc-revocation-registry/src/contract.rscontracts/vc-revocation-registry/src/error.rscontracts/vc-revocation-registry/src/events.rscontracts/vc-revocation-registry/src/lib.rscontracts/vc-revocation-registry/src/storage.rscontracts/vc-revocation-registry/src/test.rs
| [lib] | ||
| crate-type = ["rlib"] |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the manifest and nearby docs for the revocation registry contract.
git ls-files contracts/vc-revocation-registry
printf '\n--- Cargo.toml ---\n'
cat -n contracts/vc-revocation-registry/Cargo.toml
printf '\n--- README / docs references ---\n'
rg -n "stellar contract build|cdylib|crate-type|wasm|soroban" contracts/vc-revocation-registry -SRepository: ACTA-Team/contracts-acta-spikes
Length of output: 2446
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n--- Contract README build section ---\n'
sed -n '88,110p' contracts/vc-revocation-registry/README.md
printf '\n--- Contract entry points ---\n'
cat -n contracts/vc-revocation-registry/src/lib.rs
printf '\n--- Contract implementation excerpt ---\n'
sed -n '1,220p' contracts/vc-revocation-registry/src/contract.rs
printf '\n--- Other contract manifests in repo ---\n'
fd -a 'Cargo.toml' contracts | while read -r f; do
if rg -n '^\[lib\]|crate-type|soroban-sdk' "$f" >/dev/null; then
printf '\n### %s ###\n' "$f"
rg -n '^\[lib\]|crate-type|soroban-sdk' "$f"
fi
done
printf '\n--- Workspace manifest references ---\n'
rg -n 'vc-revocation-registry|stellar contract build|soroban' Cargo.toml contracts -SRepository: ACTA-Team/contracts-acta-spikes
Length of output: 11524
Add cdylib to the contract crate type.
contracts/vc-revocation-registry/Cargo.toml only declares rlib, so this Soroban contract won’t emit the WASM artifact expected by stellar contract build.
Suggested fix
[lib]
-crate-type = ["rlib"]
+crate-type = ["rlib", "cdylib"]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| [lib] | |
| crate-type = ["rlib"] | |
| [lib] | |
| crate-type = ["rlib", "cdylib"] |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@contracts/vc-revocation-registry/Cargo.toml` around lines 8 - 9, The contract
crate type for vc-revocation-registry is incomplete: [lib] only declares rlib,
so the Soroban build will not produce the WASM artifact. Update the crate-type
in Cargo.toml to include cdylib alongside rlib, keeping the change scoped to the
contract library configuration so stellar contract build can emit the expected
contract binary.
| storage::write_revocation(&e, &issuer, &credential_id, &record); | ||
| storage::extend_revocation_ttl(&e); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the TTL helper targets the same persistent revocation key used by reads/writes.
rg -nP 'fn\s+extend_revocation_ttl|extend_ttl|has_revocation|read_revocation|write_revocation' contracts/vc-revocation-registry/src/storage.rs contracts/vc-revocation-registry/src/contract.rsRepository: ACTA-Team/contracts-acta-spikes
Length of output: 1458
🏁 Script executed:
ast-grep outline contracts/vc-revocation-registry/src/storage.rs --view expanded && printf '\n---\n' && ast-grep outline contracts/vc-revocation-registry/src/contract.rs --view expandedRepository: ACTA-Team/contracts-acta-spikes
Length of output: 1741
🏁 Script executed:
sed -n '1,140p' contracts/vc-revocation-registry/src/storage.rs && printf '\n--- contract ---\n' && sed -n '55,135p' contracts/vc-revocation-registry/src/contract.rsRepository: ACTA-Team/contracts-acta-spikes
Length of output: 6665
🌐 Web query:
Soroban Storage persistent extend_ttl semantics per key or per contract revocation record
💡 Result:
In Soroban, persistent storage TTL extension semantics operate on a per-key basis [1]. Each individual entry in persistent storage is tracked as a distinct ledger entry with its own Time-To-Live (TTL), and consequently, each key must be extended individually using the extend_ttl method [2][1]. Key points regarding these semantics include: Per-Key Granularity: For Persistent and Temporary storage, every data entry has its own independent TTL. Extending the TTL for one key does not affect other keys or the contract instance itself [1]. Mechanism: The extend_ttl(key, threshold, extend_to) function (available in the Soroban SDK) performs a conditional update [3][4]. It extends the TTL of the specific entry to extend_to ledgers only if the entry's current TTL is below the specified threshold [3][2]. If the current TTL is already above the threshold, the operation is a no-op [2][1]. Contrast with Instance Storage: This contrasts with Instance storage, where all entries are stored in a single ledger entry and share a common TTL [1]. Invoking extend_ttl on the instance storage interface extends the TTL for all instance-level data, the contract instance entry, and the contract code simultaneously [1]. Revocation Records: Soroban does not have a concept of "revocation records" that participate in TTL extensions in the manner suggested. Persistent entries that expire are removed from the ledger and moved to off-chain storage (ESS), from which they can be restored via specific operations [5][6]. These restored entries cannot be recreated as new entries [5]. Contract developers are advised not to rely on TTL exhaustion or extensions for application safety, such as enforcing nonces or access control, as any user can trigger TTL extensions on entries without authorization [7].
Citations:
- 1: https://developers.stellar.org/docs/learn/fundamentals/contract-development/storage/state-archival
- 2: https://developers.stellar.org/docs/build/guides/dapps/state-archival
- 3: https://docs.rs/soroban-sdk/latest/soroban_sdk/storage/struct.Persistent.html
- 4: https://docs.rs/soroban-sdk/latest/src/soroban_sdk/storage.rs.html
- 5: https://docs.rs/soroban-sdk/latest/soroban_sdk/storage/struct.Storage.html
- 6: https://developers.stellar.org/docs/build/guides/storage/choosing-the-right-storage
- 7: https://developers.stellar.org/docs/learn/fundamentals/contract-development/storage/persisting-data
🏁 Script executed:
rg -n "extend_ttl\\(|persistent\\(\\)\\.extend_ttl|instance\\(\\)\\.extend_ttl|has\\(|get\\(" contracts/vc-revocation-registry/srcRepository: ACTA-Team/contracts-acta-spikes
Length of output: 870
🏁 Script executed:
rg -n "persistent\\(\\)\\.extend_ttl|instance\\(\\)\\.extend_ttl|extend_ttl\\(" -g '!target' -g '!node_modules' .Repository: ACTA-Team/contracts-acta-spikes
Length of output: 663
🏁 Script executed:
rg -n '"soroban-sdk"|soroban-sdk' Cargo.lock contracts/vc-revocation-registry/Cargo.toml contracts/vc-issuer-registry/Cargo.tomlRepository: ACTA-Team/contracts-acta-spikes
Length of output: 719
Extend the revocation entry TTL, not just the instance TTL. The persistent revocation record is keyed by (issuer, credential_id), so it needs its own extend_ttl call on DataKey::Revocation(...) after writes and successful reads; otherwise an expired record makes a revoked credential look unrevoked.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@contracts/vc-revocation-registry/src/contract.rs` around lines 68 - 69, The
revocation flow in contract::write_revocation currently extends only the
instance TTL, so the persistent record keyed by (issuer, credential_id) can
still expire. Update the write and successful read paths in the contract logic
to also call the storage TTL helper for DataKey::Revocation after
storage::write_revocation and after any successful revocation lookup, so the
revocation entry itself is kept alive alongside the instance.
| /// Extend persistent storage TTL for a revocation record. | ||
| pub fn extend_revocation_ttl(e: &Env) { | ||
| e.storage() | ||
| .persistent() | ||
| .extend_ttl(PERSISTENT_TTL_THRESHOLD, PERSISTENT_TTL_EXTEND_TO); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== files ==\n'
git ls-files contracts/vc-revocation-registry/src/{storage.rs,contract.rs}
printf '\n== storage.rs (around target lines) ==\n'
sed -n '1,220p' contracts/vc-revocation-registry/src/storage.rs
printf '\n== contract.rs (search TTL helpers and revocation paths) ==\n'
rg -n "extend_revocation_ttl|extend_ttl|revoke|unrevoke|get_revocation|DataKey::Revocation" contracts/vc-revocation-registry/src/contract.rs contracts/vc-revocation-registry/src/storage.rs
printf '\n== relevant contract.rs excerpts ==\n'
sed -n '1,260p' contracts/vc-revocation-registry/src/contract.rsRepository: ACTA-Team/contracts-acta-spikes
Length of output: 13894
🌐 Web query:
soroban_sdk Persistent extend_ttl signature threshold extend_to key docs
💡 Result:
In the Soroban SDK, the extend_ttl method is used to manage the Time-To-Live (TTL) of storage entries to prevent them from becoming archived [1][2]. It is available for Persistent, Instance, and Temporary storage types [1][2]. The method signature for Persistent storage is: pub fn extend_ttl(&self, key: &K, threshold: u32, extend_to: u32) [3] Parameters: - key: The key associated with the persistent data [3]. - threshold: The minimum remaining TTL (in ledgers) below which the extension will be triggered [3][1]. If the current TTL is already equal to or greater than this threshold, the operation is a no-op [1][4]. - extend_to: The new target TTL (in ledgers) that the data will be extended to if the threshold condition is met [3][1]. Note that there is no "signature threshold" parameter in this method; the term threshold refers specifically to the ledger count used to determine whether an extension should occur [3][1][4]. There is also an extend_ttl_with_limits variant for more granular control, which allows you to specify extend_to, min_extension, and max_extension values to further constrain how the TTL is updated [3][5].
Citations:
- 1: https://developers.stellar.org/docs/build/guides/dapps/state-archival
- 2: https://developers.stellar.org/docs/build/smart-contracts/getting-started/storing-data
- 3: https://docs.rs/soroban-sdk/latest/soroban_sdk/storage/struct.Persistent.html
- 4: https://developers.stellar.org/docs/build/guides/conventions/extending-wasm-ttl
- 5: https://docs.rs/soroban-sdk/latest/soroban%5Fsdk/storage/struct.Instance.html
Pass the revocation key into extend_ttl. Persistent::extend_ttl requires the storage key, so extend_revocation_ttl needs DataKey::Revocation(issuer, credential_id). Update the revoke/unrevoke call sites accordingly; unrevoke should not extend TTL after removing the record.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@contracts/vc-revocation-registry/src/storage.rs` around lines 94 - 98, The
revocation TTL helper is missing the storage key required by
Persistent::extend_ttl, so update extend_revocation_ttl to accept
DataKey::Revocation(issuer, credential_id) and pass it through when extending
the record’s TTL. Then update the revoke and unrevoke call sites to supply that
key, and make sure unrevoke stops extending TTL after it removes the revocation
record.
| fn setup() -> (Env, Address, Address) { | ||
| let e = Env::default(); | ||
| let admin = Address::random(&e); | ||
| let contract_id = Address::random(&e); | ||
| e.mock_all_auths(); | ||
| (e, contract_id, admin) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the repository's Soroban SDK version and registration pattern used by other tests.
rg -nP '\bregister(_contract)?\s*\(|VcRevocationRegistryContractClient::new|mock_all_auths' contracts -g '*.rs'Repository: ACTA-Team/contracts-acta-spikes
Length of output: 2257
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- contracts/vc-revocation-registry/src/test.rs (1-30) ---'
sed -n '1,30p' contracts/vc-revocation-registry/src/test.rs
echo
echo '--- contracts/vc-issuer-registry/src/test.rs (1-25) ---'
sed -n '1,25p' contracts/vc-issuer-registry/src/test.rsRepository: ACTA-Team/contracts-acta-spikes
Length of output: 1961
Register the contract in setup().
contract_id should come from e.register(VcRevocationRegistryContract, ()) so VcRevocationRegistryContractClient::new points at a deployed test contract instead of a random address.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@contracts/vc-revocation-registry/src/test.rs` around lines 7 - 12, The test
setup currently uses a random Address for contract_id, so
VcRevocationRegistryContractClient::new is not bound to a deployed contract.
Update setup() to register VcRevocationRegistryContract on the Env and use the
returned address for contract_id, keeping the existing admin/address/auth setup
intact.
Overview
Implements the complete
vc-revocation-registrycontract with comprehensive README and inline API documentation. This contract provides on-chain credential revocation status tracking, separating revocation governance from VC storage logic.this pr Closes #5
Changes
New Contract:
vc-revocation-registryImplements a Soroban smart contract for managing revocation status of Verifiable Credentials on-chain. Follows the architectural patterns established by
vc-issuer-registry.Contract Structure
Entry Points: 7 public functions
initialize(admin)- One-time initialization with admin setuprevoke(issuer, credential_id)- Mark credential as revoked with timestampunrevoke(issuer, credential_id)- Remove credential from revocation registryis_revoked(issuer, credential_id)- Query revocation status (read-only)get_revocation(issuer, credential_id)- Get full revocation record (read-only)admin()- Query current admin address (read-only)version()- Query contract version (read-only)Storage: Instance + Persistent
Error Codes: 5 variants
AlreadyInitialized(1)CredentialNotFound(2)CredentialAlreadyExists(3)NotInitialized(4)InvalidCredentialId(5)Events: 3 contract events
Initialized- emitted on initializationCredentialRevoked- emitted when credential is revokedCredentialUnrevoked- emitted when credential is unrevokedDocumentation
error.rslib.rsRevocationRecordstruct definitionrevokevsunrevokebehaviorTesting
Root README Update
Updated
README.mdto list both contracts:vc-issuer-registry- Allowlist and metadata registry for VC issuersvc-revocation-registry- Revocation status tracking for credentialsWorkspace Configuration
Added
vc-revocation-registryto workspace members inCargo.tomlAcceptance Criteria Met
vc-issuer-registrystructure with all required sections/// doccomments with parameter and error documentation//!documentation at top oflib.rsTechnical Details
How to Test