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
44 changes: 43 additions & 1 deletion dongle-smartcontract/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,47 @@ make deploy-testnet
- **Verification**: Request and approve project verification
- **Fee Management**: Configurable fees for operations
- **Access Control**: Owner-based permissions
- **TTL Management**: Automatic and manual Time-To-Live extension for persistent storage

## TTL (Time To Live) Management

The contract implements comprehensive TTL management for Soroban persistent storage to ensure data doesn't expire unexpectedly. See [TTL_STRATEGY.md](../TTL_STRATEGY.md) for detailed information.

### Key Features

- **Automatic TTL Extension**: TTL is automatically extended on write and read operations
- **Manual TTL Extension**: Public functions available for proactive TTL management
- **Categorized Thresholds**: Different TTL durations for critical, project, review, verification, and user data
- **Defensive Programming**: All TTL operations check for key existence before extending

### TTL Thresholds

- **Critical Data** (admin, fees, treasury): 30 days
- **Project Data**: 90 days
- **Review Data**: 60 days
- **Verification Data**: 45 days
- **User Data**: 60 days

### Manual TTL Extension Functions

```rust
// Extend TTL for a specific project
extend_project_ttl(env, project_id)

// Extend TTL for critical configuration
extend_critical_config_ttl(env)

// Extend TTL for user data
extend_user_ttl(env, user_address)

// Extend TTL for a review
extend_review_ttl(env, project_id, reviewer)

// Extend TTL for verification data
extend_verification_ttl(env, project_id)
```

For complete TTL strategy documentation, see [TTL_STRATEGY.md](../TTL_STRATEGY.md).

## Contract Functions

Expand Down Expand Up @@ -160,7 +201,7 @@ See [SETUP.md](../SETUP.md) for detailed setup, deployment, and usage instructio
```
src/
├── lib.rs # Main contract interface
├── constants.rs # Constants and limits
├── constants.rs # Constants, limits, and TTL thresholds
├── errors.rs # Error definitions
├── events.rs # Event emissions
├── fee_manager.rs # Fee handling
Expand All @@ -169,6 +210,7 @@ src/
├── verification_registry.rs # Verification logic
├── rating_calculator.rs # Rating calculations
├── storage_keys.rs # Storage keys
├── storage_manager.rs # TTL management
├── types.rs # Data structures
├── utils.rs # Utilities
└── test.rs # Tests
Expand Down
23 changes: 19 additions & 4 deletions dongle-smartcontract/src/admin_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::auth::require_admin_auth;
use crate::errors::ContractError;
use crate::events::{publish_admin_added_event, publish_admin_removed_event};
use crate::storage_keys::StorageKey;
use crate::storage_manager::StorageManager;
use soroban_sdk::{Address, Env, Vec};

pub struct AdminManager;
Expand All @@ -32,6 +33,9 @@ impl AdminManager {
.persistent()
.set(&StorageKey::AdminList, &admins);

// Extend TTL for admin data
StorageManager::extend_all_admin_ttl(env, &admin);

publish_admin_added_event(env, admin);
}

Expand All @@ -56,6 +60,9 @@ impl AdminManager {
.persistent()
.set(&StorageKey::AdminList, &admins);

// Extend TTL for admin data
StorageManager::extend_all_admin_ttl(env, &new_admin);

publish_admin_added_event(env, new_admin);

Ok(())
Expand Down Expand Up @@ -103,10 +110,18 @@ impl AdminManager {

/// Check if an address is an admin
pub fn is_admin(env: &Env, address: &Address) -> bool {
env.storage()
let is_admin = env
.storage()
.persistent()
.get(&StorageKey::Admin(address.clone()))
.unwrap_or(false)
.unwrap_or(false);

// Bump TTL on read if admin exists
if is_admin {
StorageManager::extend_admin_ttl(env, address);
}

is_admin
}

/// Require that the caller is an admin, otherwise return an error
Expand Down Expand Up @@ -169,7 +184,7 @@ mod tests {
#[test]
fn test_add_admin_duplicate() {
let env = Env::default();
let contract_id = env.register_contract(None, DongleContract);
let contract_id = env.register(DongleContract, ());
let client = DongleContractClient::new(&env, &contract_id);
let admin1 = Address::generate(&env);
let admin2 = Address::generate(&env);
Expand Down Expand Up @@ -251,7 +266,7 @@ mod tests {
#[test]
fn test_remove_admin_twice() {
let env = Env::default();
let contract_id = env.register_contract(None, DongleContract);
let contract_id = env.register(DongleContract, ());
let client = DongleContractClient::new(&env, &contract_id);
let admin1 = Address::generate(&env);
let admin2 = Address::generate(&env);
Expand Down
35 changes: 35 additions & 0 deletions dongle-smartcontract/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,38 @@ pub const MAX_CID_LEN: usize = 128;
pub const RATING_MIN: u32 = 1;
#[allow(dead_code)]
pub const RATING_MAX: u32 = 5;

// ── TTL (Time To Live) Constants ──────────────────────────────────────────

/// TTL for critical contract data (admin list, fee config, treasury).
/// Set to ~30 days (30 * 24 * 60 * 60 / 5 seconds per ledger = 518,400 ledgers).
/// This data should persist long-term and be extended regularly.
pub const LEDGER_THRESHOLD_CRITICAL: u32 = 518_400;

/// TTL for project data (projects, project stats, project counts).
/// Set to ~90 days (90 * 24 * 60 * 60 / 5 = 1,555,200 ledgers).
/// Projects are core entities and should have long persistence.
pub const LEDGER_THRESHOLD_PROJECT: u32 = 1_555_200;

/// TTL for review data (reviews, review stats).
/// Set to ~60 days (60 * 24 * 60 * 60 / 5 = 1,036,800 ledgers).
/// Reviews are important but can be archived if inactive.
pub const LEDGER_THRESHOLD_REVIEW: u32 = 1_036_800;

/// TTL for verification data (verification records, fee payments).
/// Set to ~45 days (45 * 24 * 60 * 60 / 5 = 777,600 ledgers).
/// Verification data is moderately important.
pub const LEDGER_THRESHOLD_VERIFICATION: u32 = 777_600;

/// TTL for user-related data (owner projects, user reviews).
/// Set to ~60 days (60 * 24 * 60 * 60 / 5 = 1,036,800 ledgers).
/// User data should persist reasonably long.
pub const LEDGER_THRESHOLD_USER: u32 = 1_036_800;

/// TTL bump amount - how much to extend when bumping.
/// Set to the same as the threshold to maintain consistent lifetime.
pub const LEDGER_BUMP_CRITICAL: u32 = LEDGER_THRESHOLD_CRITICAL;
pub const LEDGER_BUMP_PROJECT: u32 = LEDGER_THRESHOLD_PROJECT;
pub const LEDGER_BUMP_REVIEW: u32 = LEDGER_THRESHOLD_REVIEW;
pub const LEDGER_BUMP_VERIFICATION: u32 = LEDGER_THRESHOLD_VERIFICATION;
pub const LEDGER_BUMP_USER: u32 = LEDGER_THRESHOLD_USER;
6 changes: 6 additions & 0 deletions dongle-smartcontract/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ pub enum ContractError {
InvalidProjectDescription = 20,
/// Invalid project category - empty or whitespace only
InvalidProjectCategory = 21,
/// Project description too long
ProjectDescriptionTooLong = 22,
/// Project description contains invalid characters
InvalidProjectDescriptionFormat = 23,
/// User has exceeded maximum number of projects allowed
MaxProjectsExceeded = 24,
}

// Legacy alias to avoid breaking any code that uses `Error` directly
Expand Down
44 changes: 40 additions & 4 deletions dongle-smartcontract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ mod project_registry;
pub mod rating_calculator;
pub mod review_registry;
pub mod storage_keys;
pub mod storage_manager;
pub mod types;
pub mod utils;
mod verification_registry;

#[cfg(test)]
Expand All @@ -21,6 +23,7 @@ use crate::errors::ContractError;
use crate::fee_manager::FeeManager;
use crate::project_registry::ProjectRegistry;
use crate::review_registry::ReviewRegistry;
use crate::storage_manager::StorageManager;
use crate::types::{
FeeConfig, Project, ProjectRegistrationParams, ProjectStats, ProjectUpdateParams, Review,
VerificationRecord,
Expand Down Expand Up @@ -72,10 +75,7 @@ impl DongleContract {
ProjectRegistry::register_project(&env, params)
}

pub fn update_project(
env: Env,
params: ProjectUpdateParams,
) -> Result<Project, ContractError> {
pub fn update_project(env: Env, params: ProjectUpdateParams) -> Result<Project, ContractError> {
ProjectRegistry::update_project(&env, params)
}

Expand Down Expand Up @@ -195,4 +195,40 @@ impl DongleContract {
pub fn get_fee_config(env: Env) -> Result<FeeConfig, ContractError> {
FeeManager::get_fee_config(&env)
}

// --- TTL Management ---

/// Extend TTL for a specific project and its related data
pub fn extend_project_ttl(env: Env, project_id: u64) {
if let Some(project) = ProjectRegistry::get_project(&env, project_id) {
StorageManager::extend_project_full_ttl(&env, project_id, &project.name);
}
}

/// Extend TTL for a specific review
pub fn extend_review_ttl(env: Env, project_id: u64, reviewer: Address) {
StorageManager::extend_review_ttl(&env, project_id, &reviewer);
}

/// Extend TTL for all admin-related data
pub fn extend_admin_ttl(env: Env, admin: Address) {
StorageManager::extend_all_admin_ttl(&env, &admin);
}

/// Extend TTL for critical contract configuration (admin list, fee config, treasury)
pub fn extend_critical_config_ttl(env: Env) {
StorageManager::extend_critical_config_ttl(&env);
}

/// Extend TTL for user-related data (owner projects, user reviews)
pub fn extend_user_ttl(env: Env, user: Address) {
StorageManager::extend_owner_projects_ttl(&env, &user);
StorageManager::extend_user_reviews_ttl(&env, &user);
}

/// Extend TTL for verification data
pub fn extend_verification_ttl(env: Env, project_id: u64) {
StorageManager::extend_verification_ttl(&env, project_id);
StorageManager::extend_fee_paid_ttl(&env, project_id);
}
}
Loading
Loading