Skip to content

Storage Key Collision Between Resource Token and Tariff Namespace #9

Description

@elizabetheonoja-art

Problem Statement / Feature Objective
Resource token contracts and tariff oracle contracts share the same Soroban storage namespace when deployed under the same contract ID prefix, leading to potential key collisions if both contracts use raw DataKey enum discriminants that happen to serialize to the same byte sequence. For example, if DataKey::Balance(user) in the resource token serializes to 0x01 + user_32_bytes and DataKey::TariffRate(tier) in the tariff oracle serializes to 0x01 + tier_32_bytes, a malicious user could craft a userId that matches a tierId byte sequence, overwriting critical tariff configuration. Soroban storage is flat key-value; no automatic namespace scoping exists. The objective is to implement a domain-separated storage namespace using a unique 4-byte prefix per contract module.

Technical Invariants & Bounds
Each contract module must define a NAMESPACE_PREFIX: [u8; 4] constant unique across the protocol: RESOURCE = 0x5245534f, TARIFF = 0x54415249, SETTLEMENT = 0x5345544c, COMMON = 0x434f4d4d. All DataKey enum variants must be serialized as namespace_prefix || enum_discriminant || payload. Storage keys must never exceed 64 bytes total (Soroban key limit). Existing storage keys may collide silently; a migration script must be run to re-write all storage entries under the new key format. The migration must be done in a single contract upgrade transaction. Each storage entry read/write costs ~5,000 instructions; a full namespace migration over 10,000 entries would cost ~50M instructions (~5 stroops). Legacy key reads must return an error Err(StorageError::ObsoleteKey) after migration.

Codebase Navigation Guide
Storage key definitions: contracts/resource-token/src/storage.rs (DataKey enum), contracts/tariff-oracle/src/storage.rs (DataKey enum), contracts/settlement/src/storage.rs (DataKey enum). Key serialization logic: each file has a fn encode(&self) -> Vec or Symbol::new() conversion. The contracts/common/src/storage_utils.rs contains shared storage helpers. Deployment addresses are derived in contracts/common/src/address_utils.rs. Tests are spread across */src/test.rs. Soroban SDK docs for storage are likely referenced in contracts/common/src/lib.rs re-exports.

Implementation Blueprint
Step 1: Create contracts/common/src/namespace.rs with a StorageNamespace trait requiring const PREFIX: [u8; 4] and a method fn scoped_key(&self, raw: &[u8]) -> Vec that prepends prefix. Step 2: Add NAMESPACE_PREFIX constants to each contract's constants.rs (create if missing). Step 3: Refactor each DataKey::encode() to call scoped_key(). Step 4: Add a migration function migrate_namespace() in each contract that reads all legacy keys, writes them to new scoped keys, and deletes legacy keys. Step 5: In the contract-upgrade entry point (if exists), call migration before switching implementation. Step 6: Add a #[cfg(test)] unit test that verifies key uniqueness by enumerating all possible DataKey variants across all contracts and asserting no byte-level collisions. Step 7: Run full test suite to confirm no regressions.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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: Core-ArchitectureCore architecture design issues

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions