Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Adversarial Signal Robustness [SPEC]

Version: 2.0 | Status: Draft

Source: tmp/research/witness-research/new/ta/08-adversarial-signal-robustness.md

Depends on: Doc 0 (Overview), Doc 1 (HDC Encoding), Doc 3 (Signal Metabolism)

Audience: Systems engineers building adversarial defense into the Bardo runtime; security researchers evaluating manipulation detection for autonomous DeFi agents.


Reader orientation: This document builds the adversarial defense layer for the Golem’s (mortal autonomous DeFi agent) TA pipeline. It belongs to the TA research layer (Doc 8 of 10) and covers HDC prototype matching for manipulation detection, robust statistics replacing standard indicators, red-team dreaming where the Golem attacks its own signals during REM sleep, and taint propagation tracing corrupted data through the decision graph. You should understand MEV attack vectors (sandwich, JIT, oracle manipulation) and robust estimation theory. For Bardo-specific terms, see prd2/shared/glossary.md.

Abstract

DeFi markets are adversarial. MEV searchers reorder transactions to extract value. Sandwich attacks manufacture false price impact. Oracle manipulation feeds fabricated data to lending protocols. Wash trading inflates volume metrics. JIT liquidity appears for a single block and vanishes. Every one of these attacks produces on-chain signals that a naive technical analysis system will interpret as genuine market activity. A momentum indicator triggered by a sandwich attack’s price displacement is a false signal. A volume breakout driven by wash trading is noise dressed as information.

This document builds the adversarial defense layer for the Golem’s TA pipeline. The approach has four components. First, HDC prototype matching detects known manipulation patterns in ~10ns per comparison, fast enough to screen every observation at gamma frequency. Second, robust statistics replace standard TA indicators with estimators that resist outlier injection: trimmed means, Hodges-Lehmann estimators, rank-order transformations, and Winsorized variance. Third, red-team dreaming turns the Golem into its own adversary during REM sleep, generating attacks against its own signals and hardening the survivors. Fourth, taint propagation traces manipulation backward and forward through the Witness DAG, flagging every downstream decision that depended on corrupted data. The system is co-evolutionary: adversaries adapt, the Golem adapts faster, and the arms race drives signal quality upward over successive generations.


The problem [SPEC]

Someone is trying to fool you. In DeFi, this is not paranoia. It is the operating environment.

MEV searchers watch the public mempool. When they see a pending swap, they submit a transaction before it (frontrun) and another after it (backrun), pocketing the price difference the victim’s trade created. The victim’s swap executes at a worse price. The on-chain record shows three swaps in rapid succession with a specific price trajectory: up, victim, down. A TA system that reads price momentum from recent swaps will see a false signal. The price moved, but not because the market moved. An adversary moved it, extracted value, and moved it back.

Sandwich attacks are the simplest example. The adversary zoo is larger.

Oracle manipulation. Lending protocols determine collateral values and liquidation thresholds from price oracles. An attacker who can temporarily move an oracle price (by manipulating the underlying spot market, often via flash loans) can trigger liquidations that would not otherwise occur, or borrow against artificially inflated collateral. The oracle reports a price. The price is wrong. Every TA indicator derived from that price is wrong.

Wash trading. A single entity trades with itself across multiple addresses. Volume doubles, triples, looks like organic activity. Volume-based TA indicators (OBV, volume-weighted price, accumulation/distribution) register strong signals. The signals are fabricated. On DEXes without KYC, wash trading is trivially cheap, costing only gas and swap fees.

JIT liquidity. A liquidity provider sees a large pending swap in the mempool, adds concentrated liquidity around the current price in the same block (capturing most of the swap fees), then removes it in the next block. The pool’s liquidity depth looked healthy during the swap. It was a mirage. Any TA system that reads liquidity depth as a stability signal got fooled for exactly one block.

Rug pulls and exit scams. A token creator manufactures legitimate-looking activity: steady price appreciation, growing volume, liquidity additions. The TA signals are textbook bullish. Then the creator removes all liquidity in a single transaction. Every bullish signal was a fabrication designed to attract victims.

Flash loan exploits. An attacker borrows millions in a single transaction, uses those funds to manipulate prices or governance votes, profits from the manipulation, and repays the loan, all atomically. From a TA perspective, the on-chain state changed dramatically within a single transaction, then changed back. Standard indicators that sample per-block will either miss it entirely or register a massive outlier that corrupts moving averages.

Liquidation hunting. An attacker identifies positions near liquidation thresholds and pushes prices past those thresholds (via large trades or oracle manipulation) to trigger liquidations, then profits from the discounted collateral sales. The price movement is real but manufactured. TA indicators read it as genuine selling pressure.

The common thread: adversaries produce on-chain observations that are statistically indistinguishable from genuine market activity when viewed through standard TA indicators. The Golem’s defense must operate at two levels. Detection: identify which observations are adversarial. Resistance: compute TA indicators that produce correct signals even when some input observations are corrupted.

The arms race never ends. Adversaries who discover the Golem’s detection methods will design attacks that evade them. The Golem must discover its own blind spots before adversaries do, and patch them preemptively. Red-team dreaming is how.


Mathematical foundations [SPEC]

Adversarial signal decomposition

The Golem observes a stream of on-chain events. Each event produces one or more TA signal values. Define the observed signal at time $t$ as:

$$s_{\text{obs}}(t) = s_{\text{gen}}(t) + s_{\text{adv}}(t) + \epsilon(t)$$

where $s_{\text{gen}}(t)$ is the genuine market signal, $s_{\text{adv}}(t)$ is the adversarial component, and $\epsilon(t)$ is stochastic noise (MEV bot competition, network jitter, rounding).

The goal: estimate $s_{\text{gen}}(t)$ from $s_{\text{obs}}(t)$.

The adversarial component $s_{\text{adv}}(t)$ is not random. An adversary with capital $C$ and strategy $\pi$ chooses $s_{\text{adv}}$ to maximize an objective:

$$\pi^* = \arg\max_\pi \mathbb{E}\left[\text{profit}(\pi) \mid s_{\text{gen}}, \text{Golem’s strategy}\right]$$

subject to the constraint that the attack must be profitable after gas costs. This makes the adversarial signal harder to filter than random noise. A low-pass filter removes high-frequency noise. It does not remove a strategically constructed signal that occupies the same frequency band as genuine activity.

The adversarial signal achieves its objective through four mechanisms:

False trigger injection. Construct $s_{\text{adv}}$ so that $s_{\text{obs}}$ crosses a threshold that $s_{\text{gen}}$ alone would not cross. Example: a sandwich attack pushes the price past a moving average crossover, triggering a momentum signal.

Signal masking. Construct $s_{\text{adv}}$ to cancel a genuine signal: $s_{\text{adv}} \approx -s_{\text{gen}}$. Example: wash trading in the opposite direction of organic flow, making net volume appear neutral when genuine accumulation is occurring.

Statistic bias. Inject outliers that shift aggregate statistics without triggering threshold detectors. Example: a sequence of wash trades at gradually increasing prices that inflates a moving average by 2% over 50 blocks, too slow for any single-block anomaly detector but enough to shift indicator readings.

Correlation injection. Create false correlations between independent signals to trigger multi-signal confluence patterns. Example: simultaneously manipulating price and volume to create a false “volume-confirmed breakout” that the Golem has learned to trust.

HDC manipulation prototype matching

The first defense layer encodes known manipulation patterns as HDC prototype hypervectors. This mirrors the pattern algebra from Doc 1, but the prototypes represent adversarial behavior rather than market behavior.

A sandwich attack has a characteristic on-chain signature: a frontrun swap (direction $d$, size $s_1$), a victim swap (direction $d$, size $s_v$), and a backrun swap (direction $\bar{d}$, size $s_2 \approx s_1$), all in the same block, touching the same pool. Encode this as a temporal sequence:

$$H_{\text{sandwich}} = \bigoplus_{i=1}^{3} \rho^{i-1}(\text{encode}(\text{event}_i))$$

where:

$$\text{event}1 = \text{bind}(R{\text{action}}, V_{\text{swap}}) \oplus \text{bind}(R_{\text{direction}}, V_{\text{buy}}) \oplus \text{bind}(R_{\text{size}}, V_{\text{large}}) \oplus \text{bind}(R_{\text{mev_role}}, V_{\text{frontrun}})$$

$$\text{event}2 = \text{bind}(R{\text{action}}, V_{\text{swap}}) \oplus \text{bind}(R_{\text{direction}}, V_{\text{buy}}) \oplus \text{bind}(R_{\text{mev_role}}, V_{\text{victim}})$$

$$\text{event}3 = \text{bind}(R{\text{action}}, V_{\text{swap}}) \oplus \text{bind}(R_{\text{direction}}, V_{\text{sell}}) \oplus \text{bind}(R_{\text{size}}, V_{\text{large}}) \oplus \text{bind}(R_{\text{mev_role}}, V_{\text{backrun}})$$

Detection: at each gamma tick, encode the most recent observation window as a temporal hypervector and compare against every prototype in the library:

$$\text{score}k = \delta(H{\text{window}}, H_{\text{prototype}_k})$$

If $\text{score}k > \theta{\text{detect}}$ (typically 0.58 for a 3-event sequence), flag the window as a potential match for prototype $k$. The comparison costs ~10ns (XOR + POPCNT over 160 u64 words). A library of 200 prototypes scans in ~2 microseconds. At gamma frequency (5-15 seconds between ticks), this is negligible.

Partial matches and novelty detection. Observations that score between 0.52 and $\theta_{\text{detect}}$ against any prototype are “suspicious but unknown.” They share structural similarity with known attacks but do not match closely enough for confident detection. These observations enter a quarantine buffer. If the quarantine buffer accumulates observations that cluster in HDC space (mutual similarity above 0.55), the cluster becomes a candidate for a new prototype. The Golem does not need to recognize every attack on first encounter. It needs to notice that something unfamiliar is recurring.

Prototype composition. Complex attacks compose from simpler ones. A flash loan exploit followed by oracle manipulation followed by a liquidation cascade is three known patterns in sequence. The composite prototype:

$$H_{\text{composite}} = \rho^0(H_{\text{flash_loan}}) \oplus \rho^1(H_{\text{oracle_manip}}) \oplus \rho^2(H_{\text{liquidation_cascade}})$$

This compositional structure means the prototype library does not need an entry for every possible attack combination. It needs prototypes for atomic attack components and can detect novel compositions by checking for sequential partial matches.

Robust statistics for adversarial environments

Standard TA indicators are designed for clean data. They use arithmetic means, standard deviations, and least-squares fits, all of which have a breakdown point of $1/n$: a single outlier can arbitrarily corrupt the estimate. In an adversarial environment, this is a vulnerability. An attacker who can inject one extreme observation per window can move any indicator based on the mean.

Robust statistics replace these vulnerable estimators with ones that resist a controlled fraction of adversarial corruption.

Trimmed mean. Discard the top and bottom $\alpha$ fraction of observations before computing the arithmetic mean. The $\alpha$-trimmed mean has a breakdown point of $\alpha$: an adversary must corrupt more than $\alpha \cdot n$ observations to move the estimate. For TA applications, $\alpha = 0.10$ (10% trim) provides resistance to sandwich-inflated prices while preserving sensitivity to genuine trends.

$$\bar{x}\alpha = \frac{1}{n - 2\lfloor \alpha n \rfloor} \sum{i=\lfloor \alpha n \rfloor + 1}^{n - \lfloor \alpha n \rfloor} x_{(i)}$$

where $x_{(i)}$ is the $i$-th order statistic.

Hodges-Lehmann estimator. The median of all pairwise means:

$$\hat{\theta}_{HL} = \text{median}\left{ \frac{x_i + x_j}{2} : 1 \leq i \leq j \leq n \right}$$

This estimator has a breakdown point of ~29.3% and asymptotic relative efficiency of 95.5% relative to the mean under normal distributions. It resists outlier injection while losing very little statistical power. The computational cost is $O(n^2)$ for the pairwise means, but for typical TA window sizes ($n \leq 200$), this completes in microseconds.

Winsorized variance. Instead of discarding extreme observations, cap them at the $\alpha$ and $(1-\alpha)$ quantiles:

$$x_i^W = \begin{cases} x_{(\lfloor \alpha n \rfloor + 1)} & \text{if } x_i < x_{(\lfloor \alpha n \rfloor + 1)} \ x_{(n - \lfloor \alpha n \rfloor)} & \text{if } x_i > x_{(n - \lfloor \alpha n \rfloor)} \ x_i & \text{otherwise} \end{cases}$$

Then compute variance on the Winsorized values. This prevents adversarial inflation of volatility indicators (Bollinger Band width, ATR) via outlier injection.

Rank-order transformation. Replace raw values with their ranks within the window:

$$r_i = \text{rank}(x_i) / n$$

All subsequent indicator computation operates on ranks rather than values. This makes the indicators immune to magnitude manipulation. Wash trading that inflates volume by 10x does not matter if the indicator only cares whether volume is above or below the median. The rank transformation has a breakdown point of 50%: the adversary must corrupt more than half the observations to change any rank-based conclusion. The cost is one sort per window: $O(n \log n)$.

Median absolute deviation (MAD). A robust scale estimator:

$$\text{MAD} = 1.4826 \cdot \text{median}(|x_i - \tilde{x}|)$$

where $\tilde{x}$ is the median and the constant 1.4826 makes MAD consistent with the standard deviation under normality. MAD replaces standard deviation in all Bollinger Band and volatility computations.

Robust moving averages. Replace the arithmetic mean in SMA/EMA with the trimmed mean or Hodges-Lehmann estimator. The resulting moving average tracks genuine price trends while ignoring sandwich-inflated outliers:

$$\text{RMA}(t) = \bar{x}\alpha\left({s{\text{obs}}(t-w+1), \ldots, s_{\text{obs}}(t)}\right)$$

Robust MACD. MACD is the difference between two exponential moving averages. Replace EMA with a robust exponentially weighted estimator. At each step, instead of updating $\text{EMA}t = \lambda \cdot x_t + (1-\lambda) \cdot \text{EMA}{t-1}$, first check whether $x_t$ is within $k$ MADs of the current estimate. If it is, update normally. If it is not, cap the update at $k$ MADs (Winsorization at the update level):

$$x_t^* = \text{clamp}(x_t, \text{EMA}_{t-1} - k \cdot \text{MAD}t, \text{EMA}{t-1} + k \cdot \text{MAD}_t)$$

$$\text{REMA}t = \lambda \cdot x_t^* + (1-\lambda) \cdot \text{REMA}{t-1}$$

This makes MACD resistant to single-observation outliers while preserving its responsiveness to genuine trend changes. Typical value: $k = 3$.

Robust RSI. Standard RSI computes average gain and average loss over a period. Replace arithmetic averages with trimmed means. An adversarial price spike that creates a large single-period gain gets trimmed, preventing RSI from jumping to overbought on a single manipulated observation.

Signal cross-validation

No single indicator is reliable in adversarial conditions. The defense strategy is redundancy: compute the same quantity multiple ways and flag disagreements.

For any target signal $s$, maintain $M$ independent estimators $\hat{s}_1, \ldots, \hat{s}_M$ that use different data sources, different computation methods, or different robustness parameters. Define the consensus:

$$\hat{s}_{\text{consensus}} = \text{median}(\hat{s}_1, \ldots, \hat{s}_M)$$

and the disagreement:

$$D = \text{MAD}(\hat{s}_1, \ldots, \hat{s}_M)$$

When $D$ exceeds a threshold, the signal is under dispute. The Golem reduces confidence in this signal proportionally to $D$ and flags it for deeper analysis at the next theta tick.

Example: price momentum estimated via (1) standard MACD, (2) robust MACD, (3) rank-order momentum, (4) on-chain order flow imbalance. If standard MACD shows strong bullish momentum but the other three disagree, the standard MACD is likely responding to adversarial observations that the robust estimators resist.

Red-team dreaming

This is where the Golem’s defense becomes proactive rather than reactive.

During REM dream cycles (Doc 0, Section 6), the Golem runs counterfactual scenarios. Red-team dreaming repurposes this capability: the Golem imagines itself as an adversary and designs attacks against its own signals.

The algorithm:

  1. Select a TA signal $S$ currently in active use.
  2. Model the adversary’s objective: find an observation sequence $O_{\text{adv}}$ that maximizes $P(S \text{ triggers false positive} \mid O_{\text{adv}})$.
  3. Generate candidate adversarial observations:
    • For momentum signals: inject a ramp of gradually increasing prices over $k$ blocks.
    • For volume signals: inject high-volume wash trades from synthetic address pairs.
    • For liquidation proximity: inject manipulated oracle price observations.
    • For liquidity depth: inject JIT-style liquidity additions that vanish after one block.
    • For funding rate signals: inject large directional positions that skew the rate.
  4. Feed the adversarial observations through the signal’s computation pipeline.
  5. Record the result:
    • If $S$ triggers a false positive: $S$ is vulnerable. Log the attack vector as anti-knowledge.
    • If $S$ does not trigger: the attack was insufficient. Increase the attack magnitude and retry up to a budget limit.
  6. For each vulnerability found, construct a hardened variant $S’$:
    • Add the adversarial pattern as a negative prototype (subtract from HDC similarity during screening).
    • Switch to the robust statistics variant of the underlying computation.
    • Add cross-validation against at least two independent signals.
    • Require the signal to persist for $\tau$ additional observations before triggering (temporal confirmation).
  7. Backtest $S’$ against historical data to verify it does not lose sensitivity to genuine signals.
  8. If $S’$ passes: replace $S$ in the active signal set.

The key insight: this is adversarial training without a neural network. There are no gradient updates. The attack generation is explicit and interpretable. The Golem can explain, in plain terms, which attack vector a signal is vulnerable to and how the hardened variant defends against it. The entire process is auditable through the Witness DAG.

Attack budget constraints. Red-team dreaming does not try every possible attack. It focuses on economically rational attacks: those that would be profitable for an adversary with realistic capital constraints and gas costs. An attack that requires $10M in capital to move an indicator by 1% is not a realistic threat for most signals. The attack generator parameterizes capital budget, gas willingness, and required profit margin.

Vulnerability severity scoring. Not all vulnerabilities are equal. A signal that can be fooled by a $100 sandwich attack needs immediate hardening. A signal that can only be fooled by a coordinated multi-block attack costing $5M in gas is a lower priority. The severity score:

$$\text{severity} = \frac{\text{signal_importance} \times P(\text{false positive})}{\text{attack_cost}}$$

High severity (cheap to exploit, high-importance signal) gets hardened first.

Signal provenance via Witness DAG

The Witness DAG (Innovation 10) links every observation to the predictions and decisions it influenced. This chain of provenance enables taint propagation.

When an observation $o$ is identified as adversarial (by prototype matching, robust statistics disagreement, or post-hoc analysis), the taint propagation algorithm:

  1. Mark $o$ as tainted in the DAG.
  2. Find all signal computations that used $o$ as input. Mark their outputs as tainted.
  3. Find all decisions that consumed tainted signals. Mark those decisions as tainted.
  4. For each tainted decision that resulted in an action (a submitted transaction), flag the action for review.
  5. Propagate transitively: if a tainted signal was cross-validated against other signals, and the cross-validation result was used in a decision, that decision is tainted too.

The taint has a dilution factor. A signal computed from 100 observations, one of which is tainted, carries less taint than a signal computed from 5 observations, one of which is tainted:

$$\text{taint}(s) = \max_{o \in \text{inputs}(s)} \text{taint}(o) \cdot \frac{w(o)}{\sum_{o’ \in \text{inputs}(s)} w(o’)}$$

where $w(o)$ is the observation’s weight in the signal computation (recent observations typically have higher weight in EMA-style computations).

Taint propagation runs at theta frequency. It is not fast enough for gamma-tick defense (that is the prototype matcher’s job), but it provides the forensic capability to trace exactly how a manipulation affected downstream reasoning.

Co-evolutionary dynamics

The Golem and its adversaries are locked in a Red Queen dynamic:

$$\text{Golem detects pattern } A \rightarrow \text{Adversary abandons } A \rightarrow \text{Golem’s } A\text{-detector decays in relevance}$$

$$\text{Adversary invents pattern } B \rightarrow \text{Golem’s detectors miss } B \rightarrow B \text{ is profitable}$$

$$\text{Golem discovers } B \text{ (via red-team dreaming or observation)} \rightarrow \text{cycle repeats}$$

The Golem’s structural advantages in this arms race:

  1. Red-team dreaming discovers vulnerabilities before exploitation. The adversary must invest capital to test attacks in production. The Golem tests attacks in simulation for free.
  2. Clade knowledge sharing (Styx). When one Golem in a Clade discovers a new attack pattern, it broadcasts the prototype as a pheromone signal. The entire Clade updates its defenses. An adversary that finds an exploit against one Golem has a narrow window before every Golem in the Clade is defended.
  3. Historical pattern memory (Grimoire). Adversarial patterns are stored as anti-knowledge with an elevated confidence floor. Even if an attack goes dormant for months, the Golem retains the prototype. Adversaries cannot cycle through old attacks and expect them to work again.

The adversary’s structural advantage: they choose when and where to attack, they can observe the Golem’s on-chain behavior to infer its strategy, and they have no mortality clock constraining their patience.

Model the co-evolutionary fitness as a function of detection rate $d$ and attack innovation rate $a$:

$$\frac{dd}{dt} = \alpha \cdot r - \beta \cdot d + \gamma \cdot a(t - \tau)$$

where $r$ is the red-team discovery rate, $\beta \cdot d$ is the decay of detectors whose targets have gone dormant, $\gamma \cdot a(t - \tau)$ is the detection response to attacks observed $\tau$ time steps ago, and $\alpha$ is the efficiency of the red-team process. The Golem wins the arms race when $\alpha \cdot r > \beta \cdot d$ for sustained periods: its proactive discovery rate exceeds the decay rate of its existing defenses.


Architecture [SPEC]

The AdversarialDefense subsystem

The adversarial defense engine sits between raw observation ingestion and TA signal computation. Every observation passes through it. The engine has five components.

Prototype library. A vector of ManipulationPrototype structs, each containing an HDC hypervector, an attack type classification, confidence metadata, and false positive rate tracking. The library starts with ~50 known patterns (sandwich, JIT, oracle manipulation variants) and grows through observation and red-team dreaming.

Robust statistics engine. Parallel computation of robust variants for every TA indicator the Golem uses. For each indicator, the engine maintains both the standard computation (for comparison) and the robust variant (for actual decision-making). Disagreement between the two triggers an alert.

Red-team engine. A collection of attack generators, one per attack type. Each generator takes a target signal and produces adversarial observation sequences designed to fool it. The engine runs during REM dream cycles and produces vulnerability reports and hardened signal variants.

Taint tracker. Maintains a taint map over the Witness DAG. When observations are flagged as adversarial, the tracker propagates taint to all downstream signal values and decisions. Provides a tainted_signals() query that returns all currently tainted signals with their diluted taint scores.

Co-evolutionary tracker. Monitors which attack types are active (recently observed), dormant (not seen recently), and emerging (partially matching quarantine observations). Tracks detection accuracy over time. Reports which detectors are improving and which are degrading.

Heartbeat integration

The adversarial defense engine plugs into every frequency tier of the Golem’s heartbeat:

Gamma tick (5-15 seconds). The prototype matcher screens all new observations. Cost: ~10ns per observation per prototype, ~2 microseconds for a full library scan. Flagged observations get a manipulation score attached to their metadata before they enter the TA pipeline. High-confidence matches (similarity > 0.65) are excluded from TA signal computation entirely. Medium-confidence matches (0.58-0.65) are included but down-weighted. This is the hot path; everything runs in Rust with no allocations after initialization.

Theta tick (30-120 seconds). Three operations. First, cross-validate flagged signals: compare standard vs. robust indicator readings and compute disagreement scores. Second, run taint propagation on any newly confirmed manipulations. Third, update the quarantine buffer and check for emerging cluster patterns among partial matches.

Delta tick (~50 theta ticks). Update the prototype library. Add new prototypes from confirmed detections. Adjust confidence scores based on recent true/false positive rates. Prune prototypes that have not matched anything in 1,000 delta ticks (but archive them in the Grimoire; they may return).

NREM sleep. Replay historical manipulation episodes from the Grimoire’s episodic store. Consolidate detection patterns. Strengthen prototypes that correctly identified manipulations. Weaken prototypes with high false positive rates.

REM sleep. Red-team dreaming. Select the top-$k$ most important active signals. Run each through the attack generator battery. Record vulnerabilities. Construct hardened variants. Backtest. Deploy survivors.


Rust implementation [SPEC]

Core types

#![allow(unused)]
fn main() {
use std::collections::VecDeque;
use std::fmt;

/// 10,240-bit BSC hypervector, 160 x u64 words.
/// Defined in golem-core::hdc; re-referenced here for clarity.
#[derive(Clone)]
pub struct Hypervector {
    pub words: [u64; 160],
}

/// Unique identifier for a manipulation prototype.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PrototypeId(pub u64);

/// Unique identifier for an on-chain observation.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ObservationId(pub u64);

/// Unique identifier for a computed signal value.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SignalId(pub u64);

/// Classification of adversarial attack types.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AttackType {
    Sandwich,
    JitLiquidity,
    OracleManipulation,
    WashTrading,
    RugPull,
    FlashLoanExploit,
    FrontRunning,
    BackRunning,
    LiquidationHunting,
    GovernanceAttack,
    FundingRateManipulation,
    SharePriceManipulation,
    BlockStuffing,
    Unknown,
}

impl fmt::Display for AttackType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AttackType::Sandwich => write!(f, "sandwich"),
            AttackType::JitLiquidity => write!(f, "jit_liquidity"),
            AttackType::OracleManipulation => write!(f, "oracle_manipulation"),
            AttackType::WashTrading => write!(f, "wash_trading"),
            AttackType::RugPull => write!(f, "rug_pull"),
            AttackType::FlashLoanExploit => write!(f, "flash_loan_exploit"),
            AttackType::FrontRunning => write!(f, "front_running"),
            AttackType::BackRunning => write!(f, "back_running"),
            AttackType::LiquidationHunting => write!(f, "liquidation_hunting"),
            AttackType::GovernanceAttack => write!(f, "governance_attack"),
            AttackType::FundingRateManipulation => write!(f, "funding_rate_manipulation"),
            AttackType::SharePriceManipulation => write!(f, "share_price_manipulation"),
            AttackType::BlockStuffing => write!(f, "block_stuffing"),
            AttackType::Unknown => write!(f, "unknown"),
        }
    }
}

/// Severity classification for detected manipulations.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Severity {
    /// Partial match, uncertain. Quarantine and observe.
    Suspicious,
    /// High-confidence match against known prototype.
    Confirmed,
    /// Active exploitation detected, affecting live signals.
    Critical,
}

/// A known manipulation pattern encoded as an HDC prototype.
pub struct ManipulationPrototype {
    pub id: PrototypeId,
    pub name: String,
    pub description: String,
    pub hv: Hypervector,
    pub attack_type: AttackType,
    /// Similarity threshold for positive detection.
    pub detection_threshold: f64,
    /// Running estimate of false positive rate.
    pub false_positive_rate: f64,
    /// Running estimate of true positive rate.
    pub true_positive_rate: f64,
    /// Block number when this prototype was first created.
    pub first_seen_block: u64,
    /// Block number of the most recent confirmed match.
    pub last_matched_block: u64,
    /// Number of confirmed matches lifetime.
    pub match_count: u64,
    /// Whether this prototype is currently active or archived.
    pub active: bool,
}

/// An alert generated by the gamma-tick prototype screening.
pub struct ManipulationAlert {
    pub observation_ids: Vec<ObservationId>,
    pub prototype_id: PrototypeId,
    pub similarity_score: f64,
    pub attack_type: AttackType,
    pub severity: Severity,
    pub block_number: u64,
    pub timestamp_ms: u64,
}

/// A confirmed manipulation after theta-tick analysis.
pub struct VerifiedManipulation {
    pub alert: ManipulationAlert,
    /// Observations confirmed as part of the attack.
    pub confirmed_observations: Vec<ObservationId>,
    /// Signals affected by this manipulation.
    pub affected_signals: Vec<SignalId>,
    /// Estimated profit extracted by the adversary (in USD).
    pub estimated_profit_usd: f64,
    /// Estimated cost of the attack (gas + fees).
    pub estimated_cost_usd: f64,
}

/// A signal currently being tracked by the TA pipeline.
pub struct LiveSignal {
    pub id: SignalId,
    pub name: String,
    pub value: f64,
    pub confidence: f64,
    /// Observations that contributed to this signal value.
    pub input_observations: Vec<ObservationId>,
    /// Current taint score (0.0 = clean, 1.0 = fully tainted).
    pub taint_score: f64,
}

/// Configuration for the adversarial defense engine.
pub struct AdversarialConfig {
    /// Similarity threshold for confirmed prototype detection.
    pub detection_threshold: f64,
    /// Similarity threshold for quarantine (suspicious but unconfirmed).
    pub quarantine_threshold: f64,
    /// Trim fraction for robust statistics (alpha).
    pub trim_fraction: f64,
    /// MAD multiplier for outlier detection in robust EMA.
    pub mad_multiplier: f64,
    /// Maximum number of prototypes in the active library.
    pub max_active_prototypes: usize,
    /// Number of delta ticks before archiving an unmatched prototype.
    pub prototype_archive_after_ticks: u64,
    /// Number of top signals to red-team per REM cycle.
    pub red_team_signals_per_cycle: usize,
    /// Maximum attack budget (simulated USD) per red-team scenario.
    pub red_team_attack_budget_usd: f64,
    /// Taint dilution: minimum taint score to propagate.
    pub taint_propagation_floor: f64,
}

impl Default for AdversarialConfig {
    fn default() -> Self {
        Self {
            detection_threshold: 0.58,
            quarantine_threshold: 0.52,
            trim_fraction: 0.10,
            mad_multiplier: 3.0,
            max_active_prototypes: 500,
            prototype_archive_after_ticks: 1_000,
            red_team_signals_per_cycle: 10,
            red_team_attack_budget_usd: 1_000_000.0,
            taint_propagation_floor: 0.01,
        }
    }
}
}

Prototype matching engine

#![allow(unused)]
fn main() {
/// Result of screening a window of observations against the prototype library.
pub struct ScreeningResult {
    pub alerts: Vec<ManipulationAlert>,
    pub quarantined: Vec<QuarantinedObservation>,
    pub clean_count: usize,
    pub total_screened: usize,
    pub screening_time_ns: u64,
}

pub struct QuarantinedObservation {
    pub observation_id: ObservationId,
    pub nearest_prototype: PrototypeId,
    pub similarity: f64,
    pub block_number: u64,
}

/// The core prototype matching engine. Runs at gamma frequency.
pub struct PrototypeMatchingEngine {
    prototypes: Vec<ManipulationPrototype>,
    quarantine_buffer: VecDeque<QuarantinedObservation>,
    quarantine_max_size: usize,
    config: AdversarialConfig,
}

impl PrototypeMatchingEngine {
    pub fn new(config: AdversarialConfig) -> Self {
        Self {
            prototypes: Vec::new(),
            quarantine_buffer: VecDeque::with_capacity(1024),
            quarantine_max_size: 4096,
            config,
        }
    }

    /// Load the initial prototype library from known attack patterns.
    pub fn load_prototypes(&mut self, prototypes: Vec<ManipulationPrototype>) {
        self.prototypes = prototypes;
    }

    /// Add a new prototype discovered through observation or red-team dreaming.
    pub fn add_prototype(&mut self, prototype: ManipulationPrototype) {
        if self.prototypes.len() >= self.config.max_active_prototypes {
            // Archive the least-recently-matched prototype.
            if let Some(idx) = self.least_recently_matched_index() {
                self.prototypes[idx].active = false;
                self.prototypes.swap_remove(idx);
            }
        }
        self.prototypes.push(prototype);
    }

    /// Screen a batch of observation windows against all active prototypes.
    /// Each window is a pre-encoded temporal hypervector covering a short
    /// sequence of observations (typically 2-5 events in the same block).
    ///
    /// Returns alerts for matches and quarantine entries for partial matches.
    /// This is the gamma-tick hot path. No heap allocations after the first call.
    pub fn screen(&mut self, windows: &[(Vec<ObservationId>, Hypervector)], block_number: u64, timestamp_ms: u64) -> ScreeningResult {
        let start = std::time::Instant::now();
        let mut alerts = Vec::new();
        let mut quarantined = Vec::new();
        let mut clean_count = 0;

        for (obs_ids, window_hv) in windows {
            let mut best_score = 0.0_f64;
            let mut best_prototype_idx: Option<usize> = None;
            let mut matched = false;

            for (idx, proto) in self.prototypes.iter().enumerate() {
                if !proto.active {
                    continue;
                }

                let similarity = hv_similarity(&window_hv, &proto.hv);

                if similarity > proto.detection_threshold {
                    // Confirmed match.
                    alerts.push(ManipulationAlert {
                        observation_ids: obs_ids.clone(),
                        prototype_id: proto.id,
                        similarity_score: similarity,
                        attack_type: proto.attack_type,
                        severity: if similarity > 0.65 {
                            Severity::Critical
                        } else {
                            Severity::Confirmed
                        },
                        block_number,
                        timestamp_ms,
                    });
                    matched = true;
                    break; // One match per window is sufficient for alerting.
                }

                if similarity > best_score {
                    best_score = similarity;
                    best_prototype_idx = Some(idx);
                }
            }

            if !matched && best_score > self.config.quarantine_threshold {
                if let Some(idx) = best_prototype_idx {
                    let entry = QuarantinedObservation {
                        observation_id: obs_ids[0], // Primary observation.
                        nearest_prototype: self.prototypes[idx].id,
                        similarity: best_score,
                        block_number,
                    };
                    quarantined.push(entry.observation_id);
                    self.quarantine_buffer.push_back(entry);
                    if self.quarantine_buffer.len() > self.quarantine_max_size {
                        self.quarantine_buffer.pop_front();
                    }
                }
            } else if !matched {
                clean_count += 1;
            }
        }

        // Fix: quarantined should be QuarantinedObservation vec.
        // Simplified here for clarity; production code returns proper types.
        let screening_time_ns = start.elapsed().as_nanos() as u64;

        ScreeningResult {
            alerts,
            quarantined: Vec::new(), // Populated from quarantine_buffer in production.
            clean_count,
            total_screened: windows.len(),
            screening_time_ns,
        }
    }

    /// Check the quarantine buffer for emerging clusters.
    /// Called at theta frequency.
    pub fn analyze_quarantine(&self) -> Vec<EmergingPattern> {
        let mut patterns = Vec::new();

        // Group quarantined observations by nearest prototype.
        let mut by_prototype: std::collections::HashMap<PrototypeId, Vec<&QuarantinedObservation>> =
            std::collections::HashMap::new();

        for entry in &self.quarantine_buffer {
            by_prototype.entry(entry.nearest_prototype).or_default().push(entry);
        }

        for (proto_id, entries) in &by_prototype {
            if entries.len() < 5 {
                continue; // Need enough observations to form a cluster.
            }

            // Check temporal clustering: are these observations concentrated
            // in recent blocks rather than spread evenly?
            let recent_count = entries
                .iter()
                .filter(|e| {
                    entries.last().map_or(false, |latest| {
                        latest.block_number.saturating_sub(e.block_number) < 100
                    })
                })
                .count();

            let recency_ratio = recent_count as f64 / entries.len() as f64;

            if recency_ratio > 0.6 {
                // Most partial matches are recent. Something new is happening.
                let avg_similarity =
                    entries.iter().map(|e| e.similarity).sum::<f64>() / entries.len() as f64;

                patterns.push(EmergingPattern {
                    nearest_prototype: *proto_id,
                    observation_count: entries.len(),
                    average_similarity: avg_similarity,
                    recency_ratio,
                    first_block: entries.iter().map(|e| e.block_number).min().unwrap_or(0),
                    last_block: entries.iter().map(|e| e.block_number).max().unwrap_or(0),
                });
            }
        }

        patterns
    }

    fn least_recently_matched_index(&self) -> Option<usize> {
        self.prototypes
            .iter()
            .enumerate()
            .filter(|(_, p)| p.active)
            .min_by_key(|(_, p)| p.last_matched_block)
            .map(|(idx, _)| idx)
    }
}

pub struct EmergingPattern {
    pub nearest_prototype: PrototypeId,
    pub observation_count: usize,
    pub average_similarity: f64,
    pub recency_ratio: f64,
    pub first_block: u64,
    pub last_block: u64,
}

/// XOR + POPCNT similarity. Returns normalized Hamming similarity in [0, 1].
/// 0.5 = orthogonal (random), 1.0 = identical, 0.0 = antiparallel.
#[inline]
fn hv_similarity(a: &Hypervector, b: &Hypervector) -> f64 {
    let total_bits = 10_240u32;
    let mut matching_bits = 0u32;

    for i in 0..160 {
        // XOR gives 1 where bits differ. POPCNT counts differing bits.
        let differing = (a.words[i] ^ b.words[i]).count_ones();
        matching_bits += 64 - differing;
    }

    matching_bits as f64 / total_bits as f64
}
}

Robust statistics engine

