Skip to content

Race Condition in Resource Tokenization Mint/Burn State Machine #1

Description

@elizabetheonoja-art

Race Condition in Resource Tokenization Mint/Burn State Machine

Problem Statement

The contracts/src/resource_tokenization/mint_burn.rs module manages the minting and burning of resource-backed tokens representing utility commodities (water, energy, bandwidth). The mint() function at line 95 checks total_supply <= MAX_SUPPLY before minting, while burn() at line 140 decrements total_supply. Under concurrent invocation within the same ledger close, two mint() calls can both observe total_supply == MAX_SUPPLY - 1 and both proceed, causing total_supply to exceed MAX_SUPPLY by 1. This breaks the invariant that total_supply == ∑(token_balances), leading to a tokenization imbalance where more tokens are minted than backed by real-world resource deposits. The Stellar Soroban host does not provide native atomic compare-and-swap across contract storage keys.

State Invariants & Parameters

  • MAX_SUPPLY: 1_000_000_000_000_000 (10^15 base units)
  • MIN_MINT_AMOUNT: 1_000_000 (10^6 base units)
  • Ledger close time: 3-5 seconds
  • resource_reserve_ratio: 1:1 (each token = 1 resource unit)
  • Invariant violation: total_supply > ∑(token_balances) after race

Affected Code Paths

  • contracts/src/resource_tokenization/mint_burn.rs:90-130 — Check-and-mint without atomicity
  • contracts/src/resource_tokenization/state.rs:40-65total_supply stored as single u128 non-atomically
  • contracts/src/resource_tokenization/tests/mint_burn_tests.rs — No concurrent invocation tests

Resolution Blueprint

  1. Replace the read-check-write pattern with an atomic try_mint() using storage_has() guard: set a MINT_INFLIGHT flag (storage_set(&MINT_LOCK_KEY, &true)) before the supply check and clear it after. If the flag is already set, panic!("concurrent mint detected").
  2. Use a u128 overflow-safe accumulator pattern: let new_supply = supply.checked_add(amount).expect("supply overflow") — this eliminates silent overflow but doesn't solve the race.
  3. Implement a two-phase commit: Phase 1 reserves tokens via storage_set(&RESERVATION_KEY, &amount), Phase 2 finalizes. A background finalization process scans for uncommitted reservations.
  4. Add property-based concurrency test with simulated_concurrent_calls = 100 using Env::with_mock_host() that asserts total_supply <= MAX_SUPPLY after all calls complete.
  5. Document the locking protocol in contracts/docs/specs/mint-atomicity.md.

Labels

  • Complexity: Hardcore
  • Layer: Core-Engine
  • Type: Race-Condition

Metadata

Metadata

Assignees

Labels

Complexity: HardcoreIssues requiring deep systems-level engineering rigorGrantFox OSSIssue tracked in GrantFox OSSLayer: Core-EngineCore contract engine layerMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official CampaignType: Race-ConditionConcurrency and race condition related issues

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions