Integer Scaling Protection Failure in Resource Deposit/Burnback Reconciliation
Problem Statement
The contracts/src/resource_tokenization/reconciliation.rs module reconciles on-chain token supply with off-chain resource deposit attestations. The reconcile_deposit() function at line 110 computes tokens_to_mint = deposit_amount * TOKEN_SCALE_FACTOR / ASSET_PRECISION, where TOKEN_SCALE_FACTOR = 10^18 and ASSET_PRECISION = 10^6 (micro-units for commodity measurement). When deposit_amount is very large (e.g., 9.22e18 micro-units of water), the intermediate multiplication deposit_amount * TOKEN_SCALE_FACTOR = 9.22e36 overflows u128::MAX (3.4e38 is the limit, but 9.22e36 is close). However, a crafted input of deposit_amount = 340282366920938463463374607431768211455 (u128::MAX) paired with ASSET_PRECISION = 1 causes u128::MAX * 10^18 to overflow silently, wrapping to a small value. This allows minting tokens worth only 0.0001% of the deposited resource, or conversely — if ASSET_PRECISION is configured incorrectly — minting vastly more tokens than the deposited resource value.
State Invariants & Parameters
TOKEN_SCALE_FACTOR: 10^18 (18 decimals — Soroban token standard)
ASSET_PRECISION: configurable, range [1, 10^12]
MAX_DEPOSIT_AMOUNT: u128::MAX / TOKEN_SCALE_FACTOR (safe bound)
- Reconciliation grace period: 100 ledger closes (~500s)
- Invariant:
tokens_minted * ASSET_PRECISION ≈ deposit_amount * TOKEN_SCALE_FACTOR within precision tolerance
Affected Code Paths
contracts/src/resource_tokenization/reconciliation.rs:105-140 — reconcile_deposit() multiplication without overflow check
contracts/src/resource_tokenization/config.rs:30-55 — ASSET_PRECISION configuration with no bounds validation
contracts/src/resource_tokenization/tests/reconciliation_tests.rs — No overflow edge-case tests
Resolution Blueprint
- Add a pre-validation guard in
reconcile_deposit(): require!(deposit_amount <= MAX_SAFE_DEPOSIT, "deposit exceeds safe computation range") where MAX_SAFE_DEPOSIT = u128::MAX / TOKEN_SCALE_FACTOR.
- Replace
checked_mul().checked_div() pattern: compute tokens_to_mint = deposit_amount.checked_mul(TOKEN_SCALE_FACTOR).ok_or(OverflowError)?.checked_div(ASSET_PRECISION).ok_or(DivByZeroError)?. Return Err instead of panicking to allow graceful rejection.
- Add config bounds:
require!(ASSET_PRECISION >= 1 && ASSET_PRECISION <= 10^12, "invalid precision") at configuration time.
- Implement a
SafeScale utility struct in contracts/src/resource_tokenization/safe_scale.rs that tracks intermediate results as (numerator: U256, denominator: U256) using the uint crate for 512-bit arithmetic, then converts back to u128 at the end.
- Add property-based tests with randomly generated
deposit_amount ∈ [0, u128::MAX] and ASSET_PRECISION ∈ [1, 10^12], verifying |result_u128 - exact_u256| <= 1 (rounding error at most 1 base unit).
Labels
Complexity: Hardcore
Layer: Core-Engine
Type: Race-Condition
Integer Scaling Protection Failure in Resource Deposit/Burnback Reconciliation
Problem Statement
The
contracts/src/resource_tokenization/reconciliation.rsmodule reconciles on-chain token supply with off-chain resource deposit attestations. Thereconcile_deposit()function at line 110 computestokens_to_mint = deposit_amount * TOKEN_SCALE_FACTOR / ASSET_PRECISION, whereTOKEN_SCALE_FACTOR = 10^18andASSET_PRECISION = 10^6(micro-units for commodity measurement). Whendeposit_amountis very large (e.g.,9.22e18micro-units of water), the intermediate multiplicationdeposit_amount * TOKEN_SCALE_FACTOR = 9.22e36overflowsu128::MAX(3.4e38 is the limit, but 9.22e36 is close). However, a crafted input ofdeposit_amount = 340282366920938463463374607431768211455(u128::MAX) paired withASSET_PRECISION = 1causesu128::MAX * 10^18to overflow silently, wrapping to a small value. This allows minting tokens worth only 0.0001% of the deposited resource, or conversely — ifASSET_PRECISIONis configured incorrectly — minting vastly more tokens than the deposited resource value.State Invariants & Parameters
TOKEN_SCALE_FACTOR: 10^18 (18 decimals — Soroban token standard)ASSET_PRECISION: configurable, range [1, 10^12]MAX_DEPOSIT_AMOUNT:u128::MAX / TOKEN_SCALE_FACTOR(safe bound)tokens_minted * ASSET_PRECISION ≈ deposit_amount * TOKEN_SCALE_FACTORwithin precision toleranceAffected Code Paths
contracts/src/resource_tokenization/reconciliation.rs:105-140—reconcile_deposit()multiplication without overflow checkcontracts/src/resource_tokenization/config.rs:30-55—ASSET_PRECISIONconfiguration with no bounds validationcontracts/src/resource_tokenization/tests/reconciliation_tests.rs— No overflow edge-case testsResolution Blueprint
reconcile_deposit():require!(deposit_amount <= MAX_SAFE_DEPOSIT, "deposit exceeds safe computation range")whereMAX_SAFE_DEPOSIT = u128::MAX / TOKEN_SCALE_FACTOR.checked_mul().checked_div()pattern: computetokens_to_mint = deposit_amount.checked_mul(TOKEN_SCALE_FACTOR).ok_or(OverflowError)?.checked_div(ASSET_PRECISION).ok_or(DivByZeroError)?. ReturnErrinstead of panicking to allow graceful rejection.require!(ASSET_PRECISION >= 1 && ASSET_PRECISION <= 10^12, "invalid precision")at configuration time.SafeScaleutility struct incontracts/src/resource_tokenization/safe_scale.rsthat tracks intermediate results as(numerator: U256, denominator: U256)using theuintcrate for 512-bit arithmetic, then converts back tou128at the end.deposit_amount ∈ [0, u128::MAX]andASSET_PRECISION ∈ [1, 10^12], verifying|result_u128 - exact_u256| <= 1(rounding error at most 1 base unit).Labels
Complexity: HardcoreLayer: Core-EngineType: Race-Condition