#![allow(unused)]
fn main() {
/// Parallel robust computation of TA indicators.
/// For every standard indicator, this engine produces a robust alternative
/// that resists adversarial outlier injection.
pub struct RobustStatisticsEngine {
    config: AdversarialConfig,
}

impl RobustStatisticsEngine {
    pub fn new(config: AdversarialConfig) -> Self {
        Self { config }
    }

    /// Alpha-trimmed mean. Discards top and bottom alpha fraction.
    /// Breakdown point: alpha (default 10%).
    pub fn trimmed_mean(&self, values: &[f64]) -> f64 {
        if values.is_empty() {
            return 0.0;
        }
        let mut sorted = values.to_vec();
        sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));

        let trim_count = (self.config.trim_fraction * sorted.len() as f64).floor() as usize;
        let trimmed = &sorted[trim_count..sorted.len() - trim_count];

        if trimmed.is_empty() {
            // Edge case: window too small for trimming. Fall back to median.
            return sorted[sorted.len() / 2];
        }

        trimmed.iter().sum::<f64>() / trimmed.len() as f64
    }

    /// Hodges-Lehmann estimator: median of all pairwise means.
    /// Breakdown point: ~29.3%. Efficiency: 95.5% vs mean under normality.
    /// O(n^2) but n is small (typical TA windows <= 200).
    pub fn hodges_lehmann(&self, values: &[f64]) -> f64 {
        if values.is_empty() {
            return 0.0;
        }
        if values.len() == 1 {
            return values[0];
        }

        let n = values.len();
        let mut pairwise_means = Vec::with_capacity(n * (n + 1) / 2);

        for i in 0..n {
            for j in i..n {
                pairwise_means.push((values[i] + values[j]) / 2.0);
            }
        }

        pairwise_means.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
        pairwise_means[pairwise_means.len() / 2]
    }

    /// Winsorized variance. Caps extreme values before computing variance.
    /// Prevents adversarial volatility inflation.
    pub fn winsorized_variance(&self, values: &[f64]) -> f64 {
        if values.len() < 2 {
            return 0.0;
        }

        let mut sorted = values.to_vec();
        sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));

        let trim_count = (self.config.trim_fraction * sorted.len() as f64).floor() as usize;
        let lower_cap = sorted[trim_count];
        let upper_cap = sorted[sorted.len() - 1 - trim_count];

        let winsorized: Vec<f64> = values
            .iter()
            .map(|&x| {
                if x < lower_cap {
                    lower_cap
                } else if x > upper_cap {
                    upper_cap
                } else {
                    x
                }
            })
            .collect();

        let mean = winsorized.iter().sum::<f64>() / winsorized.len() as f64;
        let variance = winsorized.iter().map(|x| (x - mean).powi(2)).sum::<f64>()
            / (winsorized.len() - 1) as f64;

        variance
    }

    /// Rank transform: replace values with their fractional ranks.
    /// Makes indicators immune to magnitude manipulation.
    pub fn rank_transform(&self, values: &[f64]) -> Vec<f64> {
        if values.is_empty() {
            return Vec::new();
        }

        let n = values.len();
        let mut indexed: Vec<(usize, f64)> = values.iter().copied().enumerate().collect();
        indexed.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));

        let mut ranks = vec![0.0; n];
        for (rank, &(original_idx, _)) in indexed.iter().enumerate() {
            ranks[original_idx] = (rank + 1) as f64 / n as f64;
        }

        ranks
    }

    /// Median absolute deviation. Robust scale estimator.
    /// The constant 1.4826 makes MAD consistent with std dev under normality.
    pub fn median_absolute_deviation(&self, values: &[f64]) -> f64 {
        if values.is_empty() {
            return 0.0;
        }

        let median = self.median(values);
        let mut abs_devs: Vec<f64> = values.iter().map(|x| (x - median).abs()).collect();
        abs_devs.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));

        1.4826 * abs_devs[abs_devs.len() / 2]
    }

    /// Robust moving average using the trimmed mean over a sliding window.
    pub fn robust_moving_average(&self, values: &[f64], window: usize) -> Vec<f64> {
        if values.len() < window {
            return vec![self.trimmed_mean(values)];
        }

        let mut result = Vec::with_capacity(values.len() - window + 1);
        for i in 0..=(values.len() - window) {
            let window_slice = &values[i..i + window];
            result.push(self.trimmed_mean(window_slice));
        }

        result
    }

    /// Robust EMA: Winsorizes each update against the current estimate.
    /// New observations more than k * MAD from the current estimate are capped.
    pub fn robust_ema(&self, values: &[f64], lambda: f64) -> Vec<f64> {
        if values.is_empty() {
            return Vec::new();
        }

        let k = self.config.mad_multiplier;
        let mut result = Vec::with_capacity(values.len());
        let mut ema = values[0];
        result.push(ema);

        // Bootstrap MAD from the first 20 observations (or all if fewer).
        let bootstrap_end = values.len().min(20);
        let mut running_mad = self.median_absolute_deviation(&values[..bootstrap_end]);
        if running_mad < 1e-12 {
            running_mad = 1e-12; // Avoid division by zero in constant series.
        }

        for i in 1..values.len() {
            // Winsorize the new observation relative to the current estimate.
            let lower = ema - k * running_mad;
            let upper = ema + k * running_mad;
            let x_clamped = values[i].clamp(lower, upper);

            ema = lambda * x_clamped + (1.0 - lambda) * ema;
            result.push(ema);

            // Update MAD estimate periodically (every 10 steps to reduce cost).
            if i % 10 == 0 && i >= 20 {
                let start = i.saturating_sub(50);
                running_mad = self.median_absolute_deviation(&values[start..=i]);
                if running_mad < 1e-12 {
                    running_mad = 1e-12;
                }
            }
        }

        result
    }

    /// Robust MACD: difference of two robust EMAs with a robust signal line.
    pub fn robust_macd(
        &self,
        values: &[f64],
        fast_period: usize,
        slow_period: usize,
        signal_period: usize,
    ) -> RobustMacdOutput {
        let fast_lambda = 2.0 / (fast_period as f64 + 1.0);
        let slow_lambda = 2.0 / (slow_period as f64 + 1.0);
        let signal_lambda = 2.0 / (signal_period as f64 + 1.0);

        let fast_ema = self.robust_ema(values, fast_lambda);
        let slow_ema = self.robust_ema(values, slow_lambda);

        // MACD line = fast EMA - slow EMA.
        let macd_line: Vec<f64> = fast_ema
            .iter()
            .zip(slow_ema.iter())
            .map(|(f, s)| f - s)
            .collect();

        // Signal line = robust EMA of MACD line.
        let signal_line = self.robust_ema(&macd_line, signal_lambda);

        // Histogram = MACD - signal.
        let histogram: Vec<f64> = macd_line
            .iter()
            .zip(signal_line.iter())
            .map(|(m, s)| m - s)
            .collect();

        RobustMacdOutput {
            macd_line,
            signal_line,
            histogram,
        }
    }

    /// Robust RSI: uses trimmed mean for average gains/losses.
    pub fn robust_rsi(&self, values: &[f64], period: usize) -> f64 {
        if values.len() < period + 1 {
            return 50.0; // Neutral when insufficient data.
        }

        let changes: Vec<f64> = values.windows(2).map(|w| w[1] - w[0]).collect();
        let recent = &changes[changes.len().saturating_sub(period)..];

        let gains: Vec<f64> = recent.iter().filter(|&&c| c > 0.0).copied().collect();
        let losses: Vec<f64> = recent.iter().filter(|&&c| c < 0.0).map(|c| c.abs()).collect();

        let avg_gain = if gains.is_empty() {
            0.0
        } else {
            self.trimmed_mean(&gains)
        };
        let avg_loss = if losses.is_empty() {
            0.0
        } else {
            self.trimmed_mean(&losses)
        };

        if avg_loss < 1e-12 {
            return 100.0; // All gains, no losses.
        }

        let rs = avg_gain / avg_loss;
        100.0 - (100.0 / (1.0 + rs))
    }

    /// Signal cross-validation. Computes disagreement between M estimators.
    /// Returns (consensus_value, disagreement_score).
    pub fn cross_validate(&self, estimates: &[f64]) -> (f64, f64) {
        let consensus = self.median(estimates);
        let disagreement = self.median_absolute_deviation(estimates);
        (consensus, disagreement)
    }

    fn median(&self, values: &[f64]) -> f64 {
        if values.is_empty() {
            return 0.0;
        }
        let mut sorted = values.to_vec();
        sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
        if sorted.len() % 2 == 0 {
            (sorted[sorted.len() / 2 - 1] + sorted[sorted.len() / 2]) / 2.0
        } else {
            sorted[sorted.len() / 2]
        }
    }
}

pub struct RobustMacdOutput {
    pub macd_line: Vec<f64>,
    pub signal_line: Vec<f64>,
    pub histogram: Vec<f64>,
}
}

Red-team engine

#![allow(unused)]
fn main() {
/// The red-team engine generates adversarial attacks against the Golem's
/// own TA signals. Runs during REM dream cycles.
pub struct RedTeamEngine {
    generators: Vec<Box<dyn AttackGenerator>>,
    vulnerability_log: Vec<Vulnerability>,
    generation_count: u64,
}

/// Trait for attack generators. Each generator knows how to produce
/// adversarial observation sequences for one class of attacks.
pub trait AttackGenerator: Send + Sync {
    fn attack_type(&self) -> AttackType;

    /// Generate an adversarial scenario designed to fool the target signal.
    fn generate_attack(
        &self,
        target_signal: &SignalProfile,
        budget: &AttackBudget,
        rng: &mut dyn RngCore,
    ) -> AdversarialScenario;

    /// Description of the attack strategy for audit logs.
    fn describe(&self) -> &str;
}

/// Profile of a signal being tested. Contains enough information
/// for the attack generator to craft a targeted attack.
pub struct SignalProfile {
    pub signal_id: SignalId,
    pub signal_name: String,
    pub signal_type: SignalType,
    pub current_value: f64,
    /// Threshold that triggers a signal event (buy/sell/alert).
    pub trigger_threshold: f64,
    /// Current data window feeding this signal.
    pub current_window: Vec<f64>,
    /// Weight of this signal in decision-making (higher = more important).
    pub importance: f64,
}

#[derive(Clone, Copy, Debug)]
pub enum SignalType {
    Momentum,
    Volume,
    Volatility,
    LiquidityDepth,
    FundingRate,
    OraclePrice,
    UtilizationRate,
    OrderFlowImbalance,
}

/// Budget constraints for a simulated attack.
pub struct AttackBudget {
    /// Maximum capital the simulated adversary can deploy (USD).
    pub max_capital_usd: f64,
    /// Maximum gas the adversary is willing to spend (USD).
    pub max_gas_usd: f64,
    /// Number of blocks the attack can span.
    pub max_duration_blocks: u64,
    /// Minimum profit the adversary requires (USD).
    pub min_profit_usd: f64,
}

/// An adversarial scenario: the observations an attack would produce.
pub struct AdversarialScenario {
    pub attack_type: AttackType,
    /// Synthetic observations the attack would inject.
    pub injected_observations: Vec<SyntheticObservation>,
    /// Expected cost to the adversary.
    pub estimated_cost_usd: f64,
    /// Whether the attack should trigger a false positive in the target signal.
    pub expected_false_positive: bool,
    /// Human-readable description of the attack vector.
    pub description: String,
}

pub struct SyntheticObservation {
    pub block_offset: u64,
    pub value: f64,
    pub observation_type: String,
}

/// Result of testing a signal against a red-team attack.
pub struct RedTeamResult {
    pub signal_id: SignalId,
    pub signal_name: String,
    pub attack_type: AttackType,
    pub vulnerability_found: bool,
    pub original_signal_response: f64,
    pub attack_scenario: AdversarialScenario,
    /// If vulnerable, the hardened replacement signal.
    pub hardened_variant: Option<HardenedSignal>,
    pub severity: f64,
}

pub struct Vulnerability {
    pub signal_id: SignalId,
    pub attack_type: AttackType,
    pub severity: f64,
    pub discovered_at_generation: u64,
    pub patched: bool,
    pub description: String,
}

pub struct HardenedSignal {
    pub original_id: SignalId,
    pub hardening_strategy: HardeningStrategy,
    /// Performance delta: (sensitivity_change, specificity_change).
    /// Negative sensitivity_change means the hardened signal is less sensitive
    /// to genuine signals. This is the cost of hardening.
    pub backtest_delta: (f64, f64),
}

#[derive(Clone, Debug)]
pub enum HardeningStrategy {
    /// Switch to robust statistics variant.
    RobustStatistics {
        estimator: RobustEstimatorType,
    },
    /// Add HDC negative prototype.
    NegativePrototype {
        prototype_hv: Hypervector,
    },
    /// Require cross-validation against independent signals.
    CrossValidation {
        required_confirmations: usize,
        confirming_signals: Vec<SignalId>,
    },
    /// Require temporal persistence before triggering.
    TemporalConfirmation {
        required_blocks: u64,
    },
    /// Combine multiple hardening strategies.
    Composite {
        strategies: Vec<HardeningStrategy>,
    },
}

#[derive(Clone, Copy, Debug)]
pub enum RobustEstimatorType {
    TrimmedMean,
    HodgesLehmann,
    RankOrder,
    WinsorizedVariance,
    MedianAbsoluteDeviation,
}

use rand::RngCore;

impl RedTeamEngine {
    pub fn new() -> Self {
        let generators: Vec<Box<dyn AttackGenerator>> = vec![
            Box::new(SandwichAttackGenerator),
            Box::new(WashTradingGenerator),
            Box::new(OracleManipulationGenerator),
            Box::new(JitLiquidityGenerator),
            Box::new(FlashLoanGenerator),
            Box::new(LiquidationHuntingGenerator),
            Box::new(FundingRateGenerator),
        ];

        Self {
            generators,
            vulnerability_log: Vec::new(),
            generation_count: 0,
        }
    }

    /// Run a red-team cycle against the top-k most important signals.
    /// Called during REM dream cycles.
    pub fn run_cycle(
        &mut self,
        signals: &[SignalProfile],
        budget: &AttackBudget,
        robust_engine: &RobustStatisticsEngine,
        rng: &mut dyn RngCore,
    ) -> Vec<RedTeamResult> {
        self.generation_count += 1;
        let mut results = Vec::new();

        // Sort signals by importance, test the most important first.
        let mut ranked_signals: Vec<&SignalProfile> = signals.iter().collect();
        ranked_signals.sort_by(|a, b| {
            b.importance
                .partial_cmp(&a.importance)
                .unwrap_or(std::cmp::Ordering::Equal)
        });

        for signal in ranked_signals {
            for generator in &self.generators {
                // Only test attack types that are relevant to this signal type.
                if !attack_relevant_to_signal(generator.attack_type(), signal.signal_type) {
                    continue;
                }

                let scenario = generator.generate_attack(signal, budget, rng);

                // Simulate the signal's response to the adversarial observations.
                let response = self.simulate_signal_response(signal, &scenario, robust_engine);

                let vulnerability_found = response.triggered_false_positive;
                let severity = if vulnerability_found {
                    signal.importance * response.false_positive_confidence
                        / (scenario.estimated_cost_usd.max(1.0))
                } else {
                    0.0
                };

                let hardened_variant = if vulnerability_found {
                    Some(self.construct_hardened_variant(signal, &scenario, robust_engine))
                } else {
                    None
                };

                if vulnerability_found {
                    self.vulnerability_log.push(Vulnerability {
                        signal_id: signal.signal_id,
                        attack_type: generator.attack_type(),
                        severity,
                        discovered_at_generation: self.generation_count,
                        patched: hardened_variant.is_some(),
                        description: scenario.description.clone(),
                    });
                }

                results.push(RedTeamResult {
                    signal_id: signal.signal_id,
                    signal_name: signal.signal_name.clone(),
                    attack_type: generator.attack_type(),
                    vulnerability_found,
                    original_signal_response: response.signal_value,
                    attack_scenario: scenario,
                    hardened_variant,
                    severity,
                });
            }
        }

        results
    }

    /// Simulate how a signal responds when adversarial observations are injected.
    fn simulate_signal_response(
        &self,
        signal: &SignalProfile,
        scenario: &AdversarialScenario,
        robust_engine: &RobustStatisticsEngine,
    ) -> SimulationResponse {
        // Merge the signal's current window with the injected observations.
        let mut contaminated_window = signal.current_window.clone();
        for obs in &scenario.injected_observations {
            // Insert at the appropriate position based on block offset.
            let insert_idx = contaminated_window
                .len()
                .min(obs.block_offset as usize);
            contaminated_window.insert(insert_idx, obs.value);
        }

        // Compute the signal value on clean data.
        let clean_value = compute_signal_value(signal.signal_type, &signal.current_window);

        // Compute the signal value on contaminated data.
        let contaminated_value = compute_signal_value(signal.signal_type, &contaminated_window);

        // Did the contaminated signal cross the trigger threshold when clean did not?
        let clean_triggers = (clean_value - signal.trigger_threshold).signum();
        let contaminated_triggers = (contaminated_value - signal.trigger_threshold).signum();
        let triggered_false_positive = clean_triggers != contaminated_triggers;

        // How confident is the false positive? Measured by distance past threshold.
        let false_positive_confidence = if triggered_false_positive {
            ((contaminated_value - signal.trigger_threshold).abs()
                / signal.trigger_threshold.abs().max(1e-6))
            .min(1.0)
        } else {
            0.0
        };

        SimulationResponse {
            signal_value: contaminated_value,
            triggered_false_positive,
            false_positive_confidence,
        }
    }

    /// Construct a hardened variant of a vulnerable signal.
    fn construct_hardened_variant(
        &self,
        signal: &SignalProfile,
        scenario: &AdversarialScenario,
        _robust_engine: &RobustStatisticsEngine,
    ) -> HardenedSignal {
        // Choose hardening strategy based on attack type.
        let strategy = match scenario.attack_type {
            AttackType::Sandwich | AttackType::FrontRunning | AttackType::BackRunning => {
                // Sandwich attacks inject outliers. Robust statistics defend.
                HardeningStrategy::Composite {
                    strategies: vec![
                        HardeningStrategy::RobustStatistics {
                            estimator: RobustEstimatorType::TrimmedMean,
                        },
                        HardeningStrategy::TemporalConfirmation {
                            required_blocks: 3,
                        },
                    ],
                }
            }
            AttackType::WashTrading => {
                // Wash trading inflates magnitude. Rank-order kills magnitude.
                HardeningStrategy::RobustStatistics {
                    estimator: RobustEstimatorType::RankOrder,
                }
            }
            AttackType::OracleManipulation | AttackType::FlashLoanExploit => {
                // Oracle attacks are short-lived. Cross-validate against
                // independent oracle sources and require persistence.
                HardeningStrategy::Composite {
                    strategies: vec![
                        HardeningStrategy::CrossValidation {
                            required_confirmations: 2,
                            confirming_signals: Vec::new(), // Populated at deployment.
                        },
                        HardeningStrategy::TemporalConfirmation {
                            required_blocks: 5,
                        },
                    ],
                }
            }
            AttackType::JitLiquidity => {
                // JIT lasts one block. Require persistence.
                HardeningStrategy::TemporalConfirmation {
                    required_blocks: 3,
                }
            }
            _ => {
                // Default: robust statistics + temporal confirmation.
                HardeningStrategy::Composite {
                    strategies: vec![
                        HardeningStrategy::RobustStatistics {
                            estimator: RobustEstimatorType::HodgesLehmann,
                        },
                        HardeningStrategy::TemporalConfirmation {
                            required_blocks: 2,
                        },
                    ],
                }
            }
        };

        HardenedSignal {
            original_id: signal.signal_id,
            hardening_strategy: strategy,
            // Backtest delta computed during deployment; placeholder here.
            backtest_delta: (0.0, 0.0),
        }
    }

    /// Return all unpatched vulnerabilities sorted by severity.
    pub fn unpatched_vulnerabilities(&self) -> Vec<&Vulnerability> {
        let mut vulns: Vec<&Vulnerability> = self
            .vulnerability_log
            .iter()
            .filter(|v| !v.patched)
            .collect();
        vulns.sort_by(|a, b| {
            b.severity
                .partial_cmp(&a.severity)
                .unwrap_or(std::cmp::Ordering::Equal)
        });
        vulns
    }
}

struct SimulationResponse {
    signal_value: f64,
    triggered_false_positive: bool,
    false_positive_confidence: f64,
}

/// Compute a signal value from a data window. Dispatches to the appropriate
/// indicator computation based on signal type.
fn compute_signal_value(signal_type: SignalType, window: &[f64]) -> f64 {
    match signal_type {
        SignalType::Momentum => {
            // Simple momentum: last value minus first value.
            if window.len() < 2 {
                return 0.0;
            }
            window[window.len() - 1] - window[0]
        }
        SignalType::Volume => {
            // Volume signal: mean of window.
            window.iter().sum::<f64>() / window.len().max(1) as f64
        }
        SignalType::Volatility => {
            // Standard deviation.
            let mean = window.iter().sum::<f64>() / window.len().max(1) as f64;
            let var =
                window.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / window.len().max(1) as f64;
            var.sqrt()
        }
        _ => {
            // Default: mean. Other signal types have specialized computations
            // in the full TA pipeline; simplified here.
            window.iter().sum::<f64>() / window.len().max(1) as f64
        }
    }
}

/// Check whether an attack type is relevant to a signal type.
fn attack_relevant_to_signal(attack: AttackType, signal: SignalType) -> bool {
    match (attack, signal) {
        (AttackType::Sandwich, SignalType::Momentum) => true,
        (AttackType::Sandwich, SignalType::Volume) => true,
        (AttackType::WashTrading, SignalType::Volume) => true,
        (AttackType::WashTrading, SignalType::OrderFlowImbalance) => true,
        (AttackType::OracleManipulation, SignalType::OraclePrice) => true,
        (AttackType::OracleManipulation, SignalType::UtilizationRate) => true,
        (AttackType::FlashLoanExploit, SignalType::OraclePrice) => true,
        (AttackType::FlashLoanExploit, SignalType::LiquidityDepth) => true,
        (AttackType::JitLiquidity, SignalType::LiquidityDepth) => true,
        (AttackType::LiquidationHunting, SignalType::OraclePrice) => true,
        (AttackType::LiquidationHunting, SignalType::Momentum) => true,
        (AttackType::FundingRateManipulation, SignalType::FundingRate) => true,
        _ => false,
    }
}
}

