Skip to content

Commit b0e41bd

Browse files
authored
Merge pull request #442 from Abolax123/feature/family-wallet-archive-cleanup-hardening
fear:Harden archive and cleanup logic for family wallet transactions
2 parents dcbc0a8 + f93d515 commit b0e41bd

File tree

58 files changed

+2421
-443
lines changed

Some content is hidden

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

58 files changed

+2421
-443
lines changed

bill_payments/src/lib.rs

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -156,24 +156,6 @@ pub struct BillPayments;
156156

157157
#[contractimpl]
158158
impl BillPayments {
159-
/// Create a new bill
160-
///
161-
/// # Arguments
162-
/// * `owner` - Address of the bill owner (must authorize)
163-
/// * `name` - Name of the bill (e.g., "Electricity", "School Fees")
164-
/// * `amount` - Amount to pay (must be positive)
165-
/// * `due_date` - Due date as Unix timestamp
166-
/// * `recurring` - Whether this is a recurring bill
167-
/// * `frequency_days` - Frequency in days for recurring bills (must be > 0 if recurring)
168-
/// * `external_ref` - Optional external system reference ID
169-
///
170-
/// # Returns
171-
/// The ID of the created bill
172-
///
173-
/// # Errors
174-
/// * `InvalidAmount` - If amount is zero or negative
175-
/// * `InvalidFrequency` - If recurring is true but frequency_days is 0 or exceeds MAX_FREQUENCY_DAYS
176-
/// * `InvalidDueDate` - If due_date is invalid or arithmetic overflows
177159
// -----------------------------------------------------------------------
178160
// Internal helpers
179161
// -----------------------------------------------------------------------
@@ -267,9 +249,6 @@ impl BillPayments {
267249
Ok(())
268250
}
269251

270-
/// Clamp a caller-supplied limit to [1, MAX_PAGE_LIMIT].
271-
/// A value of 0 is treated as DEFAULT_PAGE_LIMIT.
272-
273252
// -----------------------------------------------------------------------
274253
// Pause / upgrade
275254
// -----------------------------------------------------------------------
@@ -1691,7 +1670,10 @@ impl BillPayments {
16911670
.get(&STORAGE_UNPAID_TOTALS)
16921671
.unwrap_or_else(|| Map::new(env));
16931672
let current = totals.get(owner.clone()).unwrap_or(0);
1694-
let next = current.checked_add(delta).expect("overflow");
1673+
let next = match current.checked_add(delta) {
1674+
Some(n) => n,
1675+
None => panic!("overflow"),
1676+
};
16951677
totals.set(owner.clone(), next);
16961678
env.storage()
16971679
.instance()
@@ -2702,7 +2684,7 @@ mod test {
27022684
/// when payment is made.
27032685
#[test]
27042686
fn prop_recurring_next_bill_due_date_follows_original(
2705-
base_due in 1_000_000u64..5_000_000u64,
2687+
_base_due in 1_000_000u64..5_000_000u64,
27062688
base_due_offset in 1_000_000u64..5_000_000u64,
27072689
pay_offset in 1u64..100_000u64,
27082690
freq_days in 1u32..366u32,

bill_payments/tests/stress_tests.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,13 +548,31 @@ fn stress_batch_pay_mixed_50() {
548548
// Create 30 valid bills for owner
549549
let mut valid_ids = soroban_sdk::Vec::new(&env);
550550
for _ in 0..30 {
551-
valid_ids.push_back(client.create_bill(&owner, &name, &100i128, &due_date, &false, &0u32));
551+
valid_ids.push_back(client.create_bill(
552+
&owner,
553+
&name,
554+
&100i128,
555+
&due_date,
556+
&false,
557+
&0u32,
558+
&None,
559+
&String::from_str(&env, "XLM"),
560+
));
552561
}
553562

554563
// Create 10 bills for 'other' (invalid for 'owner' to pay in batch)
555564
let mut other_ids = soroban_sdk::Vec::new(&env);
556565
for _ in 0..10 {
557-
other_ids.push_back(client.create_bill(&other, &name, &100i128, &due_date, &false, &0u32));
566+
other_ids.push_back(client.create_bill(
567+
&other,
568+
&name,
569+
&100i128,
570+
&due_date,
571+
&false,
572+
&0u32,
573+
&None,
574+
&String::from_str(&env, "XLM"),
575+
));
558576
}
559577

560578
// Mix them up with some non-existent IDs (total 50)

check_errors.txt

193 KB
Binary file not shown.

docs/family-wallet-design.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,43 @@ Key benefits:
398398
- **Well-documented** security assumptions and validation logic
399399

400400
- `add_member` is strict (duplicate-safe and limit-aware), while `add_family_member`/batch add overwrite records and force spending limit to `0`.
401-
- `archive_old_transactions` archives all `EXEC_TXS` entries currently present; `before_timestamp` is written into archived metadata but not used as a filter.
401+
- **Archive and cleanup (v2):** see *Transaction archive and pending cleanup* below.
402402
- `SplitConfigChange` and `PolicyCancellation` transaction execution paths currently complete without cross-contract side effects.
403403
- Token-transfer execution from `sign_transaction` path calls `proposer.require_auth()` for transfer types, so proposer authorization is required at execution time.
404404

405+
## Transaction archive and pending cleanup
406+
407+
### Storage
408+
409+
- **`EXEC_TXS`:** `Map<u64, ExecutedTxMeta>`. Each multisig-completed execution stores `tx_id`, `tx_type`, `proposer`, and `executed_at` (ledger seconds at completion). Direct (non-multisig) executions do not populate this map.
410+
- **`ARCH_TX`:** `Map<u64, ArchivedTransaction>` — long-term archive of moved-out executions.
411+
412+
### `archive_old_transactions(caller, before_timestamp)`
413+
414+
- **Authorization:** Owner or Admin; `caller.require_auth()`.
415+
- **Retention rule:** A row moves from `EXEC_TXS` to `ARCH_TX` iff `executed_at < before_timestamp`.
416+
- **Cutoff validation:** `before_timestamp <= ledger.timestamp()`. A cutoff in the “future” relative to the ledger would incorrectly treat recent executions as eligible; the contract rejects that.
417+
- **Integrity:** If `ExecutedTxMeta.tx_id` does not match the map key, the contract panics (`"Inconsistent executed transaction metadata"`) to avoid corrupting `ARCH_TX`.
418+
- **Metadata:** Archived rows copy **proposer**, **tx_type**, and **executed_at** from `ExecutedTxMeta`; `archived_at` is the ledger time of the archive call.
419+
420+
### `get_archived_transactions(caller, limit)`
421+
422+
- **Authorization:** Owner or Admin; `caller.require_auth()`. Prevents unauthenticated reads of historical transaction metadata (privacy / ownership leakage).
423+
424+
### `cleanup_expired_pending(caller)`
425+
426+
- **Authorization:** Owner or Admin; `caller.require_auth()`.
427+
- **Rule:** Removes pending entries with `expires_at < ledger.timestamp()`.
428+
- **Integrity:** If `PendingTransaction.tx_id` does not match the map key, the contract panics (`"Inconsistent pending transaction data"`).
429+
430+
### Upgrade / migration note
431+
432+
`CONTRACT_VERSION` was bumped when `EXEC_TXS` changed from `Map<u64, bool>` to `Map<u64, ExecutedTxMeta>`. Existing deployed instances with the old layout require an explicit migration path; new deployments use the v2 layout from `init`.
433+
434+
### Member record: optional precision limit
435+
436+
`FamilyMember.precision_limit` uses `PrecisionLimitOpt` (`None` / `Some(PrecisionSpendingLimit)`) because Soroban `contracttype` does not support `Option<CustomStruct>` in the same way as Rust’s `Option` for storage encoding.
437+
405438
## Error Codes
406439

407440
The contract uses a comprehensive error code system for validation failures:

family_wallet/docs/family-wallet-design.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,3 +444,9 @@ composability and for frontends that need to display specific error messages.
444444
```bash
445445
cargo test -p family_wallet
446446
```
447+
448+
## Transaction archive and pending cleanup (v2)
449+
450+
Authoritative design notes for `archive_old_transactions`, `get_archived_transactions`, and `cleanup_expired_pending` (storage layout, retention cutoff, authorization, and integrity checks) live in the repository root document:
451+
452+
- `docs/family-wallet-design.md` — section **Transaction archive and pending cleanup**

0 commit comments

Comments
 (0)