Skip to content

Commit 506ce3e

Browse files
authored
Merge pull request #81 from LaGodxy/feature/comprehensive-validation-layer
Feature/comprehensive validation layer
2 parents 978c2d7 + 7e12b15 commit 506ce3e

File tree

10 files changed

+1200
-118
lines changed

10 files changed

+1200
-118
lines changed

contracts/insurance/src/errors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use soroban_sdk::contracterror;
44
#[contracterror]
55
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
66
pub enum InsuranceError {
7+
NotInitialized = 499,
78
AlreadyInitialized = 500,
89
UserNotInsured = 501,
910
ClaimNotFound = 502,

contracts/insurance/src/lib.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,15 @@ impl InsurancePool {
6464
Ok(())
6565
}
6666

67-
pub fn pay_premium(env: Env, user: Address) {
67+
pub fn pay_premium(env: Env, user: Address) -> Result<(), InsuranceError> {
6868
user.require_auth();
6969

7070
let token_addr = env
7171
.storage()
7272
.instance()
7373
.get::<_, Address>(&DataKey::Token)
74-
.unwrap();
74+
.ok_or(InsuranceError::NotInitialized)?;
75+
7576
let premium_amount = env
7677
.storage()
7778
.instance()
@@ -84,6 +85,8 @@ impl InsurancePool {
8485
env.storage()
8586
.instance()
8687
.set(&DataKey::IsInsured(user), &true);
88+
89+
Ok(())
8790
}
8891

8992
pub fn file_claim(env: Env, user: Address, course_id: u64) -> Result<u64, InsuranceError> {
@@ -103,11 +106,12 @@ impl InsurancePool {
103106
.storage()
104107
.instance()
105108
.get::<_, u64>(&DataKey::ClaimCount)
106-
.unwrap();
109+
.unwrap_or(0);
110+
107111
claim_count += 1;
108112

109113
let claim = Claim {
110-
user: user.clone(),
114+
user,
111115
course_id,
112116
status: ClaimStatus::Pending,
113117
};
@@ -167,7 +171,8 @@ impl InsurancePool {
167171
.storage()
168172
.instance()
169173
.get::<_, Address>(&DataKey::Token)
170-
.unwrap();
174+
.ok_or(InsuranceError::NotInitialized)?;
175+
171176
let payout_amount = env
172177
.storage()
173178
.instance()
@@ -178,6 +183,7 @@ impl InsurancePool {
178183
client.transfer(&env.current_contract_address(), &claim.user, &payout_amount);
179184

180185
claim.status = ClaimStatus::Paid;
186+
181187
env.storage()
182188
.instance()
183189
.set(&DataKey::Claim(claim_id), &claim);
@@ -190,22 +196,25 @@ impl InsurancePool {
190196
Ok(())
191197
}
192198

193-
pub fn withdraw(env: Env, amount: i128) {
199+
pub fn withdraw(env: Env, amount: i128) -> Result<(), InsuranceError> {
194200
let admin = env
195201
.storage()
196202
.instance()
197203
.get::<_, Address>(&DataKey::Admin)
198-
.unwrap();
204+
.ok_or(InsuranceError::NotInitialized)?;
205+
199206
admin.require_auth();
200207

201208
let token_addr = env
202209
.storage()
203210
.instance()
204211
.get::<_, Address>(&DataKey::Token)
205-
.unwrap();
206-
let client = token::Client::new(&env, &token_addr);
212+
.ok_or(InsuranceError::NotInitialized)?;
207213

214+
let client = token::Client::new(&env, &token_addr);
208215
client.transfer(&env.current_contract_address(), &admin, &amount);
216+
217+
Ok(())
209218
}
210219

211220
// ===== View Functions =====

contracts/teachlink/src/bridge.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::storage::{
55
VALIDATORS,
66
};
77
use crate::types::{BridgeTransaction, CrossChainMessage};
8+
use crate::validation::BridgeValidator;
89
use soroban_sdk::{symbol_short, vec, Address, Env, IntoVal, Map, Vec};
910

1011
pub struct Bridge;
@@ -59,10 +60,14 @@ impl Bridge {
5960
) -> Result<u64, BridgeError> {
6061
from.require_auth();
6162

62-
// Validate inputs
63-
if amount <= 0 {
64-
return Err(BridgeError::AmountMustBePositive);
65-
}
63+
// Validate all input parameters
64+
BridgeValidator::validate_bridge_out(
65+
&env,
66+
&from,
67+
amount,
68+
destination_chain,
69+
&destination_address,
70+
)?;
6671

6772
// Check if destination chain is supported
6873
let supported_chains: Map<u32, bool> = env
@@ -161,11 +166,14 @@ impl Bridge {
161166
message: CrossChainMessage,
162167
validator_signatures: Vec<Address>,
163168
) -> Result<(), BridgeError> {
164-
// Validate that we have enough validator signatures
169+
// Validate all input parameters
165170
let min_validators: u32 = env.storage().instance().get(&MIN_VALIDATORS).unwrap();
166-
if (validator_signatures.len() as u32) < min_validators {
167-
return Err(BridgeError::InsufficientValidatorSignatures);
168-
}
171+
BridgeValidator::validate_bridge_completion(
172+
&env,
173+
&message,
174+
&validator_signatures,
175+
min_validators,
176+
)?;
169177

170178
// Verify all signatures are from valid validators
171179
let validators: Map<Address, bool> = env.storage().instance().get(&VALIDATORS).unwrap();

contracts/teachlink/src/escrow.rs

Lines changed: 31 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::events::{
55
};
66
use crate::storage::{ESCROWS, ESCROW_COUNT};
77
use crate::types::{DisputeOutcome, Escrow, EscrowApprovalKey, EscrowStatus};
8+
use crate::validation::EscrowValidator;
89
use soroban_sdk::{symbol_short, vec, Address, Bytes, Env, IntoVal, Map, Vec};
910

1011
pub struct EscrowManager;
@@ -24,32 +25,18 @@ impl EscrowManager {
2425
) -> Result<u64, EscrowError> {
2526
depositor.require_auth();
2627

27-
if amount <= 0 {
28-
return Err(EscrowError::AmountMustBePositive);
29-
}
30-
31-
if signers.len() == 0 {
32-
return Err(EscrowError::AtLeastOneSignerRequired);
33-
}
34-
35-
if threshold == 0 || threshold > signers.len() as u32 {
36-
return Err(EscrowError::InvalidSignerThreshold);
37-
}
38-
39-
let now = env.ledger().timestamp();
40-
if let Some(refund_time) = refund_time {
41-
if refund_time < now {
42-
return Err(EscrowError::RefundTimeMustBeInFuture);
43-
}
44-
}
45-
46-
if let (Some(release), Some(refund)) = (release_time, refund_time) {
47-
if refund < release {
48-
return Err(EscrowError::RefundTimeMustBeAfterReleaseTime);
49-
}
50-
}
51-
52-
Self::ensure_unique_signers(env, &signers)?;
28+
EscrowValidator::validate_create_escrow(
29+
env,
30+
&depositor,
31+
&beneficiary,
32+
&token,
33+
amount,
34+
&signers,
35+
threshold,
36+
release_time,
37+
refund_time,
38+
&arbitrator,
39+
)?;
5340

5441
env.invoke_contract::<()>(
5542
&token,
@@ -62,10 +49,11 @@ impl EscrowManager {
6249
],
6350
);
6451

65-
let mut escrow_count: u64 = env.storage().instance().get(&ESCROW_COUNT).unwrap_or(0u64);
52+
let mut escrow_count: u64 = env.storage().instance().get(&ESCROW_COUNT).unwrap_or(0);
6653
escrow_count += 1;
6754
env.storage().instance().set(&ESCROW_COUNT, &escrow_count);
6855

56+
let now = env.ledger().timestamp();
6957
let escrow = Escrow {
7058
id: escrow_count,
7159
depositor,
@@ -106,6 +94,7 @@ impl EscrowManager {
10694
escrow_id,
10795
signer: signer.clone(),
10896
};
97+
10998
if env.storage().persistent().has(&approval_key) {
11099
return Err(EscrowError::SignerAlreadyApproved);
111100
}
@@ -129,24 +118,11 @@ impl EscrowManager {
129118
caller.require_auth();
130119

131120
let mut escrow = Self::load_escrow(env, escrow_id)?;
132-
Self::ensure_pending(&escrow)?;
133121

134-
if !Self::is_release_caller(&escrow, &caller) {
135-
return Err(EscrowError::CallerNotAuthorized);
136-
}
137-
138-
if escrow.approval_count < escrow.threshold {
139-
return Err(EscrowError::InsufficientApprovals);
140-
}
141-
142-
if let Some(release_time) = escrow.release_time {
143-
let now = env.ledger().timestamp();
144-
if now < release_time {
145-
return Err(EscrowError::ReleaseTimeNotReached);
146-
}
147-
}
122+
EscrowValidator::validate_release_conditions(&escrow, &caller, env.ledger().timestamp())?;
148123

149124
Self::transfer_from_contract(env, &escrow.token, &escrow.beneficiary, escrow.amount);
125+
150126
escrow.status = EscrowStatus::Released;
151127
Self::save_escrow(env, escrow_id, escrow.clone());
152128

@@ -173,11 +149,13 @@ impl EscrowManager {
173149
let refund_time = escrow.refund_time.ok_or(EscrowError::RefundNotEnabled)?;
174150

175151
let now = env.ledger().timestamp();
152+
176153
if now < refund_time {
177154
return Err(EscrowError::RefundTimeNotReached);
178155
}
179156

180157
Self::transfer_from_contract(env, &escrow.token, &escrow.depositor, escrow.amount);
158+
181159
escrow.status = EscrowStatus::Refunded;
182160
Self::save_escrow(env, escrow_id, escrow.clone());
183161

@@ -206,6 +184,7 @@ impl EscrowManager {
206184
}
207185

208186
Self::transfer_from_contract(env, &escrow.token, &escrow.depositor, escrow.amount);
187+
209188
escrow.status = EscrowStatus::Cancelled;
210189
Self::save_escrow(env, escrow_id, escrow.clone());
211190

@@ -250,6 +229,7 @@ impl EscrowManager {
250229
arbitrator.require_auth();
251230

252231
let mut escrow = Self::load_escrow(env, escrow_id)?;
232+
253233
if escrow.status != EscrowStatus::Disputed {
254234
return Err(EscrowError::EscrowNotInDispute);
255235
}
@@ -287,47 +267,32 @@ impl EscrowManager {
287267
Ok(())
288268
}
289269

270+
// ---------- Views ----------
271+
290272
pub fn get_escrow(env: &Env, escrow_id: u64) -> Option<Escrow> {
291-
let escrows = Self::load_escrows(env);
292-
escrows.get(escrow_id)
273+
Self::load_escrows(env).get(escrow_id)
293274
}
294275

295276
pub fn get_escrow_count(env: &Env) -> u64 {
296-
env.storage().instance().get(&ESCROW_COUNT).unwrap_or(0u64)
277+
env.storage().instance().get(&ESCROW_COUNT).unwrap_or(0)
297278
}
298279

299280
pub fn has_approved(env: &Env, escrow_id: u64, signer: Address) -> bool {
300-
let approval_key = EscrowApprovalKey { escrow_id, signer };
301-
env.storage().persistent().has(&approval_key)
281+
let key = EscrowApprovalKey { escrow_id, signer };
282+
env.storage().persistent().has(&key)
302283
}
303284

304-
fn ensure_unique_signers(env: &Env, signers: &Vec<Address>) -> Result<(), EscrowError> {
305-
let mut seen: Map<Address, bool> = Map::new(env);
306-
for signer in signers.iter() {
307-
if seen.get(signer.clone()).unwrap_or(false) {
308-
return Err(EscrowError::DuplicateSigner);
309-
}
310-
seen.set(signer.clone(), true);
311-
}
312-
Ok(())
313-
}
285+
// ---------- Internal Helpers ----------
314286

315287
fn is_signer(signers: &Vec<Address>, signer: &Address) -> bool {
316-
for candidate in signers.iter() {
317-
if candidate == *signer {
288+
for s in signers.iter() {
289+
if s == *signer {
318290
return true;
319291
}
320292
}
321293
false
322294
}
323295

324-
fn is_release_caller(escrow: &Escrow, caller: &Address) -> bool {
325-
if *caller == escrow.depositor || *caller == escrow.beneficiary {
326-
return true;
327-
}
328-
Self::is_signer(&escrow.signers, caller)
329-
}
330-
331296
fn ensure_pending(escrow: &Escrow) -> Result<(), EscrowError> {
332297
if escrow.status != EscrowStatus::Pending {
333298
return Err(EscrowError::EscrowNotPending);

contracts/teachlink/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ mod score;
7171
mod storage;
7272
mod tokenization;
7373
mod types;
74+
mod validation;
7475

7576
pub use errors::{BridgeError, EscrowError, RewardsError};
7677
pub use types::{

0 commit comments

Comments
 (0)