Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/point_add/memory/DEFENSE_MODULE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Muqatta-Z Defense Module — ecdsa.fail Integration

**Author:** Peter Anari Otuke, University of Nairobi
**Framework:** Muqatta-Z Sovereign (Z-parameter formalism)

## Purpose

This defense module consumes ecdsa.fail leaderboard data as input to the
Muqatta-Z spectral framework, converting offensive circuit improvements
into calibrated defensive migration urgency.

The leaderboard score `C` (Toffoli x qubits) feeds the framework via:

```
Z15(t) = Z15(0) * (C(0) / C(t)) ^ gamma
```

where `gamma = 0.5` is a calibrated duality-elasticity constant. As the
leaderboard score drops (circuits improve), the ECDLP-PQC coupling term
increases, which raises the modeled optimal allocation toward PQC
migration, `w*(PQC)`.

## Key outputs

| Metric | Description |
|--------|-------------|
| `gamma` | Duality elasticity exponent applied to the score-improvement ratio |
| `w*(PQC)` | Modeled optimal share of migration effort allocated to PQC |
| `D*` | SegWit-PQ witness discount factor derived from witness-size ratios |
| `K` | Number of fixed-point iterations to converge under the Banach bound |
| `CR` | Spectral stability margin of the calibrated Z-matrix |

## How to reproduce

```bash
pip install numpy scipy
python src/point_add/memory/z_calibrate.py --score <score_from_score.json>
```

See `z_calibrate.py` in this directory for the full calibration
implementation, and `defense_approach.md` for the rationale behind the
approach.
48 changes: 48 additions & 0 deletions src/point_add/memory/defense_approach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Defense Module Approach Notes

## What this does

The Muqatta-Z defense module provides the complementary side of the
ecdsa.fail equation. While the challenge optimizes attack cost (fewer
Toffoli gates and qubits = cheaper attack), this module quantifies the
defense response that each circuit improvement implies for Bitcoin's
post-quantum migration planning.

## Core relationship

Every improvement to the leaderboard's circuit score increases the
modeled value of PQC migration. The relationship used here is:

```
defense urgency ~ (improvement ratio) ^ gamma, gamma = 0.5
```

This is not adversarial framing — better attack-cost estimates motivate
better-calibrated defense investment. The module quantifies how much
defense urgency each circuit improvement implies.

## Integration with circuit optimization

The defense module does not modify the circuit code in
`src/point_add/`. It reads `score.json` after a `cargo run --release`
(or `ecdsafail run`) and recalibrates:

- `w*(PQC)`: modeled share of migration effort allocated to PQC
- `D*`: SegWit-PQ witness discount factor
- `K`: number of fixed-point iterations to convergence

## Reproducibility

Given a fixed score input, all outputs are deterministic:

- numpy seed: 42
- Z-matrix: hardcoded 5x5 (see `z_calibrate.py`)
- Eigenvalues reproduce to 6 decimal places across numpy/scipy versions
tested (numpy 1.26.2, scipy 1.12.0)

## Caveats

This is a parametric modeling exercise, not a peer-reviewed empirical
result. The Z-matrix entries and `gamma` are chosen, not derived from
external data; treat the urgency outputs as illustrative sensitivity
analysis rather than calibrated forecasts.
171 changes: 171 additions & 0 deletions src/point_add/memory/z_calibrate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#!/usr/bin/env python3
"""
z_calibrate.py - Calibrate Muqatta-Z defense parameters from ecdsa.fail scores.

Usage:
python z_calibrate.py # Uses ../../../score.json
python z_calibrate.py --score 1571592960 # Manual score input
python z_calibrate.py --track # Append score to history

Author: Peter Anari Otuke, University of Nairobi
"""
import argparse
import json
import os
from datetime import datetime, timezone

import numpy as np
from scipy import linalg

# Canonical Z-matrix (seed=42). Symmetric 5x5 coupling matrix over the
# zones: ECDLP, Nonce, Verify, SideCh, PQC.
np.random.seed(42)
Z_BASE = np.array([
[8.50, 1.20, 0.80, 1.50, 2.30],
[1.20, 7.80, 0.60, 2.10, 1.80],
[0.80, 0.60, 6.20, 0.40, 0.90],
[1.50, 2.10, 0.40, 9.10, 1.70],
[2.30, 1.80, 0.90, 1.70, 10.40],
], dtype=np.float64)
ZONES = ["ECDLP", "Nonce", "Verify", "SideCh", "PQC"]

# Score recorded at the time the framework was calibrated.
C_BASELINE = 10_758_874_395
GAMMA = 0.5


def eigendecompose(z):
vals, vecs = linalg.eigh(z)
idx = np.argsort(vals)[::-1]
return vals[idx], vecs[:, idx]


def calibrate_z_matrix(current_score):
"""Scale the ECDLP<->PQC coupling by the leaderboard improvement ratio."""
improvement_ratio = C_BASELINE / current_score
z15_multiplier = improvement_ratio ** GAMMA

z_cal = Z_BASE.copy()
z_cal[0, 4] *= z15_multiplier
z_cal[4, 0] *= z15_multiplier
z_cal[1, 4] *= z15_multiplier ** 0.5
z_cal[4, 1] *= z15_multiplier ** 0.5
return z_cal, z15_multiplier, improvement_ratio


def full_analysis(current_score):
z_cal, z15_mult, imp_ratio = calibrate_z_matrix(current_score)
vals, vecs = eigendecompose(z_cal)
l1, l2, l5 = vals[0], vals[1], vals[-1]

kappa = l1 / l5
alpha = l2 / l1
beta = 0.5
cr = l2 / (beta * np.sqrt(l1 * l5))

v1 = vecs[:, 0]
wstar = np.abs(v1) / np.sum(np.abs(v1))

eps = 1e-6
k = int(np.ceil(np.log(eps) / np.log(alpha)))

w_e, w_nw, w_pq = 71, 43, 3293 # witness-size inputs (bytes)
t_target = 0.80
d_cont = t_target * w_pq / (w_e + (1 - t_target) * w_nw)
d_star = int(np.ceil(d_cont))
dd_dw = t_target / (w_e + (1 - t_target) * w_nw)

gershgorin_margins = [
z_cal[i, i] - (np.sum(np.abs(z_cal[i, :])) - z_cal[i, i])
for i in range(z_cal.shape[0])
]

return {
"score": current_score,
"improvement_ratio": imp_ratio,
"z15_multiplier": z15_mult,
"eigenvalues": vals,
"kappa": kappa,
"alpha": alpha,
"cr": cr,
"K": k,
"wstar": dict(zip(ZONES, wstar)),
"D_star": d_star,
"D_cont": d_cont,
"dD_dW": dd_dw,
"gershgorin_min": min(gershgorin_margins),
}


def load_score_json(path):
try:
with open(path) as f:
return json.load(f)["score"]
except (FileNotFoundError, KeyError, json.JSONDecodeError):
return None


def print_report(result):
print("=" * 72)
print("MUQATTA-Z DEFENSE CALIBRATION - ecdsa.fail Integration")
print("=" * 72)
print(f"\n INPUT (ecdsa.fail leaderboard):")
print(f" Current score: {result['score']:,.0f}")
print(f" Baseline score: {C_BASELINE:,.0f}")
print(f" Improvement ratio: {result['improvement_ratio']:.2f}x")
print(f" Z15 multiplier: {result['z15_multiplier']:.4f}x (gamma={GAMMA})")

vs = result["eigenvalues"]
print(f"\n SPECTRAL (calibrated Z-matrix):")
print(f" lambda = [{', '.join(f'{v:.6f}' for v in vs)}]")
print(f" kappa = {result['kappa']:.6f}")
print(f" alpha = {result['alpha']:.6f}")
print(f" CR = {result['cr']:.6f} ({'PASS' if result['cr'] >= 1.0 else 'FAIL'})")
print(f" K = {result['K']} iterations")
print(f" Gershgorin min = {result['gershgorin_min']:.6f}")

print(f"\n OPTIMAL ALLOCATION w*:")
for zone, w in result["wstar"].items():
bar = "#" * int(w * 40)
print(f" {zone:8s}: {w*100:6.2f}% {bar}")

print(f"\n SEGWIT-PQ:")
print(f" D* = {result['D_star']} (continuous: {result['D_cont']:.4f})")
print(f" dD*/dW_pq = {result['dD_dW']:.6f} per byte")
print("=" * 72)


def track_score(score, history_file="defense_history.jsonl"):
entry = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"score": score,
"improvement_ratio": C_BASELINE / score,
}
with open(history_file, "a") as f:
f.write(json.dumps(entry) + "\n")
print(f" Tracked: score={score:,.0f} at {entry['timestamp']}")


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Calibrate Muqatta-Z defense from ecdsa.fail scores"
)
parser.add_argument("--score", type=int, default=None,
help="Circuit score (Toffoli x qubits)")
parser.add_argument("--track", action="store_true",
help="Append score to tracking history")
args = parser.parse_args()

score = args.score
if score is None:
here = os.path.dirname(os.path.abspath(__file__))
score = load_score_json(os.path.join(here, "..", "..", "..", "score.json"))
if score is None:
score = C_BASELINE
print(f"No score.json found; using baseline score {score:,.0f}")

result = full_analysis(score)
print_report(result)

if args.track:
track_score(score)