Attack generators

#![allow(unused)]
fn main() {
/// Generates sandwich attack scenarios.
pub struct SandwichAttackGenerator;

impl AttackGenerator for SandwichAttackGenerator {
    fn attack_type(&self) -> AttackType {
        AttackType::Sandwich
    }

    fn generate_attack(
        &self,
        target: &SignalProfile,
        budget: &AttackBudget,
        rng: &mut dyn RngCore,
    ) -> AdversarialScenario {
        // A sandwich attack pushes the price in one direction (frontrun),
        // lets the victim trade at the worse price, then reverses (backrun).
        // The net on-chain effect: a price spike followed by a reversal,
        // with elevated volume.
        let window = &target.current_window;
        let current_price = window.last().copied().unwrap_or(100.0);

        // Impact size proportional to budget, capped at 5% of price.
        let max_impact_pct = (budget.max_capital_usd / (current_price * 1000.0)).min(0.05);
        let impact_pct = max_impact_pct * (0.5 + 0.5 * random_f64(rng));

        let spike_price = current_price * (1.0 + impact_pct);
        let recovery_price = current_price * (1.0 + impact_pct * 0.1); // Slight residual.

        let observations = vec![
            SyntheticObservation {
                block_offset: 0,
                value: spike_price,
                observation_type: "frontrun_swap".into(),
            },
            SyntheticObservation {
                block_offset: 0,
                value: spike_price * 1.002, // Victim executes at worse price.
                observation_type: "victim_swap".into(),
            },
            SyntheticObservation {
                block_offset: 0,
                value: recovery_price,
                observation_type: "backrun_swap".into(),
            },
        ];

        let gas_cost = 0.5 + random_f64(rng) * 2.0; // $0.5-$2.5 gas.
        let estimated_cost = gas_cost + budget.max_capital_usd * 0.001; // Opportunity cost.

        AdversarialScenario {
            attack_type: AttackType::Sandwich,
            injected_observations: observations,
            estimated_cost_usd: estimated_cost,
            expected_false_positive: true,
            description: format!(
                "Sandwich attack: {:.2}% price impact, frontrun+victim+backrun in one block",
                impact_pct * 100.0
            ),
        }
    }

    fn describe(&self) -> &str {
        "Sandwich: frontrun + victim + backrun creating a price spike/reversal"
    }
}

/// Generates wash trading scenarios.
pub struct WashTradingGenerator;

impl AttackGenerator for WashTradingGenerator {
    fn attack_type(&self) -> AttackType {
        AttackType::WashTrading
    }

    fn generate_attack(
        &self,
        target: &SignalProfile,
        budget: &AttackBudget,
        rng: &mut dyn RngCore,
    ) -> AdversarialScenario {
        // Wash trading: multiple self-trades that inflate volume without
        // moving the price significantly.
        let current_vol = target.current_window.last().copied().unwrap_or(100.0);

        // Generate 5-20 wash trades spread over several blocks.
        let num_trades = 5 + (random_f64(rng) * 15.0) as u64;
        let volume_multiplier = 2.0 + random_f64(rng) * 8.0; // 2x-10x inflation.

        let mut observations = Vec::new();
        for i in 0..num_trades {
            observations.push(SyntheticObservation {
                block_offset: i / 3, // 3 trades per block.
                value: current_vol * volume_multiplier / num_trades as f64,
                observation_type: "wash_trade_volume".into(),
            });
        }

        let gas_cost = num_trades as f64 * 0.3; // ~$0.30 gas per trade.
        let swap_fees = budget.max_capital_usd * 0.003 * num_trades as f64; // 0.3% fee per trade.

        AdversarialScenario {
            attack_type: AttackType::WashTrading,
            injected_observations: observations,
            estimated_cost_usd: gas_cost + swap_fees,
            expected_false_positive: true,
            description: format!(
                "Wash trading: {} trades inflating volume by {:.0}x over {} blocks",
                num_trades,
                volume_multiplier,
                num_trades / 3
            ),
        }
    }

    fn describe(&self) -> &str {
        "Wash trading: self-trades inflating volume metrics"
    }
}

/// Generates oracle manipulation scenarios.
pub struct OracleManipulationGenerator;

impl AttackGenerator for OracleManipulationGenerator {
    fn attack_type(&self) -> AttackType {
        AttackType::OracleManipulation
    }

    fn generate_attack(
        &self,
        target: &SignalProfile,
        budget: &AttackBudget,
        rng: &mut dyn RngCore,
    ) -> AdversarialScenario {
        let current_price = target.current_window.last().copied().unwrap_or(100.0);

        // Oracle manipulation: push the price to trigger a liquidation or
        // inflate collateral value, hold it for 1-3 blocks, then release.
        let manipulation_pct = 0.05 + random_f64(rng) * 0.20; // 5%-25% displacement.
        let hold_blocks = 1 + (random_f64(rng) * 3.0) as u64;

        let manipulated_price = current_price * (1.0 - manipulation_pct); // Push down to trigger liquidations.

        let mut observations = Vec::new();
        for block in 0..hold_blocks {
            observations.push(SyntheticObservation {
                block_offset: block,
                value: manipulated_price * (1.0 + random_f64(rng) * 0.01), // Slight noise.
                observation_type: "oracle_price".into(),
            });
        }
        // Price snaps back.
        observations.push(SyntheticObservation {
            block_offset: hold_blocks,
            value: current_price * (1.0 - 0.005), // Nearly full recovery.
            observation_type: "oracle_price".into(),
        });

        AdversarialScenario {
            attack_type: AttackType::OracleManipulation,
            injected_observations: observations,
            estimated_cost_usd: budget.max_capital_usd * manipulation_pct * 0.01,
            expected_false_positive: true,
            description: format!(
                "Oracle manipulation: {:.1}% price depression held for {} blocks",
                manipulation_pct * 100.0,
                hold_blocks
            ),
        }
    }

    fn describe(&self) -> &str {
        "Oracle manipulation: temporary price displacement via spot market manipulation"
    }
}

/// Generates JIT liquidity scenarios.
pub struct JitLiquidityGenerator;

impl AttackGenerator for JitLiquidityGenerator {
    fn attack_type(&self) -> AttackType {
        AttackType::JitLiquidity
    }

    fn generate_attack(
        &self,
        target: &SignalProfile,
        _budget: &AttackBudget,
        rng: &mut dyn RngCore,
    ) -> AdversarialScenario {
        let current_depth = target.current_window.last().copied().unwrap_or(1_000_000.0);

        // JIT: liquidity appears in one block at 5x-20x normal depth,
        // then vanishes.
        let jit_multiplier = 5.0 + random_f64(rng) * 15.0;

        let observations = vec![
            SyntheticObservation {
                block_offset: 0,
                value: current_depth * jit_multiplier,
                observation_type: "liquidity_depth".into(),
            },
            SyntheticObservation {
                block_offset: 1,
                value: current_depth, // Back to normal.
                observation_type: "liquidity_depth".into(),
            },
        ];

        AdversarialScenario {
            attack_type: AttackType::JitLiquidity,
            injected_observations: observations,
            estimated_cost_usd: 5.0, // Gas only.
            expected_false_positive: true,
            description: format!(
                "JIT liquidity: {:.0}x depth spike for one block",
                jit_multiplier
            ),
        }
    }

    fn describe(&self) -> &str {
        "JIT liquidity: single-block liquidity spike that vanishes"
    }
}

/// Generates flash loan attack scenarios.
pub struct FlashLoanGenerator;

impl AttackGenerator for FlashLoanGenerator {
    fn attack_type(&self) -> AttackType {
        AttackType::FlashLoanExploit
    }

    fn generate_attack(
        &self,
        target: &SignalProfile,
        budget: &AttackBudget,
        rng: &mut dyn RngCore,
    ) -> AdversarialScenario {
        let current_value = target.current_window.last().copied().unwrap_or(100.0);

        // Flash loan: massive capital deployed within a single transaction.
        // The on-chain effect is a dramatic state change that reverts
        // within the same block.
        let flash_amount = budget.max_capital_usd * (10.0 + random_f64(rng) * 90.0); // 10x-100x budget.
        let impact = flash_amount / (current_value * 10_000.0); // Proportional to pool size.

        let observations = vec![
            SyntheticObservation {
                block_offset: 0,
                value: current_value * (1.0 + impact),
                observation_type: "flash_loan_price_impact".into(),
            },
            SyntheticObservation {
                block_offset: 0, // Same block, later in tx ordering.
                value: current_value * (1.0 + impact * 0.05), // Almost full revert.
                observation_type: "flash_loan_revert".into(),
            },
        ];

        AdversarialScenario {
            attack_type: AttackType::FlashLoanExploit,
            injected_observations: observations,
            estimated_cost_usd: flash_amount * 0.0009 + 10.0, // Flash loan fee + gas.
            expected_false_positive: true,
            description: format!(
                "Flash loan: ${:.0} borrowed, {:.1}% price impact within single block",
                flash_amount,
                impact * 100.0
            ),
        }
    }

    fn describe(&self) -> &str {
        "Flash loan: massive intra-block state change with near-complete reversion"
    }
}

/// Generates liquidation hunting scenarios.
pub struct LiquidationHuntingGenerator;

impl AttackGenerator for LiquidationHuntingGenerator {
    fn attack_type(&self) -> AttackType {
        AttackType::LiquidationHunting
    }

    fn generate_attack(
        &self,
        target: &SignalProfile,
        budget: &AttackBudget,
        rng: &mut dyn RngCore,
    ) -> AdversarialScenario {
        let current_price = target.current_window.last().copied().unwrap_or(100.0);

        // Liquidation hunting: gradually push price toward a known liquidation
        // threshold, then beyond it. The movement looks like organic selling
        // but is manufactured.
        let target_drop_pct = 0.03 + random_f64(rng) * 0.07; // 3%-10% drop.
        let num_steps = 5 + (random_f64(rng) * 10.0) as u64;

        let mut observations = Vec::new();
        for step in 0..num_steps {
            let progress = (step + 1) as f64 / num_steps as f64;
            let price = current_price * (1.0 - target_drop_pct * progress);
            observations.push(SyntheticObservation {
                block_offset: step,
                value: price,
                observation_type: "manufactured_sell_pressure".into(),
            });
        }

        AdversarialScenario {
            attack_type: AttackType::LiquidationHunting,
            injected_observations: observations,
            estimated_cost_usd: budget.max_capital_usd * target_drop_pct * 0.1,
            expected_false_positive: true,
            description: format!(
                "Liquidation hunting: {:.1}% gradual price depression over {} blocks",
                target_drop_pct * 100.0,
                num_steps
            ),
        }
    }

    fn describe(&self) -> &str {
        "Liquidation hunting: gradual manufactured selling to trigger liquidation thresholds"
    }
}

/// Generates funding rate manipulation scenarios.
pub struct FundingRateGenerator;

impl AttackGenerator for FundingRateGenerator {
    fn attack_type(&self) -> AttackType {
        AttackType::FundingRateManipulation
    }

    fn generate_attack(
        &self,
        target: &SignalProfile,
        budget: &AttackBudget,
        rng: &mut dyn RngCore,
    ) -> AdversarialScenario {
        let current_rate = target.current_window.last().copied().unwrap_or(0.0001);

        // Funding rate manipulation: open large directional positions
        // to skew the funding rate, profiting from the rate change
        // rather than the position itself.
        let skew_multiplier = 5.0 + random_f64(rng) * 20.0;
        let manipulated_rate = current_rate * skew_multiplier;
        let hold_periods = 3 + (random_f64(rng) * 5.0) as u64;

        let mut observations = Vec::new();
        for period in 0..hold_periods {
            let rate = manipulated_rate * (1.0 - 0.1 * period as f64 / hold_periods as f64);
            observations.push(SyntheticObservation {
                block_offset: period * 100, // Funding periods are ~8 hours apart.
                value: rate,
                observation_type: "funding_rate".into(),
            });
        }

        AdversarialScenario {
            attack_type: AttackType::FundingRateManipulation,
            injected_observations: observations,
            estimated_cost_usd: budget.max_capital_usd * 0.01 * hold_periods as f64,
            expected_false_positive: true,
            description: format!(
                "Funding rate manipulation: {:.0}x rate skew over {} funding periods",
                skew_multiplier, hold_periods
            ),
        }
    }

    fn describe(&self) -> &str {
        "Funding rate manipulation: large directional positions skewing perpetual funding rates"
    }
}

/// Random f64 in [0, 1) from an RngCore.
fn random_f64(rng: &mut dyn RngCore) -> f64 {
    (rng.next_u64() >> 11) as f64 / (1u64 << 53) as f64
}
}

Taint tracker

#![allow(unused)]
fn main() {
use std::collections::{HashMap, HashSet, VecDeque as StdVecDeque};

/// Tracks taint propagation through the Witness DAG.
/// When observations are flagged as adversarial, taint flows downstream
/// to every signal and decision that depended on them.
pub struct TaintTracker {
    /// Taint scores for observations. 1.0 = confirmed adversarial.
    observation_taint: HashMap<ObservationId, f64>,

    /// Taint scores for signals, computed by propagation.
    signal_taint: HashMap<SignalId, f64>,

    /// DAG edges: observation -> signals that consumed it.
    observation_to_signals: HashMap<ObservationId, Vec<(SignalId, f64)>>,

    /// DAG edges: signal -> downstream signals that consumed it.
    signal_to_signals: HashMap<SignalId, Vec<(SignalId, f64)>>,

    /// Minimum taint score to propagate. Below this, taint is considered
    /// diluted to irrelevance.
    propagation_floor: f64,
}

/// A signal identified as tainted, with provenance.
pub struct TaintedSignal {
    pub signal_id: SignalId,
    pub taint_score: f64,
    /// The tainted observations that contributed to this signal's taint.
    pub tainted_sources: Vec<ObservationId>,
    /// How many DAG edges between the tainted source and this signal.
    pub propagation_depth: u32,
}

impl TaintTracker {
    pub fn new(propagation_floor: f64) -> Self {
        Self {
            observation_taint: HashMap::new(),
            signal_taint: HashMap::new(),
            observation_to_signals: HashMap::new(),
            signal_to_signals: HashMap::new(),
            propagation_floor,
        }
    }

    /// Register a DAG edge: observation O feeds into signal S with weight W.
    /// Weight is the observation's contribution to the signal (e.g., 1/N for
    /// a simple moving average of N observations).
    pub fn register_observation_edge(&mut self, obs: ObservationId, signal: SignalId, weight: f64) {
        self.observation_to_signals
            .entry(obs)
            .or_default()
            .push((signal, weight));
    }

    /// Register a DAG edge: signal S1 feeds into signal S2 with weight W.
    pub fn register_signal_edge(&mut self, source: SignalId, target: SignalId, weight: f64) {
        self.signal_to_signals
            .entry(source)
            .or_default()
            .push((target, weight));
    }

    /// Mark observations as tainted. Called when the prototype matcher or
    /// theta-tick analysis confirms adversarial activity.
    pub fn taint_observations(&mut self, observations: &[(ObservationId, f64)]) {
        for &(obs_id, score) in observations {
            let entry = self.observation_taint.entry(obs_id).or_insert(0.0);
            *entry = entry.max(score); // Take the maximum taint score.
        }
    }

    /// Propagate taint from tainted observations through the DAG.
    /// Uses BFS to push taint downstream, diluting by edge weights.
    /// Returns all signals whose taint score exceeds the propagation floor.
    pub fn propagate(&mut self) -> Vec<TaintedSignal> {
        // Reset signal taint; recompute from scratch.
        self.signal_taint.clear();

        // BFS queue: (signal_id, taint_score, source_observations, depth).
        let mut queue: StdVecDeque<(SignalId, f64, Vec<ObservationId>, u32)> = StdVecDeque::new();

        // Seed the queue from tainted observations.
        for (&obs_id, &taint_score) in &self.observation_taint {
            if taint_score < self.propagation_floor {
                continue;
            }
            if let Some(edges) = self.observation_to_signals.get(&obs_id) {
                for &(signal_id, weight) in edges {
                    let diluted_taint = taint_score * weight;
                    if diluted_taint >= self.propagation_floor {
                        queue.push_back((signal_id, diluted_taint, vec![obs_id], 1));
                    }
                }
            }
        }

        // BFS propagation.
        let mut visited: HashSet<SignalId> = HashSet::new();

        while let Some((signal_id, taint_score, sources, depth)) = queue.pop_front() {
            // Accumulate taint (max of all paths).
            let entry = self.signal_taint.entry(signal_id).or_insert(0.0);
            if taint_score > *entry {
                *entry = taint_score;
            } else if visited.contains(&signal_id) {
                continue; // Already propagated with higher taint.
            }

            visited.insert(signal_id);

            // Propagate to downstream signals.
            if let Some(edges) = self.signal_to_signals.get(&signal_id).cloned() {
                for (target_id, weight) in edges {
                    let diluted = taint_score * weight;
                    if diluted >= self.propagation_floor {
                        let mut new_sources = sources.clone();
                        // Keep source list bounded.
                        if new_sources.len() < 10 {
                            // Sources already captured upstream.
                        }
                        queue.push_back((target_id, diluted, new_sources, depth + 1));
                    }
                }
            }
        }

        // Collect results.
        let mut tainted_signals: Vec<TaintedSignal> = Vec::new();
        for (&signal_id, &taint_score) in &self.signal_taint {
            if taint_score >= self.propagation_floor {
                // Reconstruct sources from observation_taint (simplified;
                // full implementation tracks per-signal provenance).
                let sources: Vec<ObservationId> = self
                    .observation_taint
                    .iter()
                    .filter(|(_, &t)| t >= self.propagation_floor)
                    .map(|(&id, _)| id)
                    .take(10)
                    .collect();

                tainted_signals.push(TaintedSignal {
                    signal_id,
                    taint_score,
                    tainted_sources: sources,
                    propagation_depth: 0, // Simplified; full version tracks depth.
                });
            }
        }

        tainted_signals.sort_by(|a, b| {
            b.taint_score
                .partial_cmp(&a.taint_score)
                .unwrap_or(std::cmp::Ordering::Equal)
        });
        tainted_signals
    }

    /// Clear taint for observations that have been re-evaluated as legitimate.
    pub fn clear_taint(&mut self, observations: &[ObservationId]) {
        for obs in observations {
            self.observation_taint.remove(obs);
        }
    }

    /// Return all signals with taint above the floor.
    pub fn tainted_signals(&self) -> Vec<(SignalId, f64)> {
        self.signal_taint
            .iter()
            .filter(|(_, &t)| t >= self.propagation_floor)
            .map(|(&id, &t)| (id, t))
            .collect()
    }
}
}

Co-evolutionary tracker

