This document provides a structured threat model covering trust boundaries, privileged actors, cross-contract calls, upgrade/admin risk, denial of service, TTL expiry, and backend assumptions for the EarnProof Soroban contracts.
Intended audience: Security auditors, maintainers, backend developers, and deployment engineers.
Document status: Living document; updated as contracts evolve.
Last reviewed: 2026-08-26
- Assets and Trust Model
- Actors and Roles
- Trust Assumptions
- Entry Points and Attack Surface
- Cross-Contract Boundaries
- Threat Analysis
- Privacy Analysis
- Mainnet Release Gates
- Security Review Checklist
- References
-
Issuer Trust State
- Issuer approval status (Active/Suspended/Revoked)
- Issuer-to-address mappings
- Issuer metadata integrity
-
Proof Commitment Registry
- Proof existence and validity status
- Revocation state
- Expiration enforcement
-
Protocol Configuration
- Admin control
- Schema version approvals
- Global pause state
-
User Privacy
- No on-chain leakage of exact income amounts
- No storage of personal identifying information
- No raw payment history on-chain
- Funds/Assets: Contracts do not custody XLM or other Stellar assets
- Computation: Contracts do not perform income calculations or aggregate financial data
- Key Material: Contracts do not store private keys or signing secrets
| Actor | Scope | Privileges | Attack Impact if Compromised |
|---|---|---|---|
| Protocol Admin | protocol-config |
- Change admin - Pause/unpause protocol - Approve/deprecate schema versions |
Critical: Can halt all proof registrations, manipulate schema approvals, transfer admin control |
| Issuer Registry Admin | issuer-registry |
- Register new issuers - Update issuer metadata - Suspend/reactivate/revoke issuers - Rotate issuer addresses |
High: Can register malicious issuers, revoke legitimate issuers, disrupt issuer operations |
| Proof Registry Admin | proof-registry |
- Revoke any proof (admin revocation) | Medium: Can invalidate proofs but cannot create false proofs or modify existing proof data |
| Actor | Privileges | Constraints |
|---|---|---|
| Active Issuer | - Register proofs under their own address - Revoke proofs they registered |
- Cannot register proofs while suspended or revoked - Cannot revoke proofs registered by other issuers - Must be marked Active in issuer-registry |
| Public Readers | - Read issuer status - Check proof validity - Query protocol configuration |
- No write access - No privileged information access |
| Backend Indexers | - Monitor events - Query contract state |
- Read-only operations - Must properly handle TTL expiration |
-
Admin Key Security
- Protocol admin and registry admin keys must be secured
- Assumed: Multi-sig or hardware wallet custody for mainnet
- Risk: Single compromised admin key = full contract control
-
Backend Hash Integrity
- Backend must correctly hash identifiers before contract calls
- Assumed: Backend hashing implementation matches contract expectations
- Risk: Incorrect hashing = inability to verify proofs
-
Issuer Vetting
- Registry admin validates issuer legitimacy before registration
- Assumed: Off-chain due diligence process exists
- Risk: Malicious issuer registration = fraudulent proof issuance
-
Cross-Contract Reference Stability
proof-registryinstance storage points to correctissuer-registryandprotocol-configaddresses- Assumed: Deployment manifest validates contract addresses
- Risk: Incorrect reference = bypassed validation checks
-
Stellar Network Availability
- Horizon/RPC availability for TTL extension and state reads
- Assumed: Network liveness for time-sensitive operations
- Risk: Network downtime = inability to extend TTLs before expiration
| Boundary | Trusted Side | Untrusted Side | Enforcement Mechanism |
|---|---|---|---|
| Admin operations | Admin address | All other addresses | require_auth(&admin) |
| Issuer registration | Issuer Registry Admin | All callers | require_auth(&admin) |
| Proof registration | Active issuer address | Suspended/revoked issuers, non-issuers | is_active_address() check + require_auth(&issuer_address) |
| Schema validation | Approved versions | Unapproved versions | is_schema_version_approved() check |
| Protocol pause | Paused = no registrations | Active = registrations allowed | is_paused() check |
| Cross-contract calls | Contract instance storage references | External caller-provided addresses | Stored references set at initialize |
| Entry Point | Authorization | State Mutation | Attack Vectors |
|---|---|---|---|
initialize(admin) |
Caller becomes admin | Sets admin, pause=false, version=1 | Initialization race: First caller wins; deploy scripts must call immediately |
set_admin(new_admin) |
Current admin only | Transfers admin control | Admin takeover: Compromised admin can transfer control |
pause() |
Admin only | Sets paused=true | DoS: Malicious admin halts all proof registrations |
unpause() |
Admin only | Sets paused=false | Premature unblocking: Admin unpauses during incident response |
approve_schema_version(v) |
Admin only | Marks version approved | Unapproved schema injection: Malicious admin approves vulnerable schema |
deprecate_schema_version(v) |
Admin only | Marks version deprecated | Schema revocation DoS: Admin deprecates active schemas |
Read-only (no auth): get_admin, is_paused, get_config_version, is_schema_version_approved
| Entry Point | Authorization | State Mutation | Attack Vectors |
|---|---|---|---|
initialize(admin) |
Caller becomes admin | Sets admin | Initialization race: First caller wins |
register_issuer(...) |
Admin only | Creates issuer record | Malicious issuer registration: Admin registers attacker-controlled issuer |
update_issuer(...) |
Admin only | Updates metadata hash | Metadata manipulation: Admin updates metadata to point to malicious data |
suspend_issuer(...) |
Admin only | Sets status=Suspended | Issuer DoS: Admin suspends legitimate issuer |
reactivate_issuer(...) |
Admin only | Sets status=Active | Malicious reactivation: Admin reactivates compromised issuer |
revoke_issuer(...) |
Admin only | Sets status=Revoked (terminal) | Permanent issuer DoS: Admin revokes legitimate issuer |
rotate_issuer_address(...) |
Admin only | Updates issuer address | Address hijacking: Admin rotates address to attacker-controlled wallet |
Read-only (no auth): get_admin, get_issuer, get_issuer_by_address, is_active_issuer, is_active_address
| Entry Point | Authorization | State Mutation | Attack Vectors |
|---|---|---|---|
initialize(admin, issuer_registry, protocol_config) |
Caller becomes admin | Sets admin + cross-contract refs | Initialization race: First caller wins Incorrect references: Malicious contracts referenced |
register_proof(...) |
Issuer address only | Creates proof record | Duplicate registration: Prevented by contract Expired proof: Prevented by expires_at checkInactive issuer bypass: Prevented by is_active_address checkPaused protocol bypass: Prevented by is_paused check |
revoke_proof(proof_id) |
Issuer address that registered proof | Sets status=Revoked | Unauthorized revocation: Prevented by auth check Double revocation: Prevented by contract |
admin_revoke_proof(proof_id) |
Admin only | Sets status=Revoked | Admin abuse: Admin can revoke any proof |
Read-only (no auth): get_admin, get_issuer_registry, get_protocol_config, get_proof, is_valid_proof, is_revoked
proof-registry.register_proof()
├─> protocol-config.is_paused() [external call]
├─> protocol-config.is_schema_version_approved(version) [external call]
└─> issuer-registry.is_active_address(issuer_address) [external call]
-
Reference Immutability
- Contract addresses stored in
proof-registryinstance storage - Set once at initialization, not modifiable
- Control: Deployment validation required
- Contract addresses stored in
-
No Reentrancy
- Soroban does not support reentrancy during cross-contract calls
- State changes committed before external calls return
- Control: Soroban runtime enforcement
-
Call Depth Limits
- Soroban enforces maximum call depth
- Current call chain depth: 3 (proof-registry → issuer-registry/protocol-config)
- Control: Well below Soroban limits
| Threat | Impact | Implemented Control | Test Coverage |
|---|---|---|---|
| Malicious contract reference | Bypass all validation checks | Deployment manifest validation | scripts/verify-manifest.tests.ps1 |
| Contract address typo | Calls fail or hit wrong contract | Pre-deployment address verification | Deployment script validation |
| Referenced contract upgrade | Behavior changes post-deployment | Immutable references + upgrade coordination | Manual review required |
| TTL expiry of referenced contract | Cross-contract calls fail | Backend must extend TTLs | Monitoring (not automated) |
Description: Attacker attempts to execute privileged operations without proper authorization.
Attack Vectors:
- Forge admin signatures
- Exploit missing
require_authcalls - Replay valid authorization from different context
Implemented Controls:
- ✅ Every state-mutating function calls
require_auth(&address) - ✅ Soroban SDK validates signatures cryptographically
- ✅ Authorization context tied to current invocation (no replay)
Test Coverage:
- ✅ All tests use
env.mock_all_auths()to exercise auth paths - ✅ Contracts compile with auth checks (not compile-gated)
Residual Risk: Low — Soroban SDK handles signature validation
Open Issues: None
Status: ✅ Mitigated
Description: Attacker attempts to register the same issuer or proof multiple times.
Attack Vectors:
- Submit identical issuer registration twice
- Submit identical proof registration twice
- Cause state inconsistency between forward/reverse indexes
Implemented Controls:
- ✅
issuer-registry: Checkshas(&Issuer(issuer_id_hash))andhas(&AddressIssuer(address))before registration - ✅
proof-registry: Checkshas(&Proof(proof_id_hash))before registration - ✅ Rejection returns error; no partial state written
Test Coverage:
- ✅
issuer-registry:rejects_duplicate_issuer_id - ✅
proof-registry:rejects_duplicate_proof_id
Residual Risk: Low — Duplicate checks enforced
Open Issues: None
Status: ✅ Mitigated
Description: Compromised or malicious issuer attempts to issue fraudulent proofs or manipulate proof state.
Attack Vectors:
- Register proofs with false income data
- Revoke legitimate proofs from other issuers
- Continue issuing proofs after suspension
Implemented Controls:
- ✅ Proof registration requires
is_active_address(&issuer_address)check - ✅ Revocation requires auth from issuer who registered the proof
- ✅ Suspended/revoked issuers cannot register new proofs
⚠️ Gap: Contracts cannot validate proof content; off-chain verification required
Test Coverage:
- ✅
proof-registry:rejects_inactive_issuer_address - ✅ Authorization tests cover issuer-initiated revocation
Residual Risk: Medium — Malicious issuer can issue proofs with false claims until suspended
Accepted Risk: Proof content validation is off-chain; verifiers must check issuer trust status
Open Issues: #21 — Backend issuer monitoring
Status:
Description: Attacker gains control of protocol admin, issuer registry admin, or proof registry admin key.
Attack Vectors:
- Phishing admin private key
- Compromising deployment machine
- Social engineering admin key holder
Impact:
- Protocol admin: Pause protocol, manipulate schema approvals, transfer admin
- Issuer registry admin: Register malicious issuers, revoke legitimate issuers
- Proof registry admin: Revoke any proof
Implemented Controls:
⚠️ Testnet: Single-key admin (acceptable for testnet)- ❌ Mainnet: Multi-sig or hardware wallet custody required (not yet implemented)
Test Coverage: N/A (operational control)
Residual Risk: Critical for mainnet — Single compromised key = full control
Required Control: Multi-sig admin (e.g., 3-of-5) or hardware wallet custody
Open Issues: #21 — Mainnet admin custody policy
Status: ❌ Mainnet blocker — Multi-sig required before mainnet
Description: Malicious or compromised admin pauses protocol to prevent legitimate proof registrations.
Attack Vectors:
- Admin calls
pause()during normal operations - Admin refuses to call
unpause()during incident
Implemented Controls:
- ✅ Only admin can pause/unpause
- ✅ Pause state queryable via
is_paused() ⚠️ Gap: No time-lock or multi-sig requirement for pause
Test Coverage:
- ✅
proof-registry:rejects_registration_when_protocol_is_paused - ✅
protocol-config:pause_and_unpause_bump_config_version
Residual Risk: Medium — Admin can DoS proof registration
Accepted Risk: Intentional design; admin must be trusted
Mitigation: Mainnet admin should use multi-sig or DAO governance
Open Issues: #21 — Governance model for pause
Status:
Description: Referenced contracts (issuer-registry, protocol-config) become unavailable due to TTL expiry or upgrade.
Attack Vectors:
- Instance storage of referenced contract expires
- Referenced contract upgraded to incompatible version
- Referenced contract address incorrect at deployment
Implemented Controls:
- ✅ Contract addresses set once at
proof-registry.initialize() - ✅ Deployment scripts validate contract addresses before initialization
⚠️ Gap: No automated TTL extension for referenced contracts⚠️ Gap: No upgrade coordination mechanism
Test Coverage:
- ✅
scripts/verify-manifest.tests.ps1validates deployment manifest - ❌ No automated tests for TTL expiry scenarios
Residual Risk: Medium — TTL expiry would halt proof registration
Required Control: Backend monitoring + manual TTL extension for referenced contracts
Open Issues: #21 — TTL monitoring automation
Status:
Description: Contract storage entries expire due to insufficient TTL extension, causing data loss or availability issues.
Attack Vectors:
- Backend fails to extend TTL for frequently accessed entries
- Network downtime prevents TTL extension before expiry
- Low-activity issuers/proofs expire due to infrequent access
Implemented Controls:
- ✅ TTL constants defined:
TTL_THRESHOLD_LEDGERS = 50,000,TTL_EXTEND_TO_LEDGERS = 500,000 - ✅ Read operations (
get_issuer,get_proof) extend TTL ⚠️ Gap: No automated TTL monitoring⚠️ Gap: Expired entries require Soroban state archival recovery
Test Coverage:
- ✅
issuer-registry:extends_issuer_storage_ttl - ✅
proof-registry:extends_proof_storage_ttl
Residual Risk: Medium — Low-activity entries may expire
Required Control: Backend TTL monitoring + periodic reads to extend TTL
Open Issues: #21 — TTL monitoring service
Status:
Description: Attacker attempts to force invalid issuer status transitions or proof state changes.
Attack Vectors:
- Reactivate revoked issuer
- Update metadata of revoked issuer
- Rotate address of revoked issuer
- Double-revoke proof
Implemented Controls:
- ✅ Revoked issuer operations blocked:
if record.status == IssuerStatus::Revoked { return Err(...) } - ✅ Invalid status transitions rejected:
if record.status == Revoked && new_status != Revoked { return Err(InvalidTransition) } - ✅ Double-revoke proof blocked:
if record.status == ProofStatus::Revoked { return Err(ProofAlreadyRevoked) }
Test Coverage:
- ✅
issuer-registry:revoked_issuer_cannot_be_reactivated,rotate_revoked_issuer_address_emits_no_event,update_revoked_issuer_emits_no_event - ✅ Tests assert typed error codes
Residual Risk: Low — State transition validation enforced
Open Issues: None
Status: ✅ Mitigated
Description: Verifier accepts proof after expiration timestamp.
Attack Vectors:
- Backend caches
is_valid_proofresult past expiration - Off-chain verifier ignores expiration check
- Time manipulation (not possible on Stellar)
Implemented Controls:
- ✅
is_valid_proofchecksenv.ledger().timestamp() <= record.expires_at - ✅ Expiration validation on every call (no caching in contract)
⚠️ Gap: Backend must not cache validity status across expiration boundary
Test Coverage:
- ✅
proof-registry:rejects_expired_proof(registration time check) ⚠️ Gap: No test for post-registration expiration validation
Residual Risk: Low — Contract enforces expiration; backend must respect it
Required Control: Backend must query is_valid_proof at verification time, not cache
Open Issues: None (backend implementation responsibility)
Status: ✅ Mitigated (assuming backend compliance)
Description: Off-chain indexer or event consumer is deceived by fake events or misses legitimate events.
Attack Vectors:
- Attacker submits transaction with event-like data (not possible; events are contract-emitted only)
- Indexer misses event due to network partition
- Indexer processes events out of order
Implemented Controls:
- ✅ Events emitted via
#[contractevent]macro (Soroban enforced) - ✅ Only successful transactions emit events (Soroban enforced)
- ✅ Failed calls emit no success events
⚠️ Gap: Indexer must handle network partitions and reorgs
Test Coverage:
- ✅ Event emission tests verify exactly one event per successful mutation
- ✅ Failed mutations emit zero success events
Residual Risk: Low — Event integrity guaranteed by Soroban; indexer reliability is operational concern
Required Control: Indexer must implement retry logic and reorg handling
Open Issues: None (backend implementation responsibility)
Status: ✅ Mitigated (contract-side)
Description: Attacker registers proof with unapproved or deprecated schema version.
Attack Vectors:
- Submit proof with version = 0
- Submit proof with deprecated version
- Submit proof with never-approved version
Implemented Controls:
- ✅
register_proofchecksif schema_version == 0 { return Err(InvalidSchemaVersion) } - ✅
register_proofcallsprotocol_config.is_schema_version_approved(&schema_version) - ✅ Rejected if version not explicitly approved
Test Coverage:
- ✅
proof-registry:rejects_unapproved_schema_version - ✅
protocol-config:rejects_zero_schema_version
Residual Risk: Low — Schema approval enforced
Open Issues: None
Status: ✅ Mitigated
Description: Attacker compromises deployment process or build artifacts.
Attack Vectors:
- Inject malicious code during build
- Deploy incorrect contract addresses
- Tamper with deployment manifest
- Use compromised Rust toolchain or dependencies
Implemented Controls:
- ✅ Rust toolchain pinned:
rust-toolchain.toml - ✅
Cargo.lockcommitted to repository - ✅ Deployment manifest validation:
scripts/verify-manifest.ps1 ⚠️ Gap: No reproducible build verification⚠️ Gap: No multi-party deployment verification
Test Coverage:
- ✅
scripts/verify-manifest.tests.ps1validates manifest structure
Residual Risk: Medium — Single deployer can deploy malicious build
Required Control: Reproducible builds + multi-party verification for mainnet
Open Issues: #21 — Reproducible build process
Status: ❌ Mainnet blocker — Reproducible builds required
Description: Attacker floods contracts with low-value operations to exhaust resources or increase costs.
Attack Vectors:
- Register many low-activity issuers
- Register many short-lived proofs
- Repeatedly query contract state
Implemented Controls:
- ✅ Soroban resource limits (CPU, memory, ledger I/O)
- ✅ Transaction fees required for all operations
⚠️ Gap: No rate limiting at contract level⚠️ Gap: No minimum TTL or minimum expiration duration
Test Coverage: N/A (Soroban runtime enforced)
Residual Risk: Low — Griefing limited by transaction fees
Accepted Risk: Soroban fee market provides economic defense
Open Issues: None (monitoring recommended)
Status: ✅ Accepted risk — Fee market mitigation
EarnProof contracts must not store:
- Exact income amounts
- Exact payment amounts
- Raw transaction history
- Personal names
- Email addresses
- Employment documents
- Unencrypted personal information
| Data Item | Stored? | Privacy Risk | Control |
|---|---|---|---|
| Exact income | ❌ No | None | Backend hashes commitment |
| Proof ID | ❌ No (hash only) | None | SHA-256 hash prevents reversal |
| Commitment payload | ❌ No (hash only) | None | SHA-256 hash prevents reversal |
| Issuer internal ID | ❌ No (hash only) | None | SHA-256 hash prevents reversal |
| Issuer metadata | ❌ No (hash only) | None | SHA-256 hash; metadata hosted off-chain |
| Issuer Stellar address | ✅ Yes | Low | Public blockchain address; pseudonymous |
| Proof schema version | ✅ Yes | Low | Version number reveals credential type but not content |
| Proof expiration timestamp | ✅ Yes | Low | Timestamp range may infer employment period |
| Proof creation timestamp | ✅ Yes | Low | Registration time public information |
| Proof status (Active/Revoked) | ✅ Yes | Low | Status reveals proof lifecycle but not content |
- ✅ All identifiers hashed with SHA-256 before storage
- ✅ No personal identifying information in events
- ✅ No personal identifying information in storage keys
- ✅ Metadata and payloads stored off-chain
- ✅ Backend integration docs specify hashing rules
- ✅ Event fixture tests verify no personal data in event payloads
- ✅ Storage model documentation explicitly lists excluded data types
| Risk | Impact | Mitigation |
|---|---|---|
| Timing analysis | Proof registration patterns may reveal employment events | Low impact; timestamps are inherent to blockchain |
| Address correlation | Issuer addresses may be linkable to known entities | Accepted; public blockchain property |
| Schema version inference | Schema version reveals credential type | Low impact; necessary for validation |
Privacy Status: ✅ Compliant — No path stores exact income or personal identity
Before mainnet deployment, the following gates must be satisfied:
- ❌ Status: Not completed
- Requirement: Third-party security audit by Soroban-experienced firm
- Scope: All contracts, cross-contract interactions, authorization logic, TTL policies
- Deliverable: Audit report with findings resolution plan
- ❌ Status: Not implemented
- Requirement: Multi-sig (3-of-5 or stronger) or DAO governance for all admin keys
- Scope: Protocol admin, issuer registry admin, proof registry admin
- Deliverable: Multi-sig wallet deployment + key ceremony documentation
- ❌ Status: Not defined
- Requirement: Documented upgrade process including:
- Time-lock for admin changes
- Community notice period
- Rollback procedure
- Backwards compatibility requirements
- Deliverable: Upgrade policy document (docs/upgrade-policy.md)
- ❌ Status: Not implemented
- Requirement:
- TTL monitoring for all contracts
- Admin action alerting
- Issuer suspension monitoring
- Proof registration rate monitoring
- Incident response runbook
- Deliverable: Monitoring dashboard + runbook
- ❌ Status: Not completed
- Requirement:
- Worst-case TTL extension costs
- Resource usage profiling under load
- Fee estimation for all operations
- Deliverable: Resource analysis document
- ❌ Status: Not implemented
- Requirement:
- Deterministic build process
- Multi-party build verification
- WASM artifact hash publication
- Deliverable: Reproducible build instructions + artifact hashes
⚠️ Status: Partial- Requirement:
- Backend can successfully interact with deployed contracts
- Error handling tested for all failure modes
- Rate limiting and retry logic implemented
- Deliverable: Backend integration test suite passing against deployed contracts
- ❌ Status: Not completed
- Requirement:
- Mainnet deployment checklist completed
- All signers acknowledge deployment risks
- Emergency contact list established
- Deliverable: Signed deployment acknowledgement document
- ❌ Status: Not completed
- Requirement:
- Contract addresses published
- Initial state verified (admin addresses, pause state, etc.)
- Cross-contract references verified
- Sample transactions executed successfully
- Deliverable: Post-deployment verification report
- ❌ Status: Not defined
- Requirement:
- Document what can be rolled back (e.g., admin actions)
- Document what cannot be rolled back (e.g., registered proofs)
- Incident containment procedures
- Deliverable: Rollback limitations document
- ✅ Testnet default: Deployment scripts default to testnet
- ✅ Fail-closed: Scripts explicitly require
--network testnetflag ⚠️ Mainnet guard: Mainnet deployment should require multi-party approval
Use this checklist to verify security controls against the current codebase.
-
Every state-mutating function calls
require_auth- Reference:
contracts/*/src/lib.rs— search forrequire_auth - Test: All tests use
env.mock_all_auths()
- Reference:
-
Admin functions check admin address before execution
- Reference:
Self::get_admin()calls in admin functions - Test: Admin operations in test suite
- Reference:
-
Issuer operations validate issuer status
- Reference:
proof-registrychecksis_active_address - Test:
rejects_inactive_issuer_address
- Reference:
-
Issuer registration checks for duplicate ID and address
- Reference:
issuer-registry/src/lib.rs:register_issuer - Test:
rejects_duplicate_issuer_id
- Reference:
-
Proof registration checks for duplicate ID
- Reference:
proof-registry/src/lib.rs:register_proof - Test:
rejects_duplicate_proof_id
- Reference:
-
Revoked issuers cannot be updated, reactivated, or rotated
- Reference:
issuer-registry/src/lib.rs—if status == Revokedchecks - Test:
revoked_issuer_cannot_be_reactivated,update_revoked_issuer_emits_no_event,rotate_revoked_issuer_address_emits_no_event
- Reference:
-
Revoked proofs cannot be revoked again
- Reference:
proof-registry/src/lib.rs:set_revoked - Test: Double-revoke attempt in test suite (implicit)
- Reference:
-
Proof expiration must be in the future
- Reference:
proof-registry/src/lib.rs:register_proof - Test:
rejects_expired_proof
- Reference:
-
Schema version must be greater than zero
- Reference:
protocol-config/src/lib.rs:ensure_nonzero_version - Test:
rejects_zero_schema_version
- Reference:
-
Schema version must be approved
- Reference:
proof-registry/src/lib.rs:register_proof - Test:
rejects_unapproved_schema_version
- Reference:
-
Protocol pause state checked before proof registration
- Reference:
proof-registry/src/lib.rs:register_proof - Test:
rejects_registration_when_protocol_is_paused
- Reference:
-
Issuer activity checked before proof registration
- Reference:
proof-registry/src/lib.rs:register_proof - Test:
rejects_inactive_issuer_address
- Reference:
-
Contract references set at initialization and immutable
- Reference:
proof-registry/src/lib.rs:initialize - Test: Deployment manifest validation
- Reference:
-
Read operations extend persistent storage TTL
- Reference:
get_issuer,get_proofcallextend_ttl - Test:
extends_issuer_storage_ttl,extends_proof_storage_ttl
- Reference:
-
Write operations extend instance and persistent TTL
- Reference: Every mutation calls
extend_*_ttl - Test: TTL tests
- Reference: Every mutation calls
-
No exact income stored on-chain
- Reference:
packages/shared/src/lib.rs— only hash types - Test: Storage model documentation review
- Reference:
-
No personal identifying information in storage
- Reference: Storage model lists only hashes and addresses
- Test: Privacy boundary documentation review
-
No personal identifying information in events
- Reference: Event definitions in contracts
- Test:
protocol_config_no_private_data_in_fixtures
-
Unit tests cover all entry points
- Reference:
contracts/*/src/lib.rstest modules - Test: 41 tests pass
- Reference:
-
Error cases tested with typed error codes
- Reference: Tests use
try_*methods and assert error codes - Test: All error tests pass
- Reference: Tests use
-
Authorization paths exercised
- Reference: Tests use
env.mock_all_auths() - Test: All tests pass
- Reference: Tests use
-
Cross-contract integration tested
- Reference:
proof-registrytests set up full contract stack - Test: Proof registration tests
- Reference:
-
Deployment scripts default to testnet
- Reference:
scripts/deploy-testnet.ps1 - Test: Script inspection
- Reference:
-
Manifest validation enforced
- Reference:
scripts/verify-manifest.ps1 - Test:
scripts/verify-manifest.tests.ps1
- Reference:
-
Rust toolchain pinned
- Reference:
rust-toolchain.toml - Test: CI build consistency
- Reference:
-
Dependencies locked
- Reference:
Cargo.lockcommitted - Test: Build reproducibility
- Reference:
-
Multi-sig admin custody (Mainnet blocker)
- Issue: #21
-
Independent security audit (Mainnet blocker)
- Issue: #21
-
Reproducible build process (Mainnet blocker)
- Issue: #21
-
Automated TTL monitoring
- Issue: #21
-
Upgrade policy documentation
- Issue: #21
-
Incident response runbook
- Issue: #21
- Storage Model — Authoritative storage key reference
- Backend Integration — Contract call patterns and error handling
- Security Policy — Vulnerability reporting process
- Soroban Documentation — Soroban security best practices
- Issue #21 — Mainnet readiness tracking
Document Maintenance: This threat model should be updated whenever:
- New contracts are added
- Entry points or authorization logic changes
- Cross-contract interactions are modified
- New security controls are implemented
- Security issues are discovered and resolved
Next Review Date: Before mainnet deployment (no date set)