Why this matters now:
The engine supports a multi-oracle roster (added as a feature). Every call to submit_proof, approve_proof, reject_proof, dispute_proof, and resolve_dispute calls require_oracle → is_registered_oracle → read_oracles → iterates the full Vec<Address>. With even 10 oracles this is a 10-entry Vec scan on every proof operation. This is a compute cost and CPU-instruction budget problem that grows linearly with oracle count, directly threatening the test_approve_proof_budget budget assertion that must stay under 50% of the Soroban CPU limit.
Problem / What:
storage::is_registered_oracle calls read_oracles (a full Vec read from instance storage) then calls .iter().any(|o| o == *addr). This is O(n) in oracle count for every auth check. The fix is to mirror the Sponsor pattern already used in task-registry: store oracle membership as DataKey::OracleFlag(Address) -> bool in instance storage (O(1) lookup), maintaining the DataKey::Oracles Vec only for the get_oracles() enumeration endpoint. Both structures must be kept in sync on add_oracle, remove_oracle, and set_oracle.
Key Challenges:
add_oracle, remove_oracle, and set_oracle must maintain both the OracleFlag map and the Oracles Vec atomically (Soroban has no transactions, but both writes happen in the same contract invocation).
set_oracle replaces the entire roster — must clear all existing OracleFlag entries before setting the new one; requires iterating the old Vec exactly once during the replacement.
- Budget test
test_approve_proof_budget must still pass (and should improve).
Acceptance Criteria:
is_registered_oracle(addr) performs exactly 1 instance storage read regardless of oracle count.
add_oracle, remove_oracle, set_oracle keep OracleFlag(Address) and Oracles Vec consistent.
get_oracles() still returns the full list.
- All existing oracle tests pass.
test_approve_proof_budget CPU budget assertion still passes.
Relevant files/functions:
contracts/reward-engine/src/storage.rs — is_registered_oracle, push_oracle, remove_oracle_from_list, write_oracles, read_oracles, DataKey
contracts/reward-engine/src/verification.rs — require_oracle, add_oracle, remove_oracle, set_oracle
Out of scope: Changing oracle authorization semantics, TTL management.
Why this matters now:
The engine supports a multi-oracle roster (added as a feature). Every call to
submit_proof,approve_proof,reject_proof,dispute_proof, andresolve_disputecallsrequire_oracle→is_registered_oracle→read_oracles→ iterates the fullVec<Address>. With even 10 oracles this is a 10-entry Vec scan on every proof operation. This is a compute cost and CPU-instruction budget problem that grows linearly with oracle count, directly threatening thetest_approve_proof_budgetbudget assertion that must stay under 50% of the Soroban CPU limit.Problem / What:
storage::is_registered_oraclecallsread_oracles(a full Vec read from instance storage) then calls.iter().any(|o| o == *addr). This is O(n) in oracle count for every auth check. The fix is to mirror theSponsorpattern already used intask-registry: store oracle membership asDataKey::OracleFlag(Address) -> boolin instance storage (O(1) lookup), maintaining theDataKey::OraclesVec only for theget_oracles()enumeration endpoint. Both structures must be kept in sync onadd_oracle,remove_oracle, andset_oracle.Key Challenges:
add_oracle,remove_oracle, andset_oraclemust maintain both theOracleFlagmap and theOraclesVec atomically (Soroban has no transactions, but both writes happen in the same contract invocation).set_oraclereplaces the entire roster — must clear all existingOracleFlagentries before setting the new one; requires iterating the old Vec exactly once during the replacement.test_approve_proof_budgetmust still pass (and should improve).Acceptance Criteria:
is_registered_oracle(addr)performs exactly 1 instance storage read regardless of oracle count.add_oracle,remove_oracle,set_oraclekeepOracleFlag(Address)andOraclesVec consistent.get_oracles()still returns the full list.test_approve_proof_budgetCPU budget assertion still passes.Relevant files/functions:
contracts/reward-engine/src/storage.rs—is_registered_oracle,push_oracle,remove_oracle_from_list,write_oracles,read_oracles,DataKeycontracts/reward-engine/src/verification.rs—require_oracle,add_oracle,remove_oracle,set_oracleOut of scope: Changing oracle authorization semantics, TTL management.