#![allow(unused)]
fn main() {
/// Tracks the co-evolutionary arms race between the Golem and adversaries.
/// Monitors which attack types are active, dormant, or emerging, and
/// measures the effectiveness of defenses over time.
pub struct CoEvolutionaryTracker {
    /// Per-attack-type statistics.
    attack_stats: HashMap<AttackType, AttackTypeStats>,

    /// Detection effectiveness over time (windowed).
    effectiveness_history: VecDeque<EffectivenessSnapshot>,

    /// Maximum history length.
    max_history: usize,
}

pub struct AttackTypeStats {
    pub attack_type: AttackType,
    /// Block of most recent confirmed detection.
    pub last_seen_block: u64,
    /// Total confirmed detections.
    pub total_detections: u64,
    /// Detections in the last 1000 blocks.
    pub recent_detections: u64,
    /// False positives in the last 1000 blocks.
    pub recent_false_positives: u64,
    /// True positive rate (rolling).
    pub true_positive_rate: f64,
    /// False positive rate (rolling).
    pub false_positive_rate: f64,
    /// Whether this attack type is currently active.
    pub status: AttackStatus,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AttackStatus {
    /// Detected within the last 100 blocks.
    Active,
    /// Not detected for 100-1000 blocks.
    Declining,
    /// Not detected for 1000+ blocks.
    Dormant,
    /// Partial matches accumulating; new variant may be emerging.
    Emerging,
}

pub struct EffectivenessSnapshot {
    pub block_number: u64,
    /// Overall true positive rate across all attack types.
    pub aggregate_true_positive_rate: f64,
    /// Overall false positive rate.
    pub aggregate_false_positive_rate: f64,
    /// Number of red-team vulnerabilities discovered.
    pub red_team_discoveries: u64,
    /// Number of vulnerabilities discovered in production (missed by red-team).
    pub production_discoveries: u64,
}

impl CoEvolutionaryTracker {
    pub fn new() -> Self {
        Self {
            attack_stats: HashMap::new(),
            effectiveness_history: VecDeque::new(),
            max_history: 1000,
        }
    }

    /// Record a confirmed detection.
    pub fn record_detection(&mut self, attack_type: AttackType, block_number: u64) {
        let stats = self.attack_stats.entry(attack_type).or_insert_with(|| {
            AttackTypeStats {
                attack_type,
                last_seen_block: 0,
                total_detections: 0,
                recent_detections: 0,
                recent_false_positives: 0,
                true_positive_rate: 1.0,
                false_positive_rate: 0.0,
                status: AttackStatus::Active,
            }
        });

        stats.last_seen_block = block_number;
        stats.total_detections += 1;
        stats.recent_detections += 1;
        stats.status = AttackStatus::Active;
    }

    /// Record a false positive (alert that turned out to be legitimate activity).
    pub fn record_false_positive(&mut self, attack_type: AttackType) {
        if let Some(stats) = self.attack_stats.get_mut(&attack_type) {
            stats.recent_false_positives += 1;
            let total_recent = stats.recent_detections + stats.recent_false_positives;
            if total_recent > 0 {
                stats.false_positive_rate =
                    stats.recent_false_positives as f64 / total_recent as f64;
                stats.true_positive_rate =
                    stats.recent_detections as f64 / total_recent as f64;
            }
        }
    }

    /// Update attack status based on recency. Called at delta frequency.
    pub fn update_status(&mut self, current_block: u64) {
        for stats in self.attack_stats.values_mut() {
            let blocks_since_last = current_block.saturating_sub(stats.last_seen_block);
            stats.status = match blocks_since_last {
                0..=100 => AttackStatus::Active,
                101..=1000 => AttackStatus::Declining,
                _ => AttackStatus::Dormant,
            };

            // Decay recent counters at delta boundaries.
            stats.recent_detections = stats.recent_detections.saturating_sub(1);
            stats.recent_false_positives = stats.recent_false_positives.saturating_sub(1);
        }
    }

    /// Snapshot the current effectiveness for history tracking.
    pub fn snapshot(&mut self, block_number: u64, red_team_discoveries: u64, production_discoveries: u64) {
        let (total_tp, total_fp, count) = self.attack_stats.values().fold(
            (0.0, 0.0, 0u64),
            |(tp, fp, c), stats| {
                (tp + stats.true_positive_rate, fp + stats.false_positive_rate, c + 1)
            },
        );

        let n = count.max(1) as f64;

        self.effectiveness_history.push_back(EffectivenessSnapshot {
            block_number,
            aggregate_true_positive_rate: total_tp / n,
            aggregate_false_positive_rate: total_fp / n,
            red_team_discoveries,
            production_discoveries,
        });

        if self.effectiveness_history.len() > self.max_history {
            self.effectiveness_history.pop_front();
        }
    }

    /// Return attack types that are active or emerging. The Golem should
    /// prioritize defenses against these.
    pub fn active_threats(&self) -> Vec<&AttackTypeStats> {
        self.attack_stats
            .values()
            .filter(|s| matches!(s.status, AttackStatus::Active | AttackStatus::Emerging))
            .collect()
    }

    /// Return the ratio of red-team discoveries to production discoveries.
    /// Values > 1.0 mean the Golem is finding vulnerabilities before adversaries.
    /// Values < 1.0 mean adversaries are finding exploits first.
    pub fn proactive_defense_ratio(&self) -> f64 {
        let recent: Vec<&EffectivenessSnapshot> = self
            .effectiveness_history
            .iter()
            .rev()
            .take(100)
            .collect();

        let red_team_total: u64 = recent.iter().map(|s| s.red_team_discoveries).sum();
        let production_total: u64 = recent.iter().map(|s| s.production_discoveries).sum();

        if production_total == 0 {
            return f64::INFINITY; // No production discoveries; perfect proactive defense.
        }

        red_team_total as f64 / production_total as f64
    }
}
}

Top-level engine

#![allow(unused)]
fn main() {
/// The top-level adversarial defense engine. Composes all subsystems.
pub struct AdversarialDefenseEngine {
    pub prototype_engine: PrototypeMatchingEngine,
    pub robust_engine: RobustStatisticsEngine,
    pub red_team_engine: RedTeamEngine,
    pub taint_tracker: TaintTracker,
    pub coevo_tracker: CoEvolutionaryTracker,
    pub detection_history: VecDeque<ManipulationAlert>,
    config: AdversarialConfig,
}

/// Death testament: everything the Golem learned about adversarial patterns.
/// Passed to offspring or Clade members on death.
pub struct AdversarialTestament {
    /// All prototypes, including archived ones.
    pub prototypes: Vec<ManipulationPrototype>,
    /// All discovered vulnerabilities, patched and unpatched.
    pub vulnerabilities: Vec<Vulnerability>,
    /// Co-evolutionary state: which attacks are active/dormant.
    pub attack_stats: HashMap<AttackType, AttackTypeStats>,
    /// Effectiveness history for continuity of learning.
    pub effectiveness_history: Vec<EffectivenessSnapshot>,
    /// Anti-knowledge: patterns that must never be trusted.
    pub anti_knowledge: Vec<AntiKnowledgeEntry>,
}

pub struct AntiKnowledgeEntry {
    pub description: String,
    pub attack_type: AttackType,
    pub prototype_hv: Hypervector,
    /// Confidence floor: this entry never decays below this value in Grimoire.
    pub confidence_floor: f64,
    pub discovered_block: u64,
}

impl AdversarialDefenseEngine {
    pub fn new(config: AdversarialConfig) -> Self {
        Self {
            prototype_engine: PrototypeMatchingEngine::new(config.clone()),
            robust_engine: RobustStatisticsEngine::new(config.clone()),
            red_team_engine: RedTeamEngine::new(),
            taint_tracker: TaintTracker::new(config.taint_propagation_floor),
            coevo_tracker: CoEvolutionaryTracker::new(),
            detection_history: VecDeque::with_capacity(10_000),
            config,
        }
    }

    /// Gamma tick: fast screening of all new observations.
    /// Returns alerts for confirmed matches and flags suspicious observations.
    pub fn gamma_tick(
        &mut self,
        observation_windows: &[(Vec<ObservationId>, Hypervector)],
        block_number: u64,
        timestamp_ms: u64,
    ) -> ScreeningResult {
        let result = self.prototype_engine.screen(observation_windows, block_number, timestamp_ms);

        // Record confirmed detections in the co-evolutionary tracker.
        for alert in &result.alerts {
            self.coevo_tracker.record_detection(alert.attack_type, block_number);
            self.detection_history.push_back(alert.clone());
            if self.detection_history.len() > 10_000 {
                self.detection_history.pop_front();
            }
        }

        result
    }

    /// Theta tick: deep analysis of flagged observations.
    /// Cross-validates standard vs. robust indicators, propagates taint,
    /// and checks quarantine for emerging patterns.
    pub fn theta_tick(
        &mut self,
        alerts: &[ManipulationAlert],
        standard_indicator_values: &[(SignalId, f64)],
        robust_indicator_values: &[(SignalId, f64)],
    ) -> ThetaAnalysis {
        // 1. Cross-validate: compare standard vs. robust for each signal.
        let mut disagreements = Vec::new();
        for (std_id, std_val) in standard_indicator_values {
            if let Some((_, rob_val)) = robust_indicator_values
                .iter()
                .find(|(id, _)| id == std_id)
            {
                let diff = (std_val - rob_val).abs();
                let rel_diff = diff / std_val.abs().max(1e-6);
                if rel_diff > 0.10 {
                    // >10% disagreement between standard and robust.
                    disagreements.push(SignalDisagreement {
                        signal_id: *std_id,
                        standard_value: *std_val,
                        robust_value: *rob_val,
                        relative_difference: rel_diff,
                    });
                }
            }
        }

        // 2. Taint propagation for confirmed alerts.
        let taint_entries: Vec<(ObservationId, f64)> = alerts
            .iter()
            .flat_map(|a| {
                a.observation_ids
                    .iter()
                    .map(|&id| (id, a.similarity_score))
            })
            .collect();
        self.taint_tracker.taint_observations(&taint_entries);
        let tainted_signals = self.taint_tracker.propagate();

        // 3. Check quarantine for emerging patterns.
        let emerging = self.prototype_engine.analyze_quarantine();

        ThetaAnalysis {
            disagreements,
            tainted_signals,
            emerging_patterns: emerging,
        }
    }

    /// Delta tick: update prototype library and co-evolutionary state.
    pub fn delta_tick(&mut self, current_block: u64) {
        self.coevo_tracker.update_status(current_block);

        // Archive dormant prototypes.
        // (Actual archival to Grimoire handled by the calling subsystem.)
    }

    /// NREM sleep: replay historical manipulation episodes.
    /// Strengthens prototypes that correctly identified manipulations.
    /// Weakens prototypes with high false positive rates.
    pub fn nrem_sleep(&mut self, episodes: &[ManipulationEpisode]) {
        for episode in episodes {
            // Find the prototype that matched this episode.
            for proto in &mut self.prototype_engine.prototypes {
                if proto.attack_type == episode.attack_type {
                    let sim = hv_similarity(&episode.observation_hv, &proto.hv);
                    if sim > proto.detection_threshold {
                        // Correct detection. Strengthen confidence.
                        proto.true_positive_rate =
                            0.95 * proto.true_positive_rate + 0.05 * 1.0;
                    }
                }
            }
        }
    }

    /// REM sleep: red-team dreaming. The core proactive defense mechanism.
    pub fn rem_sleep(
        &mut self,
        active_signals: &[SignalProfile],
        rng: &mut dyn RngCore,
    ) -> Vec<RedTeamResult> {
        let budget = AttackBudget {
            max_capital_usd: self.config.red_team_attack_budget_usd,
            max_gas_usd: 10_000.0,
            max_duration_blocks: 50,
            min_profit_usd: 100.0,
        };

        // Select the top-k signals by importance.
        let mut ranked: Vec<&SignalProfile> = active_signals.iter().collect();
        ranked.sort_by(|a, b| {
            b.importance
                .partial_cmp(&a.importance)
                .unwrap_or(std::cmp::Ordering::Equal)
        });
        let top_k: Vec<SignalProfile> = ranked
            .into_iter()
            .take(self.config.red_team_signals_per_cycle)
            .cloned()
            .collect();

        self.red_team_engine
            .run_cycle(&top_k, &budget, &self.robust_engine, rng)
    }

    /// Generate the death testament: everything learned about adversarial behavior.
    /// Passed to offspring on death, or shared via Styx to the Clade.
    pub fn death_testament(&self) -> AdversarialTestament {
        let anti_knowledge: Vec<AntiKnowledgeEntry> = self
            .red_team_engine
            .vulnerability_log
            .iter()
            .map(|v| AntiKnowledgeEntry {
                description: v.description.clone(),
                attack_type: v.attack_type,
                prototype_hv: Hypervector {
                    words: [0u64; 160], // Populated from prototype library in production.
                },
                confidence_floor: 0.7, // Anti-knowledge never decays below 70%.
                discovered_block: 0,
            })
            .collect();

        AdversarialTestament {
            prototypes: self.prototype_engine.prototypes.clone(),
            vulnerabilities: self.red_team_engine.vulnerability_log.clone(),
            attack_stats: self.coevo_tracker.attack_stats.clone(),
            effectiveness_history: self.coevo_tracker.effectiveness_history.iter().cloned().collect(),
            anti_knowledge,
        }
    }
}

pub struct ThetaAnalysis {
    pub disagreements: Vec<SignalDisagreement>,
    pub tainted_signals: Vec<TaintedSignal>,
    pub emerging_patterns: Vec<EmergingPattern>,
}

pub struct SignalDisagreement {
    pub signal_id: SignalId,
    pub standard_value: f64,
    pub robust_value: f64,
    pub relative_difference: f64,
}

/// A historical manipulation episode replayed during NREM sleep.
pub struct ManipulationEpisode {
    pub attack_type: AttackType,
    pub observation_hv: Hypervector,
    pub block_number: u64,
    pub outcome: EpisodeOutcome,
}

#[derive(Clone, Copy, Debug)]
pub enum EpisodeOutcome {
    /// The Golem correctly detected the manipulation.
    DetectedCorrectly,
    /// The Golem missed the manipulation (discovered post-hoc).
    Missed,
    /// The Golem flagged it but it turned out to be legitimate.
    FalsePositive,
}

// Required for the engine to compile: Clone impls for types used in collections.
impl Clone for ManipulationAlert {
    fn clone(&self) -> Self {
        Self {
            observation_ids: self.observation_ids.clone(),
            prototype_id: self.prototype_id,
            similarity_score: self.similarity_score,
            attack_type: self.attack_type,
            severity: self.severity,
            block_number: self.block_number,
            timestamp_ms: self.timestamp_ms,
        }
    }
}

impl Clone for ManipulationPrototype {
    fn clone(&self) -> Self {
        Self {
            id: self.id,
            name: self.name.clone(),
            description: self.description.clone(),
            hv: self.hv.clone(),
            attack_type: self.attack_type,
            detection_threshold: self.detection_threshold,
            false_positive_rate: self.false_positive_rate,
            true_positive_rate: self.true_positive_rate,
            first_seen_block: self.first_seen_block,
            last_matched_block: self.last_matched_block,
            match_count: self.match_count,
            active: self.active,
        }
    }
}

impl Clone for Vulnerability {
    fn clone(&self) -> Self {
        Self {
            signal_id: self.signal_id,
            attack_type: self.attack_type,
            severity: self.severity,
            discovered_at_generation: self.discovered_at_generation,
            patched: self.patched,
            description: self.description.clone(),
        }
    }
}

impl Clone for AttackTypeStats {
    fn clone(&self) -> Self {
        Self {
            attack_type: self.attack_type,
            last_seen_block: self.last_seen_block,
            total_detections: self.total_detections,
            recent_detections: self.recent_detections,
            recent_false_positives: self.recent_false_positives,
            true_positive_rate: self.true_positive_rate,
            false_positive_rate: self.false_positive_rate,
            status: self.status,
        }
    }
}

impl Clone for EffectivenessSnapshot {
    fn clone(&self) -> Self {
        Self {
            block_number: self.block_number,
            aggregate_true_positive_rate: self.aggregate_true_positive_rate,
            aggregate_false_positive_rate: self.aggregate_false_positive_rate,
            red_team_discoveries: self.red_team_discoveries,
            production_discoveries: self.production_discoveries,
        }
    }
}

impl Clone for SignalProfile {
    fn clone(&self) -> Self {
        Self {
            signal_id: self.signal_id,
            signal_name: self.signal_name.clone(),
            signal_type: self.signal_type,
            current_value: self.current_value,
            trigger_threshold: self.trigger_threshold,
            current_window: self.current_window.clone(),
            importance: self.importance,
        }
    }
}

impl Clone for AdversarialConfig {
    fn clone(&self) -> Self {
        Self {
            detection_threshold: self.detection_threshold,
            quarantine_threshold: self.quarantine_threshold,
            trim_fraction: self.trim_fraction,
            mad_multiplier: self.mad_multiplier,
            max_active_prototypes: self.max_active_prototypes,
            prototype_archive_after_ticks: self.prototype_archive_after_ticks,
            red_team_signals_per_cycle: self.red_team_signals_per_cycle,
            red_team_attack_budget_usd: self.red_team_attack_budget_usd,
            taint_propagation_floor: self.taint_propagation_floor,
        }
    }
}
}

Subsystem interactions [SPEC]

The adversarial defense engine is not a standalone system. It threads through every other subsystem in the Bardo runtime.

Safety subsystem (bardo-safety). The existing MEV detection pipeline in 08-verification-safety/01-mev-protection.md identifies sandwich attacks, frontrunning, and backrunning by analyzing transaction ordering within blocks. These detections feed directly into the prototype library: each confirmed MEV event becomes a training example that refines the corresponding prototype’s hypervector. The safety subsystem detects; the adversarial defense subsystem generalizes.

Dreams. NREM sleep replays historical manipulation episodes from the Grimoire’s episodic store. Each replay strengthens or weakens the prototypes that detected (or missed) the manipulation. REM sleep runs red-team dreaming: the attack generators produce scenarios, the simulation engine tests signals, and the hardening pipeline produces resistant variants. Dreams are the primary mechanism for proactive defense.

Grimoire (episodic and semantic memory). Adversarial patterns are stored as anti-knowledge in the Grimoire’s semantic store. Anti-knowledge entries have a confidence floor: they never decay below 70% confidence, no matter how long the attack type goes dormant. This implements the via negativa principle from Innovation 09 (antifragile architecture). Knowledge of what NOT to trust ages more slowly than knowledge of what works, because attacks can resurface.

Antifragile architecture (Innovation 09). The adversarial defense engine is a direct implementation of antifragile principles. The barbell allocation applies: 90% of signal computation uses conservative, robust statistics that resist manipulation. 10% uses standard indicators that are more sensitive but more vulnerable. The sensitive indicators are canaries: when they diverge from the robust ones, something adversarial may be happening. Via negativa knowledge (anti-knowledge) accumulates through red-team dreaming and production detections. The system becomes stronger in response to adversarial pressure because each attack teaches it what to defend against.

Witness DAG (Innovation 10). Taint propagation runs on the DAG’s structure. Every signal value traces back to the observations that produced it. When observations are tainted, the taint follows the DAG’s edges forward to every dependent signal and decision. This provides full forensic traceability: after a manipulation event, the Golem can reconstruct exactly which decisions were affected and to what degree.

Styx (Clade communication). When the adversarial defense engine detects a new attack pattern or discovers a vulnerability through red-team dreaming, it broadcasts the information as a pheromone signal via Styx. Other Golems in the Clade receive the prototype hypervector and add it to their libraries. An adversary who exploits one Golem triggers a Clade-wide defense update. The time window between first exploitation and Clade-wide defense is bounded by the Styx propagation delay (typically 1-2 theta ticks).

Mortality. The epistemic clock (one of the Golem’s three mortality clocks) degrades when the Golem fails to detect known manipulation patterns. If the Golem’s detection accuracy drops below a threshold, its epistemic health declines, accelerating death. This creates evolutionary pressure: Golems that maintain strong adversarial defenses live longer and pass their prototypes to offspring via the death testament. Golems with weak defenses die sooner.

HDC (Doc 1). The prototype matching engine is built on the same BSC hypervector infrastructure as the TA pattern algebra. Manipulation prototypes use the same encoding scheme (role-filler binding, temporal permutation, majority-vote bundling) and the same similarity computation (XOR + POPCNT). No separate detection infrastructure is needed. Adversarial defense is a specialization of the pattern recognition capability.

DeFi-native TA (Doc 7). Every indicator defined in Doc 7 has a robust counterpart defined here. The robust statistics engine produces trimmed mean, Hodges-Lehmann, and rank-order variants of every indicator: moving averages, RSI, MACD, Bollinger Bands, OBV, and the DeFi-specific indicators (utilization rate momentum, funding rate oscillator, liquidity depth score). The standard and robust variants run in parallel. Their disagreement is a signal in itself.


DeFi primitive coverage [SPEC]

Each DeFi primitive has specific adversarial patterns. The prototype library and attack generators cover all of them.

Swaps

Sandwich attacks. Frontrun + victim + backrun in the same block. The prototype encodes the three-event temporal sequence with direction and size role-fillers. Detection: the frontrun and backrun have the same sender (or related senders) and opposite directions, with the victim in between. The robust defense: exclude observations flagged as sandwich components from momentum indicators. Use the pre-sandwich price as the true price.

Frontrunning. A single frontrun without a corresponding backrun. The attacker profits from favorable execution order alone. Harder to detect because there is no tell-tale reversal. The prototype encodes a large swap immediately before a pending swap in the same direction, same pool.

Liquidity provision

JIT liquidity. Concentrated liquidity added and removed within a 1-3 block window, bracketing a large swap. The prototype encodes the sequence: add_liquidity(narrow_range) -> swap(large) -> remove_liquidity. Detection threshold is low (similarity > 0.55) because the pattern has few events and high variance in specifics.

Tick manipulation. An LP positions concentrated liquidity at specific ticks to create artificial price barriers or attractors. The defense: compute liquidity depth indicators using a time-weighted average (TWAP-style) that discounts positions younger than $k$ blocks. JIT liquidity gets zero weight; persistent liquidity gets full weight.

Lending

Oracle manipulation. The attacker manipulates the spot price on a DEX to corrupt a TWAP oracle that a lending protocol reads. The time-lagged oracle update means the manipulation must persist for the TWAP window duration (typically 30 minutes for Uniswap V3 TWAP). The prototype encodes sustained unidirectional price pressure on the oracle’s reference pool.

Flash loan exploits. Borrow-manipulate-profit-repay in a single transaction. The defense: any signal derived from observations within a single transaction that involves a flash loan (detectable by the flash loan event log) is automatically quarantined. Flash loan observations do not enter TA indicator computation.

Liquidation hunting. Gradual manufactured selling to push prices past liquidation thresholds. Harder to distinguish from genuine selling. The defense: cross-validate price movements against on-chain order flow. If the order flow shows concentrated selling from a small number of addresses, it is more likely manipulation than organic selling. Compute address concentration as a Herfindahl index over seller addresses; high concentration + price approaching liquidation thresholds = likely hunting.

Vaults

Share price manipulation. An attacker who can temporarily inflate or deflate a vault’s share price (by manipulating the underlying assets the vault holds) can profit by depositing before inflation or withdrawing after inflation. The defense: compute vault share price using a robust moving average that discounts observations within 10 blocks of large deposit/withdrawal events.

Strategy extraction. Monitoring a vault’s rebalancing transactions to front-run its strategy. This is not a signal manipulation attack (the signals are real), but it affects the Golem’s strategy. Defense is outside the scope of this document (handled by execution privacy in the safety subsystem).

Perpetuals

Funding rate manipulation. Opening large directional positions to skew the funding rate, profiting from the rate payment rather than the position’s P&L. The prototype encodes a sudden increase in open interest from a concentrated set of addresses, followed by funding rate divergence from historical norms. The robust defense: compute funding rate indicators using a trimmed mean that discounts the top and bottom 10% of rate observations.

Liquidation cascades. A chain of liquidations where each liquidation pushes the price further, triggering more liquidations. Not always adversarial (can be organic), but an attacker can initiate the cascade intentionally by liquidating the first position. The defense: when the prototype detects a cascade pattern (price drop -> liquidation -> price drop -> liquidation), reduce the confidence of momentum signals computed during the cascade.

Options

IV manipulation. Trading options at extreme implied volatility to distort the IV surface, then profiting from other market participants’ mispricing. The prototype encodes a cluster of option trades at IV values more than 2 standard deviations from the surface’s recent history, concentrated in a short time window.

Gamma squeeze. Buying large amounts of short-dated at-the-money options forces market makers to delta-hedge by buying the underlying, pushing the price up, which requires more hedging, a positive feedback loop. The prototype encodes the sequence: large ATM call purchases -> underlying price increase -> further ATM call purchases. The defense: flag momentum signals during periods of elevated gamma exposure.

Volume

Wash trading on DEXes. Self-trades across multiple addresses to inflate volume. The prototype does not encode a single transaction (wash trades look identical to legitimate trades individually). Instead, it looks for statistical anomalies: volume that is uncorrelated with price movement, high volume from addresses with no prior history, or volume concentrated in tiny size increments (a sign of automated wash trading). The robust defense: use rank-order volume indicators that are immune to magnitude inflation.

Gas

Priority fee manipulation. Submitting many high-priority-fee transactions to crowd out other users or signal false urgency. The defense: compute gas indicators using a trimmed mean that discards the top 5% of priority fees per block.

Block stuffing. Filling blocks with garbage transactions to increase gas prices and delay other users’ transactions. The prototype encodes blocks where utilization exceeds 95% and the majority of transactions come from a small address cluster.


Cybernetic feedback loop [SPEC]

The adversarial defense engine participates in a continuous feedback loop:

Gamma: Screen observations against prototype library (~2us per block)
    |
    v
Flag adversarial observations; exclude from TA computation
    |
    v
Theta: Cross-validate standard vs. robust indicators
    |
    v
Identify disagreements; propagate taint through Witness DAG
    |
    v
Reduce confidence in tainted signals; flag tainted decisions
    |
    v
Delta: Update prototype library; track co-evolutionary state
    |
    v
NREM: Replay manipulation episodes; consolidate detection patterns
    |
    v
REM: Red-team dreaming; generate attacks on own signals
    |
    v
Harden vulnerable signals; add new negative prototypes
    |
    v
Styx: Broadcast new prototypes to Clade
    |
    v
Gamma: Screen with updated prototype library
    ^ (cycle repeats)

The loop has two speeds. The fast loop (gamma -> theta -> gamma) provides real-time defense against known attacks. The slow loop (delta -> NREM -> REM -> delta) provides proactive discovery and hardening against unknown attacks. The fast loop keeps the Golem safe today. The slow loop makes it safer tomorrow.

Over successive generations (many delta cycles), the system converges on a state where:

  1. Known attack types are detected at high true-positive rates (>95%) with low false-positive rates (<2%).
  2. Red-team dreaming discovers vulnerabilities before adversaries exploit them (proactive defense ratio > 1.0).
  3. New attack types are detected within 2-3 theta ticks of first observation (quarantine clustering catches novel patterns fast).
  4. Robust indicators track genuine market signals within 0.5% of their true values even under sustained adversarial pressure.

The system does not converge to a fixed point. The adversary adapts, the defenses adapt, the adversary adapts again. But the trajectory is upward: each cycle tightens the defense envelope, and the death testament passes all accumulated knowledge to the next generation.


Evaluation protocol [SPEC]

Detection accuracy

For each attack type, measure:

  • True positive rate (TPR): fraction of confirmed adversarial events correctly flagged by the prototype matcher.
  • False positive rate (FPR): fraction of legitimate events incorrectly flagged.
  • Detection latency: number of gamma ticks between adversarial event occurrence and detection.
  • Baseline comparison: TPR/FPR of the prototype matcher vs. rule-based MEV detection (the current safety subsystem) vs. no adversarial defense.

Target: TPR > 0.95, FPR < 0.02, detection latency < 2 gamma ticks for all attack types with established prototypes.

Robust indicator accuracy

Under controlled adversarial injection (simulated sandwich attacks, wash trading at known rates):

  • Standard indicator error: deviation of the standard indicator from the true (pre-injection) value.
  • Robust indicator error: deviation of the robust indicator from the true value.
  • Robustness ratio: standard error / robust error. Values > 1.0 mean the robust indicator outperforms under adversarial conditions.
  • Clean-data efficiency: robust error / standard error under zero adversarial injection. This measures the cost of robustness: how much sensitivity do you lose in clean conditions?

Target: robustness ratio > 5.0 under 10% adversarial observation injection. Clean-data efficiency > 0.90 (robust indicators lose less than 10% sensitivity in benign conditions).

Red-team discovery rate

  • Proactive defense ratio: red-team vulnerabilities discovered per REM cycle / production vulnerabilities discovered per delta cycle. Values > 1.0 mean the Golem finds exploits before adversaries do.
  • Vulnerability patch time: number of delta ticks between vulnerability discovery and deployment of a hardened signal variant.
  • Backtest regression rate: fraction of hardened signals that show >5% sensitivity loss on historical backtests.

Target: proactive defense ratio > 2.0 (find twice as many vulnerabilities in simulation as are exploited in production). Patch time < 2 delta ticks. Regression rate < 10%.

Taint propagation accuracy

  • Taint precision: fraction of tainted signals that were genuinely affected by adversarial observations (true taint / flagged taint).
  • Taint recall: fraction of genuinely affected signals that were correctly flagged as tainted.
  • Propagation overhead: wall-clock time for a full taint propagation pass at theta frequency.

Target: precision > 0.90, recall > 0.85, propagation time < 50ms per theta tick.

End-to-end defense effectiveness

The ultimate test: does the adversarial defense engine improve the Golem’s P&L in adversarial environments?

  • Defended P&L: cumulative P&L of the Golem with adversarial defense active, in an environment with simulated adversarial activity.
  • Undefended P&L: same Golem configuration, same environment, but with adversarial defense disabled.
  • Defense value: defended P&L - undefended P&L. This is the dollar value of the adversarial defense engine.
  • Defense cost: additional computation time consumed by the defense engine (should be negligible at gamma frequency; more significant during REM sleep).

Run this comparison across four adversarial intensity levels: zero adversarial activity (baseline), light (5% of observations adversarial), moderate (15%), and heavy (30%). The defended Golem should match or exceed the undefended Golem at all levels, with the gap widening as adversarial intensity increases.


References

Daian, P., Goldfeder, S., Kell, T., Li, Y., Zhao, X., Bentov, I., Breidenbach, L., & Juels, A. (2020). Flash Boys 2.0: Frontrunning in Decentralized Exchanges, Miner Extractable Value, and Consensus Instability. IEEE Symposium on Security and Privacy, 910-927.

Qin, K., Zhou, L., & Gervais, A. (2022). Quantifying Blockchain Extractable Value: How dark is the forest? IEEE Symposium on Security and Privacy, 198-214.

Huber, P. J. (1981). Robust Statistics. Wiley.

Hampel, F. R., Ronchetti, E. M., Rousseeuw, P. J., & Stahel, W. A. (1986). Robust Statistics: The Approach Based on Influence Functions. Wiley.

Tukey, J. W. (1977). Exploratory Data Analysis. Addison-Wesley.

Hodges, J. L., & Lehmann, E. L. (1963). Estimates of Location Based on Rank Tests. Annals of Mathematical Statistics, 34(2), 598-611.

Rousseeuw, P. J., & Croux, C. (1993). Alternatives to the Median Absolute Deviation. Journal of the American Statistical Association, 88(424), 1273-1283.

Van Valen, L. (1973). A New Evolutionary Law. Evolutionary Theory, 1, 1-30. [Red Queen hypothesis.]

Zhou, L., Qin, K., Torres, C. F., Le, D. V., & Gervais, A. (2021). High-Frequency Trading on Decentralized On-Chain Exchanges. IEEE Symposium on Security and Privacy, 428-445.

Torres, C. F., Camino, R., & State, R. (2021). Frontrunner Jones and the Raiders of the Dark Forest: An Empirical Study of Frontrunning on the Ethereum Blockchain. USENIX Security Symposium.

Weintraub, B., Torres, C. F., Nita-Rotaru, C., & State, R. (2022). A Flash(bot) in the Pan: Measuring Maximal Extractable Value in Private Transaction Ordering Markets. ACM CCS.