Skip to content

DeFi Oracle Network

**FinTech · DeFi · Decentralized data feeds · Aggregation trust**

Overview

DeFi Oracle Network

FinTech · DeFi · Decentralized data feeds · Aggregation trust

The problem

DeFi protocols consume off-chain data: crypto prices, FX rates, sports outcomes, weather for parametric insurance, real-world asset data. The industry’s oracle solutions (Chainlink, Pyth, API3, Band) each make specific security trade-offs:

  • Single-operator oracles (an exchange’s own API) = total centralization.
  • Staked committee oracles (Chainlink-style) = requires a native token and introduces the “stake-based slashing” economic model, which has its own failure modes (validator extraction, correlated failures during crashes).
  • Proof-of-stake signed feeds (Pyth) = consumers trust the chain’s validator set to have vetted signers.
  • First-price commit-reveal schemes = increase latency, and are complex.

The missing piece is subjectivity. A DeFi lending protocol with $500M TVL has different risk tolerance than a micro- application with $5k. They should weigh oracle reporters differently. Today they largely use the same feeds with the same aggregation.

Why Quidnug fits

Oracle feeds are signed claims from identifiable signers. A consumer’s acceptance of a feed should depend on the consumer’s trust in the signers. That’s relational trust.

ProblemQuidnug primitive
”Which reporters do I trust, and by how much?”Trust edges in oracles.price-feeds.*
”Has each report been signed by the reporter?”ECDSA-signed events
”Has this report been replayed?”Monotonic anchor nonces per reporter
”How do I aggregate many reports?”Consumer-side weighted by each reporter’s trust
”A reporter went rogue, how do I kick them?”Lower trust edge / guardian recovery on their side
”A new protocol wants to use these feeds”K-of-K bootstrap from trusted peers

High-level architecture

Off-chain data sources
┌──────────┬──────────┬──────────┐
│ │ │ │
Exchange A Exchange B Exchange C Reference
│ │ │ │
▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│Oracle-1│ │Oracle-2│ │Oracle-3│ │Oracle-4│ (reporter quids)
│Quid │ │Quid │ │Quid │ │Quid │
└────┬───┘ └────┬───┘ └────┬───┘ └────┬───┘
│ │ │ │
│ push │ push │ push │ push
│ gossip │ gossip │ gossip │ gossip
▼ ▼ ▼ ▼
┌──────────────────────────────────────────────────┐
│ Quidnug Network (consortium) │
│ │
│ Events on "oracles.price-feeds.ethereum.btc-usd" │
└──────────────────────────────────────────────────┘
│ │ │ │
│ pull │ pull │ pull │ pull
│ │ │ │
┌────▼───┐ ┌────▼───┐ ┌────▼───┐ ┌────▼───┐
│Lending │ │DEX │ │Insur- │ │Stable- │
│Protocol│ │ │ │ance │ │coin │
│(cons.) │ │ │ │ │ │ │
└────────┘ └────────┘ └────────┘ └────────┘
Each consumer computes their OWN aggregate:
weighted-sum(reports) weighted by their OWN trust
in each reporter in oracles.price-feeds.*

Data model

Quids

  • Reporters: one per oracle node operator (oracle-chainlink-geth, oracle-pyth-eth-1, etc.).
  • Consumers: one per DeFi protocol (lending-protocol-aave-fork-x).
  • Feed identity (optional): one per named feed (e.g., btc-usd-oracle-feed as a quid whose events are the feed), with the feed’s operators as its guardian set.

Domain

oracles.price-feeds.ethereum
├── oracles.price-feeds.ethereum.btc-usd
├── oracles.price-feeds.ethereum.eth-usd
├── oracles.price-feeds.ethereum.stablecoins
└── ...
oracles.weather-feeds.parametric-insurance
oracles.sports-feeds.prediction-markets

Domain scoping lets a consumer subscribe only to the feeds they care about and weigh reporters differently per domain.

A price report

{
"type": "EVENT",
"subjectId": "btc-usd-oracle-feed", /* the feed's quid */
"subjectType": "QUID",
"eventType": "oracle.price-report",
"payload": {
"reporter": "oracle-chainlink-geth",
"symbol": "BTC-USD",
"price": "67423.50",
"timestamp": 1713400000,
"source": "aggregate of Binance, Coinbase, Kraken",
"confidence": 0.95,
"roundId": 12345
},
"signature": "<reporter's ECDSA over canonical bytes>"
}

Each report is one event on the feed’s stream. Events are naturally ordered by the stream’s monotonic sequence; consumers process the most recent window.

Consumer-side aggregation

A consumer’s logic for determining the “effective price”:

type WeightedReport struct {
Reporter string
Price float64
Trust float64
Age time.Duration
}
func (c *Consumer) AggregatePrice(ctx context.Context, feedID string, window time.Duration) (float64, error) {
events, err := c.quidnug.GetRecentEvents(feedID, "QUID", window)
if err != nil { return 0, err }
reports := make([]WeightedReport, 0, len(events))
for _, ev := range events {
if ev.EventType != "oracle.price-report" { continue }
reporter := ev.Payload["reporter"].(string)
price, _ := strconv.ParseFloat(ev.Payload["price"].(string), 64)
// Relational trust in this reporter FROM THIS CONSUMER's view.
trust, err := c.quidnug.GetTrust(ctx, c.selfQuid, reporter,
"oracles.price-feeds.ethereum.btc-usd", nil)
if err != nil { continue }
reports = append(reports, WeightedReport{
Reporter: reporter,
Price: price,
Trust: trust.TrustLevel,
Age: time.Since(time.Unix(int64(ev.Payload["timestamp"].(float64)), 0)),
})
}
return c.weightedMedian(reports), nil
}
// weightedMedian picks the price at the middle of trust-weighted
// cumulative distribution. Resistant to outliers AND to low-
// trust reporters' input.
func (c *Consumer) weightedMedian(reports []WeightedReport) float64 {
// Filter out reports older than some max age, below some min trust.
filtered := filter(reports, func(r WeightedReport) bool {
return r.Trust >= c.MinReporterTrust && r.Age < c.MaxReportAge
})
sort.Slice(filtered, func(i, j int) bool { return filtered[i].Price < filtered[j].Price })
var totalWeight float64
for _, r := range filtered { totalWeight += r.Trust }
cumulative := 0.0
for _, r := range filtered {
cumulative += r.Trust
if cumulative >= totalWeight/2 {
return r.Price
}
}
return 0
}

Consumer variations

  • Large lending protocol: requires ≥ 5 reports with trust ≥ 0.8 from reporters whose feeds haven’t been countered recently.
  • Stablecoin: extra conservative. Weighted median of trust ≥ 0.9 reporters. Also checks for staleness: if no reporter has updated in > 1 minute, circuit-break.
  • Small experimental DEX: uses trust ≥ 0.5 minimum, tolerates one-off outliers.

Same data feed, very different effective prices for different consumers. That’s the whole point.

Reporter failure handling

Scenario: exchange API outage

Oracle-1 sources from Exchange A. Exchange A goes down; Oracle-1 stops reporting. Consumers that had Oracle-1 at high trust will see their aggregate drop one report.

Automatic recovery: as long as enough other reporters are active, consumers’ aggregation still works.

Trust-edge decay: consumers might naturally apply a staleness discount, a reporter silent for > 5 minutes has effective trust 0.

Scenario: reporter compromised

Oracle-2’s signing key is stolen. Attacker starts reporting manipulated prices.

Detection: consumers can detect outliers (Oracle-2’s price diverges from the weighted-median of trusted reporters).

Response path:

  1. Consumer lowers trust in Oracle-2 to 0 locally.
  2. Oracle-2’s operator is notified (out-of-band).
  3. Operator initiates guardian recovery to rotate Oracle-2’s key to a new HSM.
  4. During the time-lock window, the compromised key still signs but its reports are low-weight from most consumers’ perspectives, operator plus consumer reactions limit damage.
  5. Post-rotation, old key is invalid; attacker is locked out.

Scenario: reporter goes rogue (not compromised, malicious by choice)

Oracle-3 deliberately reports inflated prices.

Response: same as compromised, except the operator itself is the attacker. Consumers simply set Oracle-3’s trust to 0 and look for new reporters to trust. No protocol-level “slashing” needed, market-level “not trusted anymore” is sufficient.

Key Quidnug features used

  • Relational trust per-consumer, different DeFi protocols weight the same reporters differently.
  • Event streams, feed = subject quid + append-only stream.
  • Push gossip (QDP-0005), feeds propagate within seconds.
  • Monotonic nonces (QDP-0001), replay protection on reports.
  • Guardian recovery (QDP-0002), reporter key recovery.
  • Domain hierarchy, per-asset-pair subdomains.
  • K-of-K bootstrap (QDP-0008), new consumer joins by fetching trust snapshots from K existing consumers.
  • Lazy epoch probe (QDP-0007), detect stale reporter state when a consumer hasn’t seen a reporter in a while.

Scale estimates

Representative deployment:

  • 50 reporters across major exchanges/aggregators
  • 200 feeds (BTC-USD, ETH-USD, 100+ alt pairs, FX, commodities)
  • 1 Hz update rate per feed = 200 events/second globally
  • 500 consumers (protocols)

Workload:

  • 17 M events/day
  • Each consumer node sees all feeds they subscribed to (sub-MB bandwidth)
  • Trust graph: 50 × 500 = 25,000 edges

Push gossip fanout latency: ~1 second per hop, ~3 hops max to full coverage. Comfortable.

Economic model

Reporters’ incentive to be honest: reporter fees paid in-band by protocols that use their feeds. Protocols with tighter trust requirements pay premium rates to reporters whose signatures carry high trust. Market-based reputation → market-based pricing.

This is not baked into the protocol. It’s an application-layer billing and contract concern. Quidnug’s relational trust is the substrate that makes the economic model work cleanly.

Value delivered

DimensionBeforeWith Quidnug
Consumer customizationSingle “Chainlink feed” for everyoneEach consumer picks and weighs own reporters
Cross-chain feedChain-specific oracle contractsReporter quids are cross-chain; consumer bridge reads
Reporter compromise recoveryProtocol-level reconfiguration, downtimeGuardian recovery + market-level trust degrade
Onboarding new reportersToken staking + governance voteBuild reputation, earn trust edges organically
Feed resilienceTied to one oracle contract200 feeds × N reporters = high redundancy
Auditability of “why was this price?”Aggregator’s internal algorithmPer-consumer weighting is public + reproducible

What’s in this folder

Runnable POC

Full end-to-end demo at examples/defi-oracle-network/:

  • oracle_aggregation.py, pure weighted-median aggregation with MAD-based outlier filter, freshness window, trust floor, and minimum-reporter count.
  • oracle_aggregation_test.py, 10 pytest cases.
  • demo.py, five-step end-to-end flow: four reporters, two consumers with different trust graphs, consensus scenario, outlier scenario across three policy variants.
Terminal window
cd examples/defi-oracle-network
python demo.py

Implementation

Concrete API calls, pseudocode, signing shape.

Implementation: DeFi Oracle Network

Concrete steps to deploy a Quidnug-based oracle network.

0. Architecture topology

Four component roles:

  1. Reporter nodes, one per oracle operator. Each has a quid, runs its own Quidnug node, and periodically submits signed price reports.
  2. Feed identities, one quid per feed (BTC-USD, ETH-USD…). A feed’s stream accumulates reports from many reporters. The feed’s guardian set is the operators authorized to participate in that feed.
  3. Consumer nodes, DeFi protocols. Run Quidnug client code (or full node) to read feeds.
  4. Cross-chain bridge (out of scope for this doc), a separate component that reads Quidnug feeds and writes canonical prices to EVM smart contracts.

1. Set up a reporter

Terminal window
# Reporter-side Quidnug node configuration
ENABLE_NONCE_LEDGER=true
ENABLE_PUSH_GOSSIP=true
ENABLE_LAZY_EPOCH_PROBE=true
SUPPORTED_DOMAINS=oracles.price-feeds.*,oracles.weather-feeds.*
SEED_NODES=[...]
./bin/quidnug

Create the reporter quid with a guardian set for recovery:

Terminal window
curl -X POST http://localhost:8080/api/identities -d '{
"quidId": "oracle-operator-alpha",
"name": "Oracle Alpha",
"creator": "oracle-operator-alpha",
"updateNonce": 1,
"homeDomain": "oracles.price-feeds.ethereum",
"attributes": {
"operatorType": "independent",
"jurisdictions": ["US", "CH"],
"sourceExchanges": ["Binance", "Coinbase", "Kraken"]
}
}'
# Install guardian set, operator's security team is the guardians
curl -X POST http://localhost:8080/api/v2/guardian/set-update -d '{...}'

2. Create a feed identity (one-time setup, done by consortium)

A feed like “BTC-USD on Ethereum” is its own quid. Consortium governance (or an organizing entity) creates it:

Terminal window
curl -X POST http://localhost:8080/api/identities -d '{
"quidId": "btc-usd-eth-feed",
"name": "BTC-USD Ethereum Oracle Feed",
"creator": "oracle-consortium",
"updateNonce": 1,
"homeDomain": "oracles.price-feeds.ethereum.btc-usd"
}'
# The feed's guardian set lists authorized reporter operators
# (weight=1 each; threshold=1 since a single trusted reporter
# submitting a report is sufficient to accept into the stream).
curl -X POST http://localhost:8080/api/v2/guardian/set-update -d '{
"subjectQuid": "btc-usd-eth-feed",
"newSet": {
"guardians": [
{"quid":"oracle-operator-alpha","weight":1,"epoch":0},
{"quid":"oracle-operator-beta","weight":1,"epoch":0},
{"quid":"oracle-operator-gamma","weight":1,"epoch":0},
{"quid":"oracle-operator-delta","weight":1,"epoch":0},
{"quid":"oracle-operator-epsilon","weight":1,"epoch":0}
],
"threshold": 1,
"recoveryDelay": 3600000000000
},
...
}'

Alternatively, feeds can be open: any reporter can submit, and consumers individually decide whose reports to trust. Simpler but no protocol-level filter.

3. Reporter submits price reports

Reporter’s local process pulls prices from its sources and submits events every tick:

package reporter
import (
"context"
"encoding/json"
"net/http"
"time"
)
type Reporter struct {
quid string
feedID string
nodeAPI string
sources []PriceSource
anchorNonce int64
}
func (r *Reporter) Tick(ctx context.Context) error {
// 1. Collect from sources.
prices, err := r.collectPrices(ctx)
if err != nil { return err }
// 2. Aggregate (reporter's own heuristic, median, VWAP, etc.)
aggregated := r.aggregate(prices)
// 3. Build and sign an event.
r.anchorNonce++
payload := map[string]interface{}{
"reporter": r.quid,
"symbol": "BTC-USD",
"price": formatPrice(aggregated.Value),
"timestamp": time.Now().Unix(),
"source": "median of " + strings.Join(r.sourceNames(), ","),
"confidence": aggregated.Confidence,
"roundId": aggregated.Round,
"anchorNonce": r.anchorNonce,
}
event := map[string]interface{}{
"type": "EVENT",
"subjectId": r.feedID,
"subjectType": "QUID",
"eventType": "oracle.price-report",
"payload": payload,
"creator": r.quid,
}
event["signature"] = r.signEvent(event)
// 4. Submit.
body, _ := json.Marshal(event)
resp, err := http.Post(r.nodeAPI+"/api/v1/events", "application/json", bytes.NewReader(body))
if err != nil { return err }
defer resp.Body.Close()
return nil
}

Reporter ticks once per second for BTC-USD (or whatever the feed cadence is). Subscribers see the push gossip propagate within seconds.

4. Consumer subscribes and aggregates

Consumer (a DeFi protocol) runs a Quidnug read-mostly node:

package consumer
type Consumer struct {
selfQuid string
nodeAPI string
feedID string
minTrust float64 // e.g., 0.7
maxReportAge time.Duration // e.g., 30 * time.Second
minReporters int // e.g., 3
}
func (c *Consumer) LatestPrice(ctx context.Context) (float64, error) {
// 1. Fetch recent events in the feed.
events, err := c.readEvents(ctx)
if err != nil { return 0, err }
// 2. For each report, fetch relational trust in the reporter.
var reports []WeightedReport
for _, ev := range events {
if ev.EventType != "oracle.price-report" { continue }
reporter := ev.Payload["reporter"].(string)
trust, err := c.getTrust(ctx, c.selfQuid, reporter,
"oracles.price-feeds.ethereum.btc-usd")
if err != nil {
continue // reporter unknown → skip
}
if trust.TrustLevel < c.minTrust {
continue // not trusted enough
}
ts := time.Unix(int64(ev.Payload["timestamp"].(float64)), 0)
if time.Since(ts) > c.maxReportAge {
continue // stale
}
reports = append(reports, WeightedReport{
Reporter: reporter,
Price: parsePrice(ev.Payload["price"].(string)),
Trust: trust.TrustLevel,
Age: time.Since(ts),
})
}
if len(reports) < c.minReporters {
return 0, ErrInsufficientReporters
}
// 3. Weighted median.
return c.weightedMedian(reports), nil
}
func (c *Consumer) weightedMedian(reports []WeightedReport) float64 {
sort.Slice(reports, func(i, j int) bool { return reports[i].Price < reports[j].Price })
var total float64
for _, r := range reports { total += r.Trust }
cum := 0.0
for _, r := range reports {
cum += r.Trust
if cum >= total/2 {
return r.Price
}
}
return 0
}

Consumer can (and should) configure additional sanity checks:

  • Staleness circuit-breaker (“no update in 60s → halt lending”)
  • Cross-feed sanity (“if btc-usd and eth-usd drift differently from expected correlation, pause”)

5. Consumer trust declarations

Consumer initially declares trust in a set of known-good reporters:

Terminal window
# For each reporter the consumer vouches for
curl -X POST http://localhost:8080/api/trust -d '{
"truster":"lending-protocol-aave-fork",
"trustee":"oracle-operator-alpha",
"trustLevel":0.95,
"domain":"oracles.price-feeds.ethereum.btc-usd",
"nonce":1,
"validUntil":<now + 90d>
}'

Consumer can also leverage transitive trust. If Lending Protocol doesn’t know Reporter Gamma directly, but Reporter Alpha (whom Lending trusts) declared trust in Gamma, Lending gets a transitive view.

6. Cross-chain bridge (brief sketch)

A bridge component reads Quidnug feeds and writes to EVM:

func (b *Bridge) Tick() {
for _, feed := range b.feeds {
price, err := b.consumer.LatestPrice(context.Background())
if err != nil { continue }
// Submit to on-chain price-feed contract.
tx, err := b.contract.UpdatePrice(context.Background(),
feed.OnChainID, price)
...
}
}

This is a trusted bridge, the bridge operator is a single point for this specific on-chain contract. DeFi protocols that can’t tolerate bridge risk would run the bridge themselves.

7. Reporter compromise response

Oracle Alpha’s key is stolen. Incident response:

Alpha’s side

Terminal window
# Guardian recovery to rotate to a new key
curl -X POST http://localhost:8080/api/v2/guardian/recovery/init -d '{
"subjectQuid":"oracle-operator-alpha",
"fromEpoch":0,
"toEpoch":1,
"newPublicKey":"<hex>",
...
}'
# Time-lock elapses → commit → Alpha's epoch is now 1

Consumer-side (automatic)

Consumer’s next LatestPrice() call uses Quidnug’s epoch- validated signatures. Reports signed by Alpha’s epoch-0 key after the rotation will fail verification (they’d need to be signed at epoch 1). Attacker’s signatures are invalid; real Alpha’s new-epoch signatures are valid.

8. Bootstrap a new consumer

cfg := core.DefaultBootstrapConfig()
cfg.Quorum = 3
// Seed trust list, known-good existing consumers who can
// attest to the reporter set. The new consumer might get this
// list from the oracle consortium's website or from a trade
// association.
node.SeedBootstrapTrustList([]core.BootstrapTrustEntry{
{Quid:"oracle-operator-alpha",PublicKey:"<hex>"},
{Quid:"oracle-operator-beta", PublicKey:"<hex>"},
{Quid:"oracle-operator-gamma",PublicKey:"<hex>"},
}, 3)
sess, err := node.BootstrapFromPeers(ctx,
"oracles.price-feeds.ethereum.btc-usd", cfg)
if err != nil || sess.State != core.BootstrapQuorumMet {
log.Fatal("bootstrap failed: ", err)
}
node.ApplyBootstrapSnapshot(cfg.ShadowBlocks)
// After bootstrap, consumer declares its own trust edges
// (initially to the same reporters it bootstrapped from).

9. Monitoring

Prometheus metrics to alert on:

  • quidnug_events_total{type="oracle.price-report"} per reporter, detect a reporter going silent.
  • quidnug_gossip_rate_limited_total{producer="..."}, reporter exceeding rate; potentially compromised.
  • quidnug_probe_failure_total for high-value reporters, connectivity issue may hide a rotation.

App-layer alerts:

  • “Aggregate price diverges from external reference by > 1% for

    30s” → circuit-break.

  • “Fewer than N reporters active in the last minute” → halt price updates.
  • “Outlier report from a high-trust reporter” → alert; possible compromise.

10. Testing

func TestOracle_WeightedAggregation(t *testing.T) {
// 5 reporters with varying trust from consumer's view
// Submit reports at slightly different prices
// Expected: weighted median closer to higher-trust reporters' prices
}
func TestOracle_OutlierRejection(t *testing.T) {
// 5 trusted reporters report $67,000-$67,500
// 1 reporter reports $200 (attack / bug)
// Verify: weighted median still in trusted range
}
func TestOracle_ReporterKeyCompromise(t *testing.T) {
// Reporter's key rotated via guardian recovery
// Post-rotation, old-key signatures are rejected
}
func TestOracle_StalenessDetection(t *testing.T) {
// No new reports for >30s → consumer circuit-break
}

Where to go next

Threat model

Adversaries, assumed capabilities, mitigations.

Threat Model: DeFi Oracle Network

Assets

  1. Price accuracy, DeFi protocols relying on a feed can be drained via manipulated prices.
  2. Reporter identity integrity, a valid-looking signature that’s actually from a compromised key enables false reports.
  3. Consumer protocol treasury, ultimate target; an attacker manipulating a feed causes the protocol’s logic to transfer funds to the attacker.

Attackers

AttackerCapabilityMotivation
Flash-loan arbitrage attackerCan execute in a single tx, query oraclesProfit per block
Reporter compromiseValid reporter keyMarket manipulation
MEV bot operatorSees pending txsSandwich oracle updates
Malicious consortium memberOperates own reporterReputation destruction
External market manipulatorControls underlying source (e.g. a thin market)Manipulate reporter input

Threats and mitigations

T1. Compromised reporter reports manipulated prices

Attack. Attacker steals Oracle-X’s signing key and submits reports at $200 instead of $67,400 for BTC.

Mitigations.

  • Consumer-side outlier rejection, weighted median across trusted reporters. One outlier from one reporter doesn’t move the aggregate significantly.
  • Min-reporters requirement, consumer doesn’t trust an aggregate based on fewer than N reporters. Attacker has to compromise multiple reporters simultaneously.
  • Trust-based weighting, compromised reporter’s trust typically drops after first outlier (auto-de-weighting).
  • Emergency rotation, operator rotates the key via guardian recovery. Attacker’s signatures stop validating.

Residual risk. If attacker compromises the majority-by-trust of reporters a consumer relies on, they can manipulate. Hence the “diversify trust across many reporters” principle.

T2. Reporter operator going rogue

Attack. A reporter’s legitimate operator deliberately reports manipulated prices. Same on-wire effect as compromise but no “recovery” path (the operator is the attacker).

Mitigation.

  • Consumer-level: reduce that reporter’s trust to 0 locally. Other consumers make their own decisions.
  • The Quidnug network doesn’t require unanimous “kick”, bad actors naturally get routed around by consumers who observe the bad behavior.

Residual risk. Informed consumers adapt; uninformed ones (not monitoring) might keep trusting. Operational discipline.

T3. Correlated reporter compromises

Attack. Attacker coordinates the compromise of 3 or 4 of the top-trust reporters at once to push a coordinated fake price through.

Mitigations.

  • Reporter diversity, consumers should explicitly spread trust across uncorrelated operators (different jurisdictions, different source feeds, different infrastructure vendors).
  • Cross-feed sanity, if btc-usd reports deviate from eth-btc × eth-usd by > 1%, halt.
  • Circuit-breaker, if price moves > X% in Y seconds, pause lending. This is app-layer but widely recommended.

Residual risk. A sufficiently resourced attacker who compromises top reporters is a sovereign-scale threat. Protocol can only do so much; protocol + external oracle (e.g., centralized backup) hybrid models exist.

T4. Replay attack

Attack. Attacker replays a valid old report from 10 seconds ago to try to pin the price.

Mitigation.

  • Consumer-side staleness check (MaxReportAge), old reports don’t count toward aggregation.
  • Anchor nonces, even if a consumer’s aggregation window is wider than 10s, the nonce monotonicity check would flag a duplicate from the ledger.

Residual risk. None.

T5. Report front-running

Attack. Observer sees a reporter’s price update in push-gossip layer before it’s incorporated into a consumer aggregate. Submits a front-running tx based on the new price.

Mitigation.

  • This is a DeFi architectural issue, not a Quidnug one. Push gossip propagates publicly; MEV-resistant designs need commit-reveal or time-delay at the consumer contract layer.
  • Quidnug doesn’t make this worse or better than any other public oracle.

T6. Feed identity takeover

Attack. Attacker compromises the feed identity (e.g., btc-usd-eth-feed quid) itself, and modifies its guardian set to remove legitimate reporters and add attacker-controlled ones.

Mitigation.

  • Feed identity’s own guardian set (typically = consortium governance) gates set updates.
  • Typical setup: feed guardians = full consortium quorum (e.g., 5-of-7 of the reporter operators themselves plus some industry observers).
  • Subject-level RequireGuardianRotation flag can be set on high-value feeds so only guardian-quorum approved rotations work, no primary-key fast path.

T7. Source manipulation (e.g., wash trading on a thin exchange)

Attack. Attacker manipulates the underlying data source (a low-liquidity exchange) that Oracle-X aggregates from. Oracle-X faithfully reports the manipulated price.

Mitigation.

  • Reporters should source from multiple exchanges and compute volume-weighted prices, making thin-market manipulation expensive.
  • Consumer-side source diversity, prefer reporters who aggregate from 3+ major exchanges.
  • Reporter metadata declares sources; consumers can refuse reporters whose sources are too narrow.

Residual risk. Source manipulation is an orthogonal problem (market-microstructure), not a protocol problem.

T8. Denial of service on oracle feed

Attack. Flood the Quidnug network with junk traffic to prevent reports from propagating.

Mitigation.

  • Push gossip rate limiting, per-producer caps prevent any single party (including attackers) from monopolizing bandwidth.
  • Consumer fallback, app-layer circuit-breaker when no recent reports.

T9. New-consumer onboarding attack

Attack. Malicious bootstrap peer returns a snapshot with a compromised trust-list that points the new consumer at attacker-controlled reporters.

Mitigation.

  • K-of-K bootstrap (QDP-0008) requires K peers to agree. Three malicious peers are the attack prerequisite, a much higher bar than one.
  • Trust list is manually seeded from a known-good source (consortium website signed with multiple consortium members’ keys, obtained out-of-band).

T10. Smart-contract-level issues (out of scope)

  • Reentrancy on the bridge contract
  • Pausable / upgradeable contract misuse
  • Flash-loan-against-oracle tx ordering

These are the DeFi protocol’s contract-layer concerns. Quidnug doesn’t affect them.

Not defended against

  1. Off-chain source truth. If every exchange reports a wrong BTC-USD price (wouldn’t happen but theoretically), every reporter would faithfully relay wrongness. Market- structure integrity is outside Quidnug’s scope.

  2. Majority-of-trusted-reporters collusion. If 60% of consumer’s trusted reporters are colluding, they win. Mitigation is consumer-side: spread trust widely.

  3. Consumer’s own misconfiguration. If a consumer sets minTrust=0.1, they get what they asked for.

  4. Privacy of which consumer uses which feed. Public by design, all queries go through the same nodes.

Monitoring

Critical Prometheus metrics:

  • quidnug_events_total{eventType="oracle.price-report",reporter="..."} detect reporter going silent.
  • quidnug_gossip_rate_limited_total{producer=<reporter>}, compromised reporter flooding.
  • Application metric: price-variance across reporters per feed. Rising variance = market chaos or attack precursor.

References