Skip to content

[SC] Oracle security: admin rotation and emergency pause #27

Description

@Cbiux

Summary

Harden score-registry for production: oracle admin rotation, emergency pause, and operational runbooks. The current contract sets admin once at init with no recovery path if the oracle key is compromised.


Problem

Risk Current behavior
Oracle key leaked Attacker can set arbitrary scores forever
Incident / bug No way to stop attestations without redeploying
Key rotation Must deploy new contract

Production oracle secret lives in Vercel (ORACLE_SECRET_KEY) — rotation must be supported on-chain and off-chain.


Proposed contract functions

File: Contracts/score-registry/src/lib.rs

const PAUSED_KEY: &str = "PAUSED";

pub fn transfer_admin(env: Env, new_admin: Address) {
    let admin = /* current admin */;
    admin.require_auth();
    env.storage().instance().set(&ADMIN_KEY, &new_admin);
}

pub fn pause(env: Env) {
    admin.require_auth();
    env.storage().instance().set(&PAUSED_KEY, &true);
}

pub fn unpause(env: Env) {
    admin.require_auth();
    env.storage().instance().set(&PAUSED_KEY, &false);
}

pub fn paused(env: Env) -> bool {
    env.storage().instance().get(&PAUSED_KEY).unwrap_or(false)
}

Modify set_score / batch:

if paused(env) { panic!("attestation paused"); }

Operational runbook (add to Contracts/README.md)

Rotate oracle key

  1. Generate new Stellar keypair (secure enclave / password manager)
  2. Fund new public key with XLM for fees
  3. Call transfer_admin(new_public_key) signed by old oracle
  4. Update Vercel ORACLE_SECRET_KEY to new secret
  5. Redeploy not required if only key changes
  6. Verify attest with test wallet

Emergency pause

  1. Call pause() via current admin
  2. API returns 503 with clear message (update contracts.controller.ts)
  3. Investigate incident
  4. Call unpause() when safe

Server changes

File: Server/src/controllers/contracts.controller.ts

When simulation/ submit fails due to pause:

{
  "success": false,
  "error": "On-chain attestation paused by oracle. Try again later."
}

Optional admin-only HTTP route to check pause state (read-only RPC call).


Future enhancement (separate issue)

Two-step admin transfer: propose_admin + accept_admin prevents typo bricking.


Tests

  • set_score fails when paused
  • transfer_admin changes auth requirement
  • Old admin cannot call after transfer
  • paused() public read accurate

Acceptance criteria

  • pause / unpause / transfer_admin implemented
  • Runbook in Contracts/README.md
  • Server graceful 503 on pause
  • Deployed testnet demo of rotation on test contract
  • Security review note in README Deploy section

Related


References

  • OpenZeppelin pause patterns (conceptual analog)
  • ZCore incident: .env leaked on first Vercel deploy — rotate all secrets

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    GrantFox OSSPart of the GrantFox OSS programMaybe RewardedThis issue may receive a reward or bountyOfficial CampaignPart of an official ZCore campaignenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions