Skip to content

Research cross-contract and cross-network replay/nonce safety of admin-authorized operations #22

Description

@chonilius

Background / Context

Per the README, all three deployed testnet contracts share a single admin/treasury address (GBUXADZJ7O4NM7S7CDZYVXGP37M772D2TYMFBT2QFH2JSRCFEJPAVW5N), and mergefi-backend is expected to hold this admin keypair and sign release/refund/allocate/release_issue/withdraw/cancel_milestone calls across potentially multiple deployed instances (the README notes escrow/milestones/maintenance-pool "can share one admin key or use separate ones per environment"). None of the three contracts implement any custom __check_auth logic — they rely entirely on Soroban's built-in Address::require_auth()/authorization framework.

Problem Statement

This is a research task to rigorously verify (not assume) that Soroban's standard authorization framework provides adequate replay protection in this specific multi-contract, potentially multi-network, shared-admin-key deployment shape. Specific questions to resolve: does Soroban's built-in auth-entry nonce/signature-expiration scheme bind an authorized invocation to a specific contract address and network passphrase such that a valid signed authorization for, say, mergefi-escrow's release call could never be replayed against mergefi-milestones' release_issue (different function, but confirm the binding is to more than just "this admin's signature" generically) or against the same contract redeployed at a new address (e.g., after an upgrade, if the upgrade-mechanism work lands) or on a different network (testnet signature replayed on a future mainnet deployment using the same admin keypair, if the team reuses keys across environments contrary to the "separate ones per environment" README caveat)? What is the actual nonce/expiration-ledger window behavior for a standard require_auth() call with no custom auth contract, and what operational practices (e.g., never reusing the same keypair as admin across testnet and mainnet, regardless of what the contract enforces) does this imply mergefi-backend's deployment procedure should follow?

Requirements

  • Study Soroban's authorization framework internals (the SorobanAuthorizationEntry structure, its binding to network id, contract address, function name/args, and nonce/signature-expiration-ledger fields) directly from the Soroban protocol/host source or official specification, not secondhand summaries.
  • Produce concrete test scenarios (to the extent they're constructible in testutils::Env, which may have limits on simulating true multi-network/multi-deployment auth-entry replay — document what can and can't be empirically tested in the current test harness vs. what must be reasoned about from the specification alone) demonstrating the actual replay-safety boundaries.
  • Deliver a clear, actionable write-up: what Soroban's framework guarantees for free (contract-address and network binding, if confirmed), what it does not guarantee (anything cross-function within the same admin's authority, if that's found to be a gap), and what operational safeguards (key-per-environment policy, key rotation cadence) mergefi-backend's deployment runbook should therefore enforce, given no contract code change may be able to fix a pure operational-key-reuse risk.
  • If any genuine contract-level gap is found (not just an operational recommendation), file it as a precise, separate follow-up issue with a reproducing scenario, and implement a fix here if straightforward.

Acceptance Criteria

  • Written technical explanation of Soroban's SorobanAuthorizationEntry binding guarantees, sourced from primary Soroban documentation/specification
  • Explicit answer to each of the three replay scenarios posed (cross-function within a contract, cross-contract-address after redeploy, cross-network with key reuse)
  • Test scenarios implemented for whatever is empirically testable in testutils::Env, with clear notes on what isn't
  • Operational recommendations for mergefi-backend's key-management practices
  • Any genuine contract-level gap found is fixed or filed with a precise repro

Technical Notes / Hints

  • Relevant primitive: every privileged call in this codebase uses the plain pattern require_admin(&env)?.require_auth() with no custom __check_auth/CustomAccountInterface — the analysis is entirely about the guarantees of Soroban's default account-based authorization for a classic (non-contract) admin address, which is very likely what's actually deployed per the README's G...-prefixed admin address.
  • soroban_sdk::testutils::Address as _/Ledger and the env.mock_all_auths() shortcut used throughout the existing test suite deliberately bypasses real auth-entry construction — this research task requires looking past that shortcut to the real signed-authorization-entry mechanics.

Difficulty Justification

This demands genuinely specialized knowledge of Soroban's authorization/signature-binding internals that goes well beyond what's exercised by this repo's existing mock_all_auths()-based tests, careful primary-source research rather than assumption or analogy from other chains, and the judgment to correctly separate "the protocol guarantees this" from "this needs an operational policy because the protocol can't prevent it" — exactly the kind of ambiguous, investigation-first task a spike is for.

Metadata

Metadata

Assignees

No one assigned

    Labels

    GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26securitySecurity-related issuevery hardVery difficult task, expert-level effort required

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions