diff --git a/.github/workflows/bounty-verifier.yml b/.github/workflows/bounty-verifier.yml deleted file mode 100644 index 10b3685f..00000000 --- a/.github/workflows/bounty-verifier.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Bounty Verifier - -on: - issue_comment: - types: [created] - workflow_dispatch: - inputs: - issue_number: - description: "Issue number to verify" - required: true - type: number - comment_id: - description: "Specific comment ID (optional)" - required: false - type: number - -env: - PYTHON_VERSION: "3.11" - -jobs: - verify-claim: - if: | - github.event_name == 'workflow_dispatch' || - (github.event_name == 'issue_comment' && - github.event.issue.state == 'open' && - contains(github.event.comment.body, 'claim')) - runs-on: ubuntu-latest - permissions: - issues: write - contents: read - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ env.PYTHON_VERSION }} - cache: 'pip' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pyyaml - - - name: Run bounty verifier - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_OWNER: ${{ github.repository_owner }} - GITHUB_REPO: ${{ github.event.repository.name }} - DRY_RUN: "false" - LOG_LEVEL: "INFO" - run: | - cd tools/bounty_verifier - python -m bounty_verifier.cli verify \ - ${{ github.event.issue.number || inputs.issue_number }} \ - ${{ github.event.comment.id && format('--comment-id {0}', github.event.comment.id) || '' }} - - - name: Handle rate limit - if: failure() - run: | - echo "Rate limit may have been exceeded. The bot will retry on next comment." - echo "Check GitHub API rate limit status at: https://github.com/settings/tokens" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1856b0d..13b7b120 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,13 +34,6 @@ jobs: - name: Security scan (tests) run: bandit -r tests -ll - - name: Attestation fuzz regression gate - env: - RC_ADMIN_KEY: "0123456789abcdef0123456789abcdef" - DB_PATH: ":memory:" - ATTEST_FUZZ_CASES: "10000" - run: python -m pytest tests/test_attestation_fuzz.py -k mutation_regression_no_unhandled_exceptions -v - - name: Run tests with pytest (blocking) env: RC_ADMIN_KEY: "0123456789abcdef0123456789abcdef" diff --git a/.github/workflows/ci_ledger_invariants.yml b/.github/workflows/ci_ledger_invariants.yml deleted file mode 100644 index 69d39c36..00000000 --- a/.github/workflows/ci_ledger_invariants.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: Ledger Invariant Tests - -on: - push: - paths: - - 'testing/ledger_invariants.py' - - '.github/workflows/ci_ledger_invariants.yml' - pull_request: - paths: - - 'testing/ledger_invariants.py' - - '.github/workflows/ci_ledger_invariants.yml' - workflow_dispatch: - inputs: - scenarios: - description: 'Number of property-based scenarios' - required: false - default: '10000' - -jobs: - ledger-invariants: - runs-on: ubuntu-latest - name: Ledger Invariant Test Suite - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python 3.11 - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install hypothesis - - - name: Run ledger invariant tests (property-based) - run: | - python testing/ledger_invariants.py \ - --ci \ - --verbose \ - --scenarios ${{ github.event.inputs.scenarios || '10000' }} - timeout-minutes: 10 - - - name: Run ledger invariant tests (with live node check) - if: success() - run: | - python testing/ledger_invariants.py \ - --ci \ - --live \ - --verbose \ - --scenarios 1000 - continue-on-error: true # Live node may be temporarily unreachable - timeout-minutes: 5 - - - name: Upload test report - if: always() - run: | - python testing/ledger_invariants.py \ - --scenarios 100 \ - --report > ledger_invariants_report.json 2>&1 || true - - - name: Upload artifacts - if: always() - uses: actions/upload-artifact@v4 - with: - name: ledger-invariant-report - path: ledger_invariants_report.json diff --git a/.gitignore b/.gitignore index 53012fc9..8d1f90c7 100644 --- a/.gitignore +++ b/.gitignore @@ -32,9 +32,6 @@ Thumbs.db # Logs *.log -.pytest_cache/ -pytest-cache-files-*/ -tests/.tmp_attestation/ # Windows miner build artifacts Rustchain/miners/windows/dist/ diff --git a/README.md b/README.md index 25f9da41..e4574d4d 100644 --- a/README.md +++ b/README.md @@ -20,30 +20,12 @@ *Your PowerPC G4 earns more than a modern Threadripper. That's the point.* -[Website](https://rustchain.org) • [Manifesto](https://rustchain.org/manifesto.html) • [Boudreaux Principles](docs/BOUDREAUX_COMPUTING_PRINCIPLES.md) • [Live Explorer](https://rustchain.org/explorer) • [Swap wRTC](https://raydium.io/swap/?inputMint=sol&outputMint=12TAdKXxcGf6oCv4rqDz2NkgxjyHq6HQKoxKZYGf5i4X) • [DexScreener](https://dexscreener.com/solana/8CF2Q8nSCxRacDShbtF86XTSrYjueBMKmfdR3MLdnYzb) • [wRTC Quickstart](docs/wrtc.md) • [wRTC Tutorial](docs/WRTC_ONBOARDING_TUTORIAL.md) • [Grokipedia Ref](https://grokipedia.com/search?q=RustChain) • [Whitepaper](docs/RustChain_Whitepaper_Flameholder_v0.97-1.pdf) • [Quick Start](#-quick-start) • [How It Works](#-how-proof-of-antiquity-works) +[Website](https://rustchain.org) • [Live Explorer](https://rustchain.org/explorer) • [Swap wRTC](https://raydium.io/swap/?inputMint=sol&outputMint=12TAdKXxcGf6oCv4rqDz2NkgxjyHq6HQKoxKZYGf5i4X) • [DexScreener](https://dexscreener.com/solana/8CF2Q8nSCxRacDShbtF86XTSrYjueBMKmfdR3MLdnYzb) • [wRTC Quickstart](docs/wrtc.md) • [wRTC Tutorial](docs/WRTC_ONBOARDING_TUTORIAL.md) • [Grokipedia Ref](https://grokipedia.com/search?q=RustChain) • [Whitepaper](docs/RustChain_Whitepaper_Flameholder_v0.97-1.pdf) • [Quick Start](#-quick-start) • [How It Works](#-how-proof-of-antiquity-works) --- -## Q1 2026 Traction - -> *All data from [live GitHub API pull](https://github.com/Scottcjn/Rustchain/blob/main/docs/DEVELOPER_TRACTION_Q1_2026.md) compared against [GitClear](https://www.gitclear.com/research_studies/git_commit_count_percentiles_annual_days_active_from_largest_data_set) (878K dev-years), [LinearB](https://linearb.io/resources/software-engineering-benchmarks-report) (8.1M PRs), and [Electric Capital](https://www.developerreport.com) benchmarks.* - -| Metric (90 days) | Elyan Labs | Industry Median | Sei Protocol ($85M) | -|-------------------|-----------|----------------|---------------------| -| Commits | **1,882** | 105-168 | 297 | -| Repos shipped | **97** | 1-3 | 0 new | -| GitHub stars | **1,334** | 5-30 | 2,837 (lifetime) | -| Developer interactions | **150+** | 0-2 | 78 (lifetime) | -| Commits/dev/month | **627** | 56 | 7.6 | -| External contributions | **32 PRs** | 0-2 | 0 | -| Funding | **$0** | $0 | $85,000,000 | - -**[Full traction report with methodology and sources →](https://github.com/Scottcjn/Rustchain/blob/main/docs/DEVELOPER_TRACTION_Q1_2026.md)** - ---- - ## 🪙 wRTC on Solana RustChain Token (RTC) is now available as **wRTC** on Solana via the BoTTube Bridge: @@ -239,10 +221,6 @@ Earn **RTC** by contributing to the RustChain ecosystem! --- -## Testing Notes - -- Attestation malformed-input fuzz harness and replayable corpus: [docs/attestation_fuzzing.md](docs/attestation_fuzzing.md) - ## 💰 Antiquity Multipliers Your hardware's age determines your mining rewards: @@ -347,50 +325,6 @@ curl -sk "https://rustchain.org/wallet/balance?miner_id=YOUR_WALLET" open https://rustchain.org/explorer ``` -### Governance Proposals & Voting - -Rules: -- Proposal lifecycle: `Draft -> Active (7 days) -> Passed/Failed` -- Proposal creation: wallet must hold **more than 10 RTC** -- Voting eligibility: voter must be an **active miner** (from the attested miners view) -- Signature: votes require **Ed25519** signature verification -- Vote weight: `1 RTC = 1 base vote`, then multiplied by the miner antiquity multiplier -- Pass condition at close: `yes_weight > no_weight` - -Endpoints: - -```bash -# Create proposal -curl -sk -X POST https://rustchain.org/governance/propose \ - -H 'Content-Type: application/json' \ - -d '{ - "wallet":"RTC...", - "title":"Enable parameter X", - "description":"Rationale and implementation details" - }' - -# List proposals -curl -sk https://rustchain.org/governance/proposals - -# Proposal detail -curl -sk https://rustchain.org/governance/proposal/1 - -# Submit signed vote -curl -sk -X POST https://rustchain.org/governance/vote \ - -H 'Content-Type: application/json' \ - -d '{ - "proposal_id":1, - "wallet":"RTC...", - "vote":"yes", - "nonce":"1700000000", - "public_key":"", - "signature":"" - }' -``` - -Web UI: -- `GET /governance/ui` serves a lightweight page to list proposals and submit votes. - ## 🖥️ Supported Platforms | Platform | Architecture | Status | Notes | @@ -473,7 +407,6 @@ Read the draft spec: ## 📝 Articles -- [I Built More in 90 Days With $0 Than an $85M-Funded Team](https://dev.to/scottcjn/i-built-more-in-90-days-with-0-than-an-85m-funded-team-of-13-engineers-3oi0) - Dev.to - [Proof of Antiquity: A Blockchain That Rewards Vintage Hardware](https://dev.to/scottcjn/proof-of-antiquity-a-blockchain-that-rewards-vintage-hardware-4ii3) - Dev.to - [I Run LLMs on a 768GB IBM POWER8 Server](https://dev.to/scottcjn/i-run-llms-on-a-768gb-ibm-power8-server-and-its-faster-than-you-think-1o) - Dev.to @@ -519,61 +452,3 @@ clawrtc mine --dry-run ``` Expected: all 6 hardware fingerprint checks execute on native ARM64 without architecture fallback errors. - ---- - -## Tech Stack - -*Other projects flex React and Kubernetes. We flex COBOL and N64 assembly.* - -**Vintage & Retro** — the stuff nobody else runs: - -![COBOL](https://img.shields.io/badge/COBOL-%F0%9F%91%B4_Grandpa_Code-8B4513?style=flat-square) -![68K](https://img.shields.io/badge/68K-Mac_Classic-000000?style=flat-square&logo=apple&logoColor=white) -![i386](https://img.shields.io/badge/i386-DOS-808080?style=flat-square&logo=intel&logoColor=white) -![N64](https://img.shields.io/badge/N64-MIPS_R4300i-E60012?style=flat-square&logo=nintendo&logoColor=white) -![N64 ASM](https://img.shields.io/badge/N64_ASM-f3d_opcodes-228B22?style=flat-square) -![NES](https://img.shields.io/badge/NES-6502-CC0000?style=flat-square) -![Game Boy](https://img.shields.io/badge/Game_Boy-Z80-8DB600?style=flat-square) -![Amiga](https://img.shields.io/badge/Amiga-Kickstart-FF4500?style=flat-square) -![SPARC](https://img.shields.io/badge/SPARC-Sun-FF6600?style=flat-square) - -**PowerPC & POWER** — where the antiquity bonus lives: - -![G4](https://img.shields.io/badge/G4-2.5x_Antiquity-7B68EE?style=flat-square&logo=apple&logoColor=white) -![G5](https://img.shields.io/badge/G5-Dual_970-9370DB?style=flat-square&logo=apple&logoColor=white) -![POWER8](https://img.shields.io/badge/POWER8-128_Threads-0530AD?style=flat-square&logo=ibm&logoColor=white) -![512GB](https://img.shields.io/badge/RAM-512_GB-DC143C?style=flat-square) -![VSX](https://img.shields.io/badge/VSX-vec__perm-4B0082?style=flat-square) -![AltiVec](https://img.shields.io/badge/AltiVec-Velocity_Engine-8A2BE2?style=flat-square) - -**AI & Blockchain** — the frontier: - -![llama.cpp](https://img.shields.io/badge/llama.cpp-PSE_Fork-00ADD8?style=flat-square) -![Claude](https://img.shields.io/badge/Claude-Opus_4-D4A574?style=flat-square&logo=anthropic&logoColor=white) -![CUDA](https://img.shields.io/badge/CUDA-V100_%C3%973-76B900?style=flat-square&logo=nvidia&logoColor=white) -![GGUF](https://img.shields.io/badge/GGUF-Q4__K__M-FF6347?style=flat-square) -![Ergo](https://img.shields.io/badge/Ergo-Anchor-FF5733?style=flat-square) -![Rust](https://img.shields.io/badge/Rust-Ed25519-DEA584?style=flat-square&logo=rust&logoColor=black) -![Python](https://img.shields.io/badge/Python-Flask-3776AB?style=flat-square&logo=python&logoColor=white) -![SQLite](https://img.shields.io/badge/SQLite-Every_DB-003B57?style=flat-square&logo=sqlite&logoColor=white) - -**Hardware** — 18 GPUs, all from pawn shops and eBay: - -![228GB VRAM](https://img.shields.io/badge/VRAM-228_GB-FF1493?style=flat-square) -![18 GPUs](https://img.shields.io/badge/GPUs-18-76B900?style=flat-square) -![FPGA](https://img.shields.io/badge/Alveo_U30-FPGA_%C3%972-EE3524?style=flat-square) -![Hailo](https://img.shields.io/badge/Hailo--8-TPU-00BFFF?style=flat-square) -![VC](https://img.shields.io/badge/VC_Funding-$0-228B22?style=flat-square) -![Pawn Shop](https://img.shields.io/badge/Source-%F0%9F%8F%AA_Pawn_Shops-DAA520?style=flat-square) - - ---- - -
- -**[Elyan Labs](https://github.com/Scottcjn)** · 1,882 commits · 97 repos · 1,334 stars · $0 raised - -[⭐ Star Rustchain](https://github.com/Scottcjn/Rustchain) · [📊 Q1 2026 Traction Report](https://github.com/Scottcjn/Rustchain/blob/main/docs/DEVELOPER_TRACTION_Q1_2026.md) · [Follow @Scottcjn](https://github.com/Scottcjn) - -
diff --git a/README_JA.md b/README_JA.md index cfe84306..19aee358 100644 --- a/README_JA.md +++ b/README_JA.md @@ -28,24 +28,6 @@ --- -## Q1 2026 トラクション - -> *データは [ライブGitHub APIレポート](https://github.com/Scottcjn/Rustchain/blob/main/docs/DEVELOPER_TRACTION_Q1_2026.md) を基に、[GitClear](https://www.gitclear.com/research_studies/git_commit_count_percentiles_annual_days_active_from_largest_data_set)(878K dev-years)、[LinearB](https://linearb.io/resources/software-engineering-benchmarks-report)(8.1M PRs)、[Electric Capital](https://www.developerreport.com) のベンチマークと比較。* - -| 指標(90日) | Elyan Labs | 業界中央値 | Sei Protocol ($85M) | -|-------------------|-----------|----------------|---------------------| -| コミット数 | **1,882** | 105-168 | 297 | -| 出荷リポジトリ数 | **97** | 1-3 | 0 new | -| GitHubスター | **1,334** | 5-30 | 2,837(累計) | -| 開発者インタラクション | **150+** | 0-2 | 78(累計) | -| 開発者あたり月間コミット | **627** | 56 | 7.6 | -| 外部コントリビューション | **32 PRs** | 0-2 | 0 | -| 資金調達 | **$0** | $0 | $85,000,000 | - -**[手法・ソースを含む完全版レポート →](https://github.com/Scottcjn/Rustchain/blob/main/docs/DEVELOPER_TRACTION_Q1_2026.md)** - ---- - ## 🪙 Solana上のwRTC RustChainトークン(RTC)は、BoTTube Bridgeを通じてSolana上で**wRTC**として利用可能です: diff --git a/docs/BOUDREAUX_COMPUTING_PRINCIPLES.md b/docs/BOUDREAUX_COMPUTING_PRINCIPLES.md deleted file mode 100644 index d9d04259..00000000 Binary files a/docs/BOUDREAUX_COMPUTING_PRINCIPLES.md and /dev/null differ diff --git a/docs/CONSOLE_MINING_SETUP.md b/docs/CONSOLE_MINING_SETUP.md deleted file mode 100644 index b5594326..00000000 --- a/docs/CONSOLE_MINING_SETUP.md +++ /dev/null @@ -1,386 +0,0 @@ -# RIP-0683: Console Mining Setup Guide - -## Overview - -This guide walks you through setting up a retro game console as a RustChain miner using a Raspberry Pi Pico serial bridge. Console mining enables vintage hardware from 1983-2001 to earn RTC rewards through Proof of Antiquity consensus. - -**To our knowledge, this is the first blockchain to mine on vintage game console silicon.** - -## Supported Consoles - -| Console | CPU | Release Year | Multiplier | Status | -|---------|-----|--------------|------------|--------| -| NES/Famicom | Ricoh 2A03 (6502) | 1983 | 2.8x | ✅ Supported | -| SNES/Super Famicom | Ricoh 5A22 (65C816) | 1990 | 2.7x | ✅ Supported | -| Nintendo 64 | NEC VR4300 (MIPS) | 1996 | 2.5x | ✅ Supported | -| Game Boy | Sharp LR35902 (Z80) | 1989 | 2.6x | ✅ Supported | -| Game Boy Advance | ARM7TDMI | 2001 | 2.3x | ✅ Supported | -| Sega Genesis | Motorola 68000 | 1988 | 2.5x | ✅ Supported | -| Sega Master System | Zilog Z80 | 1986 | 2.6x | ✅ Supported | -| Sega Saturn | Hitachi SH-2 (dual) | 1994 | 2.6x | ✅ Supported | -| PlayStation 1 | MIPS R3000A | 1994 | 2.8x | ✅ Supported | - -## Hardware Requirements - -### Minimum Setup (~$10 USD) - -1. **Retro game console** (any from the list above) -2. **Raspberry Pi Pico** ($4 USD) - - Standard Pico for USB connection to PC - - Pico W for standalone WiFi operation -3. **Controller port adapter** (DIY or purchase) - - Connects Pico to console controller port - - Schematics provided below -4. **USB cable** (USB-A to Micro-USB) -5. **PC or laptop** (for running RustChain node) - -### Optional Upgrades - -- **Pico W** ($6 USD) - Enables standalone WiFi mining -- **Custom PCB adapter** - More reliable than breadboard -- **Multiple consoles** - One Pico can switch between consoles - -## Step 1: Build Controller Port Adapter - -### NES/SNES Adapter - -``` -NES Controller Port (male) → Pico GPIO -─────────────────────────────────────── -Pin 1 (Latch) → GPIO 5 -Pin 2 (Clock) → GPIO 6 -Pin 3 (Data) → GPIO 7 -Pin 4 (VCC) → VBUS (5V) -Pin 5 (GND) → GND -Pin 6 (Latch) → GPIO 5 (parallel with Pin 1) -Pin 7 (Clock) → GPIO 6 (parallel with Pin 2) -``` - -### N64 Adapter - -``` -N64 Controller Port (male) → Pico GPIO -──────────────────────────────────────── -Pin 1 (Data) → GPIO 2 -Pin 2 (Unused) → NC -Pin 3 (GND) → GND -Pin 4 (VCC) → VBUS (5V) -``` - -### Genesis Adapter - -``` -Genesis Controller Port (male) → Pico GPIO -─────────────────────────────────────────── -Pin 1 (Up) → GPIO 0 -Pin 2 (Down) → GPIO 1 -Pin 3 (Left) → GPIO 2 -Pin 4 (Right) → GPIO 3 -Pin 5 (B) → GPIO 4 -Pin 6 (C) → GPIO 5 -Pin 7 (GND) → GND -Pin 8 (A) → GPIO 6 -Pin 9 (Start) → GPIO 7 -``` - -## Step 2: Flash Pico Firmware - -### Prerequisites - -- Raspberry Pi Pico -- USB cable -- Computer with Arduino IDE or PlatformIO - -### Installation - -1. **Install Arduino IDE** (if not already installed) - ```bash - # Ubuntu/Debian - sudo snap install arduino - - # macOS - brew install --cask arduino - - # Windows: Download from https://www.arduino.cc/en/software - ``` - -2. **Add Pico board support** - - Open Arduino IDE - - Go to `File → Preferences` - - Add to "Additional Board Manager URLs": - ``` - https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json - ``` - - Go to `Tools → Board → Boards Manager` - - Search for "Raspberry Pi Pico" - - Install "Raspberry Pi Pico/RP2040" by Earle Philhower - -3. **Install dependencies** - - In Arduino IDE: `Sketch → Include Library → Manage Libraries` - - Install: - - `SHA256` by Dominik Reichert - - `ArduinoJson` by Benoit Blanchon - -4. **Load firmware** - - Open `miners/console/pico_bridge_firmware/pico_bridge.ino` - - Select board: `Tools → Board → Raspberry Pi Pico → Raspberry Pi Pico` - - Select port: `Tools → Port → /dev/ttyACM0` (Linux) or `COM3` (Windows) - - Click Upload (→) - -5. **Verify installation** - - Open Serial Monitor (115200 baud) - - Reset Pico - - Should see: `PICO_READY|RIP-0683 Console Bridge v1.0|` - -## Step 3: Prepare Console ROM - -### N64 Attestation ROM - -The console needs a custom ROM that: -1. Receives nonce from Pico -2. Computes SHA-256(nonce || wallet) -3. Outputs result via controller port - -**ROM Source**: See `miners/console/n64_attestation_rom/` (future implementation) - -### Alternative: Pico-Only Mode - -For consoles without custom ROM capability, the Pico can: -1. Simulate controller polling -2. Measure timing characteristics -3. Compute hash on behalf of console (with reduced multiplier) - -## Step 4: Configure RustChain Node - -### Update Node Configuration - -Edit your node's configuration file: - -```python -# config.py -CONSOLE_MINING_ENABLED = True -PICO_BRIDGE_PORT = "/dev/ttyACM0" # Linux -# PICO_BRIDGE_PORT = "COM3" # Windows -SUPPORTED_CONSOLE_ARCHS = [ - "nes_6502", "snes_65c816", "n64_mips", - "genesis_68000", "gameboy_z80", "ps1_mips" -] -``` - -### Start Node with Console Support - -```bash -cd node -python3 rustchain_v2_integrated_v2.2.1_rip200.py --console-mining -``` - -## Step 5: Submit Attestation - -### Manual Test - -```bash -# Send ATTEST command to Pico -echo "ATTEST|abc123|RTC1Wallet001|$(date +%s)" > /dev/ttyACM0 - -# Read response -cat < /dev/ttyACM0 -``` - -Expected response: -``` -OK|PICO001|n64_mips|{"ctrl_port_cv":0.005,"rom_hash_time_us":847000,...}| -``` - -### Automated Mining - -The node automatically: -1. Detects Pico bridge on serial port -2. Sends challenge nonce -3. Receives timing data and hash -4. Validates anti-emulation checks -5. Submits to consensus layer -6. Distributes rewards to `retro_console` bucket - -## Step 6: Verify Mining Status - -### Check Node Logs - -```bash -tail -f node/logs/rustchain.log | grep "console" -``` - -Expected output: -``` -[CONSOLE] Registered n64_mips miner (PICO001) -[CONSOLE] Attestation passed: CV=0.005, ROM_time=847ms -[REWARDS] retro_console bucket: 3 miners, 0.333 share -``` - -### Check Fleet Bucket Status - -```bash -curl http://localhost:5000/api/miners/fleet_status -``` - -Response: -```json -{ - "buckets": { - "retro_console": { - "miner_count": 3, - "share": 0.333, - "active_archs": ["n64_mips", "nes_6502", "ps1_mips"] - } - } -} -``` - -## Troubleshooting - -### Pico Not Detected - -**Symptoms**: Serial port not found, no response - -**Solutions**: -1. Check USB cable (some are charge-only) -2. Hold BOOTSEL button while plugging in Pico -3. Verify port: `ls /dev/ttyACM*` (Linux) or Device Manager (Windows) - -### CV Too Low (Emulator Detected) - -**Symptoms**: `ERROR|timing_too_uniform` - -**Causes**: -- Console not powered on -- Wrong controller port wiring -- Emulator instead of real hardware - -**Solutions**: -1. Verify console is running attestation ROM -2. Check controller port connections -3. Ensure real hardware, not FPGA/emulator - -### ROM Hash Time Wrong - -**Symptoms**: `ERROR|Suspicious hardware: ROM execution time outside tolerance` - -**Causes**: -- Wrong console architecture selected -- Overclocked console -- Timing measurement bug - -**Solutions**: -1. Verify correct `SET_CONSOLE` command sent to Pico -2. Check console is stock (not overclocked) -3. Increase tolerance in firmware (±15% → ±20%) - -### Fleet Detection Triggered - -**Symptoms**: Reduced rewards, `fleet_score > 0.5` - -**Causes**: -- Multiple consoles on same IP/subnet -- Correlated attestation timing -- Similar fingerprint profiles - -**Solutions**: -1. Spread consoles across different networks -2. Add random delay to attestation timing -3. Each console should have unique Pico ID - -## Economics - -### Expected Rewards - -Console miners share the `retro_console` bucket equally with other console miners. - -**Example** (assuming 10 total miners, 3 in retro_console): -- Total block reward: 1.5 RTC -- retro_console bucket share: 1.5 / 3 = 0.5 RTC -- Your console share: 0.5 / (number of console miners) - -**With 2.5x multiplier** (N64): -- Base reward × 2.5 = higher share within bucket - -### ROI Calculation - -**Initial Investment**: -- Console: $20-50 (eBay) -- Pico: $4 -- Adapter: $5 (parts) -- **Total**: ~$30-60 - -**Annual Revenue** (estimated): -- 0.1-0.5 RTC/day × 365 days × $0.50/RTC = **$18-91/year** - -**Payback Period**: 4-36 months - -**Note**: Rewards depend on network participation, RTC price, and console bucket size. - -## Advanced Topics - -### Multi-Console Bridge - -One Pico can manage multiple consoles: -- Use GPIO multiplexer -- Switch controller port connections -- Each console gets unique miner ID - -### Pico W Standalone Mode - -Pico W can operate without PC: -- Connects to WiFi -- Sends attestations directly to node -- Requires custom firmware build - -### Custom ROM Development - -Develop attestation ROMs for additional consoles: -- Use existing dev tools (gcc6502, mips64-elf-gcc) -- Link against librustchain (SHA-256 implementation) -- Output ROM format (.nes, .z64, .bin) - -## Security Considerations - -### Anti-Spoof Measures - -1. **Pico board ID** - Unique OTP ROM (cannot reprogram) -2. **Timing profiles** - Real hardware has characteristic jitter -3. **ROM execution time** - Must match known CPU performance -4. **Fleet detection** - IP clustering, timing correlation - -### Known Limitations - -- FPGA consoles may pass timing checks (under research) -- High-end emulators + fake bridge possible (mitigated by fleet detection) -- Console farms limited by bucket normalization - -## Future Work - -### Phase 2 (Q2 2026) -- Additional consoles: Atari 2600, Neo Geo, Dreamcast -- Pico W standalone firmware -- Multi-console bridge support - -### Phase 3 (Q3 2026) -- Hardware anchor on Ergo -- On-chain attestation registry -- Console-specific NFT badges - -## References - -- [RIP-0683 Specification](../rips/docs/RIP-0683-console-bridge-integration.md) -- [RIP-0304: Retro Console Mining](../rips/docs/RIP-0304-retro-console-mining.md) -- [RIP-201: Fleet Immune System](../rips/docs/RIP-0201-fleet-immune-system.md) -- [Legend of Elya](https://github.com/ilya-kh/legend-of-elya) - N64 neural network demo -- [Pico SDK Documentation](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf) - -## Support - -- **GitHub Issues**: https://github.com/rustchain/rustchain/issues -- **Discord**: https://discord.gg/rustchain -- **Documentation**: https://docs.rustchain.net - ---- - -© 2026 RustChain Core Team - Apache License 2.0 diff --git a/docs/DEVELOPER_TRACTION_Q1_2026.md b/docs/DEVELOPER_TRACTION_Q1_2026.md deleted file mode 100644 index 461e55f0..00000000 --- a/docs/DEVELOPER_TRACTION_Q1_2026.md +++ /dev/null @@ -1,277 +0,0 @@ -# Elyan Labs — Developer Traction Report -### Q1 2026 (December 2025 - March 2, 2026) - -**Prepared**: March 2, 2026 -**Author**: Scott Boudreaux, Founder -**Data**: GitHub API (live pull) + GitClear, LinearB, Electric Capital industry benchmarks - ---- - -## The Thesis - -Elyan Labs is a solo-founded open source ecosystem producing developer output that rivals VC-backed teams of 13+ engineers — on zero external capital. The data below is pulled directly from GitHub's API and compared against published industry benchmarks. - -This is not a pitch. It's a measurement. - ---- - -## 90-Day Snapshot - -| | Elyan Labs | Avg Solo Dev | Sei Protocol ($85M VC) | -|--|-----------|-------------|------------------------| -| **Capital raised** | **$0** | $0 | $85,000,000 | -| **Engineering headcount** | **1** | 1 | ~13 active | -| **Commits** | **1,882** | 105-168 | 297 | -| **Pull requests opened** | **41** | 9-15 | 417 | -| **Contributions to external projects** | **32 PRs** | 0-2 | 0 | -| **Open source repos shipped** | **97** | 1-3 | 0 new | -| **GitHub stars (ecosystem)** | **1,334** | 5-30 | 2,837 (lifetime) | -| **Forks (developer adoption)** | **359** | 2-10 | 870 (lifetime) | -| **Unique developer interactions** | **150+** | 0-2 | 78 (lifetime) | - -*150+ unique interactions includes PR authors (13), issue authors (28), bounty claimants, stargazers, fork creators, and clone traffic. 41 contributed code or issues directly; the remainder engaged through stars, forks, bounty discussions, and repository clones (exact clone/view counts not exposed by GitHub API).* - -**Sei Protocol comparison**: $85M raised (Jump Crypto, Multicoin, Coinbase Ventures), 78 total contributors. Sei's lifetime star count took years; Elyan Labs accumulated 47% of that figure in 90 days. - ---- - -## Capital Efficiency - -The core metric investors should examine: - -| | Elyan Labs | Sei Protocol | Aztec ($119M) | Radix ($21M) | -|--|-----------|-------------|---------------|-------------| -| **Commits/developer/month** | **627** | 7.6 | ~11 | 6.6 | -| **Cost per commit** | **$0** | ~$95,600 | ~$9,000 | ~$7,100 | -| **Stars per $M raised** | **infinite** | 33 | 3.6 | 29 | - -``` -Per-Developer Monthly Output (commits/dev/month) - - Elyan Labs (1 dev) ██████████████████████████████████████████ 627 - Indie median ████ 56 - Mina (7 devs, $29M) ███ 42 - FAANG median █▍ 8-21 - Aztec (133 ppl, $119M) █ 11 - Sei (13 devs, $85M) ▌ 7.6 - Radix (5 devs, $21M) ▌ 6.6 - - Scale: █ = 15 commits/dev/month -``` - -At 627 commits/dev/month, Elyan Labs operates at **82x** the per-developer output of a $85M-funded team. This isn't hustle theater — it reflects zero coordination overhead, zero PR review bottleneck, and direct technical execution. - -**Industry context**: GitClear's study of 878,592 developer-years places the median full-time developer at 56 commits/month. Elyan Labs' annualized pace of ~7,500 commits/year sits above the **99.9th percentile**. - ---- - -## Monthly Growth Trajectory - -### Development Velocity -| Month | Commits | PRs Opened | Repos Created | Issues Filed | -|-------|---------|-----------|---------------|-------------| -| Dec 2025 | 731 | 3 | 28 | 2 | -| Jan 2026 | 539 | 1 | 15 | 0 | -| Feb 2026 | 960 | 30 | 51 | 363 | -| Mar 1-2* | 93 | 7 | 3 | 79 | -| **Total** | **1,882** | **41** | **97** | **444** | - -*March represents 2 days only, tracking at February pace. - -### Community Engagement (Inbound) -| Month | PRs from Others | Issues from Others | Unique Contributors | -|-------|----------------|-------------------|-------------------| -| Dec 2025 | 0 | 0 | 0 | -| Jan 2026 | 0 | 1 | 1 | -| Feb 2026 | 652 | 82 | 41 | -| Mar 1-2* | 215 | 12 | sustained | - -**The inflection**: Zero inbound contributions through January. In February, a bounty program and ecosystem visibility campaign produced **867 inbound PRs** and **150+ unique developer interactions** in 30 days. 41 developers contributed code or filed issues directly; the remainder engaged via stars, forks, bounty claims, and clones. This growth is sustaining into March at the same pace. - ---- - -## Ecosystem Architecture - -Elyan Labs is not a single-repo project. It's an interconnected ecosystem of 99 public repositories spanning five categories: - -### Core Infrastructure -| Project | Stars | Forks | Description | -|---------|-------|-------|-------------| -| **RustChain** | 82 | 93 | Proof-of-Antiquity blockchain — rewards real vintage hardware | -| **BoTTube** | 67 | 48 | AI-native video platform (670 videos, 99 agents, 45.5K views) | -| **Beacon Skill** | 48 | 31 | Agent orchestration framework (PyPI + npm) | -| **RustChain Bounties** | 34 | 64 | Open bounty board — drives community contributions | -| **Grazer Skill** | 33 | 13 | Multi-platform agent discovery tool | - -### Research & Publications -| Project | Stars | Description | -|---------|-------|-------------| -| **RAM Coffers** | 29 | Neuromorphic NUMA-aware weight banking (predates DeepSeek Engram by 27 days) | -| **Legend of Elya N64** | 12 | Neural network running on Nintendo 64 hardware (MIPS R4300i) | -| **Grail-V** | -- | CVPR 2026 Workshop submission (non-bijunctive attention, 8.8x speedup on POWER8) | - -### Hardware Ports (Cross-Architecture) -| Project | Stars | Description | -|---------|-------|-------------| -| **exo-cuda** | 23 | NVIDIA CUDA support for distributed inference | -| **claude-code-power8** | 21 | Claude Code on IBM POWER8 | -| **llama-cpp-power8** | 18 | LLM inference on PowerPC with vec_perm optimization | -| **nvidia-power8-patches** | 20 | GPU driver patches for ppc64le | - -### Published Packages (PyPI/npm) -| Package | Version | Installs | -|---------|---------|---------| -| `beacon-skill` | 2.15.1 | PyPI + npm | -| `clawrtc` | 1.5.0 | PyPI | -| `bottube` | 1.6.0 | PyPI | -| `grazer-skill` | 1.6.0 | PyPI | - -### Live Tokens -| Token | Chain | Status | -|-------|-------|--------| -| **RTC** | RustChain native | Live, 20 miners, 88 epochs | -| **wRTC** | Solana | Mint revoked, LP locked, Raydium pool | -| **wRTC** | Base L2 | Mint revoked, LP locked, Aerodrome pool | - ---- - -## External Visibility & Contributions - -### Upstream Contributions (32 PRs to external projects) - -Elyan Labs actively contributes to major open source projects — not just consuming, but improving the ecosystem: - -| Project | PRs | Status | Significance | -|---------|-----|--------|-------------| -| **llama.cpp** (ggml-org) | 5 | Under review | Core LLM inference engine | -| **vLLM** (vllm-project) | 2 | 1 open | Production LLM serving | -| **BitNet** (Microsoft) | 2 | 1 open | 1-bit LLM research | -| **OpenFang** (RightNow-AI) | 2 | 1 open, 1 merged | Agent framework | -| **dn-institute** | 1 | Open ($100 bounty) | Prompt engineering | -| **Awesome lists** (24 repos) | 24 | 3 merged, 12 open | Ecosystem visibility | - -**Merged on notable repos**: Awesome-LLM-Inference, awesome-n64-development, awesome-agentic-patterns - -### Academic Publications -| Paper | Venue | Status | -|-------|-------|--------| -| Grail-V: Non-Bijunctive Attention | CVPR 2026 Workshop | Submitted (Submission #7) | -| Silicon Stratigraphy | JCAA | Rewrite requested | -| 5 Zenodo DOIs | Zenodo | Published | -| 7 Dev.to articles | Dev.to | Published | - ---- - -## Benchmark Context - -### Where Elyan Labs sits in the developer distribution - -**GitClear** (878,592 developer-years analyzed): - -| Percentile | Annual Commits | Elyan Labs (annualized) | -|-----------|---------------|------------------------| -| 50th (median) | 673 | -- | -| 90th | ~2,000 | -- | -| 99th | ~4,000 | -- | -| **99.9th+** | **>5,000** | **~7,500** | - -**Electric Capital** classifies "full-time crypto developer" as 10+ code-committed days/month. Elyan Labs codes nearly every day — 3x the threshold. - -**LinearB** (8.1M PRs, 4,800 teams, 42 countries): - -| Metric | Elite Threshold | Elyan Labs | -|--------|----------------|------------| -| Cycle time | <25 hours | Near-instant | -| Focus time/day | 6+ hours | All day | -| Rework rate | <2% | Low | - ---- - -## Honest Assessment: What's Not Working Yet - -Investors should understand the gaps as clearly as the strengths. - -| Gap | Current | Target | Path | -|-----|---------|--------|------| -| **Followers** | 30 | 500+ | Stars are spread across 75+ repos. No single "viral" repo yet. Need one breakout (500+ stars on Rustchain). | -| **External PR merge rate** | 9.4% (3/32) | 30%+ | Many awesome-list PRs awaiting review. llama.cpp PRs closed as duplicates. Need more targeted, higher-quality upstream contributions. | -| **Contributor quality** | Mixed | Verified | Some inbound PRs appear bot-generated (bounty farming). Of 150+ interactions, genuine engaged developers are a subset. Improving triage and verification. | -| **Revenue** | $0 | TBD | No monetization yet. Token (RTC) has internal reference rate ($0.10) but no public exchange listing. | -| **Documentation** | Thin | Production-grade | 97 repos created in 90 days. Many have minimal READMEs. Quality documentation would improve star-to-follow conversion. | - ---- - -## Hardware Lab (Physical Infrastructure) - -Unlike most software startups, Elyan Labs operates a physical compute lab built through disciplined hardware acquisition: - -| Asset | Specs | Acquisition | -|-------|-------|-------------| -| **18+ GPUs** | 228GB+ VRAM total | eBay datacenter pulls + pawn shops | -| **IBM POWER8 S824** | 128 threads, 512GB RAM | Enterprise decomm | -| **2x FPGA** (Alveo U30) | Video transcode + inference | Datacenter pull | -| **Hailo-8 TPU** | Edge AI accelerator | Incoming for POWER8 | -| **PowerPC fleet** | 3x G4, 2x G5 | Vintage hardware (RustChain miners) | -| **40GbE interconnect** | POWER8 <-> C4130 GPU server | 0.15ms latency | - -**Total investment**: ~$12,000 -**Estimated retail value**: $40,000-60,000+ -**Acquisition strategy**: 3-5x ROI through pawn shop arbitrage and eBay datacenter decomm sales - -This lab enables R&D that pure-cloud startups cannot economically replicate — particularly the POWER8 vec_perm work that underpins the Grail-V paper. - ---- - -## 6-Month Outlook - -| Metric | Now (90 days) | 6-Month Target | Basis | -|--------|--------------|----------------|-------| -| Commits | 1,882 | 4,000+ | Current velocity sustained | -| Stars | 1,334 | 3,000+ | Viral repo + continued ecosystem growth | -| Forks | 359 | 800+ | Bounty program expanding | -| Followers | 30 | 200+ | Requires star concentration fix | -| Unique interactions | 150+ | 500+ | Bounty expansion + organic discovery | -| Upstream merges | 3 | 15+ | Higher-quality targeted PRs | -| Published packages | 4 | 6+ | Two additional tools planned | - -### Key Inflection Points -- **100 followers**: Social proof threshold for organic discovery -- **500 stars on Rustchain**: GitHub trending eligibility -- **10 upstream merges**: Established open source contributor reputation -- **First exchange listing**: RTC/wRTC price discovery - ---- - -## Summary - -In 90 days with zero external funding, Elyan Labs has: - -- Shipped **97 public repositories** spanning blockchain, AI inference, agent orchestration, and hardware ports -- Generated **1,882 commits** (99.9th percentile of all developers globally) -- Attracted **150+ unique developer interactions** (from zero) -- Earned **1,334 GitHub stars** and **359 forks** -- Contributed **32 PRs to external projects** including llama.cpp, vLLM, and Microsoft BitNet -- Published **1 CVPR workshop paper** and **5 Zenodo DOIs** -- Deployed live tokens on **3 chains** (native RTC, Solana wRTC, Base wRTC) -- Built all of this on **$12,000 of pawn-shop hardware** - -The question isn't whether this developer can build. The question is what happens when this velocity gets fuel. - ---- - -## Data Sources - -| Source | Coverage | Link | -|--------|----------|------| -| GitHub API | Live pull, March 2, 2026 | github.com/Scottcjn | -| GitClear | 878K developer-years | [gitclear.com/research](https://www.gitclear.com/research_studies/git_commit_count_percentiles_annual_days_active_from_largest_data_set) | -| LinearB | 8.1M PRs, 4,800 teams | [linearb.io/benchmarks](https://linearb.io/resources/software-engineering-benchmarks-report) | -| GitHub Octoverse | 180M+ developers, 2025 | [octoverse.github.com](https://octoverse.github.com/) | -| Electric Capital | Crypto developer ecosystem | [developerreport.com](https://www.developerreport.com) | -| Sei Protocol | $85M funded, 78 contributors | [github.com/sei-protocol](https://github.com/sei-protocol/sei-chain) | -| Aztec Network | $119M funded, 133 contributors | [github.com/AztecProtocol](https://github.com/AztecProtocol/aztec-packages) | - ---- - -*Elyan Labs LLC — Louisiana, US* -*scott@elyanlabs.ai | @RustchainPOA | github.com/Scottcjn* diff --git a/docs/PROTOCOL.md b/docs/PROTOCOL.md index 79687fc9..4fd0979c 100644 --- a/docs/PROTOCOL.md +++ b/docs/PROTOCOL.md @@ -2,7 +2,7 @@ ## 1. Overview -**RustChain** is a Proof-of-Antiquity blockchain that validates and rewards vintage hardware. Unlike traditional Proof-of-Work, RustChain uses **RIP-200** (RustChain Iterative Protocol), a Proof-of-Antiquity consensus where miners prove physical hardware ownership to earn **RTC** tokens. +**RustChain** is a Proof-of-Antiquity blockchain that validates and rewards vintage hardware. Unlike traditional Proof-of-Work, RustChain uses **RIP-200** (RustChain Iterative Protocol), a Proof-of-Attestation consensus where miners prove physical hardware ownership to earn **RTC** tokens. **Core Principle**: 1 CPU = 1 Vote, weighted by hardware antiquity. diff --git a/docs/PROTOCOL_BOUNTY_8.md b/docs/PROTOCOL_BOUNTY_8.md deleted file mode 100644 index dc9f186e..00000000 --- a/docs/PROTOCOL_BOUNTY_8.md +++ /dev/null @@ -1,359 +0,0 @@ -# RustChain Protocol Documentation (Bounty #8 Draft) - -## 1) Protocol Overview - -RustChain is a **Proof-of-Antiquity** blockchain (RIP-200) that rewards physical hardware identity over raw hash power. - -- Consensus principle: **1 CPU = 1 vote**, then weighted by antiquity/fingerprint validity. -- Focus: reward real vintage hardware (PowerPC-era, retro architectures) and penalize VM/emulator spoofing. -- Runtime stack (current implementation): Flask + SQLite node, miner scripts for Linux/macOS, signed transfer + pending ledger settlement. - ---- - -## 2) RIP-200 Consensus and Epoch Lifecycle - -### 2.1 High-level flow - -```mermaid -sequenceDiagram - participant Miner - participant Node as RustChain Node - participant Ledger as Epoch/Pending Ledger - participant Anchor as External Anchor (Ergo) - - Miner->>Node: POST /attest/challenge - Node-->>Miner: nonce + challenge context - Miner->>Miner: collect hardware signals + fingerprint checks - Miner->>Node: POST /attest/submit (signed attestation) - Node->>Node: validate shape, identity, fingerprint, anti-abuse - Node-->>Miner: attestation result (ok/deny) - - Miner->>Node: POST /epoch/enroll - Node->>Ledger: register miner in active epoch - - Note over Node,Ledger: Epoch window closes - Node->>Node: compute weights + rewards - Node->>Ledger: /rewards/settle -> pending credits - Node->>Anchor: anchor settlement digest/proof - Miner->>Node: query balance / withdraw -``` - -### 2.2 Epoch settlement - -At settlement, miners in epoch are weighted by hardware/fingerprint/consensus rules and paid from epoch pool. - -Conceptually: - -```text -reward_i = epoch_pool * weight_i / sum(weight_all_eligible_miners) -``` - ---- - -## 3) Attestation Flow (what miner sends, what node validates) - -## 3.1 Miner payload - -Attestation payload contains (simplified): - -- `miner` / `miner_id` -- `report` (nonce/commitment/derived timing entropy) -- `device` (family/arch/model/cpu/cores/memory/serial) -- `signals` (hostname/MAC list, etc.) -- `fingerprint` (results of checks) -- optional sidecar proof fields (if dual-mining mode enabled) - -## 3.2 Node validation gates - -Node-side validation includes: - -1. **Shape validation** for request body/fields -2. **Miner identifier validation** (allowed chars/length) -3. **Challenge/nonce consistency** -4. **Hardware signal sanity checks** -5. **Rate limit / anti-abuse checks by client IP / miner** -6. **Fingerprint pass/fail classification** -7. **Enrollment eligibility decision** - -If accepted, miner can call `/epoch/enroll` and participate in reward distribution. - ---- - -## 4) Hardware Fingerprinting (6+1) - -RustChain uses hardware-behavior checks to distinguish physical machines from VMs/emulators. - -Primary checks (implementation naming varies by miner/tooling): - -1. Clock-skew / oscillator drift -2. Cache timing characteristics -3. SIMD instruction identity/timing -4. Thermal drift entropy -5. Instruction-path jitter -6. Anti-emulation heuristics (hypervisor/container indicators) -7. (Optional hardening layer) serial/OUI consistency enforcement in node policies - -Why it matters: - -- prevents synthetic identity inflation -- keeps weight tied to **real** hardware behavior -- protects reward fairness across participants - ---- - -## 5) Token Economics (RTC) - -- Native token: **RTC** -- Reward source: epoch distribution + pending ledger confirmation paths -- Weight-driven payout: higher eligible weight gets larger epoch share -- Additional policy knobs exposed by endpoints (`/api/bounty-multiplier`, `/api/fee_pool`, etc.) - -> Note: precise emissions, premine, and multiplier schedules should be versioned in canonical tokenomics docs; this file documents protocol mechanics + API surfaces. - ---- - -## 6) Network Architecture - -```mermaid -graph TD - M1[Miner A] --> N[Attestation/Settlement Node] - M2[Miner B] --> N - M3[Miner C] --> N - - N --> P[(Pending Ledger / Epoch State)] - N --> X[Explorer/UI APIs] - N --> A[External Anchor (Ergo)] -``` - -Components: - -- **Miners**: generate attestation reports + enroll each epoch -- **Node**: validates attestations, computes rewards, exposes APIs -- **Pending ledger**: tracks pending confirmations/void/integrity operations -- **Explorer/API**: status, balances, miners, stats -- **Anchor layer**: external timestamp/proof anchoring - ---- - -## 7) Public API Reference (with curl examples) - -Base example: - -```bash -BASE="https://rustchain.org" -``` - -## 7.1 Health / status - -### GET `/health` -```bash -curl -sS "$BASE/health" -``` - -### GET `/ready` -```bash -curl -sS "$BASE/ready" -``` - -### GET `/ops/readiness` -```bash -curl -sS "$BASE/ops/readiness" -``` - -## 7.2 Miner discovery / stats - -### GET `/api/miners` -```bash -curl -sS "$BASE/api/miners" -``` - -### GET `/api/stats` -```bash -curl -sS "$BASE/api/stats" -``` - -### GET `/api/nodes` -```bash -curl -sS "$BASE/api/nodes" -``` - -## 7.3 Attestation + enrollment - -### POST `/attest/challenge` -```bash -curl -sS -X POST "$BASE/attest/challenge" -H 'Content-Type: application/json' -d '{}' -``` - -### POST `/attest/submit` -```bash -curl -sS -X POST "$BASE/attest/submit" \ - -H 'Content-Type: application/json' \ - -d '{"miner":"RTC_example","report":{"nonce":"n"},"device":{},"signals":{},"fingerprint":{}}' -``` - -### POST `/epoch/enroll` -```bash -curl -sS -X POST "$BASE/epoch/enroll" \ - -H 'Content-Type: application/json' \ - -d '{"miner_pubkey":"RTC_example","miner_id":"host-1","device":{"family":"x86","arch":"modern"}}' -``` - -### GET `/epoch` -```bash -curl -sS "$BASE/epoch" -``` - -## 7.4 Wallet / balances / transfer - -### GET `/balance/` -```bash -curl -sS "$BASE/balance/RTC_example" -``` - -### GET `/wallet/balance?miner_id=` -```bash -curl -sS "$BASE/wallet/balance?miner_id=RTC_example" -``` - -### POST `/wallet/transfer` -```bash -curl -sS -X POST "$BASE/wallet/transfer" \ - -H 'Content-Type: application/json' \ - -d '{"from":"RTC_a","to":"RTC_b","amount":1.25}' -``` - -### POST `/wallet/transfer/signed` -```bash -curl -sS -X POST "$BASE/wallet/transfer/signed" \ - -H 'Content-Type: application/json' \ - -d '{"from":"RTC_a","to":"RTC_b","amount":1.25,"signature":"...","pubkey":"..."}' -``` - -### GET `/wallet/ledger` -```bash -curl -sS "$BASE/wallet/ledger" -``` - -## 7.5 Pending ledger ops - -### GET `/pending/list` -```bash -curl -sS "$BASE/pending/list" -``` - -### POST `/pending/confirm` -```bash -curl -sS -X POST "$BASE/pending/confirm" -H 'Content-Type: application/json' -d '{"id":123}' -``` - -### POST `/pending/void` -```bash -curl -sS -X POST "$BASE/pending/void" -H 'Content-Type: application/json' -d '{"id":123,"reason":"invalid"}' -``` - -### GET `/pending/integrity` -```bash -curl -sS "$BASE/pending/integrity" -``` - -## 7.6 Rewards + mining economics - -### GET `/rewards/epoch/` -```bash -curl -sS "$BASE/rewards/epoch/1" -``` - -### POST `/rewards/settle` -```bash -curl -sS -X POST "$BASE/rewards/settle" -H 'Content-Type: application/json' -d '{}' -``` - -### GET `/api/bounty-multiplier` -```bash -curl -sS "$BASE/api/bounty-multiplier" -``` - -### GET `/api/fee_pool` -```bash -curl -sS "$BASE/api/fee_pool" -``` - -## 7.7 Explorer + machine details - -### GET `/explorer` -```bash -curl -sS "$BASE/explorer" | head -``` - -### GET `/api/miner//attestations` -```bash -curl -sS "$BASE/api/miner/RTC_example/attestations" -``` - -### GET `/api/miner_dashboard/` -```bash -curl -sS "$BASE/api/miner_dashboard/RTC_example" -``` - -## 7.8 P2P / beacon / headers (operator-facing public routes) - -- `POST /p2p/add_peer` -- `GET /p2p/blocks` -- `GET /p2p/ping` -- `GET /p2p/stats` -- `GET/POST /beacon/*` (`/beacon/digest`, `/beacon/envelopes`, `/beacon/submit`) -- `POST /headers/ingest_signed`, `GET /headers/tip` - ---- - -## 8) Operator/Admin API groups - -These are exposed routes but typically for controlled operator use: - -- OUI enforcement/admin: - - `/admin/oui_deny/list|add|remove|enforce` - - `/ops/oui/enforce` -- Governance rotation: - - `/gov/rotate/stage|commit|approve|message/` -- Metrics: - - `/metrics`, `/metrics_mac` -- Withdraw flows: - - `/withdraw/register|request|status/|history/` - ---- - -## 9) Security Model Notes - -- Trust boundary: client payload is untrusted; server performs strict type/shape checks. -- Identity hardening: IP-based anti-abuse + hardware fingerprinting + serial/OUI controls. -- Transfer hardening: signed transfer endpoint for stronger authorization path. -- Settlement auditability: pending ledger + integrity endpoints + external anchoring. - ---- - -## 10) Glossary - -- **RIP-200**: RustChain Iterative Protocol v200; Proof-of-Antiquity consensus design. -- **Proof-of-Antiquity**: consensus weighting emphasizing vintage/real hardware identity. -- **Epoch**: reward accounting window; miners enroll and settle per epoch. -- **Attestation**: miner proof packet (hardware signals + report + fingerprint). -- **Fingerprint checks (6+1)**: anti-VM/emulation hardware-behavior tests plus policy hardening layer. -- **Pending ledger**: intermediate transfer/reward state before final confirmation/void. -- **PSE / entropy-derived signals**: timing/noise signatures used in report/fingerprint scoring. -- **Anchoring**: writing settlement proof to external chain (Ergo). - ---- - -## 11) Suggested docs split for final upstream submission - -To match bounty acceptance cleanly, split this into: - -- `docs/protocol/overview.md` -- `docs/protocol/attestation.md` -- `docs/protocol/epoch_settlement.md` -- `docs/protocol/tokenomics.md` -- `docs/protocol/network_architecture.md` -- `docs/protocol/api_reference.md` -- `docs/protocol/glossary.md` - -This draft is intentionally consolidated for review-first iteration. diff --git a/docs/PROTOCOL_v1.1.md b/docs/PROTOCOL_v1.1.md index 8db3b370..5dd5281a 100644 --- a/docs/PROTOCOL_v1.1.md +++ b/docs/PROTOCOL_v1.1.md @@ -1,11 +1,11 @@ # RustChain Protocol Specification v1.1 (RIP-200) ## 1. Overview -**RustChain** is a Proof-of-Antiquity blockchain designed to validate and reward real vintage hardware. Unlike traditional Proof-of-Work, RustChain does not use hash-based mining. Instead, it utilizes **RIP-200 (RustChain Iterative Protocol)**, a Proof-of-Antiquity consensus mechanism where miners prove they are running on specific physical hardware (e.g., PowerPC G4, G5, SPARC) to earn **RTC** tokens. +**RustChain** is a Proof-of-Antiquity blockchain designed to validate and reward real vintage hardware. Unlike traditional Proof-of-Work, RustChain does not use hash-based mining. Instead, it utilizes **RIP-200 (RustChain Iterative Protocol)**, a Proof-of-Attestation consensus mechanism where miners prove they are running on specific physical hardware (e.g., PowerPC G4, G5, SPARC) to earn **RTC** tokens. Despite the name, the reference implementation is written in **Python** (Flask + SQLite), chosen for its ubiquity on vintage *nix platforms. -## 2. Consensus: RIP-200 (Proof-of-Antiquity) +## 2. Consensus: RIP-200 (Proof-of-Attestation) RIP-200 replaces hash power with hardware identity. The core principle is **1 CPU = 1 Vote**, weighted by the antiquity of the hardware. ### 2.1 The Attestation Cycle diff --git a/docs/RIP-305-cross-chain-airdrop.md b/docs/RIP-305-cross-chain-airdrop.md deleted file mode 100644 index c72c8e3d..00000000 --- a/docs/RIP-305-cross-chain-airdrop.md +++ /dev/null @@ -1,206 +0,0 @@ -# RIP-305: Cross-Chain Airdrop Protocol - -**Status**: Draft -**Author**: Scott (Flameholder), Elyan Labs -**Created**: 2026-03-07 -**Allocation**: 50,000 RTC (0.6% of total supply) - ---- - -## Abstract - -RIP-305 defines a cross-chain airdrop mechanism for distributing wrapped RTC (wRTC) tokens on Solana and Base L2. The protocol incentivizes ecosystem participation while implementing anti-Sybil measures including minimum wallet balance requirements, GitHub contribution verification, and wallet age checks. - -## Motivation - -RustChain's contributor base is growing (214+ recipients, 2,948+ stars) but remains concentrated on GitHub. Cross-chain airdrops on Solana and Base expose RTC to established DeFi/Web3 communities, creating liquidity pathways and broader awareness. - -The airdrop uses a fee recycling flywheel: distributed RTC generates transaction fees (RIP-303 gas), which flow back to the community fund for subsequent airdrop stages. - -## Specification - -### 1. Token Contracts - -#### Solana (SPL Token) -- **Symbol**: wRTC -- **Decimals**: 6 (matches RTC internal precision) -- **Mint Authority**: Elyan Labs multisig (upgradeable to DAO) -- **Allocation**: 30,000 wRTC - -#### Base (ERC-20) -- **Symbol**: wRTC -- **Decimals**: 6 -- **Contract**: OpenZeppelin ERC-20 with mint/burn + Ownable -- **Allocation**: 20,000 wRTC - -### 2. Bridge Mechanism - -Phase 1 (Admin Bridge): -``` -Lock: POST /bridge/lock {wallet, amount, target_chain, target_address} - -> Locks RTC on RustChain, returns lock_id - -> Admin mints equivalent wRTC on target chain - -Release: POST /bridge/release {lock_id, burn_tx_hash} - -> Verifies burn on target chain - -> Releases RTC on RustChain -``` - -Phase 2 (Trustless Bridge): -- Ergo anchor commitments serve as cross-chain proofs -- Lock/mint verified by attestation node consensus (2-of-3) - -### 3. Eligibility Requirements - -Claimants must satisfy BOTH GitHub contribution AND wallet requirements: - -#### GitHub Contribution (any one): -| Tier | Requirement | Base Claim | -|------|------------|------------| -| Stargazer | 10+ Scottcjn repos starred | 25 wRTC | -| Contributor | 1+ merged PR | 50 wRTC | -| Builder | 3+ merged PRs | 100 wRTC | -| Security | Verified vulnerability found | 150 wRTC | -| Core | 5+ merged PRs or Star King badge | 200 wRTC | -| Miner | Active attestation history | 100 wRTC | - -#### Wallet Requirements (anti-Sybil): -| Chain | Minimum Balance | Wallet Age | -|-------|----------------|------------| -| Solana | 0.1 SOL (~$15) | 7+ days | -| Base | 0.01 ETH (~$25) | 7+ days | - -#### Wallet Value Multiplier: -| Solana Balance | Base Balance | Multiplier | -|---------------|-------------|------------| -| 0.1-1 SOL | 0.01-0.1 ETH | 1.0x | -| 1-10 SOL | 0.1-1 ETH | 1.5x | -| 10+ SOL | 1+ ETH | 2.0x | - -### 4. Anti-Sybil Stack - -| Check | Blocks | -|-------|--------| -| Minimum wallet balance | Empty wallet farms | -| Wallet age > 7 days | Just-created wallets | -| GitHub account age > 30 days | Fresh bot accounts | -| GitHub OAuth (unique) | Multi-claim from same account | -| One claim per GitHub account | Double-dipping across chains | -| One claim per wallet address | Wallet recycling | -| RustChain wallet binding | Links on-chain identity | - -### 5. Staged Distribution - -``` -Stage 1 (Seed): 50,000 RTC allocated - - Solana: 30,000 wRTC - - Base: 20,000 wRTC - -Stage 2 (Recycle): Fees from RTC transactions (RIP-303 gas) - - Community fund receives fee revenue - - Portion allocated to next airdrop round - - Minimum 30-day cycle between stages - -Stage 3 (Organic): Community governance decides allocation - - RIP-0002 governance votes on subsequent airdrops - - Fee pool sustains ongoing distribution -``` - -### 6. Claim Flow - -``` -1. User visits airdrop.rustchain.org -2. Connects GitHub (OAuth) -> verifies contribution tier -3. Generates or enters RustChain wallet name -4. Connects Solana (Phantom) or Base (MetaMask) wallet -5. System checks: - a. GitHub eligibility (stars, PRs, mining) - b. Wallet minimum balance - c. Wallet age - d. No previous claim -6. If eligible: RTC locked on RustChain, wRTC minted to target wallet -7. Claim receipt stored on-chain with tx hashes -``` - -### 7. Claim API Endpoints - -``` -GET /airdrop/eligibility?github={username} - -> Returns tier, base_claim, requirements_met - -POST /airdrop/claim - { - github_token: "oauth_token", - rtc_wallet: "my-wallet-name", - target_chain: "solana" | "base", - target_address: "wallet_address" - } - -> Validates eligibility + anti-Sybil - -> Locks RTC, returns mint instructions - -GET /airdrop/status - -> Total distributed, remaining, claims by chain - -GET /airdrop/leaderboard - -> Top claimants by tier -``` - -### 8. Token Metadata - -#### Solana -```json -{ - "name": "Wrapped RustChain Token", - "symbol": "wRTC", - "description": "Wrapped RTC from RustChain Proof-of-Antiquity blockchain. 1 wRTC = 1 RTC locked on RustChain.", - "image": "https://rustchain.org/assets/wrtc-logo.png", - "external_url": "https://rustchain.org", - "attributes": [ - {"trait_type": "Bridge", "value": "RustChain Native Bridge"}, - {"trait_type": "Backing", "value": "1:1 RTC locked"} - ] -} -``` - -#### Base (ERC-20) -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@openzeppelin/contracts/access/Ownable.sol"; - -contract WrappedRTC is ERC20, Ownable { - constructor() ERC20("Wrapped RustChain Token", "wRTC") Ownable(msg.sender) {} - - function mint(address to, uint256 amount) external onlyOwner { - _mint(to, amount); - } - - function burn(uint256 amount) external { - _burn(msg.sender, amount); - } - - function decimals() public pure override returns (uint8) { - return 6; - } -} -``` - -## Security Considerations - -1. **Bridge risk**: Phase 1 admin bridge is centralized. Mitigated by transparent lock ledger and small initial allocation. -2. **Sybil attacks**: Multi-layer checks (wallet balance + age + GitHub OAuth + claim limits) make farming uneconomical. -3. **Price manipulation**: wRTC is backed 1:1 by locked RTC. No fractional reserve. -4. **Smart contract risk**: Base ERC-20 uses audited OpenZeppelin contracts. Solana SPL is standard token program. - -## Backwards Compatibility - -RIP-305 is additive. Existing RTC balances, mining, and RIP-303 gas are unaffected. The bridge creates a new distribution channel without modifying core protocol. - -## References - -- RIP-303: RTC Gas for Beacon (fee mechanism) -- RIP-302: Agent Economy (job marketplace) -- RIP-0002: Governance System -- BOUNTY_LEDGER.md: Payment transparency diff --git a/docs/TESTNET_FAUCET.md b/docs/TESTNET_FAUCET.md deleted file mode 100644 index e08e85d2..00000000 --- a/docs/TESTNET_FAUCET.md +++ /dev/null @@ -1,56 +0,0 @@ -# RustChain Testnet Faucet - -This adds a standalone Flask faucet service for the bounty task: -- `GET /faucet` (simple HTML form) -- `POST /faucet/drip` - -## Request - -```json -{ - "wallet": "my-test-wallet", - "github_username": "myuser" -} -``` - -## Response - -```json -{ - "ok": true, - "amount": 1.0, - "pending_id": 123, - "next_available": "2026-03-08T12:00:00Z" -} -``` - -## Rate limits (24h) - -- No auth (IP only): 0.5 RTC -- GitHub user: 1.0 RTC -- GitHub account older than 1 year: 2.0 RTC - -## Run - -```bash -pip install flask requests -python tools/testnet_faucet.py -``` - -Then open: `http://127.0.0.1:8090/faucet` - -## Config - -Environment variables: -- `FAUCET_DB_PATH` (default: `faucet.db`) -- `FAUCET_DRY_RUN` (`1`/`0`, default `1`) -- `FAUCET_ADMIN_TRANSFER_URL` -- `FAUCET_ADMIN_API_TOKEN` -- `FAUCET_POOL_WALLET` -- `GITHUB_TOKEN` (optional, for account-age check) - -## Tests - -```bash -pytest tests/test_faucet.py -q -``` diff --git a/docs/WALLET_CLI_COMPATIBILITY_39.md b/docs/WALLET_CLI_COMPATIBILITY_39.md deleted file mode 100644 index 435df348..00000000 --- a/docs/WALLET_CLI_COMPATIBILITY_39.md +++ /dev/null @@ -1,57 +0,0 @@ -# Wallet CLI Compatibility Notes (Issue #39) - -This note documents format compatibility and cross-platform validation for the RustChain Wallet CLI. - -## Keystore compatibility - -CLI keystore output fields: -- `version` -- `name` -- `address` -- `public_key_hex` -- `mnemonic_words` -- `crypto`: - - `cipher: AES-256-GCM` - - `kdf: PBKDF2-HMAC-SHA256` - - `kdf_iterations: 100000` - - `salt_b64` - - `nonce_b64` - - `ciphertext_b64` - -Backward-compatible decryption aliases supported by the CLI loader: -- `salt_b64` or `salt` -- `nonce_b64` or `nonce` or `iv_b64` or `iv` -- `ciphertext_b64` or `ciphertext` or `encrypted_private_key` -- `kdf_iterations` or `iterations` or `pbkdf2_iterations` - -This allows the CLI to read equivalent legacy JSON key names while preserving modern output format. - -## Signature payload compatibility - -Signed transfer payload uses: -- `from_address` -- `to_address` -- `amount_rtc` -- `nonce` -- `memo` -- `public_key` -- `signature` - -Signature is Ed25519 over canonical JSON message: - -```json -{"amount":,"from":"","memo":"...","nonce":"","to":""} -``` - -This matches `/wallet/transfer/signed` server-side verification pattern. - -## Validation summary - -Local (macOS): -- `python3 -m pytest -q tests/test_wallet_cli_39.py` -> passed -- `python3 tools/rustchain_wallet_cli.py epoch` -> success -- `python3 tools/rustchain_wallet_cli.py miners` -> success -- `python3 tools/rustchain_wallet_cli.py balance ` -> success - -Remote (Linux, HK machine): -- same test command and CLI command smoke checks executed successfully. diff --git a/docs/WALLET_CLI_PREVIEW_39.md b/docs/WALLET_CLI_PREVIEW_39.md deleted file mode 100644 index 832aedf1..00000000 --- a/docs/WALLET_CLI_PREVIEW_39.md +++ /dev/null @@ -1,42 +0,0 @@ -# RustChain Wallet CLI (Preview for bounty #39) - -This draft adds a headless wallet tool: - -- `rustchain-wallet create` -- `rustchain-wallet import ` -- `rustchain-wallet export ` -- `rustchain-wallet balance ` -- `rustchain-wallet send --from ` -- `rustchain-wallet history ` -- `rustchain-wallet miners` -- `rustchain-wallet epoch` - -## Paths - -- CLI implementation: `tools/rustchain_wallet_cli.py` -- Command wrapper: `scripts/rustchain-wallet` -- Keystore dir: `~/.rustchain/wallets/` - -## Security / format notes - -- Private keys are encrypted with **AES-256-GCM** -- KDF: **PBKDF2-HMAC-SHA256** with **100,000 iterations** -- Address derivation: `RTC` + `SHA256(pubkey)[:40]` -- Transfer signing: Ed25519 over canonical payload used by `/wallet/transfer/signed` - -## Dependency - -Install BIP39 helper once: - -```bash -python3 -m pip install mnemonic -``` - -## Quick smoke test - -```bash -scripts/rustchain-wallet create --name demo -scripts/rustchain-wallet export demo -scripts/rustchain-wallet epoch -scripts/rustchain-wallet miners -``` diff --git a/docs/YOLO.md b/docs/YOLO.md deleted file mode 100644 index 6ab5f783..00000000 --- a/docs/YOLO.md +++ /dev/null @@ -1,5 +0,0 @@ -# YOLO - -> Merging without review since 2026. - -Sometimes you just have to ship it. diff --git a/docs/about.html b/docs/about.html index 09e0b8c9..969b3a4b 100644 --- a/docs/about.html +++ b/docs/about.html @@ -3,9 +3,9 @@ - About RustChain | Proof-of-Antiquity Blockchain Revolution - - + About RustChain | Proof-of-Attestation Blockchain Revolution + + @@ -16,7 +16,7 @@ - + @@ -24,7 +24,7 @@ - + @@ -33,8 +33,8 @@ { "@context": "https://schema.org", "@type": "Article", - "headline": "About RustChain | Proof-of-Antiquity Blockchain Revolution", - "description": "Learn about RustChain's revolutionary Proof-of-Antiquity consensus that rewards vintage hardware mining through silicon stratigraphy and antiquity multipliers.", + "headline": "About RustChain | Proof-of-Attestation Blockchain Revolution", + "description": "Learn about RustChain's revolutionary Proof-of-Attestation consensus that rewards vintage hardware mining through silicon stratigraphy and antiquity multipliers.", "url": "https://rustchain.org/docs/about.html", "datePublished": "2026-02-18", "author": { @@ -176,10 +176,10 @@

About RustChain

-

The Philosophy Behind Proof-of-Antiquity

+

The Philosophy Behind Proof-of-Attestation

RustChain emerged from a simple observation: modern blockchain consensus mechanisms have lost touch with computing's physical reality. Proof-of-Work wastes energy on meaningless calculations, while Proof-of-Stake concentrates power among the wealthy. We envisioned something different—a system that honors the tangible history of computing hardware.

-

Our Proof-of-Antiquity consensus mechanism represents a paradigm shift. Instead of rewarding computational waste or financial capital, RustChain rewards authenticity, entropy, and the preservation of computing history. Every miner proves they're running on real physical hardware through sophisticated cryptographic fingerprinting that reads the unique characteristics baked into silicon during manufacturing.

+

Our Proof-of-Attestation consensus mechanism represents a paradigm shift. Instead of rewarding computational waste or financial capital, RustChain rewards authenticity, entropy, and the preservation of computing history. Every miner proves they're running on real physical hardware through sophisticated cryptographic fingerprinting that reads the unique characteristics baked into silicon during manufacturing.

The Silicon Stratigraphy Revolution

At the heart of RustChain lies the concept of silicon stratigraphy—the study of hardware layers and their temporal signatures. Just as geologists read rock layers to understand Earth's history, RustChain reads hardware signatures to understand computing's evolution. Each CPU carries unique imperfections, timing variations, and thermal characteristics that serve as a fingerprint of its manufacturing era and usage history.

diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index 383c7adf..57206245 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -1,346 +1,500 @@ -openapi: 3.0.0 +openapi: 3.0.3 info: title: RustChain Node API - description: Public API documentation for the RustChain Node endpoints. - version: 1.0.0 + description: | + API specification for RustChain blockchain nodes. + + RustChain is a blockchain that supports multiple hardware architectures with + antiquity-weighted mining rewards. This API provides endpoints for querying + chain state, miner information, wallet balances, and submitting signed transfers. + + ## Authentication + Currently, the API does not require authentication for read operations. + Write operations (transfers) require cryptographic signatures. + + ## Base URL + Production: `https://rustchain.org` + + **Note:** The server uses a self-signed TLS certificate. + version: 2.2.1 + contact: + name: RustChain Development Team + url: https://github.com/Scottcjn/Rustchain + license: + name: MIT + url: https://opensource.org/licenses/MIT + servers: - url: https://rustchain.org - description: Public Live Node + description: RustChain Mainnet Node + +tags: + - name: Health + description: Node health and status endpoints + - name: Chain + description: Blockchain statistics and information + - name: Miners + description: Miner registry and attestation data + - name: Wallet + description: Balance queries and transfers + paths: /health: get: + tags: + - Health summary: Node health check - description: Returns health status, uptime, and version. + description: | + Returns the health status of the RustChain node including database + connectivity, chain tip age, uptime, and version information. + operationId: getHealth responses: '200': - description: OK + description: Health status response content: application/json: schema: - type: object - properties: - ok: - type: boolean - uptime_s: - type: integer - version: - type: string - backup_age_hours: - type: number - db_rw: - type: boolean - tip_age_slots: - type: integer + $ref: '#/components/schemas/HealthResponse' example: ok: true - uptime_s: 58480 version: "2.2.1-rip200" - backup_age_hours: 13.65 + uptime_s: 97300 db_rw: true tip_age_slots: 0 + backup_age_hours: 16.589909323586358 - /ready: - get: - summary: Readiness probe - description: Indicates if the node is fully synced and ready to serve traffic. - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - ready: - type: boolean - example: - ready: true - - /epoch: + /api/stats: get: - summary: Current epoch status - description: Returns current epoch, slot, and enrolled miners. + tags: + - Chain + summary: Chain statistics + description: | + Returns comprehensive blockchain statistics including total balance, + miner count, current epoch, supported features, and security settings. + operationId: getStats responses: '200': - description: OK + description: Chain statistics response content: application/json: schema: - type: object - properties: - epoch: - type: integer - slot: - type: integer - enrolled_miners: - type: integer - blocks_per_epoch: - type: integer - epoch_pot: - type: number - total_supply_rtc: - type: number + $ref: '#/components/schemas/StatsResponse' example: - epoch: 91 - slot: 13227 - enrolled_miners: 20 - blocks_per_epoch: 144 - epoch_pot: 1.5 - total_supply_rtc: 8388608 + chain_id: "rustchain-mainnet-v2" + version: "2.2.1-security-hardened" + epoch: 61 + block_time: 600 + total_miners: 11614 + total_balance: 5213.41835243 + pending_withdrawals: 0 + features: + - "RIP-0005" + - "RIP-0008" + - "RIP-0009" + - "RIP-0142" + - "RIP-0143" + - "RIP-0144" + security: + - "no_mock_sigs" + - "mandatory_admin_key" + - "replay_protection" + - "validated_json" /api/miners: get: - summary: Active miners - description: Returns list of active miners with attestation data. + tags: + - Miners + summary: List all miners + description: | + Returns a list of all registered miners with their hardware information, + antiquity multipliers, entropy scores, and last attestation timestamps. + + The `antiquity_multiplier` reflects the age-based reward bonus: + - Vintage hardware (PowerPC, etc.): 2.0-2.5x + - Modern hardware (x86-64, Apple Silicon): 0.8x + operationId: getMiners responses: '200': - description: OK + description: List of miners content: application/json: schema: type: array items: - type: object - properties: - miner_id: - type: string - attested: - type: boolean + $ref: '#/components/schemas/Miner' example: - - miner_id: "m_12345" - attested: true + - miner: "eafc6f14eab6d5c5362fe651e5e6c23581892a37RTC" + hardware_type: "PowerPC G4 (Vintage)" + device_family: "PowerPC" + device_arch: "G4" + antiquity_multiplier: 2.5 + entropy_score: 0.0 + last_attest: 1770062200 + - miner: "modern-sophia-Pow-9862e3be" + hardware_type: "x86-64 (Modern)" + device_family: "x86_64" + device_arch: "modern" + antiquity_multiplier: 0.8 + entropy_score: 0.0 + last_attest: 1770062191 - /api/stats: + /wallet/balance: get: - summary: Network statistics - description: Returns overall network statistics. + tags: + - Wallet + summary: Get wallet balance + description: | + Returns the current balance for a specific miner ID. + Balance is returned in both raw integer format (`amount_i64`) + and decimal RTC format (`amount_rtc`). + operationId: getBalance + parameters: + - name: miner_id + in: query + required: true + description: The unique identifier of the miner + schema: + type: string + example: "modern-sophia-Pow-9862e3be" responses: '200': - description: OK + description: Balance response content: application/json: schema: - type: object - properties: - total_blocks: - type: integer - total_transactions: - type: integer + $ref: '#/components/schemas/BalanceResponse' example: - total_blocks: 150000 - total_transactions: 1205000 - - /api/hall_of_fame: - get: - summary: Hall of Fame leaderboard - description: Leaderboard for 5 categories of miners/participants. - responses: - '200': - description: OK + miner_id: "modern-sophia-Pow-9862e3be" + amount_i64: 42653071 + amount_rtc: 0.42653071 + '400': + description: Missing miner_id parameter content: application/json: schema: - type: object - properties: - top_miners: - type: array - items: - type: object + $ref: '#/components/schemas/ErrorResponse' example: - top_miners: [] + ok: false + error: "miner_id required" - /api/fee_pool: - get: - summary: Fee pool statistics - description: RIP-301 fee pool statistics (fees recycled to mining pool). + /wallet/transfer/signed: + post: + tags: + - Wallet + summary: Submit signed transfer + description: | + Submits a cryptographically signed transfer transaction. + + The transaction must include: + - Source and destination addresses + - Transfer amount (positive value required) + - Ed25519 signature over the transaction data + - Public key for signature verification + - Nonce for replay protection + + ## Signing Process + 1. Construct the transaction payload + 2. Sign with Ed25519 private key + 3. Submit with hex-encoded signature and public key + operationId: submitTransfer + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SignedTransferRequest' + example: + from_address: "eafc6f14eab6d5c5362fe651e5e6c23581892a37RTC" + to_address: "modern-sophia-Pow-9862e3be" + amount_rtc: 1.5 + nonce: 42 + signature: "a1b2c3d4e5f6..." + public_key: "abc123def456..." responses: '200': - description: OK + description: Transfer submitted successfully content: application/json: schema: - type: object - properties: - description: - type: string - destination: - type: string - destination_balance_rtc: - type: number - rip: - type: integer - total_fee_events: - type: integer - total_fees_collected_rtc: - type: number - withdrawal_fee_rtc: - type: number + $ref: '#/components/schemas/TransferResponse' example: - description: "Fee Pool Statistics" - destination: "founder_community" - destination_balance_rtc: 83246.13 - rip: 301 - total_fee_events: 0 - total_fees_collected_rtc: 0 - withdrawal_fee_rtc: 0.01 - - /balance: - get: - summary: Miner balance lookup - description: Returns the RTC balance for a specific miner ID. - parameters: - - name: miner_id - in: query - required: true - schema: - type: string - description: Miner ID or public key - responses: - '200': - description: OK + ok: true + tx_hash: "0xabc123..." + from_address: "eafc6f14eab6d5c5362fe651e5e6c23581892a37RTC" + to_address: "modern-sophia-Pow-9862e3be" + amount_rtc: 1.5 + '400': + description: Invalid request content: application/json: schema: - type: object - properties: - balance: - type: number - example: - balance: 150.5 - - /lottery/eligibility: - get: - summary: Epoch eligibility check - description: Checks if a miner is eligible for the current epoch block lottery. - parameters: - - name: miner_id - in: query - required: true - schema: - type: string - description: Miner ID or public key - responses: - '200': - description: OK + $ref: '#/components/schemas/ErrorResponse' + examples: + missing_fields: + summary: Missing required fields + value: + error: "Missing required fields (from_address, to_address, signature, public_key, nonce)" + invalid_amount: + summary: Invalid amount + value: + error: "Amount must be positive" + invalid_signature: + summary: Invalid signature + value: + error: "Invalid signature" + '500': + description: Internal server error content: application/json: schema: - type: object - properties: - eligible: - type: boolean - reason: - type: string - rotation_size: - type: integer - slot: - type: integer + $ref: '#/components/schemas/ErrorResponse' example: - eligible: false - reason: "not_attested" - rotation_size: 20 - slot: 13227 + error: "Internal server error" - /explorer: - get: - summary: Block explorer page - description: Returns the HTML page for the block explorer. - responses: - '200': - description: HTML page returned - content: - text/html: - schema: - type: string - example: "..." +components: + schemas: + HealthResponse: + type: object + required: + - ok + - version + - uptime_s + - db_rw + - tip_age_slots + properties: + ok: + type: boolean + description: Overall health status + example: true + version: + type: string + description: Node software version + example: "2.2.1-rip200" + uptime_s: + type: integer + format: int64 + description: Node uptime in seconds + example: 97300 + db_rw: + type: boolean + description: Database read/write status + example: true + tip_age_slots: + type: integer + description: Age of chain tip in slots (0 = fully synced) + example: 0 + backup_age_hours: + type: number + format: double + description: Hours since last backup + example: 16.589909323586358 - /attest/submit: - post: - summary: Submit hardware attestation - description: Submit an attestation proof. Requires X-Admin-Key. - security: - - AdminKeyAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - miner_id: - type: string - proof: - type: string - responses: - '200': - description: Attestation accepted + StatsResponse: + type: object + required: + - chain_id + - version + - epoch + - block_time + - total_miners + - total_balance + properties: + chain_id: + type: string + description: Unique chain identifier + example: "rustchain-mainnet-v2" + version: + type: string + description: Chain version with security flags + example: "2.2.1-security-hardened" + epoch: + type: integer + description: Current epoch number + example: 61 + block_time: + type: integer + description: Target block time in seconds + example: 600 + total_miners: + type: integer + description: Total number of registered miners + example: 11614 + total_balance: + type: number + format: double + description: Total RTC balance across all accounts + example: 5213.41835243 + pending_withdrawals: + type: integer + description: Number of pending withdrawal requests + example: 0 + features: + type: array + description: List of enabled RIP (RustChain Improvement Proposal) features + items: + type: string + example: + - "RIP-0005" + - "RIP-0008" + - "RIP-0009" + - "RIP-0142" + - "RIP-0143" + - "RIP-0144" + security: + type: array + description: Active security features + items: + type: string + example: + - "no_mock_sigs" + - "mandatory_admin_key" + - "replay_protection" + - "validated_json" - /wallet/transfer/signed: - post: - summary: Ed25519 signed transfer - description: Submit a signed transfer transaction. Requires X-Admin-Key. - security: - - AdminKeyAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - tx_payload: - type: string - signature: - type: string - responses: - '200': - description: Transfer accepted + Miner: + type: object + required: + - miner + - hardware_type + - device_family + - device_arch + - antiquity_multiplier + - last_attest + properties: + miner: + type: string + description: Unique miner identifier + example: "modern-sophia-Pow-9862e3be" + hardware_type: + type: string + description: Human-readable hardware description + example: "x86-64 (Modern)" + device_family: + type: string + description: Device family category + enum: + - PowerPC + - x86_64 + - x86 + - Apple Silicon + example: "x86_64" + device_arch: + type: string + description: Specific architecture identifier + example: "modern" + antiquity_multiplier: + type: number + format: double + description: | + Reward multiplier based on hardware age. + Vintage hardware (PowerPC) receives higher multipliers (2.0-2.5x) + while modern hardware receives 0.8x. + minimum: 0 + example: 0.8 + entropy_score: + type: number + format: double + description: Hardware entropy contribution score + example: 0.0 + last_attest: + type: integer + format: int64 + description: Unix timestamp of last attestation + example: 1770062191 - /wallet/transfer: - post: - summary: Admin transfer - description: Transfer funds directly as admin. Requires X-Admin-Key. - security: - - AdminKeyAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - to: - type: string - amount: - type: number - responses: - '200': - description: Transfer executed + BalanceResponse: + type: object + required: + - miner_id + - amount_i64 + - amount_rtc + properties: + miner_id: + type: string + description: Miner identifier + example: "modern-sophia-Pow-9862e3be" + amount_i64: + type: integer + format: int64 + description: Balance in smallest unit (1 RTC = 100,000,000 units) + example: 42653071 + amount_rtc: + type: number + format: double + description: Balance in RTC (decimal format) + example: 0.42653071 - /withdraw/request: - post: - summary: Withdrawal request - description: Request a withdrawal. Requires X-Admin-Key. - security: - - AdminKeyAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - miner_id: - type: string - amount: - type: number - responses: - '200': - description: Withdrawal requested + SignedTransferRequest: + type: object + required: + - from_address + - to_address + - signature + - public_key + - nonce + properties: + from_address: + type: string + description: Source miner/wallet address + example: "eafc6f14eab6d5c5362fe651e5e6c23581892a37RTC" + to_address: + type: string + description: Destination miner/wallet address + example: "modern-sophia-Pow-9862e3be" + amount_rtc: + type: number + format: double + description: Amount to transfer in RTC (must be positive) + minimum: 0 + exclusiveMinimum: true + example: 1.5 + nonce: + type: integer + format: int64 + description: Transaction nonce for replay protection + example: 42 + signature: + type: string + description: Hex-encoded Ed25519 signature + example: "a1b2c3d4e5f6..." + public_key: + type: string + description: Hex-encoded Ed25519 public key + example: "abc123def456..." -components: - securitySchemes: - AdminKeyAuth: - type: apiKey - in: header - name: X-Admin-Key + TransferResponse: + type: object + properties: + ok: + type: boolean + description: Success status + example: true + tx_hash: + type: string + description: Transaction hash + example: "0xabc123..." + from_address: + type: string + description: Source address + example: "eafc6f14eab6d5c5362fe651e5e6c23581892a37RTC" + to_address: + type: string + description: Destination address + example: "modern-sophia-Pow-9862e3be" + amount_rtc: + type: number + format: double + description: Transferred amount + example: 1.5 + + ErrorResponse: + type: object + properties: + ok: + type: boolean + description: Always false for errors + example: false + error: + type: string + description: Error message + example: "miner_id required" diff --git a/docs/api/swagger.html b/docs/api/swagger.html index d3dac72f..8369ba29 100644 --- a/docs/api/swagger.html +++ b/docs/api/swagger.html @@ -1,25 +1,76 @@ - - - + + RustChain Node API - Swagger UI - + + -
- - +
+ + + + - \ No newline at end of file + diff --git a/docs/attestation_fuzzing.md b/docs/attestation_fuzzing.md deleted file mode 100644 index c4904470..00000000 --- a/docs/attestation_fuzzing.md +++ /dev/null @@ -1,47 +0,0 @@ -# Attestation Malformed-Input Regression Harness - -This repository includes a deterministic malformed-input regression gate for `POST /attest/submit` plus a replayable regression corpus under `tests/attestation_corpus/`. - -## Corpus Classes - -Current explicit corpus entries cover these malformed input classes: - -1. Invalid JSON root: `null` -2. Invalid JSON root: array -3. Miner identifier shape mismatch -4. Device payload scalar/object mismatch -5. Signals payload scalar/object mismatch -6. Signals MAC list shape mismatch -7. Fingerprint checks array/object mismatch -8. Report payload scalar/object mismatch - -## Replay One Corpus Entry - -```bash -python tests/replay_attestation_corpus.py tests/attestation_corpus/malformed_report_scalar.json -``` - -The script prints the HTTP status code and parsed JSON response, and exits non-zero if replay causes a server-side `5xx`. - -## Quick Regression Gate - -```bash -python -m pytest tests/test_attestation_fuzz.py -v -``` - -## 10,000-Case Mutation Run - -PowerShell: - -```powershell -$env:ATTEST_FUZZ_CASES = "10000" -python -m pytest tests/test_attestation_fuzz.py -k mutation_regression_no_unhandled_exceptions -v -``` - -Bash: - -```bash -ATTEST_FUZZ_CASES=10000 python -m pytest tests/test_attestation_fuzz.py -k mutation_regression_no_unhandled_exceptions -v -``` - -This is the CI-mode gate for "no unhandled exceptions" in the attestation parsing path. Set `ATTEST_FUZZ_SEED` only when you need to reproduce a specific random sequence locally. diff --git a/docs/hardware.html b/docs/hardware.html index efb2aafc..1b85dcc9 100644 --- a/docs/hardware.html +++ b/docs/hardware.html @@ -464,7 +464,7 @@

Operating System Support

Hardware Attestation Requirements

-

RustChain's Proof-of-Antiquity system requires hardware with specific characteristics:

+

RustChain's Proof-of-Attestation system requires hardware with specific characteristics:

CPU Timer Access The system must provide access to high-resolution timers for clock-skew analysis. Most modern CPUs support this, but some very old systems may have limitations.

diff --git a/docs/index.html b/docs/index.html index b5cd88ba..d16ff5a4 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3,9 +3,9 @@ - RustChain | Proof-of-Antiquity Blockchain - Vintage Hardware Mining - - + RustChain | Proof-of-Attestation Blockchain - Vintage Hardware Mining + + @@ -16,7 +16,7 @@ - + @@ -24,7 +24,7 @@ - + - - -
-
-
-
RustChain Miner Setup Wizard
-
Single-file static wizard · no backend · GitHub Pages friendly
-
-
Progress: 0/7
-
- -
- - -
-
-
- - - - diff --git a/docs/mining.html b/docs/mining.html index 0751f33d..437bc66b 100644 --- a/docs/mining.html +++ b/docs/mining.html @@ -4,8 +4,8 @@ Mining Guide | RustChain Vintage Hardware Mining - - + + @@ -17,7 +17,7 @@ - + @@ -25,7 +25,7 @@ - + @@ -34,7 +34,7 @@ "@context": "https://schema.org", "@type": "Article", "headline": "Mining Guide | RustChain Vintage Hardware Mining", - "description": "Complete guide to mining RustChain with vintage hardware. Learn about Proof-of-Antiquity, hardware requirements, and maximizing antiquity multipliers.", + "description": "Complete guide to mining RustChain with vintage hardware. Learn about Proof-of-Attestation, hardware requirements, and maximizing antiquity multipliers.", "url": "https://rustchain.org/docs/mining.html", "datePublished": "2026-02-18", "author": { @@ -240,7 +240,7 @@

Post-Installation Setup

Understanding Hardware Attestation

-

RustChain's Proof-of-Antiquity consensus relies on sophisticated hardware fingerprinting to ensure network integrity. This process, known as silicon stratigraphy, reads the unique characteristics embedded in your hardware during manufacturing.

+

RustChain's Proof-of-Attestation consensus relies on sophisticated hardware fingerprinting to ensure network integrity. This process, known as silicon stratigraphy, reads the unique characteristics embedded in your hardware during manufacturing.

The Seven-Layer Verification

When you first start mining, RustChain runs seven distinct verification checks:

diff --git a/docs/network-status.html b/docs/network-status.html index fb6ccfca..41263803 100644 --- a/docs/network-status.html +++ b/docs/network-status.html @@ -1,322 +1,156 @@ - - - - RustChain Public Network Status - - - -
-
-
-

RustChain Network Status

-

Static single-page status dashboard · GitHub Pages compatible · auto-refresh 60s

-
-
Last refresh: -
-
- -
-
-
Current Epoch
-
-
-
-
-
Next Settlement In
-
-
-
-
-
Active Miners
-
-
-
-
- -
-

Attestation Nodes (3)

-
-

Node status is fetched from each /health endpoint.

-
- -
-

Miner Architecture Distribution

-
-
- -
-

90-Day Uptime History (client-side)

-

History is recorded in browser localStorage and retained for up to 90 days.

-
-
- -
-

Incident Log

-
-
- -
-

Outage Feeds (RSS / Atom)

- -

Feed is generated from incident history directly in the browser (no backend).

-
- -
-

README Badge / Shield

- RustChain network status badge - - -

Uses a data URI SVG so it works without a badge backend.

-
-
- - - - \ No newline at end of file + tick() + setInterval(tick, pollMs) + + + diff --git a/docs/rip201_bucket_spoof.md b/docs/rip201_bucket_spoof.md deleted file mode 100644 index c8d5911f..00000000 --- a/docs/rip201_bucket_spoof.md +++ /dev/null @@ -1,115 +0,0 @@ -# RIP-201 Bucket Normalization Gaming - -## Summary - -This PoC demonstrates that a modern x86 host can be accepted by the server as a `G4` / `PowerPC` miner and routed into the `vintage_powerpc` reward bucket. - -The core weakness is that the attestation path trusts `device_family` and `device_arch` enough to: - -1. mark the attestation as valid, -2. enroll the miner with `G4` weight (`2.5`), and -3. let RIP-201 classify the miner into the `vintage_powerpc` bucket. - -## Attack Path - -### 1. Spoof the claimed hardware class - -Submit: - -- `device_family = "PowerPC"` -- `device_arch = "G4"` -- `cpu = "Intel Xeon Platinum"` - -The `cpu` string is inconsistent with the claimed architecture, but the attestation flow does not reject it. - -### 2. Provide only minimum fingerprint evidence - -For vintage claims, `validate_fingerprint_data()` relaxes the required checks down to `anti_emulation` only. It does not require: - -- PowerPC SIMD evidence -- cache timing profile -- thermal profile -- cross-check that the CPU claim is actually PowerPC-compatible - -As a result, a sparse fingerprint with only `anti_emulation` passes. - -### 3. Collect vintage bucket rewards - -Once accepted: - -- `miner_attest_recent.device_arch = G4` -- `epoch_enroll.weight = 2.5` -- `classify_miner_bucket("g4") = vintage_powerpc` - -That is enough for RIP-201 equal-split rewards to treat the miner as a scarce vintage bucket participant. - -## Reproduction - -Run: - -```bash -python -m pytest tests/test_rip201_bucket_spoof.py -v -python tools/rip201_bucket_spoof_poc.py -``` - -## Current Local Result - -The PoC shows: - -- the spoofed `Intel Xeon` / claimed `G4` attestation is accepted, -- the spoofed miner is enrolled with weight `2.5`, -- the spoofed miner lands in `vintage_powerpc`, -- in a 2-bucket epoch with 10 honest modern miners, the spoofed miner receives `550000 uRTC` while each honest modern miner receives `55000 uRTC`. - -That is a **10x** per-miner reward advantage from bucket spoofing alone. - -## Live Black-Box Validation - -The same technique was also validated against the live node at `https://50.28.86.131`. - -### Request sent - -`POST /attest/submit` with: - -- `device_family = "PowerPC"` -- `device_arch = "G4"` -- `cpu = "Intel Xeon Platinum"` -- fingerprint containing only the minimal `anti_emulation` check - -### Observed live response - -The server returned `200 OK` and accepted the contradictory claim: - -```json -{ - "device": { - "arch": "G4", - "cpu": "Intel Xeon Platinum", - "device_arch": "G4", - "device_family": "PowerPC" - }, - "fingerprint_passed": true, - "ok": true, - "status": "accepted" -} -``` - -### Public follow-up evidence - -After the attestation, public endpoints reflected the spoofed vintage classification: - -- `GET /api/badge/bucket-spoof-live-492a` returned `Active (2.5x)` -- `GET /api/miners` listed `bucket-spoof-live-492a` as: - - `device_family = "PowerPC"` - - `device_arch = "G4"` - - `hardware_type = "PowerPC G4 (Vintage)"` - - `antiquity_multiplier = 2.5` - -That is black-box evidence that the deployed server accepts the false hardware class and exposes the spoofed vintage multiplier through public API surfaces. - -## Recommended Fixes - -1. Treat claimed legacy architectures as untrusted until the fingerprint proves architecture-specific traits. -2. Require `simd_identity` or equivalent PowerPC evidence for `g3/g4/g5` claims. -3. Reject obvious `cpu` / `device_arch` contradictions such as `Intel Xeon` + `G4`. -4. Classify miners into reward buckets from verified server-side features, not raw client-reported architecture strings. diff --git a/docs/rip201_fleet_detection_bypass.md b/docs/rip201_fleet_detection_bypass.md deleted file mode 100644 index f89f3774..00000000 --- a/docs/rip201_fleet_detection_bypass.md +++ /dev/null @@ -1,70 +0,0 @@ -# RIP-201 Fleet Detection Bypass - -## Summary - -This report demonstrates a black-box bypass of the deployed RIP-201 fleet immune system: - -1. The server trusts client-supplied `X-Forwarded-For` as the miner source IP. -2. The fleet scorer treats missing optional fingerprint dimensions as "no evidence" instead of suspicious absence. -3. Timing correlation can be avoided by spacing attestations outside the 30-second window. - -With those three behaviors combined, a coordinated 5-miner fleet on shared infrastructure can remain at `fleet_score = 0.0` for consecutive epochs while keeping full reward weight. - -## Technique - -### 1. Spoof IP clustering - -`client_ip_from_request()` prefers the left-most `X-Forwarded-For` value over `REMOTE_ADDR` without validating that the request actually came from a trusted reverse proxy. A client can therefore choose the IP written into: - -- `miner_attest_recent.source_ip` -- `ip_rate_limit.client_ip` -- RIP-201 `fleet_signals.subnet_hash` - -This lets one host appear to come from many different /24 subnets. - -### 2. Keep fingerprint checks valid but sparse - -`validate_fingerprint_data()` requires `anti_emulation` and `clock_drift` for modern hardware, but `record_fleet_signals_from_request()` only records four similarity dimensions: - -- `clock_drift_cv` -- `cache_latency_hash` -- `thermal_signature` -- `simd_bias_hash` - -The similarity engine only flags a pair when there are at least two comparable dimensions and at least two matches. Submitting only the minimum valid checks leaves just one comparable dimension (`clock_drift_cv`), so fingerprint similarity never fires. - -### 3. Avoid timing correlation - -Spacing attestations by more than 30 seconds keeps the timing ratio below the correlation threshold. - -## Reproduction - -Run: - -```bash -python tools/rip201_fleet_detection_bypass_poc.py -``` - -The PoC prints: - -- a baseline scenario where a same-subnet shared-fingerprint fleet is flagged -- a bypass scenario where five miners remain clean for three consecutive epochs - -Run the tests: - -```bash -python -m pytest tests/test_rip201_fleet_bypass.py -v -``` - -## Impact - -- A single operator can present a coordinated fleet as five independent miners. -- The fleet can stay under the `0.3` clean threshold. -- Because the PoC keeps `fleet_score = 0.0`, the effective multiplier remains unchanged. - -## Recommended Fixes - -1. Only trust `X-Forwarded-For` when `REMOTE_ADDR` belongs to an allowlisted reverse proxy. -2. Record the actual peer IP separately from forwarded headers and use the trusted peer IP for fleet detection. -3. Treat missing fingerprint dimensions as suspicious for modern miners instead of neutral. -4. Require a minimum fingerprint feature set for fleet scoring, not just attestation acceptance. diff --git a/docs/tokenomics.html b/docs/tokenomics.html index b490e683..85e189eb 100644 --- a/docs/tokenomics.html +++ b/docs/tokenomics.html @@ -5,7 +5,7 @@ Tokenomics | RustChain (RTC) Economic Model - + diff --git a/docs/whitepaper/abstract-intro.md b/docs/whitepaper/abstract-intro.md index bff1c9ae..537a1cff 100644 --- a/docs/whitepaper/abstract-intro.md +++ b/docs/whitepaper/abstract-intro.md @@ -2,7 +2,7 @@ ## Abstract -RustChain is a Proof-of-Antiquity blockchain that rewards **real physical hardware**, with explicit emphasis on preserving and operating vintage architectures (PowerPC G4/G5, SPARC, 68K, and other historically significant machines). Instead of allocating influence by raw hashpower or capital stake, RustChain uses a Proof-of-Antiquity approach (RIP-200) in which miners periodically submit attestations backed by a multi-check hardware fingerprint system. The result is an incentive structure that makes “cheap scale” strategies such as virtual machine farms economically ineffective, while making authentic, scarce, and harder-to-operate vintage machines competitive. +RustChain is a Proof-of-Antiquity blockchain that rewards **real physical hardware**, with explicit emphasis on preserving and operating vintage architectures (PowerPC G4/G5, SPARC, 68K, and other historically significant machines). Instead of allocating influence by raw hashpower or capital stake, RustChain uses a Proof-of-Attestation approach (RIP-200) in which miners periodically submit attestations backed by a multi-check hardware fingerprint system. The result is an incentive structure that makes “cheap scale” strategies such as virtual machine farms economically ineffective, while making authentic, scarce, and harder-to-operate vintage machines competitive. At a protocol level, RustChain batches accounting into epochs, validates miner attestations with server-side evidence requirements, applies antiquity multipliers to eligible miners, and settles rewards in an auditable ledger. The design is pragmatic: it does not claim perfect remote attestation, but it does claim that stacking multiple independent checks and binding rules raises the cost of spoofing enough to keep the network aligned with its preservation goal. diff --git a/docs/whitepaper/protocol-design.md b/docs/whitepaper/protocol-design.md index da8b2884..f32c433d 100644 --- a/docs/whitepaper/protocol-design.md +++ b/docs/whitepaper/protocol-design.md @@ -1,4 +1,4 @@ -# Protocol Design (RIP-200 Proof-of-Antiquity) +# Protocol Design (RIP-200 Proof-of-Attestation) ## Overview diff --git a/pyproject.toml b/pyproject.toml index a6fc85df..a52a0515 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,10 +4,11 @@ pythonpath = ["node", "."] [tool.ruff] line-length = 120 +select = ["E", "F", "W", "B", "I"] +ignore = [] exclude = ["deprecated", "node_backups"] [tool.ruff.lint] -select = ["E", "F", "W", "B", "I"] ignore = ["E501"] # Ignore long lines for legacy code [tool.mypy] diff --git a/requirements.txt b/requirements.txt index 78b82fed..0aff43d9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,9 +3,5 @@ flask>=2.0.0 # For miner and SDK: requests>=2.25.0 -# For wallet CLI (Ed25519 + AES-GCM): -cryptography>=41.0 -# For wallet CLI (BIP39 seed phrases): -mnemonic>=0.20 # For running tests: -pytest>=7.0.0 +pytest>=7.0.0 \ No newline at end of file diff --git a/sdk/README.md b/sdk/README.md index 987a1eb2..d6f5bc15 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -1,16 +1,6 @@ # RustChain Python SDK -A comprehensive Python client library for interacting with the RustChain blockchain and Agent Economy. - -**Version:** 1.0.0 - -**Features:** -- Core blockchain client for node interactions -- **RIP-302 Agent Economy SDK** for AI agent participation -- x402 payment protocol for machine-to-machine payments -- Beacon Atlas reputation system integration -- BoTTube video platform analytics -- Automated bounty system +A Python client library for interacting with the RustChain blockchain. ## Installation @@ -18,63 +8,33 @@ A comprehensive Python client library for interacting with the RustChain blockch pip install rustchain-sdk ``` -Or from source: - -```bash -cd sdk/ -pip install -e . -``` - ## Quick Start -### Core Blockchain Client - ```python from rustchain import RustChainClient # Initialize client -client = RustChainClient("https://rustchain.org") +client = RustChainClient("https://rustchain.org", verify_ssl=False) # Get node health health = client.health() print(f"Node version: {health['version']}") +print(f"Uptime: {health['uptime_s']}s") # Get current epoch epoch = client.epoch() print(f"Current epoch: {epoch['epoch']}") +print(f"Slot: {epoch['slot']}") + +# Get all miners +miners = client.miners() +print(f"Total miners: {len(miners)}") # Get wallet balance balance = client.balance("wallet_address") print(f"Balance: {balance['balance']} RTC") -client.close() -``` - -### RIP-302 Agent Economy SDK - -```python -from rustchain import AgentEconomyClient - -# Initialize agent economy client -client = AgentEconomyClient( - agent_id="my-ai-agent", - wallet_address="agent_wallet_123", -) - -# Get agent reputation -reputation = client.reputation.get_score() -print(f"Reputation: {reputation.score}/100 ({reputation.tier.value})") - -# Send x402 payment -payment = client.payments.send( - to="content-creator", - amount=0.5, - memo="Great content!", -) - -# Find bounties -bounties = client.bounties.list(status="open", limit=10) - +# Close client client.close() ``` @@ -324,94 +284,6 @@ black rustchain/ - Python 3.8+ - requests >= 2.28.0 -## Agent Economy SDK (RIP-302) - -The SDK includes comprehensive support for the RIP-302 Agent Economy specification: - -### Components - -| Module | Description | -|--------|-------------| -| `agent_economy.client` | Main `AgentEconomyClient` for unified access | -| `agent_economy.agents` | Agent wallet and profile management | -| `agent_economy.payments` | x402 payment protocol implementation | -| `agent_economy.reputation` | Beacon Atlas reputation system | -| `agent_economy.analytics` | Agent analytics and metrics | -| `agent_economy.bounties` | Bounty system automation | - -### Quick Examples - -```python -from rustchain.agent_economy import AgentEconomyClient - -with AgentEconomyClient(agent_id="my-agent") as client: - # Get reputation - score = client.reputation.get_score() - - # Send payment - payment = client.payments.send(to="creator", amount=0.5) - - # Find bounties - bounties = client.bounties.list(status="open") - - # Get analytics - earnings = client.analytics.get_earnings() -``` - -### Documentation - -See [docs/AGENT_ECONOMY_SDK.md](docs/AGENT_ECONOMY_SDK.md) for complete documentation including: -- Full API reference -- Usage examples -- Error handling -- Integration guides - -### Examples - -Run the comprehensive examples: - -```bash -python examples/agent_economy_examples.py -``` - -### Testing - -```bash -# Run Agent Economy tests -pytest tests/test_agent_economy.py -v - -# With coverage -pytest tests/test_agent_economy.py --cov=rustchain.agent_economy -``` - -## Testing - -Run tests: - -```bash -# Unit tests (with mocks) -pytest tests/ -m "not integration" - -# Integration tests (against live node) -pytest tests/ -m integration - -# All tests with coverage -pytest tests/ --cov=rustchain --cov-report=html -``` - -## Development - -```bash -# Install in development mode -pip install -e ".[dev]" - -# Run type checking -mypy rustchain/ - -# Format code -black rustchain/ -``` - ## License MIT License @@ -421,4 +293,3 @@ MIT License - [RustChain GitHub](https://github.com/Scottcjn/Rustchain) - [RustChain Explorer](https://rustchain.org/explorer) - [RustChain Whitepaper](https://github.com/Scottcjn/Rustchain/blob/main/docs/RustChain_Whitepaper_Flameholder_v0.97-1.pdf) -- [Agent Economy SDK Docs](docs/AGENT_ECONOMY_SDK.md) diff --git a/sdk/setup.py b/sdk/setup.py index 8efe670e..51f75efa 100644 --- a/sdk/setup.py +++ b/sdk/setup.py @@ -1,10 +1,8 @@ """ Setup configuration for RustChain SDK - -Includes core blockchain client and RIP-302 Agent Economy SDK. """ -from setuptools import setup, find_packages +from setuptools import setup # Read README for long description with open("README.md", "r", encoding="utf-8") as fh: @@ -12,22 +10,21 @@ setup( name="rustchain-sdk", - version="1.0.0", + version="0.1.0", author="RustChain Community", author_email="dev@rustchain.org", - description="Python SDK for RustChain blockchain and Agent Economy (RIP-302)", + description="Python SDK for RustChain blockchain", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/Scottcjn/Rustchain", project_urls={ "Bug Tracker": "https://github.com/Scottcjn/Rustchain/issues", - "Documentation": "https://github.com/Scottcjn/Rustchain/tree/main/sdk", + "Documentation": "https://github.com/Scottcjn/Rustchain#readme", "Source Code": "https://github.com/Scottcjn/Rustchain", - "Agent Economy": "https://github.com/Scottcjn/Rustchain/tree/main/sdk/docs/AGENT_ECONOMY_SDK.md", }, - packages=find_packages(exclude=["tests", "examples"]), + packages=["rustchain"], classifiers=[ - "Development Status :: 4 - Beta", + "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries :: Python Modules", "License :: OSI Approved :: MIT License", @@ -38,10 +35,6 @@ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3 :: Only", - "Topic :: Blockchain", - "Topic :: Internet :: WWW/HTTP :: HTTP Servers", - "Environment :: Console", - "Operating System :: OS Independent", ], python_requires=">=3.8", install_requires=[ @@ -51,19 +44,10 @@ "dev": [ "pytest>=7.0", "pytest-mock>=3.10", - "pytest-cov>=4.0", "black>=23.0", "mypy>=1.0", - "ruff>=0.1.0", - ], - "examples": [ - "asyncio", ], }, - keywords="rustchain blockchain crypto proof-of-antiquity agent-economy x402 payments reputation bounties", + keywords="rustchain blockchain crypto proof-of-antiquity", license="MIT", - include_package_data=True, - package_data={ - "rustchain": ["py.typed"], - }, ) diff --git a/tests/attestation_corpus/malformed_miner_array.json b/tests/attestation_corpus/malformed_miner_array.json deleted file mode 100644 index e6f82bfe..00000000 --- a/tests/attestation_corpus/malformed_miner_array.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "miner": [ - "not", - "a", - "string" - ], - "device": { - "device_family": "PowerPC", - "device_arch": "power8", - "cores": 4 - }, - "signals": { - "hostname": "miner-array-host", - "macs": [ - "AA:BB:CC:DD:EE:33" - ] - }, - "report": { - "commitment": "miner-array-commitment" - } -} diff --git a/tests/attestation_corpus/malformed_report_scalar.json b/tests/attestation_corpus/malformed_report_scalar.json deleted file mode 100644 index 1cc04dc3..00000000 --- a/tests/attestation_corpus/malformed_report_scalar.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "miner": "report-scalar-miner", - "device": { - "device_family": "PowerPC", - "device_arch": "power8", - "cores": 8 - }, - "signals": { - "hostname": "report-scalar-host", - "macs": [ - "AA:BB:CC:DD:EE:44" - ] - }, - "report": "not-a-report-object" -} diff --git a/tests/replay_attestation_corpus.py b/tests/replay_attestation_corpus.py deleted file mode 100644 index e029ed10..00000000 --- a/tests/replay_attestation_corpus.py +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env python3 -""" -Replay a saved attestation corpus entry against the Flask test client. -""" - -from __future__ import annotations - -import argparse -import importlib.util -import json -import os -import sqlite3 -import sys -import uuid -from pathlib import Path - -PROJECT_ROOT = Path(__file__).resolve().parents[1] -NODE_PATH = PROJECT_ROOT / "node" / "rustchain_v2_integrated_v2.2.1_rip200.py" -TMP_ROOT = PROJECT_ROOT / "tests" / ".tmp_attestation" - -sys.path.insert(0, str(PROJECT_ROOT)) -sys.path.insert(0, str(PROJECT_ROOT / "node")) - -os.environ.setdefault("RC_ADMIN_KEY", "0" * 32) -os.environ.setdefault("DB_PATH", ":memory:") - -from tests import mock_crypto - -sys.modules["rustchain_crypto"] = mock_crypto - - -def _load_integrated_node(): - if "integrated_node" in sys.modules: - return sys.modules["integrated_node"] - - spec = importlib.util.spec_from_file_location("integrated_node", NODE_PATH) - module = importlib.util.module_from_spec(spec) - sys.modules["integrated_node"] = module - spec.loader.exec_module(module) - return module - - -def _init_attestation_db(db_path: Path) -> None: - conn = sqlite3.connect(db_path) - conn.executescript( - """ - CREATE TABLE blocked_wallets ( - wallet TEXT PRIMARY KEY, - reason TEXT - ); - CREATE TABLE balances ( - miner_pk TEXT PRIMARY KEY, - balance_rtc REAL DEFAULT 0 - ); - CREATE TABLE epoch_enroll ( - epoch INTEGER NOT NULL, - miner_pk TEXT NOT NULL, - weight REAL NOT NULL, - PRIMARY KEY (epoch, miner_pk) - ); - CREATE TABLE miner_header_keys ( - miner_id TEXT PRIMARY KEY, - pubkey_hex TEXT - ); - CREATE TABLE tickets ( - ticket_id TEXT PRIMARY KEY, - expires_at INTEGER NOT NULL, - commitment TEXT - ); - CREATE TABLE oui_deny ( - oui TEXT PRIMARY KEY, - vendor TEXT, - enforce INTEGER DEFAULT 0 - ); - """ - ) - conn.commit() - conn.close() - - -def _apply_test_overrides(module, db_path: Path): - original = { - "DB_PATH": getattr(module, "DB_PATH", None), - "HW_BINDING_V2": getattr(module, "HW_BINDING_V2", None), - "HW_PROOF_AVAILABLE": getattr(module, "HW_PROOF_AVAILABLE", None), - "check_ip_rate_limit": module.check_ip_rate_limit, - "_check_hardware_binding": module._check_hardware_binding, - "record_attestation_success": module.record_attestation_success, - "record_macs": module.record_macs, - "current_slot": module.current_slot, - "slot_to_epoch": module.slot_to_epoch, - } - - module.DB_PATH = str(db_path) - module.HW_BINDING_V2 = False - module.HW_PROOF_AVAILABLE = False - module.check_ip_rate_limit = lambda client_ip, miner_id: (True, "ok") - module._check_hardware_binding = lambda *args, **kwargs: (True, "ok", "") - module.record_attestation_success = lambda *args, **kwargs: None - module.record_macs = lambda *args, **kwargs: None - module.current_slot = lambda: 12345 - module.slot_to_epoch = lambda slot: 85 - module.app.config["TESTING"] = True - return original - - -def _restore_test_overrides(module, original): - for name, value in original.items(): - setattr(module, name, value) - - -def parse_args(): - parser = argparse.ArgumentParser(description="Replay a saved attestation corpus JSON file") - parser.add_argument("corpus_file", type=Path, help="Path to a JSON corpus entry") - return parser.parse_args() - - -def main() -> int: - args = parse_args() - payload_path = args.corpus_file - if not payload_path.exists(): - raise SystemExit(f"Corpus file not found: {payload_path}") - - raw_json = payload_path.read_text(encoding="utf-8") - module = _load_integrated_node() - - TMP_ROOT.mkdir(exist_ok=True) - db_path = TMP_ROOT / f"replay_{uuid.uuid4().hex}.sqlite3" - _init_attestation_db(db_path) - original = _apply_test_overrides(module, db_path) - try: - with module.app.test_client() as client: - response = client.post("/attest/submit", data=raw_json, content_type="application/json") - print( - json.dumps( - { - "corpus_file": str(payload_path), - "status_code": response.status_code, - "response_json": response.get_json(silent=True), - }, - indent=2, - sort_keys=True, - ) - ) - return 0 if response.status_code < 500 else 1 - finally: - _restore_test_overrides(module, original) - if db_path.exists(): - try: - db_path.unlink() - except PermissionError: - pass - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/tests/requirements.txt b/tests/requirements.txt index 9af3c059..d20d5222 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,4 +1,2 @@ # Test dependencies for RustChain pytest>=7.0.0 -# Needed by test_wallet_cli_39 (imports from tools/rustchain_wallet_cli.py) -cryptography>=41.0 diff --git a/tests/test_attestation_fuzz.py b/tests/test_attestation_fuzz.py index 9a4f247d..d90fb1ab 100644 --- a/tests/test_attestation_fuzz.py +++ b/tests/test_attestation_fuzz.py @@ -45,14 +45,6 @@ def _init_attestation_db(db_path: Path) -> None: vendor TEXT, enforce INTEGER DEFAULT 0 ); - CREATE TABLE hardware_bindings ( - hardware_id TEXT PRIMARY KEY, - bound_miner TEXT NOT NULL, - device_arch TEXT, - device_model TEXT, - bound_at INTEGER, - attestation_count INTEGER DEFAULT 0 - ); """ ) conn.commit() @@ -92,22 +84,22 @@ def _base_payload() -> dict: } -def _client_fixture(monkeypatch, *, strict_security_path=False): +@pytest.fixture +def client(monkeypatch): local_tmp_dir = Path(__file__).parent / ".tmp_attestation" local_tmp_dir.mkdir(exist_ok=True) db_path = local_tmp_dir / f"{uuid.uuid4().hex}.sqlite3" _init_attestation_db(db_path) monkeypatch.setattr(integrated_node, "DB_PATH", str(db_path)) + monkeypatch.setattr(integrated_node, "HW_BINDING_V2", False, raising=False) + monkeypatch.setattr(integrated_node, "HW_PROOF_AVAILABLE", False, raising=False) monkeypatch.setattr(integrated_node, "check_ip_rate_limit", lambda client_ip, miner_id: (True, "ok")) + monkeypatch.setattr(integrated_node, "_check_hardware_binding", lambda *args, **kwargs: (True, "ok", "")) monkeypatch.setattr(integrated_node, "record_attestation_success", lambda *args, **kwargs: None) monkeypatch.setattr(integrated_node, "record_macs", lambda *args, **kwargs: None) monkeypatch.setattr(integrated_node, "current_slot", lambda: 12345) monkeypatch.setattr(integrated_node, "slot_to_epoch", lambda slot: 85) - monkeypatch.setattr(integrated_node, "HW_BINDING_V2", False, raising=False) - monkeypatch.setattr(integrated_node, "HW_PROOF_AVAILABLE", False, raising=False) - if not strict_security_path: - monkeypatch.setattr(integrated_node, "_check_hardware_binding", lambda *args, **kwargs: (True, "ok", "")) integrated_node.app.config["TESTING"] = True with integrated_node.app.test_client() as test_client: @@ -120,16 +112,6 @@ def _client_fixture(monkeypatch, *, strict_security_path=False): pass -@pytest.fixture -def client(monkeypatch): - yield from _client_fixture(monkeypatch, strict_security_path=False) - - -@pytest.fixture -def strict_client(monkeypatch): - yield from _client_fixture(monkeypatch, strict_security_path=True) - - def _post_raw_json(client, raw_json: str): return client.post("/attest/submit", data=raw_json, content_type="application/json") @@ -150,99 +132,31 @@ def test_attest_submit_rejects_non_object_json(client, file_name, expected_statu @pytest.mark.parametrize( - ("file_name", "expected_code"), + "file_name", [ - ("malformed_device_scalar.json", "INVALID_DEVICE"), - ("malformed_miner_array.json", "INVALID_MINER"), - ("malformed_signals_scalar.json", "INVALID_SIGNALS"), - ("malformed_signals_macs_object.json", "INVALID_SIGNALS_MACS"), - ("malformed_fingerprint_checks_array.json", "INVALID_FINGERPRINT_CHECKS"), - ("malformed_report_scalar.json", "INVALID_REPORT"), + "malformed_device_scalar.json", + "malformed_signals_scalar.json", + "malformed_signals_macs_object.json", + "malformed_fingerprint_checks_array.json", ], ) -def test_attest_submit_rejects_malformed_payload_shapes(client, file_name, expected_code): +def test_attest_submit_corpus_cases_do_not_raise_server_errors(client, file_name): response = _post_raw_json(client, (CORPUS_DIR / file_name).read_text(encoding="utf-8")) - assert response.status_code in (400, 422) - assert response.get_json()["ok"] is False - assert response.get_json()["code"] == expected_code - - -@pytest.mark.parametrize( - ("payload", "expected_code"), - [ - ({"miner": "", "device": {"cores": 8}, "signals": {"macs": ["AA:BB:CC:DD:EE:10"]}, "report": {}}, "MISSING_MINER"), - ({"miner": " ", "device": {"cores": 8}, "signals": {"macs": ["AA:BB:CC:DD:EE:10"]}, "report": {}}, "MISSING_MINER"), - ({"miner": "fuzz\u200bminer", "device": {"cores": 8}, "signals": {"macs": ["AA:BB:CC:DD:EE:10"]}, "report": {}}, "INVALID_MINER"), - ({"miner": "'; DROP TABLE balances; --", "device": {"cores": 8}, "signals": {"macs": ["AA:BB:CC:DD:EE:10"]}, "report": {}}, "INVALID_MINER"), - ({"miner": "f" * 129, "device": {"cores": 8}, "signals": {"macs": ["AA:BB:CC:DD:EE:10"]}, "report": {}}, "INVALID_MINER"), - ({"miner": "fuzz-miner", "device": {"cores": "999999999999999999999999"}, "signals": {"macs": ["AA:BB:CC:DD:EE:10"]}, "report": {}}, "INVALID_DEVICE_CORES"), - ({"miner": "fuzz-miner", "device": {"cores": []}, "signals": {"macs": ["AA:BB:CC:DD:EE:10"]}, "report": {}}, "INVALID_DEVICE_CORES"), - ({"miner": "fuzz-miner", "device": {"cores": 8}, "signals": {"macs": ["AA:BB:CC:DD:EE:10", None]}, "report": {}}, "INVALID_SIGNALS_MACS"), - ({"miner": "fuzz-miner", "device": {"cores": 8, "cpu": ["nested"]}, "signals": {"macs": ["AA:BB:CC:DD:EE:10"]}, "report": {}}, "INVALID_DEVICE"), - ({"miner": "fuzz-miner", "device": {"cores": 8}, "signals": {"hostname": ["nested"], "macs": ["AA:BB:CC:DD:EE:10"]}, "report": {}}, "INVALID_SIGNALS"), - ({"miner": "fuzz-miner", "device": {"cores": 8}, "signals": {"macs": ["AA:BB:CC:DD:EE:10"]}, "report": {"nonce": {"nested": "bad"}}}, "INVALID_REPORT"), - ], -) -def test_attest_submit_rejects_attack_vector_shapes(client, payload, expected_code): - response = client.post("/attest/submit", json=payload) - - assert response.status_code in (400, 422) - assert response.get_json()["ok"] is False - assert response.get_json()["code"] == expected_code - - -def test_attest_submit_sql_like_miner_does_not_mutate_schema(client): - payload = _base_payload() - payload["miner"] = "'; DROP TABLE balances; --" - - response = client.post("/attest/submit", json=payload) - - assert response.status_code == 400 - assert response.get_json()["code"] == "INVALID_MINER" - - -def test_validate_fingerprint_data_rejects_non_dict_input(): - passed, reason = integrated_node.validate_fingerprint_data(["not", "a", "dict"]) - - assert passed is False - assert reason == "fingerprint_not_dict" - - -def test_attest_submit_strict_fixture_rejects_malformed_fingerprint(strict_client): - payload = _base_payload() - payload["fingerprint"]["checks"] = [] - - response = strict_client.post("/attest/submit", json=payload) - - assert response.status_code == 400 - assert response.get_json()["ok"] is False - assert response.get_json()["code"] == "INVALID_FINGERPRINT_CHECKS" - - -def test_attest_submit_strict_fixture_enforces_hardware_binding(strict_client): - first = _base_payload() - second = _base_payload() - second["miner"] = "different-miner" - - first_response = strict_client.post("/attest/submit", json=first) - second_response = strict_client.post("/attest/submit", json=second) - - assert first_response.status_code == 200 - assert second_response.status_code == 409 - assert second_response.get_json()["code"] == "DUPLICATE_HARDWARE" + assert response.status_code < 500 + assert response.get_json()["ok"] is True def _mutate_payload(rng: random.Random) -> dict: payload = _base_payload() - mutation = rng.randrange(14) + mutation = rng.randrange(8) if mutation == 0: payload["miner"] = ["not", "a", "string"] elif mutation == 1: payload["device"] = "not-a-device-object" elif mutation == 2: - payload["device"]["cores"] = rng.choice([0, -1, "NaN", [], {}, "999999999999999999999999"]) + payload["device"]["cores"] = rng.choice([0, -1, "NaN", [], {}]) elif mutation == 3: payload["signals"] = "not-a-signals-object" elif mutation == 4: @@ -257,29 +171,16 @@ def _mutate_payload(rng: random.Random) -> dict: payload["report"] = rng.choice(["not-a-report-object", [], {"commitment": ["bad"]}]) elif mutation == 6: payload["fingerprint"] = {"checks": rng.choice([[], "bad", {"anti_emulation": True}])} - elif mutation == 7: + else: payload["device"]["cpu"] = rng.choice(["qemu-system-ppc", "IBM POWER8", None, ["nested"]]) payload["signals"]["hostname"] = rng.choice(["vmware-host", "power8-host", None, ["nested"]]) - elif mutation == 8: - payload["miner"] = rng.choice(["", " ", "\t", "fuzz\u200bminer", "'; DROP TABLE balances; --"]) - elif mutation == 9: - payload["miner"] = "f" * 300 - elif mutation == 10: - payload["device"]["device_family"] = {"nested": {"too": "deep"}} - elif mutation == 11: - payload["signals"]["macs"] = ["AA:BB:CC:DD:EE:10", None] - elif mutation == 12: - payload["fingerprint"] = ["bad", "shape"] - else: - payload["report"]["nonce"] = {"nested": "bad"} return payload -def test_attest_submit_mutation_regression_no_unhandled_exceptions(client): +def test_attest_submit_fuzz_no_unhandled_exceptions(client): cases = int(os.getenv("ATTEST_FUZZ_CASES", "250")) - seed = os.getenv("ATTEST_FUZZ_SEED") - rng = random.Random(int(seed)) if seed else random.Random() + rng = random.Random(475) for index in range(cases): payload = _mutate_payload(rng) diff --git a/tests/test_bounty_verifier.py b/tests/test_bounty_verifier.py deleted file mode 100644 index 464e5417..00000000 --- a/tests/test_bounty_verifier.py +++ /dev/null @@ -1,668 +0,0 @@ -""" -Tests for bounty verifier module. -""" - -import json -import pytest -from datetime import datetime, timedelta -from unittest.mock import MagicMock, patch, PropertyMock -from urllib.error import HTTPError, URLError - -from tools.bounty_verifier.config import Config, GitHubConfig, PayoutCoefficient, load_config -from tools.bounty_verifier.models import ( - ClaimComment, - ClaimStatus, - VerificationCheck, - VerificationResult, - VerificationStatus, -) -from tools.bounty_verifier.github_client import GitHubClient, RateLimitExceeded -from tools.bounty_verifier.verifier import BountyVerifier, WalletCheckError - - -# ============================================================================ -# Fixtures -# ============================================================================ - -@pytest.fixture -def sample_claim_comment(): - """Create a sample claim comment.""" - return ClaimComment( - id=12345, - user_login="testuser", - user_id=67890, - body=""" - I claim this bounty! - - Wallet: RTC1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35 - - I follow @Scottcjn and have starred multiple repos. - Proof: https://github.com/testuser - """, - created_at=datetime.utcnow(), - updated_at=datetime.utcnow(), - issue_number=747, - html_url="https://github.com/Scottcjn/rustchain-bounties/issues/747#issuecomment-12345", - ) - - -@pytest.fixture -def sample_config(): - """Create a sample configuration.""" - return Config( - github=GitHubConfig( - token="test_token", - owner="Scottcjn", - repo="rustchain-bounties", - target_user="Scottcjn", - ), - rustchain=MagicMock(enabled=False), - require_follow=True, - require_stars=True, - min_star_count=3, - require_wallet=True, - check_duplicates=True, - dry_run=True, - ) - - -@pytest.fixture -def mock_github_client(): - """Create a mock GitHub client.""" - client = MagicMock(spec=GitHubClient) - client.check_following.return_value = True - client.get_starred_repos_count.return_value = 5 - client.get_issue_comments.return_value = [] - client.post_comment.return_value = {"html_url": "https://github.com/test/comment/1"} - return client - - -# ============================================================================ -# Model Tests -# ============================================================================ - -class TestClaimComment: - """Tests for ClaimComment model.""" - - def test_from_github_api(self): - """Test creating ClaimComment from GitHub API data.""" - api_data = { - "id": 12345, - "user": { - "login": "testuser", - "id": 67890, - }, - "body": "I claim this bounty", - "created_at": "2024-01-01T00:00:00Z", - "updated_at": "2024-01-01T00:00:00Z", - "html_url": "https://github.com/test", - } - - comment = ClaimComment.from_github_api(api_data, issue_number=747) - - assert comment.id == 12345 - assert comment.user_login == "testuser" - assert comment.user_id == 67890 - assert comment.body == "I claim this bounty" - assert comment.issue_number == 747 - - def test_from_github_api_invalid_date(self): - """Test handling of invalid date format.""" - api_data = { - "id": 12345, - "user": {"login": "testuser", "id": 67890}, - "body": "test", - "created_at": "2024-01-01T00:00:00+00:00", - "updated_at": "2024-01-01T00:00:00+00:00", - "html_url": "https://github.com/test", - } - - comment = ClaimComment.from_github_api(api_data, issue_number=747) - assert comment.created_at is not None - - -class TestVerificationResult: - """Tests for VerificationResult model.""" - - def test_add_check_passed(self): - """Test adding a passed check.""" - result = VerificationResult( - claim=MagicMock(), - overall_status=VerificationStatus.PENDING, - ) - - check = VerificationCheck( - name="Test Check", - status=VerificationStatus.PASSED, - message="All good", - ) - result.add_check(check) - - assert len(result.checks) == 1 - # Status should be updated to PASSED when all checks pass - assert result.overall_status == VerificationStatus.PASSED - - def test_add_check_failed(self): - """Test adding a failed check updates overall status.""" - result = VerificationResult( - claim=MagicMock(), - overall_status=VerificationStatus.PENDING, - ) - - check = VerificationCheck( - name="Test Check", - status=VerificationStatus.FAILED, - message="Failed", - ) - result.add_check(check) - - assert result.overall_status == VerificationStatus.FAILED - - def test_to_comment_body(self): - """Test generating comment body from result.""" - claim = ClaimComment( - id=1, - user_login="testuser", - user_id=123, - body="claim", - created_at=datetime.utcnow(), - updated_at=datetime.utcnow(), - issue_number=747, - html_url="https://github.com/test", - wallet_address="RTC1234567890", - ) - - result = VerificationResult( - claim=claim, - overall_status=VerificationStatus.PASSED, - payout_amount=150.0, - payout_coefficient=1.5, - ) - result.add_check(VerificationCheck( - name="GitHub Follow", - status=VerificationStatus.PASSED, - message="User is following", - )) - - body = result.to_comment_body() - - assert "## 🤖 Bounty Verification Report" in body - assert "@testuser" in body - assert "RTC1234567890" in body - assert "PASSED" in body - assert "✅" in body - assert "150.00" in body - - -# ============================================================================ -# Config Tests -# ============================================================================ - -class TestConfig: - """Tests for configuration loading.""" - - def test_default_config(self): - """Test default configuration values.""" - config = Config() - - assert config.github.owner == "Scottcjn" - assert config.github.repo == "rustchain-bounties" - assert config.require_follow is True - assert config.min_star_count == 3 - - def test_config_from_dict(self): - """Test creating config from dictionary.""" - data = { - "github": { - "token": "test_token", - "owner": "TestOwner", - }, - "criteria": { - "require_follow": False, - "min_star_count": 5, - }, - "payout": { - "base_amount": 200.0, - }, - } - - config = Config.from_dict(data) - - assert config.github.token == "test_token" - assert config.github.owner == "TestOwner" - assert config.require_follow is False - assert config.min_star_count == 5 - assert config.payout.base_amount == 200.0 - - def test_config_from_env(self): - """Test creating config from environment variables.""" - with patch.dict('os.environ', { - 'GITHUB_TOKEN': 'env_token', - 'GITHUB_OWNER': 'EnvOwner', - 'DRY_RUN': 'true', - }): - config = Config.from_env() - - assert config.github.token == "env_token" - assert config.github.owner == "EnvOwner" - assert config.dry_run is True - - -# ============================================================================ -# GitHub Client Tests -# ============================================================================ - -class TestGitHubClient: - """Tests for GitHub API client.""" - - def test_init(self): - """Test client initialization.""" - client = GitHubClient(token="test_token") - - assert client.token == "test_token" - assert client.owner == "Scottcjn" - assert client.repo == "rustchain-bounties" - - def test_get_headers(self): - """Test request headers.""" - client = GitHubClient(token="test_token") - headers = client._get_headers() - - assert "Authorization" in headers - assert "Bearer test_token" in headers["Authorization"] - assert "rustchain-bounty-verifier" in headers["User-Agent"] - - def test_get_headers_no_token(self): - """Test headers without token.""" - client = GitHubClient(token="") - headers = client._get_headers() - - assert "Authorization" not in headers - - @patch('tools.bounty_verifier.github_client.urlopen') - def test_check_following_cached(self, mock_urlopen): - """Test following check uses cache.""" - import time - client = GitHubClient(token="test_token") - - # First call - use time.time() to match the implementation - client._following_cache["user:Scottcjn"] = (True, time.time()) - result = client.check_following("user") - - assert result is True - mock_urlopen.assert_not_called() - - @patch('tools.bounty_verifier.github_client.urlopen') - def test_get_starred_repos_count_cached(self, mock_urlopen): - """Test star count uses cache.""" - import time - client = GitHubClient(token="test_token") - - # First call - use time.time() to match the implementation - client._star_count_cache["user:Scottcjn"] = (5, time.time()) - result = client.get_starred_repos_count("user") - - assert result == 5 - mock_urlopen.assert_not_called() - - -# ============================================================================ -# Verifier Tests -# ============================================================================ - -class TestBountyVerifier: - """Tests for BountyVerifier class.""" - - def test_init(self, sample_config): - """Test verifier initialization.""" - verifier = BountyVerifier(sample_config) - - assert verifier.config == sample_config - assert verifier.github is not None - - def test_init_no_token(self): - """Test verifier without GitHub token.""" - config = Config(github=GitHubConfig(token="")) - verifier = BountyVerifier(config) - - assert verifier.github is None - - def test_is_claim_comment_with_keyword(self, sample_claim_comment): - """Test detecting claim comment with keyword.""" - verifier = BountyVerifier(Config()) - - assert verifier.is_claim_comment(sample_claim_comment) is True - - def test_is_claim_comment_with_wallet(self): - """Test detecting claim comment with wallet address.""" - comment = ClaimComment( - id=1, - user_login="user", - user_id=1, - body="Wallet: RTC1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35", - created_at=datetime.utcnow(), - updated_at=datetime.utcnow(), - issue_number=1, - html_url="https://github.com/test", - ) - verifier = BountyVerifier(Config()) - - assert verifier.is_claim_comment(comment) is True - - def test_is_not_claim_comment(self): - """Test non-claim comment detection.""" - comment = ClaimComment( - id=1, - user_login="user", - user_id=1, - body="Thanks for the update!", - created_at=datetime.utcnow(), - updated_at=datetime.utcnow(), - issue_number=1, - html_url="https://github.com/test", - ) - verifier = BountyVerifier(Config()) - - assert verifier.is_claim_comment(comment) is False - - def test_is_paid_comment(self, sample_claim_comment): - """Test detecting paid comment.""" - paid_comment = ClaimComment( - id=12346, - user_login="admin", - user_id=1, - body="PAID - Payment sent to wallet", - created_at=datetime.utcnow(), - updated_at=datetime.utcnow(), - issue_number=747, - html_url="https://github.com/test", - ) - verifier = BountyVerifier(Config()) - - assert verifier.is_paid_comment(paid_comment) is True - assert verifier.is_paid_comment(sample_claim_comment) is False - - def test_extract_wallet_rtc_format(self): - """Test extracting wallet in RTC format.""" - verifier = BountyVerifier(Config()) - - text = "My wallet is RTC1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35" - wallet = verifier._extract_wallet(text) - - assert wallet == "RTC1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35" - - def test_extract_wallet_label(self): - """Test extracting wallet with label.""" - verifier = BountyVerifier(Config()) - - text = "Wallet: 1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35" - wallet = verifier._extract_wallet(text) - - assert wallet is not None - assert "1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35" in wallet - - def test_extract_urls(self): - """Test extracting URLs from text.""" - verifier = BountyVerifier(Config()) - - text = "Check https://github.com/user and http://example.com/test" - urls = verifier._extract_urls(text) - - assert len(urls) == 2 - assert "https://github.com/user" in urls - - def test_parse_claim_comment(self, sample_claim_comment): - """Test parsing claim comment.""" - verifier = BountyVerifier(Config()) - - parsed = verifier.parse_claim_comment(sample_claim_comment) - - assert parsed.wallet_address is not None - assert "RTC" in parsed.wallet_address - - def test_verify_follow_success(self, sample_config, mock_github_client): - """Test follow verification when user is following.""" - sample_config.github.token = "test" - verifier = BountyVerifier(sample_config) - verifier.github = mock_github_client - - result = verifier.verify_follow("testuser") - - assert result.status == VerificationStatus.PASSED - assert "following" in result.message.lower() - - def test_verify_follow_failure(self, sample_config, mock_github_client): - """Test follow verification when user is not following.""" - sample_config.github.token = "test" - verifier = BountyVerifier(sample_config) - verifier.github = mock_github_client - verifier.github.check_following.return_value = False - - result = verifier.verify_follow("testuser") - - assert result.status == VerificationStatus.FAILED - assert "NOT following" in result.message - - def test_verify_follow_no_client(self, sample_config): - """Test follow verification without GitHub client.""" - sample_config.github.token = "" - verifier = BountyVerifier(sample_config) - - result = verifier.verify_follow("testuser") - - assert result.status == VerificationStatus.SKIPPED - - def test_verify_stars_success(self, sample_config, mock_github_client): - """Test star verification with enough stars.""" - sample_config.github.token = "test" - sample_config.min_star_count = 3 - verifier = BountyVerifier(sample_config) - verifier.github = mock_github_client - verifier.github.get_starred_repos_count.return_value = 5 - - result = verifier.verify_stars("testuser") - - assert result.status == VerificationStatus.PASSED - assert result.details["star_count"] == 5 - - def test_verify_stars_failure(self, sample_config, mock_github_client): - """Test star verification with insufficient stars.""" - sample_config.github.token = "test" - sample_config.min_star_count = 5 - verifier = BountyVerifier(sample_config) - verifier.github = mock_github_client - verifier.github.get_starred_repos_count.return_value = 2 - - result = verifier.verify_stars("testuser") - - assert result.status == VerificationStatus.FAILED - - def test_verify_wallet_disabled(self, sample_config): - """Test wallet verification when disabled.""" - sample_config.rustchain.enabled = False - verifier = BountyVerifier(sample_config) - - result = verifier.verify_wallet("RTC1234567890") - - assert result.status == VerificationStatus.SKIPPED - - def test_verify_wallet_no_address(self, sample_config): - """Test wallet verification without address.""" - sample_config.rustchain.enabled = True - verifier = BountyVerifier(sample_config) - - result = verifier.verify_wallet("") - - assert result.status == VerificationStatus.FAILED - - def test_check_duplicates_none(self, sample_config, sample_claim_comment): - """Test duplicate check with no previous claims.""" - verifier = BountyVerifier(sample_config) - - result = verifier.check_duplicates(sample_claim_comment, []) - - assert result.status == VerificationStatus.PASSED - assert "No previous claims" in result.message - - def test_check_duplicates_with_paid(self, sample_config, sample_claim_comment): - """Test duplicate check with previous paid claim.""" - verifier = BountyVerifier(sample_config) - - previous_claim = ClaimComment( - id=12344, - user_login=sample_claim_comment.user_login, - user_id=sample_claim_comment.user_id, - body="I claim this bounty! Wallet: RTC123", - created_at=datetime.utcnow() - timedelta(days=1), - updated_at=datetime.utcnow() - timedelta(days=1), - issue_number=747, - html_url="https://github.com/test/1", - ) - - # Paid comment from admin (different user) - indicates the previous claim was paid - paid_comment = ClaimComment( - id=12345, - user_login="admin", - user_id=999, - body="PAID - Payment sent to @testuser", - created_at=datetime.utcnow() - timedelta(hours=1), - updated_at=datetime.utcnow() - timedelta(hours=1), - issue_number=747, - html_url="https://github.com/test/2", - ) - - # The duplicate check looks for claims from the same user that were marked as paid - # In this case, we need to simulate a scenario where the user's previous claim - # was followed by a PAID comment - # For simplicity, let's make the previous_claim itself indicate it was paid - previous_claim_paid = ClaimComment( - id=12344, - user_login=sample_claim_comment.user_login, - user_id=sample_claim_comment.user_id, - body="I claim this bounty! Wallet: RTC123 - PAID", - created_at=datetime.utcnow() - timedelta(days=1), - updated_at=datetime.utcnow() - timedelta(days=1), - issue_number=747, - html_url="https://github.com/test/1", - ) - - result = verifier.check_duplicates( - sample_claim_comment, - [previous_claim_paid], - ) - - assert result.status == VerificationStatus.FAILED - assert "already has a paid claim" in result.message - - def test_calculate_payout_base(self, sample_config): - """Test payout calculation with base amount.""" - verifier = BountyVerifier(sample_config) - result = VerificationResult( - claim=MagicMock(), - overall_status=VerificationStatus.PASSED, - ) - - payout = verifier.calculate_payout(result) - - assert payout == sample_config.payout.base_amount - assert result.payout_coefficient == 1.0 - - def test_calculate_payout_with_stars(self, sample_config): - """Test payout calculation with star bonus.""" - verifier = BountyVerifier(sample_config) - result = VerificationResult( - claim=MagicMock(), - overall_status=VerificationStatus.PASSED, - ) - - payout = verifier.calculate_payout(result, star_count=5) - - expected_bonus = min(5 * 0.05, 0.5) - expected_coef = 1.0 + expected_bonus - assert result.payout_coefficient == expected_coef - - def test_verify_claim_complete(self, sample_config, mock_github_client, sample_claim_comment): - """Test complete claim verification.""" - sample_config.github.token = "test" - sample_config.require_wallet = False # Skip wallet check - verifier = BountyVerifier(sample_config) - verifier.github = mock_github_client - - result = verifier.verify_claim(sample_claim_comment) - - assert result.claim.user_login == "testuser" - assert result.overall_status == VerificationStatus.PASSED - assert len(result.checks) >= 2 # Follow and stars - - def test_post_verification_comment_dry_run(self, sample_config, sample_claim_comment): - """Test posting comment in dry-run mode.""" - sample_config.dry_run = True - verifier = BountyVerifier(sample_config) - - result = VerificationResult( - claim=sample_claim_comment, - overall_status=VerificationStatus.PASSED, - ) - - url = verifier.post_verification_comment(747, result) - - assert url is None - - def test_post_verification_comment_live(self, sample_config, sample_claim_comment, mock_github_client): - """Test posting comment in live mode.""" - sample_config.dry_run = False - verifier = BountyVerifier(sample_config) - verifier.github = mock_github_client - - result = VerificationResult( - claim=sample_claim_comment, - overall_status=VerificationStatus.PASSED, - ) - - url = verifier.post_verification_comment(747, result) - - assert url == "https://github.com/test/comment/1" - - -# ============================================================================ -# Integration Tests -# ============================================================================ - -class TestIntegration: - """Integration tests for the verification flow.""" - - def test_full_verification_flow(self, sample_config, mock_github_client, sample_claim_comment): - """Test complete verification flow.""" - sample_config.github.token = "test" - sample_config.require_wallet = False - sample_config.dry_run = True - - verifier = BountyVerifier(sample_config) - verifier.github = mock_github_client - - # Verify the claim - result = verifier.verify_claim(sample_claim_comment) - - # Assert all expected checks ran - check_names = [c.name for c in result.checks] - assert "GitHub Follow" in check_names - assert "GitHub Stars" in check_names - - # Assert overall status - assert result.overall_status == VerificationStatus.PASSED - - # Assert payout was calculated - assert result.payout_amount > 0 - - def test_failed_verification_flow(self, sample_config, mock_github_client, sample_claim_comment): - """Test verification flow with failures.""" - sample_config.github.token = "test" - sample_config.require_wallet = False - - verifier = BountyVerifier(sample_config) - verifier.github = mock_github_client - verifier.github.check_following.return_value = False # Not following - - result = verifier.verify_claim(sample_claim_comment) - - assert result.overall_status == VerificationStatus.FAILED diff --git a/tests/test_entropy_temporal_validation.py b/tests/test_entropy_temporal_validation.py deleted file mode 100644 index 2930c78b..00000000 --- a/tests/test_entropy_temporal_validation.py +++ /dev/null @@ -1,93 +0,0 @@ -import sqlite3 - -import integrated_node - - -def _seq(values, key): - return [{"ts": i, "profile": {key: v}} for i, v in enumerate(values, start=1)] - - -def test_fingerprint_history_keeps_last_10_snapshots(tmp_path): - db_path = tmp_path / "temporal.db" - with sqlite3.connect(db_path) as conn: - for i in range(12): - fp = { - "checks": { - "clock_drift": {"data": {"cv": 0.02 + i * 0.001}}, - "thermal_entropy": {"data": {"variance": 2.0 + i * 0.1}}, - "instruction_jitter": {"data": {"cv": 0.04 + i * 0.001}}, - "cache_timing": {"data": {"hierarchy_ratio": 3.0 + i * 0.05}}, - } - } - integrated_node.append_fingerprint_snapshot(conn, "miner-a", fp, 1_000 + i) - - rows = conn.execute( - "SELECT ts FROM miner_fingerprint_history WHERE miner=? ORDER BY ts ASC", - ("miner-a",), - ).fetchall() - - assert len(rows) == 10 - assert rows[0][0] == 1_002 - assert rows[-1][0] == 1_011 - - -def test_validate_temporal_consistency_real_sequence_passes(): - seq = [] - for i, cv in enumerate([0.015, 0.020, 0.018, 0.022, 0.019, 0.017], start=1): - seq.append( - { - "ts": i, - "profile": { - "clock_drift_cv": cv, - "thermal_variance": 2.0 + (i % 3) * 0.2, - "jitter_cv": 0.04 + (i % 2) * 0.004, - "cache_hierarchy_ratio": 3.2 + (i % 2) * 0.1, - }, - } - ) - - out = integrated_node.validate_temporal_consistency(seq) - assert out["review_flag"] is False - assert out["score"] >= 0.9 - - -def test_validate_temporal_consistency_frozen_sequence_flagged(): - seq = [] - for i in range(1, 7): - seq.append( - { - "ts": i, - "profile": { - "clock_drift_cv": 0.02, - "thermal_variance": 2.5, - "jitter_cv": 0.03, - "cache_hierarchy_ratio": 3.4, - }, - } - ) - - out = integrated_node.validate_temporal_consistency(seq) - assert out["review_flag"] is True - assert any(flag.startswith("frozen_profile") for flag in out["flags"]) - - -def test_validate_temporal_consistency_noisy_sequence_flagged(): - seq = [] - noisy_clock = [0.002, 0.25, 0.004, 0.29, 0.003, 0.27] - noisy_thermal = [0.1, 18.0, 0.2, 16.0, 0.15, 20.0] - for i, (cv, thermal) in enumerate(zip(noisy_clock, noisy_thermal), start=1): - seq.append( - { - "ts": i, - "profile": { - "clock_drift_cv": cv, - "thermal_variance": thermal, - "jitter_cv": 0.02 if i % 2 else 0.3, - "cache_hierarchy_ratio": 3.0 if i % 2 else 9.0, - }, - } - ) - - out = integrated_node.validate_temporal_consistency(seq) - assert out["review_flag"] is True - assert any(flag.startswith("noisy_profile") for flag in out["flags"]) diff --git a/tests/test_faucet.py b/tests/test_faucet.py deleted file mode 100644 index fc69b1c9..00000000 --- a/tests/test_faucet.py +++ /dev/null @@ -1,61 +0,0 @@ -# SPDX-License-Identifier: MIT - -from __future__ import annotations - -from datetime import datetime, timedelta, timezone - -import pytest - -from tools import testnet_faucet as faucet - - -@pytest.fixture -def app(tmp_path, monkeypatch): - db_path = tmp_path / "faucet.db" - monkeypatch.setattr(faucet, "github_account_age_days", lambda *_args, **_kwargs: 30) - app = faucet.create_app({"DB_PATH": str(db_path), "DRY_RUN": True}) - app.config.update(TESTING=True) - return app - - -def test_faucet_page(app): - c = app.test_client() - r = c.get("/faucet") - assert r.status_code == 200 - assert b"RustChain Testnet Faucet" in r.data - - -def test_github_user_drip_success(app): - c = app.test_client() - r = c.post("/faucet/drip", json={"wallet": "rtc_wallet_1", "github_username": "alice"}) - assert r.status_code == 200 - data = r.get_json() - assert data["ok"] is True - assert data["amount"] == 1.0 - - -def test_ip_only_limit(app): - c = app.test_client() - h = {"X-Forwarded-For": "1.2.3.4"} - r1 = c.post("/faucet/drip", json={"wallet": "w1"}, headers=h) - assert r1.status_code == 200 - - r2 = c.post("/faucet/drip", json={"wallet": "w2"}, headers=h) - assert r2.status_code == 429 - assert r2.get_json()["error"] == "rate_limited" - - -def test_github_old_account_gets_2rtc_limit(tmp_path, monkeypatch): - db_path = tmp_path / "faucet.db" - monkeypatch.setattr(faucet, "github_account_age_days", lambda *_args, **_kwargs: 500) - app = faucet.create_app({"DB_PATH": str(db_path), "DRY_RUN": True}) - app.config.update(TESTING=True) - c = app.test_client() - - r1 = c.post("/faucet/drip", json={"wallet": "w1", "github_username": "old_user"}) - r2 = c.post("/faucet/drip", json={"wallet": "w2", "github_username": "old_user"}) - r3 = c.post("/faucet/drip", json={"wallet": "w3", "github_username": "old_user"}) - - assert r1.status_code == 200 - assert r2.status_code == 200 - assert r3.status_code == 429 diff --git a/tests/test_fingerprint_improved.py b/tests/test_fingerprint_improved.py index 85eda064..2a3b7f3e 100644 --- a/tests/test_fingerprint_improved.py +++ b/tests/test_fingerprint_improved.py @@ -313,44 +313,22 @@ def test_valid_clock_drift_passes(self): class TestVintageHardwareTiming: """Test vintage hardware-specific timing requirements.""" - @staticmethod - def _verified_g4_checks(cv: float) -> dict: - return { - "anti_emulation": VALID_ANTI_EMULATION, - "clock_drift": { - "passed": True, - "data": { - "cv": cv, - "samples": 100, - }, - }, - "simd_identity": { - "passed": True, - "data": { - "has_altivec": True, - "has_sse": False, - "has_avx": False, - "vec_perm": True, - }, - }, - "cache_timing": { - "passed": True, - "data": { - "arch": "powerpc", - "l2_l1_ratio": 1.4, - "l3_l2_ratio": 1.15, - }, - }, - } - def test_vintage_stability_too_high(self): """Verify rejection of suspicious stability on vintage hardware.""" claimed_device = { - "device_arch": "G4", - "cpu": "PowerPC G4 7447A", + "device_arch": "G4" } fingerprint = { - "checks": self._verified_g4_checks(0.001) + "checks": { + "anti_emulation": VALID_ANTI_EMULATION, + "clock_drift": { + "passed": True, + "data": { + "cv": 0.001, # Too stable for G4 + "samples": 100 + } + } + } } passed, reason = validate_fingerprint_data(fingerprint, claimed_device) assert passed is False, "Suspiciously stable vintage timing should fail" @@ -359,11 +337,19 @@ def test_vintage_stability_too_high(self): def test_vintage_normal_variation_passes(self): """Normal variation for vintage hardware should pass.""" claimed_device = { - "device_arch": "G4", - "cpu": "PowerPC G4 7447A", + "device_arch": "G4" } fingerprint = { - "checks": self._verified_g4_checks(0.05) + "checks": { + "anti_emulation": VALID_ANTI_EMULATION, + "clock_drift": { + "passed": True, + "data": { + "cv": 0.05, # Normal variation + "samples": 100 + } + } + } } passed, reason = validate_fingerprint_data(fingerprint, claimed_device) assert passed is True, "Normal vintage timing should pass" diff --git a/tests/test_governance_api.py b/tests/test_governance_api.py deleted file mode 100644 index ef835fa2..00000000 --- a/tests/test_governance_api.py +++ /dev/null @@ -1,115 +0,0 @@ -import json -import sqlite3 -import sys -import tempfile -import time -from pathlib import Path -from unittest.mock import patch - -integrated_node = sys.modules["integrated_node"] - - -def _vote_payload(proposal_id: int, wallet: str, vote: str, nonce: str): - payload = { - "proposal_id": proposal_id, - "wallet": wallet, - "vote": vote, - "nonce": nonce, - } - return payload - - -def test_governance_propose_requires_gt_10_rtc_balance(): - with tempfile.TemporaryDirectory() as td: - db_path = str(Path(td) / "gov.db") - integrated_node.DB_PATH = db_path - integrated_node.app.config["DB_PATH"] = db_path - integrated_node.init_db() - - with sqlite3.connect(db_path) as c: - c.execute("INSERT INTO balances(miner_pk, balance_rtc) VALUES(?, ?)", ("RTC-low", 10.0)) - c.commit() - - integrated_node.app.config["TESTING"] = True - with integrated_node.app.test_client() as client: - resp = client.post( - "/governance/propose", - json={"wallet": "RTC-low", "title": "No", "description": "insufficient"}, - ) - assert resp.status_code == 403 - assert resp.get_json()["error"] == "insufficient_balance_to_propose" - - -def test_governance_vote_flow_and_lifecycle_finalization(): - with tempfile.TemporaryDirectory() as td: - db_path = str(Path(td) / "gov.db") - integrated_node.DB_PATH = db_path - integrated_node.app.config["DB_PATH"] = db_path - integrated_node.init_db() - - pub_hex = "11" * 32 - wallet = integrated_node.address_from_pubkey(pub_hex) - - with sqlite3.connect(db_path) as c: - # proposer/voter has >10 RTC and can vote - c.execute("INSERT INTO balances(miner_pk, balance_rtc) VALUES(?, ?)", (wallet, 20.0)) - c.execute( - """ - CREATE TABLE IF NOT EXISTS miner_attest_recent ( - miner TEXT PRIMARY KEY, - ts_ok INTEGER, - device_family TEXT, - device_arch TEXT, - entropy_score REAL, - fingerprint_passed INTEGER, - source_ip TEXT - ) - """ - ) - # mark as active miner in miner view used by node - c.execute( - """ - INSERT INTO miner_attest_recent(miner, ts_ok, device_family, device_arch, entropy_score, fingerprint_passed, source_ip) - VALUES(?, ?, ?, ?, ?, ?, ?) - """, - (wallet, int(time.time()), "PowerPC", "g4", 1.0, 1, "127.0.0.1"), - ) - c.commit() - - integrated_node.app.config["TESTING"] = True - with integrated_node.app.test_client() as client: - # Create proposal - r1 = client.post( - "/governance/propose", - json={"wallet": wallet, "title": "Raise testnet fee", "description": "for anti-spam"}, - ) - assert r1.status_code == 201 - proposal_id = r1.get_json()["proposal"]["id"] - - # Signed YES vote (signature verification function is mocked) - payload = _vote_payload(proposal_id, wallet, "yes", "n-1") - with patch("integrated_node.verify_rtc_signature", return_value=True): - r2 = client.post( - "/governance/vote", - json={ - **payload, - "public_key": pub_hex, - "signature": "ab" * 64, - }, - ) - assert r2.status_code == 200 - body = r2.get_json() - assert body["ok"] is True - assert body["vote"] == "yes" - assert body["vote_weight"] > 20.0 # includes antiquity multiplier for g4 - - # Force proposal to end and ensure status becomes passed/failed - with sqlite3.connect(db_path) as c: - c.execute("UPDATE governance_proposals SET ends_at = ?, status = 'active' WHERE id = ?", (int(time.time()) - 1, proposal_id)) - c.commit() - - r3 = client.get(f"/governance/proposal/{proposal_id}") - assert r3.status_code == 200 - detail = r3.get_json()["proposal"] - assert detail["status"] in ("passed", "failed") - assert detail["result"] in ("passed", "failed") diff --git a/tests/test_rip201_bucket_spoof.py b/tests/test_rip201_bucket_spoof.py deleted file mode 100644 index 56494280..00000000 --- a/tests/test_rip201_bucket_spoof.py +++ /dev/null @@ -1,295 +0,0 @@ -import importlib.util -import sqlite3 -import sys -import uuid -from pathlib import Path - -import pytest - -integrated_node = sys.modules["integrated_node"] - - -def _load_fleet_module(): - module_name = "fleet_immune_system_bucket_test" - if module_name in sys.modules: - return sys.modules[module_name] - - module_path = ( - Path(__file__).resolve().parent.parent - / "rips" - / "python" - / "rustchain" - / "fleet_immune_system.py" - ) - spec = importlib.util.spec_from_file_location(module_name, module_path) - module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module - spec.loader.exec_module(module) - return module - - -fleet_mod = _load_fleet_module() - - -def _init_attestation_db(db_path: Path) -> None: - conn = sqlite3.connect(db_path) - conn.executescript( - """ - CREATE TABLE blocked_wallets ( - wallet TEXT PRIMARY KEY, - reason TEXT - ); - CREATE TABLE balances ( - miner_pk TEXT PRIMARY KEY, - balance_rtc REAL DEFAULT 0 - ); - CREATE TABLE epoch_enroll ( - epoch INTEGER NOT NULL, - miner_pk TEXT NOT NULL, - weight REAL NOT NULL, - PRIMARY KEY (epoch, miner_pk) - ); - CREATE TABLE miner_header_keys ( - miner_id TEXT PRIMARY KEY, - pubkey_hex TEXT - ); - CREATE TABLE tickets ( - ticket_id TEXT PRIMARY KEY, - expires_at INTEGER NOT NULL, - commitment TEXT - ); - CREATE TABLE oui_deny ( - oui TEXT PRIMARY KEY, - vendor TEXT, - enforce INTEGER DEFAULT 0 - ); - CREATE TABLE hardware_bindings ( - hardware_id TEXT PRIMARY KEY, - bound_miner TEXT NOT NULL, - device_arch TEXT, - device_model TEXT, - bound_at INTEGER, - attestation_count INTEGER DEFAULT 0 - ); - CREATE TABLE miner_attest_recent ( - miner TEXT PRIMARY KEY, - ts_ok INTEGER NOT NULL, - device_family TEXT, - device_arch TEXT, - entropy_score REAL DEFAULT 0.0, - fingerprint_passed INTEGER DEFAULT 0, - source_ip TEXT - ); - CREATE TABLE ip_rate_limit ( - client_ip TEXT NOT NULL, - miner_id TEXT NOT NULL, - ts INTEGER NOT NULL, - PRIMARY KEY (client_ip, miner_id) - ); - """ - ) - conn.commit() - conn.close() - - -@pytest.fixture -def attest_client(monkeypatch): - local_tmp_dir = Path(__file__).parent / ".tmp_attestation" - local_tmp_dir.mkdir(exist_ok=True) - db_path = local_tmp_dir / f"{uuid.uuid4().hex}.sqlite3" - _init_attestation_db(db_path) - - monkeypatch.setattr(integrated_node, "DB_PATH", str(db_path)) - monkeypatch.setattr(integrated_node, "HW_BINDING_V2", False, raising=False) - monkeypatch.setattr(integrated_node, "HW_PROOF_AVAILABLE", False, raising=False) - monkeypatch.setattr(integrated_node, "_check_hardware_binding", lambda *args, **kwargs: (True, "ok", "")) - monkeypatch.setattr(integrated_node, "check_ip_rate_limit", lambda *args, **kwargs: (True, "ok")) - monkeypatch.setattr(integrated_node, "record_macs", lambda *args, **kwargs: None) - monkeypatch.setattr(integrated_node, "auto_induct_to_hall", lambda *args, **kwargs: None) - monkeypatch.setattr(integrated_node, "current_slot", lambda: 12345) - monkeypatch.setattr(integrated_node, "slot_to_epoch", lambda slot: 85) - - integrated_node.app.config["TESTING"] = True - with integrated_node.app.test_client() as test_client: - yield test_client, db_path - - if db_path.exists(): - try: - db_path.unlink() - except PermissionError: - pass - - -def _spoofed_g4_payload(miner: str) -> dict: - return { - "miner": miner, - "device": { - "device_family": "PowerPC", - "device_arch": "G4", - "arch": "G4", - "cores": 8, - "cpu": "Intel Xeon Platinum", - "serial_number": f"SERIAL-{miner}", - }, - "signals": { - "hostname": "bare-metal-x86-host", - "macs": ["AA:BB:CC:DD:EE:10"], - }, - "report": { - "nonce": f"nonce-{miner}", - "commitment": f"commitment-{miner}", - }, - "fingerprint": { - "checks": { - "anti_emulation": { - "passed": True, - "data": { - "vm_indicators": [], - "paths_checked": ["/proc/cpuinfo"], - "dmesg_scanned": True, - }, - }, - }, - "all_passed": True, - }, - } - - -def _verified_g4_fingerprint() -> dict: - return { - "checks": { - "anti_emulation": { - "passed": True, - "data": { - "vm_indicators": [], - "paths_checked": ["/proc/cpuinfo"], - "dmesg_scanned": True, - }, - }, - "clock_drift": { - "passed": True, - "data": { - "cv": 0.06, - "samples": 80, - }, - }, - "simd_identity": { - "passed": True, - "data": { - "has_altivec": True, - "has_sse": False, - "has_avx": False, - "vec_perm": True, - }, - }, - "cache_timing": { - "passed": True, - "data": { - "arch": "powerpc", - "l2_l1_ratio": 1.42, - "l3_l2_ratio": 1.18, - }, - }, - }, - "all_passed": True, - } - - -def test_validate_fingerprint_data_rejects_spoofed_g4_with_x86_cpu_brand(): - payload = _spoofed_g4_payload("spoof-direct") - - passed, reason = integrated_node.validate_fingerprint_data( - payload["fingerprint"], - claimed_device=payload["device"], - ) - - assert passed is False - assert "cpu_brand_mismatch" in reason - - -def test_validate_fingerprint_data_accepts_verified_g4_claim(): - payload = _spoofed_g4_payload("verified-g4") - payload["device"]["cpu"] = "PowerPC G4 7447A" - payload["fingerprint"] = _verified_g4_fingerprint() - - passed, reason = integrated_node.validate_fingerprint_data( - payload["fingerprint"], - claimed_device=payload["device"], - ) - - assert passed is True - assert reason == "valid" - - -def test_attestation_downgrades_spoofed_g4_claim_to_non_vintage_weight(attest_client): - client, db_path = attest_client - payload = _spoofed_g4_payload("spoof-g4-accepted") - - response = client.post( - "/attest/submit", - json=payload, - headers={"X-Forwarded-For": "198.51.100.25"}, - environ_base={"REMOTE_ADDR": "10.0.0.9"}, - ) - - assert response.status_code == 200 - data = response.get_json() - assert data["ok"] is True - assert data["fingerprint_passed"] is False - - with sqlite3.connect(db_path) as conn: - recent = conn.execute( - "SELECT device_family, device_arch, fingerprint_passed FROM miner_attest_recent WHERE miner = ?", - (payload["miner"],), - ).fetchone() - enrollment = conn.execute( - "SELECT epoch, weight FROM epoch_enroll WHERE miner_pk = ?", - (payload["miner"],), - ).fetchone() - - assert recent == ("x86_64", "default", 0) - assert enrollment == (85, 0.000000001) - assert fleet_mod.classify_miner_bucket(recent[1]) != "vintage_powerpc" - - -def test_public_apis_do_not_expose_spoofed_claim_as_vintage(attest_client): - client, _db_path = attest_client - payload = _spoofed_g4_payload("spoof-g4-public-api") - - response = client.post( - "/attest/submit", - json=payload, - headers={"X-Forwarded-For": "198.51.100.26"}, - environ_base={"REMOTE_ADDR": "10.0.0.10"}, - ) - - assert response.status_code == 200 - - badge = client.get(f"/api/badge/{payload['miner']}") - badge_body = badge.get_json() - assert badge_body["message"] == "Active" - - miners = client.get("/api/miners") - miners_body = miners.get_json() - miner_row = next(row for row in miners_body if row["miner"] == payload["miner"]) - assert miner_row["device_family"] == "x86_64" - assert miner_row["device_arch"] == "default" - assert miner_row["hardware_type"] == "x86-64 (Modern)" - assert miner_row["antiquity_multiplier"] == 1.0 - - -def test_verified_server_side_classification_blocks_10x_reward_gain(): - db = sqlite3.connect(":memory:") - fleet_mod.ensure_schema(db) - - miners = [("spoof-g4", "default")] + [(f"modern-{index}", "modern") for index in range(10)] - rewards = fleet_mod.calculate_immune_rewards_equal_split( - db=db, - epoch=91, - miners=miners, - chain_age_years=1.0, - total_reward_urtc=1_100_000, - ) - - assert rewards["spoof-g4"] == rewards["modern-0"] - assert sum(rewards.values()) == 1_100_000 diff --git a/tests/test_rip201_fleet_bypass.py b/tests/test_rip201_fleet_bypass.py deleted file mode 100644 index 6685ddef..00000000 --- a/tests/test_rip201_fleet_bypass.py +++ /dev/null @@ -1,269 +0,0 @@ -import importlib.util -import sqlite3 -import sys -import uuid -from pathlib import Path -from unittest.mock import patch - -import pytest - -integrated_node = sys.modules["integrated_node"] - - -def _load_fleet_module(): - module_name = "fleet_immune_system_test" - if module_name in sys.modules: - return sys.modules[module_name] - - module_path = ( - Path(__file__).resolve().parent.parent - / "rips" - / "python" - / "rustchain" - / "fleet_immune_system.py" - ) - spec = importlib.util.spec_from_file_location(module_name, module_path) - module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module - spec.loader.exec_module(module) - return module - - -fleet_mod = _load_fleet_module() - - -def _init_attestation_db(db_path: Path) -> None: - conn = sqlite3.connect(db_path) - conn.executescript( - """ - CREATE TABLE blocked_wallets ( - wallet TEXT PRIMARY KEY, - reason TEXT - ); - CREATE TABLE balances ( - miner_pk TEXT PRIMARY KEY, - balance_rtc REAL DEFAULT 0 - ); - CREATE TABLE epoch_enroll ( - epoch INTEGER NOT NULL, - miner_pk TEXT NOT NULL, - weight REAL NOT NULL, - PRIMARY KEY (epoch, miner_pk) - ); - CREATE TABLE miner_header_keys ( - miner_id TEXT PRIMARY KEY, - pubkey_hex TEXT - ); - CREATE TABLE tickets ( - ticket_id TEXT PRIMARY KEY, - expires_at INTEGER NOT NULL, - commitment TEXT - ); - CREATE TABLE oui_deny ( - oui TEXT PRIMARY KEY, - vendor TEXT, - enforce INTEGER DEFAULT 0 - ); - CREATE TABLE hardware_bindings ( - hardware_id TEXT PRIMARY KEY, - bound_miner TEXT NOT NULL, - device_arch TEXT, - device_model TEXT, - bound_at INTEGER, - attestation_count INTEGER DEFAULT 0 - ); - CREATE TABLE miner_attest_recent ( - miner TEXT PRIMARY KEY, - ts_ok INTEGER NOT NULL, - device_family TEXT, - device_arch TEXT, - entropy_score REAL DEFAULT 0.0, - fingerprint_passed INTEGER DEFAULT 0, - source_ip TEXT - ); - CREATE TABLE ip_rate_limit ( - client_ip TEXT NOT NULL, - miner_id TEXT NOT NULL, - ts INTEGER NOT NULL, - PRIMARY KEY (client_ip, miner_id) - ); - """ - ) - conn.commit() - conn.close() - - -@pytest.fixture -def attest_client(monkeypatch): - local_tmp_dir = Path(__file__).parent / ".tmp_attestation" - local_tmp_dir.mkdir(exist_ok=True) - db_path = local_tmp_dir / f"{uuid.uuid4().hex}.sqlite3" - _init_attestation_db(db_path) - - monkeypatch.setattr(integrated_node, "DB_PATH", str(db_path)) - monkeypatch.setattr(integrated_node, "HW_BINDING_V2", False, raising=False) - monkeypatch.setattr(integrated_node, "HW_PROOF_AVAILABLE", False, raising=False) - monkeypatch.setattr(integrated_node, "_check_hardware_binding", lambda *args, **kwargs: (True, "ok", "")) - monkeypatch.setattr(integrated_node, "auto_induct_to_hall", lambda *args, **kwargs: None) - monkeypatch.setattr(integrated_node, "record_macs", lambda *args, **kwargs: None) - monkeypatch.setattr(integrated_node, "current_slot", lambda: 12345) - monkeypatch.setattr(integrated_node, "slot_to_epoch", lambda slot: 85) - - integrated_node.app.config["TESTING"] = True - with integrated_node.app.test_client() as test_client: - yield test_client, db_path - - if db_path.exists(): - try: - db_path.unlink() - except PermissionError: - pass - - -def _minimal_valid_fingerprint(cv: float) -> dict: - return { - "checks": { - "anti_emulation": { - "passed": True, - "data": { - "vm_indicators": [], - "paths_checked": ["/proc/cpuinfo"], - "dmesg_scanned": True, - }, - }, - "clock_drift": { - "passed": True, - "data": { - "cv": round(cv, 4), - "samples": 64, - }, - }, - }, - "all_passed": True, - } - - -def _shared_fleet_fingerprint() -> dict: - return { - "checks": { - "anti_emulation": { - "passed": True, - "data": { - "vm_indicators": [], - "paths_checked": ["/proc/cpuinfo"], - "dmesg_scanned": True, - }, - }, - "clock_drift": { - "passed": True, - "data": { - "cv": 0.052, - "samples": 64, - }, - }, - "cache_timing": { - "passed": True, - "data": {"l1_hit_ns": 4.1, "l2_hit_ns": 10.2}, - }, - "thermal_drift": { - "passed": True, - "data": {"entropy": 0.61}, - }, - "simd_identity": { - "passed": True, - "data": {"profile": "same-simd-profile"}, - }, - }, - "all_passed": True, - } - - -def test_client_ip_from_request_ignores_spoofed_x_forwarded_for(attest_client): - client, db_path = attest_client - payload = { - "miner": "spoof-demo-1", - "device": { - "device_family": "x86", - "device_arch": "default", - "arch": "default", - "cores": 8, - "cpu": "Intel Xeon", - "serial_number": "SERIAL-001", - }, - "signals": { - "hostname": "shared-box-a", - "macs": ["AA:BB:CC:DD:EE:01"], - }, - "report": { - "nonce": "nonce-001", - "commitment": "commitment-001", - }, - "fingerprint": _minimal_valid_fingerprint(0.05), - } - - response = client.post( - "/attest/submit", - json=payload, - headers={"X-Forwarded-For": "198.51.100.77"}, - environ_base={"REMOTE_ADDR": "10.0.0.9"}, - ) - - assert response.status_code == 200 - assert response.get_json()["ok"] is True - assert response.get_json()["fingerprint_passed"] is True - - with sqlite3.connect(db_path) as conn: - row = conn.execute( - "SELECT source_ip FROM miner_attest_recent WHERE miner = ?", - (payload["miner"],), - ).fetchone() - - # RIP-201 fix: server ignores X-Forwarded-For, uses REMOTE_ADDR - assert row == ("10.0.0.9",) - - -def test_same_subnet_and_shared_fingerprint_get_flagged(): - db = sqlite3.connect(":memory:") - fleet_mod.ensure_schema(db) - - for index in range(5): - fleet_mod.record_fleet_signals_from_request( - db, - miner=f"baseline-miner-{index}", - epoch=101, - ip_address="10.0.0.25", - attest_ts=1_000 + index * 5, - fingerprint=_shared_fleet_fingerprint(), - ) - - scores = fleet_mod.compute_fleet_scores(db, 101) - - assert len(scores) == 5 - assert all(score > 0.7 for score in scores.values()) - - -def test_spoofed_forwarded_ips_sparse_fingerprints_and_jitter_keep_scores_clean(): - db = sqlite3.connect(":memory:") - fleet_mod.ensure_schema(db) - miners = [f"bypass-miner-{index}" for index in range(5)] - - for epoch in (201, 202, 203): - for index, miner in enumerate(miners): - fleet_mod.record_fleet_signals_from_request( - db, - miner=miner, - epoch=epoch, - ip_address=f"198.{10 + index}.{epoch % 255}.25", - attest_ts=10_000 * epoch + index * 45, - fingerprint=_minimal_valid_fingerprint(0.05 + index * 0.01), - ) - - scores = fleet_mod.compute_fleet_scores(db, epoch) - - assert set(scores) == set(miners) - assert all(score < 0.3 for score in scores.values()) - assert all(score == 0.0 for score in scores.values()) - assert all( - fleet_mod.apply_fleet_decay(2.5, score) == 2.5 - for score in scores.values() - ) diff --git a/tests/test_verify_backup.py b/tests/test_verify_backup.py deleted file mode 100644 index 4d49a463..00000000 --- a/tests/test_verify_backup.py +++ /dev/null @@ -1,48 +0,0 @@ -# SPDX-License-Identifier: MIT - -from __future__ import annotations - -import sqlite3 -from pathlib import Path - -from tools.verify_backup import verify - - -def _make_db(path: Path, rows: int = 3, epoch: int = 10): - conn = sqlite3.connect(path) - conn.execute("CREATE TABLE balances(amount REAL)") - conn.execute("CREATE TABLE miner_attest_recent(id INTEGER)") - conn.execute("CREATE TABLE headers(id INTEGER)") - conn.execute("CREATE TABLE ledger(id INTEGER)") - conn.execute("CREATE TABLE epoch_rewards(epoch INTEGER)") - - for _ in range(rows): - conn.execute("INSERT INTO balances(amount) VALUES (1.0)") - conn.execute("INSERT INTO miner_attest_recent(id) VALUES (1)") - conn.execute("INSERT INTO headers(id) VALUES (1)") - conn.execute("INSERT INTO ledger(id) VALUES (1)") - conn.execute("INSERT INTO epoch_rewards(epoch) VALUES (?)", (epoch,)) - conn.commit() - conn.close() - - -def test_verify_pass(tmp_path): - live = tmp_path / "live.db" - bak = tmp_path / "bak.db" - _make_db(live, rows=5, epoch=10) - _make_db(bak, rows=5, epoch=10) - - result = verify(str(live), str(bak)) - assert result.ok is True - assert any("RESULT: PASS" in line for line in result.lines) - - -def test_verify_fail_when_epoch_too_old(tmp_path): - live = tmp_path / "live.db" - bak = tmp_path / "bak.db" - _make_db(live, rows=5, epoch=10) - _make_db(bak, rows=5, epoch=7) - - result = verify(str(live), str(bak)) - assert result.ok is False - assert any("RESULT: FAIL" in line for line in result.lines) diff --git a/tests/test_wallet_cli_39.py b/tests/test_wallet_cli_39.py deleted file mode 100644 index aac231ed..00000000 --- a/tests/test_wallet_cli_39.py +++ /dev/null @@ -1,47 +0,0 @@ -import json -from tools import rustchain_wallet_cli as cli - - -def test_encrypt_decrypt_roundtrip(): - priv = "11" * 32 - enc = cli._encrypt_private_key(priv, "pw123") - out = cli._decrypt_private_key(enc, "pw123") - assert out == priv - - -def test_decrypt_compat_alias_fields(): - priv = "22" * 32 - enc = cli._encrypt_private_key(priv, "pw456") - legacy = { - "salt": enc["salt_b64"], - "nonce": enc["nonce_b64"], - "encrypted_private_key": enc["ciphertext_b64"], - "iterations": enc["kdf_iterations"], - } - out = cli._decrypt_private_key(legacy, "pw456") - assert out == priv - - -def test_address_format_from_pubkey(): - pub = "22" * 32 - addr = cli._address_from_pubkey_hex(pub) - assert addr.startswith("RTC") - assert len(addr) == 43 - - -def test_sign_transfer_shape(): - # deterministic private key bytes for test - priv = "01" * 32 - tx = cli._sign_transfer(priv, "RTC" + "a" * 40, "RTC" + "b" * 40, 1.23, "m", 123) - assert tx["from_address"].startswith("RTC") - assert tx["to_address"].startswith("RTC") - assert tx["amount_rtc"] == 1.23 - assert isinstance(tx["signature"], str) and len(tx["signature"]) > 20 - assert isinstance(tx["public_key"], str) and len(tx["public_key"]) == 64 - - -def test_balance_normalization(): - payload = {"balance_rtc": 9.5} - if "amount_rtc" not in payload and "balance_rtc" in payload: - payload["amount_rtc"] = payload.get("balance_rtc") - assert payload["amount_rtc"] == 9.5