Skip to content

initialize accepts admin == operator with no validation or warning: the two-party consent at deployment can be a single address #514

Description

@usmanimamu17-create

Problem

initialize (src/lib.rs) requires both addresses to authorize but never requires them to be distinct:

pub fn initialize(env: Env, admin: Address, operator: Address) -> Result<(), SLAError> {
    ...
    admin.require_auth();
    operator.require_auth();   // two signatures required...
    env.storage().instance().set(&ADMIN_KEY, &admin);
    env.storage().instance().set(&OPERATOR_KEY, &operator);   // ...but they may be the same address
    ...
}

The two-party requirement is real (both must sign), yet the same address can fill both roles — one signature satisfies require_auth twice.

Consequences:

  • The separation-of-duties the design implies is optional at deployment: the role model (fix: cross_contract_safety hardcodes error symbol and drops context #28) exists so config/roles (admin) and calculations (operator) are distinct; initialize lets a single address hold both, and the runtime setters can recreate that state (companion issue), so the invariant is never established.
  • The two-party consent is illusory in the single-address case: require_auth() on the same address twice is one signature; a deployer reading the code as "two parties must agree" is misled.
  • No signal marks the merged deployment: get_full_audit_state returns admin == operator with no flag, so downstream tooling cannot distinguish "deliberate single-key deployment" from "misconfiguration".

Root cause

initialize was written to require auth from both arguments without considering that the arguments may be equal; no role-distinctness invariant was defined (the setters companion issue shows the same gap).

Why this is architecturally hard

  1. Enforcing distinct roles at initialize is a behavior change for any existing deployment that merged them; the error choice (reuse InvalidInput vs. new variant) is an ABI decision (companion error-catalog issue).
  2. The policy question — must admin and operator always differ? — affects set_operator/propose_operator (companion issue) and the renounce flow; the invariant should be defined once and enforced at every mutation point.
  3. The two-party semantics (both must authorize) and the distinctness rule are separate contracts; the docs should state both, and tests should cover the single-address case.

Acceptance criteria

  • initialize with admin == operator is either rejected or explicitly documented as a supported mode.
  • The role-distinctness invariant is documented and enforced consistently across initialize and the role setters.
  • Tests cover the equal-address deployment case.

Out of scope

The role-setter equal-role gap (companion issue in batch 08) and the two-party initialize descriptor mislabel (companion issue in batch 03).

Getting started

just test

Good first files to read: apexchainx_calculator/src/lib.rs (initialize, require_admin, require_operator).

Activity

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

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardStellar WaveIssues in the Stellar wave programThird CampaignCampaign: Third Campaignarea/governanceImported campaign issue labelpriority/mediumImported campaign issue label

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions