Skip to content

Commit f73d19c

Browse files
committed
fix: resolve merge conflicts in create_user_profile - integrate rate limiting
2 parents 42accbc + 1a6af6b commit f73d19c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1120
-540
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,3 @@ lto = true
2525
[profile.release-with-logs]
2626
inherits = "release"
2727
debug-assertions = true
28-

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# skillcert_contracts
22

3-
### Description
3+
## Description
44

55
Skillcert is platform for issuing NFT-based digital certificates on the Stellar blockchain, ensuring authenticity, traceability, and instant verification. It also features an educational marketplace for courses with automated validated certification.
66

@@ -11,7 +11,6 @@ We are currently in the integration phase between the Web3 logic and the fronten
1111
The project roadmap is available in Notion:
1212
🔗 [View Roadmap in Notion](https://www.notion.so/Skillcert-240bfdf2613c805898c9c91f0990600e)
1313

14-
1514
### 📁 Project Architecture Overview
1615

1716
```txt
@@ -38,14 +37,13 @@ The project roadmap is available in Notion:
3837
├── lib.rs
3938
└── test.rs
4039
```
41-
> As the project grows, new modules, contracts and functions will arise in the architecture.
4240

41+
> As the project grows, new modules, contracts and functions will arise in the architecture.
4342
4443
#### Contract Deployment
4544

4645
To deploy the contracts to the local network (Course Access, Course Registry, User Management), use the provided script:
4746

48-
4947
```bash
5048

5149
./scripts/deploy_contracts.sh
@@ -58,6 +56,7 @@ This will deploy all contracts and save their addresses in a `contract.json` fil
5856
Here are some example interactions with the deployed contracts:
5957

6058
1. Create a Course:
59+
6160
```bash
6261
stellar contract invoke \
6362
--id <course_registry_contract_id> \
@@ -74,6 +73,7 @@ stellar contract invoke \
7473
```
7574

7675
2. Grant Course Access:
76+
7777
```bash
7878
stellar contract invoke \
7979
--id <course_access_contract_id> \
@@ -85,6 +85,7 @@ stellar contract invoke \
8585
```
8686

8787
3. List User's Courses:
88+
8889
```bash
8990
stellar contract invoke \
9091
--id <course_access_contract_id> \
@@ -95,6 +96,7 @@ stellar contract invoke \
9596
```
9697

9798
4. View Course Details:
99+
98100
```bash
99101
stellar contract invoke \
100102
--id <course_registry_contract_id> \

contracts/course/course_access/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ publish = false
99
crate-type = ["cdylib"]
1010
doctest = false
1111

12-
13-
1412
[dependencies]
1513
soroban-sdk = { workspace = true }
1614

contracts/course/course_access/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ pub enum Error {
1616
InvalidCourseId = 7,
1717
InvalidUser = 8,
1818
EmptyCourseId = 9,
19-
// Removed InvalidInput - replaced with specific validation errors below
2019
InvalidTransferData = 10,
2120
SameUserTransfer = 11,
21+
Initialized = 12
2222
}
2323

2424
pub fn handle_error(env: &Env, error: Error) -> ! {

contracts/course/course_access/src/functions/config.rs

Lines changed: 9 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,18 @@
44
use soroban_sdk::{Address, Env, Symbol, symbol_short};
55
use soroban_sdk::storage::Instance;
66

7+
use crate::error::{Error, handle_error};
78
use crate::schema::{KEY_COURSE_REG_ADDR, KEY_USER_MGMT_ADDR};
89

910
const INIT_EVENT: Symbol = symbol_short!("initialz");
1011
const UPDATE_ADDRESS_EVENT: Symbol = symbol_short!("updAddr");
1112

12-
/// Storage key for initialization flag
13+
1314
const KEY_INIT: &str = "init";
1415

15-
/// Storage key for contract owner address
1616
const KEY_OWNER: &str = "owner";
1717

18-
/// One-time constructor that sets owner and external contract addresses.
19-
///
20-
/// This function initializes the contract with the necessary configuration
21-
/// and can only be called once during the contract's lifetime.
22-
///
23-
/// # Arguments
24-
///
25-
/// * `env` - The Soroban environment
26-
/// * `caller` - The address of the contract deployer who becomes the owner
27-
/// * `user_mgmt_addr` - Address of the user management contract
28-
/// * `course_registry_addr` - Address of the course registry contract
29-
///
30-
/// # Panics
31-
///
32-
/// Panics if the contract has already been initialized.
18+
3319
pub fn initialize(
3420
env: Env,
3521
caller: Address,
@@ -45,11 +31,10 @@ pub fn initialize(
4531
.get::<_, bool>(&((KEY_INIT,),))
4632
.unwrap_or(false)
4733
{
48-
// TODO: Implement graceful initialization error handling instead of panic
49-
panic!("already initialized");
34+
handle_error(&env, Error::Initialized);
5035
}
5136

52-
let inst = env.storage().instance();
37+
let inst: Instance = env.storage().instance();
5338
inst.set(&(KEY_OWNER,), &caller);
5439
inst.set(&(KEY_USER_MGMT_ADDR,), &user_mgmt_addr);
5540
inst.set(&(KEY_COURSE_REG_ADDR,), &course_registry_addr);
@@ -59,22 +44,7 @@ pub fn initialize(
5944
.publish((INIT_EVENT,), (caller, user_mgmt_addr, course_registry_addr));
6045
}
6146

62-
/// Update external contract addresses.
63-
///
64-
/// This function allows the contract owner to update the addresses of external
65-
/// contracts that this contract depends on for authentication and authorization.
66-
/// Only the stored owner can perform this operation.
67-
///
68-
/// # Arguments
69-
///
70-
/// * `env` - The Soroban environment
71-
/// * `caller` - The address of the user attempting to update the configuration
72-
/// * `user_mgmt_addr` - New address of the user management contract
73-
/// * `course_registry_addr` - New address of the course registry contract
74-
///
75-
/// # Panics
76-
///
77-
/// Panics if the contract is not initialized or if the caller is not the owner.
47+
7848
pub fn set_contract_addrs(
7949
env: Env,
8050
caller: Address,
@@ -93,7 +63,7 @@ pub fn set_contract_addrs(
9363
panic!("not initialized");
9464
}
9565

96-
// Only owner may update
66+
9767
let owner: Address = env
9868
.storage()
9969
.instance()
@@ -110,6 +80,6 @@ pub fn set_contract_addrs(
11080
.publish((UPDATE_ADDRESS_EVENT,), (caller, user_mgmt_addr, course_registry_addr));
11181
}
11282

113-
/// TTL configuration constants for persistent storage entries
83+
/* /// TTL configuration constants for persistent storage entries
11484
pub const TTL_TTL: u32 = 1000; // time-to-live
115-
pub const TTL_BUMP: u32 = 100; // bump amount on access
85+
pub const TTL_BUMP: u32 = 100; // bump amount on access */

contracts/course/course_access/src/functions/contract_versioning.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ pub enum VersioningError {
2222
MigrationFailed = 6,
2323
}
2424

25-
/// Storage keys for versioning data
25+
2626
const VERSION_HISTORY_KEY: &str = "version_history";
2727
const MIGRATION_STATUS_KEY: &str = "migration_status";
2828

29-
/// Get the version history of the contract
29+
3030
pub fn get_version_history(env: &Env) -> Vec<String> {
3131
let key = String::from_str(env, VERSION_HISTORY_KEY);
3232
env.storage()
@@ -35,18 +35,17 @@ pub fn get_version_history(env: &Env) -> Vec<String> {
3535
.unwrap_or_else(|| vec![env])
3636
}
3737

38-
/// Store a new version in the history
3938
fn store_version_in_history(env: &Env, version: String) {
40-
let mut history = get_version_history(env);
39+
let mut history: Vec<String> = get_version_history(env);
4140
history.push_back(version.clone());
4241

43-
let key = String::from_str(env, VERSION_HISTORY_KEY);
42+
let key: String = String::from_str(env, VERSION_HISTORY_KEY);
4443
env.storage().instance().set(&key, &history);
4544
}
4645

47-
/// Check if a version exists in the history
46+
4847
fn version_exists_in_history(env: &Env, version: &String) -> bool {
49-
let history = get_version_history(env);
48+
let history: Vec<String> = get_version_history(env);
5049
for v in history.iter() {
5150
if &v == version {
5251
return true;
@@ -55,31 +54,29 @@ fn version_exists_in_history(env: &Env, version: &String) -> bool {
5554
false
5655
}
5756

58-
/// Get migration status information
57+
5958
pub fn get_migration_status(env: &Env) -> String {
60-
let key = String::from_str(env, MIGRATION_STATUS_KEY);
59+
let key: String = String::from_str(env, MIGRATION_STATUS_KEY);
6160
env.storage()
6261
.instance()
6362
.get::<String, String>(&key)
6463
.unwrap_or_else(|| String::from_str(env, "No migrations pending"))
6564
}
6665

67-
/// Set migration status
66+
6867
fn set_migration_status(env: &Env, status: String) {
6968
let key = String::from_str(env, MIGRATION_STATUS_KEY);
7069
env.storage().instance().set(&key, &status);
7170
}
7271

73-
/// Check compatibility between two versions
72+
7473
pub fn is_version_compatible(_env: &Env, _from_version: String, _to_version: String) -> bool {
7574
// Simple compatibility check - for now, assume all versions are compatible
7675
// In a real implementation, you would parse semantic versions properly
7776
true
7877
}
7978

80-
/// Check if the caller is authorized to perform migration
81-
/// For course access, we'll allow any authenticated user for now
82-
/// In a production environment, this should check for admin privileges
79+
8380
fn is_authorized_for_migration(_env: &Env, _caller: Address) -> bool {
8481
// For now, we'll allow any authenticated user
8582
// In a real implementation, you would check against user management contract
@@ -93,40 +90,39 @@ fn is_authorized_for_migration(_env: &Env, _caller: Address) -> bool {
9390
true // Placeholder - allow all authenticated users
9491
}
9592

96-
/// Migrate access data between contract versions
9793
pub fn migrate_access_data(
9894
env: &Env,
9995
caller: Address,
10096
from_version: String,
10197
to_version: String,
10298
) -> bool {
103-
// Check if caller is authorized
99+
104100
if !is_authorized_for_migration(env, caller.clone()) {
105101
set_migration_status(env, String::from_str(env, "Migration failed: Unauthorized"));
106102
return false;
107103
}
108104

109-
// Validate versions exist in history
105+
110106
if !version_exists_in_history(env, &from_version) {
111107
set_migration_status(env, String::from_str(env, "Migration failed: Source version not found"));
112108
return false;
113109
}
114110

115-
// Check compatibility
111+
116112
if !is_version_compatible(env, from_version.clone(), to_version.clone()) {
117113
set_migration_status(env, String::from_str(env, "Migration failed: Versions not compatible"));
118114
return false;
119115
}
120116

121-
// Perform migration based on version differences
122-
let migration_result = perform_access_data_migration(env, &from_version, &to_version);
117+
118+
let migration_result: bool = perform_access_data_migration(env, &from_version, &to_version);
123119

124120
if migration_result {
125121
// Update version history with new version
126122
store_version_in_history(env, to_version.clone());
127123

128124
// Set successful migration status
129-
let status = String::from_str(env, "Migration completed successfully");
125+
let status: String = String::from_str(env, "Migration completed successfully");
130126
set_migration_status(env, status);
131127

132128
// Emit migration event

contracts/course/course_access/src/functions/grant_access.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub fn course_access_grant_access(env: Env, course_id: String, user: Address) {
5454
}
5555

5656
// Update CourseUsers
57-
let course_users_key = DataKey::CourseUsers(course_id.clone());
57+
let course_users_key: DataKey = DataKey::CourseUsers(course_id.clone());
5858
let mut course_users: CourseUsers = env
5959
.storage()
6060
.persistent()
@@ -70,4 +70,4 @@ pub fn course_access_grant_access(env: Env, course_id: String, user: Address) {
7070
}
7171
env.events()
7272
.publish((COURSE_ACCESS_EVENT, &user.clone()), (course_id, user, course_users.users.len(),));
73-
}
73+
}

contracts/course/course_access/src/functions/list_user_courses.rs

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,11 @@
11
// SPDX-License-Identifier: MIT
22
// Copyright (c) 2025 SkillCert
3+
34
use soroban_sdk::{Address, Env, Vec};
5+
46
use crate::schema::{DataKey, UserCourses};
57

6-
/// List all courses that a specific user has access to.
7-
///
8-
/// This function retrieves all courses that the specified user is enrolled in
9-
/// or has been granted access to. If the user has no courses, it returns
10-
/// an empty UserCourses struct.
11-
///
12-
/// # Arguments
13-
///
14-
/// * `env` - The Soroban environment
15-
/// * `user` - The address of the user to query courses for
16-
///
17-
/// # Returns
18-
///
19-
/// Returns a `UserCourses` struct containing the user's address and a list
20-
/// of course IDs they have access to. If no courses are found, returns
21-
/// an empty list.
8+
229
pub fn list_user_courses(env: Env, user: Address) -> UserCourses {
2310
let key: DataKey = DataKey::UserCourses(user.clone());
2411
let res: UserCourses = env.storage().persistent().get(&key).unwrap_or(UserCourses {

contracts/course/course_access/src/functions/mod.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,3 @@ pub mod revoke_access;
1010
pub mod revoke_all_access;
1111
pub mod save_profile;
1212
pub mod transfer_course_access;
13-
14-
pub use config::*;
15-
pub use grant_access::*;
16-
pub use list_course_access::*;
17-
pub use list_user_courses::*;
18-
pub use revoke_access::*;
19-
pub use revoke_all_access::*;
20-
pub use save_profile::*;

contracts/course/course_access/src/functions/revoke_all_access.rs

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,7 @@ const COURSES_KEY: Symbol = symbol_short!("courses");
1515
/// Event symbol for revoke all access operations
1616
const REVOKE_ALL_EVENT: Symbol = symbol_short!("revokeAll");
1717

18-
/// Revoke access for all users from a specific course.
19-
///
20-
/// This function removes access for all users from the specified course and
21-
/// updates all related storage structures. Only admin users or course creators
22-
/// are authorized to perform this operation.
23-
///
24-
/// # Arguments
25-
///
26-
/// * `env` - The Soroban environment
27-
/// * `caller` - The address of the user requesting the operation
28-
/// * `course_id` - The unique identifier of the course to revoke all access from
29-
///
30-
/// # Returns
31-
///
32-
/// Returns the number of users whose access was revoked.
33-
///
34-
/// # Authorization
35-
///
36-
/// The caller must be either:
37-
/// - An admin user (verified via user management contract)
38-
/// - The creator of the course (verified via course registry contract)
39-
///
40-
/// # Events
41-
///
42-
/// Emits a `revokeall` event with the course ID and number of affected users.
43-
///
44-
/// # Panics
45-
///
46-
/// Panics with `Error::Unauthorized` if the caller is not authorized to perform this operation.
18+
4719
pub fn revoke_all_access(env: Env, caller: Address, course_id: String) -> u32 {
4820
caller.require_auth();
4921

0 commit comments

Comments
 (0)