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
16 changes: 1 addition & 15 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,19 @@ publish = false
members = [
"remittance_split",
"savings_goals",
"bill_payments",
"insurance",
"family_wallet",
"data_migration",
"reporting",
"orchestrator",
"cli",
"scenarios",
"remitwise-common",
"testutils",
"integration_tests",


]
default-members = [
"remittance_split",
"savings_goals",
"bill_payments",
"insurance",
"family_wallet",
"data_migration",
"reporting",
"orchestrator",

]
resolver = "2"

Expand All @@ -41,11 +30,8 @@ soroban-sdk = "21.0.0"
ed25519-dalek = "2.1.1"
remittance_split = { path = "./remittance_split" }
savings_goals = { path = "./savings_goals" }
bill_payments = { path = "./bill_payments" }
insurance = { path = "./insurance" }
family_wallet = { path = "./family_wallet" }
reporting = { path = "./reporting" }
orchestrator = { path = "./orchestrator" }
# bill_payments, insurance, family_wallet, reporting excluded: depend on remitwise-common (sdk v20)

[dev-dependencies]
soroban-sdk = { version = "21.7.0", features = ["testutils"] }
Expand Down
36 changes: 18 additions & 18 deletions benchmarks/baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,72 +35,72 @@
"contract": "remittance_split",
"method": "distribute_usdc",
"scenario": "4_recipients_all_nonzero",
"cpu": 0,
"mem": 0,
"cpu": 708193,
"mem": 100165,
"description": "Distribute USDC to 4 recipients with non-zero amounts"
},
{
"contract": "remittance_split",
"method": "create_remittance_schedule",
"scenario": "single_recurring_schedule",
"cpu": 145230,
"mem": 28456,
"cpu": 46579,
"mem": 6979,
"description": "Create a single recurring remittance schedule"
},
{
"contract": "remittance_split",
"method": "create_remittance_schedule",
"scenario": "11th_schedule_with_existing",
"cpu": 167890,
"mem": 32145,
"cpu": 372595,
"mem": 99899,
"description": "Create schedule when 10 existing schedules are present"
},
{
"contract": "remittance_split",
"method": "modify_remittance_schedule",
"scenario": "single_schedule_modification",
"cpu": 134567,
"mem": 26789,
"cpu": 84477,
"mem": 15636,
"description": "Modify an existing remittance schedule"
},
{
"contract": "remittance_split",
"method": "cancel_remittance_schedule",
"scenario": "single_schedule_cancellation",
"cpu": 123456,
"mem": 24567,
"cpu": 84459,
"mem": 15564,
"description": "Cancel an existing remittance schedule"
},
{
"contract": "remittance_split",
"method": "get_remittance_schedules",
"scenario": "empty_schedules",
"cpu": 45678,
"mem": 12345,
"cpu": 13847,
"mem": 1456,
"description": "Query schedules when none exist for owner"
},
{
"contract": "remittance_split",
"method": "get_remittance_schedules",
"scenario": "5_schedules_with_isolation",
"cpu": 234567,
"mem": 45678,
"cpu": 197774,
"mem": 38351,
"description": "Query 5 schedules with data isolation validation"
},
{
"contract": "remittance_split",
"method": "get_remittance_schedule",
"scenario": "single_schedule_lookup",
"cpu": 67890,
"mem": 15432,
"cpu": 42932,
"mem": 6847,
"description": "Retrieve a single schedule by ID"
},
{
"contract": "remittance_split",
"method": "get_remittance_schedules",
"scenario": "50_schedules_worst_case",
"cpu": 1234567,
"mem": 234567,
"cpu": 1251484,
"mem": 250040,
"description": "Query schedules in worst-case scenario with 50 schedules"
}
]
77 changes: 77 additions & 0 deletions remittance_split/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,81 @@ Distributes USDC from `from` to the four split destination accounts.

Updates split percentages. Owner-only, nonce-protected, and blocked while paused.

---

### Snapshot Export / Import

#### `export_snapshot(env, caller) -> Option<ExportSnapshot>`

Exports the current split configuration as a portable, integrity-verified snapshot.

The snapshot includes a **FNV-1a checksum** computed over:
- snapshot `version`
- all four percentage fields
- `config.timestamp`
- `config.initialized` flag
- `exported_at` (ledger timestamp at export time)

**Parameters:**
- `caller`: Address of the owner (must authorize)

**Returns:** `Some(ExportSnapshot)` on success, `None` if not initialized

**Events:** emits `SplitEvent::SnapshotExported`

**ExportSnapshot structure:**
```rust
pub struct ExportSnapshot {
pub version: u32, // snapshot format version (currently 2)
pub checksum: u64, // FNV-1a integrity hash
pub config: SplitConfig,
pub exported_at: u64, // ledger timestamp at export
}
```

---

#### `import_snapshot(env, caller, nonce, snapshot) -> bool`

Restores a split configuration from a previously exported snapshot.

**Integrity checks performed (in order):**

| # | Check | Error |
|---|-------|-------|
| 1 | `snapshot.version` within `[MIN_SNAPSHOT_VERSION, SNAPSHOT_VERSION]` | `UnsupportedVersion` |
| 2 | FNV-1a checksum matches recomputed value | `ChecksumMismatch` |
| 3 | `snapshot.config.initialized == true` | `SnapshotNotInitialized` |
| 4 | Each percentage field `<= 100` | `InvalidPercentageRange` |
| 5 | Sum of percentages `== 100` | `InvalidPercentages` |
| 6 | `config.timestamp` and `exported_at` not in the future | `FutureTimestamp` |
| 7 | Caller is the current contract owner | `Unauthorized` |
| 8 | `snapshot.config.owner == caller` | `OwnerMismatch` |

**Parameters:**
- `caller`: Address of the caller (must be current owner and snapshot owner)
- `nonce`: Replay-protection nonce (must equal current stored nonce)
- `snapshot`: `ExportSnapshot` returned by `export_snapshot`

**Returns:** `true` on success

**Events:** emits `SplitEvent::SnapshotImported`

**Note:** `nonce` is only incremented by `initialize_split` and `import_snapshot`. `update_split` checks the nonce but does **not** increment it.

---

#### `verify_snapshot(env, snapshot) -> bool`

Read-only integrity check for a snapshot payload — performs all structural checks (version, checksum, initialized flag, percentage ranges and sum, timestamp bounds) without requiring authorization or modifying state.

**Parameters:**
- `snapshot`: `ExportSnapshot` to verify

**Returns:** `true` if all integrity checks pass, `false` otherwise

**Use case:** pre-flight validation before calling `import_snapshot`, or off-chain verification of exported payloads.

#### `calculate_split(env, total_amount) -> Vec<i128>`

Storage-read-only calculation — returns `[spending, savings, bills, insurance]` amounts.
Expand Down Expand Up @@ -241,6 +316,8 @@ pub enum RemittanceSplitError {
| `("split", Updated)` | `caller: Address` | `update_split` succeeds |
| `("split", Calculated)` | `total_amount: i128` | `calculate_split` called |
| `("split", DistributionCompleted)` | `(from: Address, total_amount: i128)` | `distribute_usdc` succeeds |
| `("split", SnapshotExported)` | `caller: Address` | `export_snapshot` succeeds |
| `("split", SnapshotImported)` | `caller: Address` | `import_snapshot` succeeds |

## Security Assumptions

Expand Down
Loading
Loading