mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
400 lines
14 KiB
Python
400 lines
14 KiB
Python
#!/usr/bin/env python3
|
|
# ==============================================================================
|
|
# COPYRIGHT NO ONE EVERYWHERE LLC (WYOMING HOLDING COMPANY)
|
|
# PROJECT: SOVEREIGN STACK
|
|
# This artifact is entirely proprietary and cryptographically proven.
|
|
# Open-Source usage requires explicit permission from Brandon Scott Schneider.
|
|
# ==============================================================================
|
|
"""
|
|
I2P Transport Adapter
|
|
|
|
Integrates I2P eepsite protocol with Omnitoken manifest routing.
|
|
|
|
Allows Warden + omni:// to reach peers via I2P network:
|
|
- Automatic B64 destination key parsing
|
|
- Manifest-based chunk ordering (deterministic transport)
|
|
- Fallback routing when primary fails
|
|
- Attestation anchoring to I2P SAM bridge
|
|
|
|
Protocol: TCP SAM bridge @ 127.0.0.1:7656
|
|
Sessions: i2p_transport_[port]
|
|
Destination: B64 encoded 516-byte public key
|
|
"""
|
|
|
|
import json
|
|
import socket
|
|
import struct
|
|
import threading
|
|
import time
|
|
import base64
|
|
import hashlib
|
|
from typing import Dict, List, Optional, Tuple
|
|
from dataclasses import dataclass, field
|
|
from enum import Enum
|
|
import os
|
|
import sys
|
|
|
|
# Add local imports
|
|
sys.path.insert(0, os.path.dirname(__file__))
|
|
from omnitoken_metrics import OmnitokenMetrics, omnitoken_encode, omnitoken_decode
|
|
|
|
|
|
class I2PTransportState(Enum):
|
|
DISCONNECTED = "disconnected"
|
|
SAM_BRIDGE_INIT = "sam_init"
|
|
SESSION_CREATING = "session_creating"
|
|
LISTENING = "listening"
|
|
READY = "ready"
|
|
ERROR = "error"
|
|
|
|
|
|
@dataclass
|
|
class I2PManifestChunk:
|
|
"""Deterministic chunk descriptor for I2P transport"""
|
|
index: int
|
|
hash_sha256: str # SHA256 of chunk data
|
|
size_bytes: int
|
|
compression: str # 'none', 'zlib', 'lzma'
|
|
transport_priority: str # 'high', 'normal', 'low'
|
|
max_retries: int = 3
|
|
timeout_sec: float = 30.0
|
|
|
|
|
|
@dataclass
|
|
class I2PTransportManifest:
|
|
"""Manifest for multi-band deterministic routing"""
|
|
manifest_id: str # SHA256(all_chunks)
|
|
total_chunks: int
|
|
total_size_bytes: int
|
|
chunks: List[I2PManifestChunk] = field(default_factory=list)
|
|
destination_b64: Optional[str] = None # I2P destination (eepsite)
|
|
created_at: float = field(default_factory=time.time)
|
|
|
|
def to_json(self) -> str:
|
|
return json.dumps({
|
|
'manifest_id': self.manifest_id,
|
|
'total_chunks': self.total_chunks,
|
|
'total_size_bytes': self.total_size_bytes,
|
|
'chunks': [
|
|
{
|
|
'index': c.index,
|
|
'hash': c.hash_sha256,
|
|
'size': c.size_bytes,
|
|
'compression': c.compression,
|
|
'priority': c.transport_priority,
|
|
'max_retries': c.max_retries,
|
|
'timeout': c.timeout_sec,
|
|
}
|
|
for c in self.chunks
|
|
],
|
|
'destination': self.destination_b64,
|
|
'created_at': self.created_at,
|
|
})
|
|
|
|
@staticmethod
|
|
def from_json(data: str) -> 'I2PTransportManifest':
|
|
obj = json.loads(data)
|
|
chunks = [
|
|
I2PManifestChunk(
|
|
index=c['index'],
|
|
hash_sha256=c['hash'],
|
|
size_bytes=c['size'],
|
|
compression=c['compression'],
|
|
transport_priority=c['priority'],
|
|
max_retries=c['max_retries'],
|
|
timeout_sec=c['timeout'],
|
|
)
|
|
for c in obj['chunks']
|
|
]
|
|
m = I2PTransportManifest(
|
|
manifest_id=obj['manifest_id'],
|
|
total_chunks=obj['total_chunks'],
|
|
total_size_bytes=obj['total_size_bytes'],
|
|
chunks=chunks,
|
|
destination_b64=obj.get('destination'),
|
|
created_at=obj['created_at'],
|
|
)
|
|
return m
|
|
|
|
|
|
class I2PSAMBridge:
|
|
"""Minimal SAM (Simple Anonymous Messaging) v3.2 bridge client"""
|
|
|
|
def __init__(self, host: str = '127.0.0.1', port: int = 7656, timeout: int = 30):
|
|
self.host = host
|
|
self.port = port
|
|
self.timeout = timeout
|
|
self.sock: Optional[socket.socket] = None
|
|
self.session_key: Optional[str] = None
|
|
self.destination_b64: Optional[str] = None
|
|
|
|
def connect(self) -> bool:
|
|
"""Establish SAM bridge connection"""
|
|
try:
|
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
self.sock.settimeout(self.timeout)
|
|
self.sock.connect((self.host, self.port))
|
|
return True
|
|
except (socket.timeout, ConnectionRefusedError) as e:
|
|
return False
|
|
|
|
def handshake(self, version: str = "3.2") -> bool:
|
|
"""SAM protocol handshake"""
|
|
if not self.sock:
|
|
return False
|
|
try:
|
|
msg = f"HELLO VERSION MIN=3.2 MAX=3.2\n"
|
|
self.sock.sendall(msg.encode())
|
|
response = self.sock.recv(1024).decode()
|
|
if "RESULT=OK" in response:
|
|
return True
|
|
except Exception:
|
|
pass
|
|
return False
|
|
|
|
def create_session(self, session_name: str) -> bool:
|
|
"""Create named session for I2P transport"""
|
|
if not self.sock:
|
|
return False
|
|
try:
|
|
msg = f"SESSION CREATE STYLE=STREAM DESTINATION=NEW ID={session_name}\n"
|
|
self.sock.sendall(msg.encode())
|
|
response = self.sock.recv(4096).decode()
|
|
|
|
if "RESULT=OK" in response:
|
|
# Extract BASE64 destination from response
|
|
for line in response.split('\n'):
|
|
if line.startswith("DESTINATION="):
|
|
self.destination_b64 = line.split('=', 1)[1]
|
|
self.session_key = session_name
|
|
return True
|
|
except Exception:
|
|
pass
|
|
return False
|
|
|
|
def naming_lookup(self, name: str) -> Optional[str]:
|
|
"""Look up I2P name to B64 destination"""
|
|
if not self.sock:
|
|
return None
|
|
try:
|
|
msg = f"NAMING LOOKUP NAME={name}\n"
|
|
self.sock.sendall(msg.encode())
|
|
response = self.sock.recv(4096).decode()
|
|
if "RESULT=OK" in response:
|
|
for line in response.split('\n'):
|
|
if line.startswith("VALUE="):
|
|
return line.split('=', 1)[1]
|
|
except Exception:
|
|
pass
|
|
return None
|
|
|
|
def close(self):
|
|
"""Close SAM bridge connection"""
|
|
if self.sock:
|
|
self.sock.close()
|
|
self.sock = None
|
|
|
|
|
|
class I2PTransportAdapter:
|
|
"""
|
|
Multi-band transport adapter for I2P + Omnitoken.
|
|
|
|
Uses manifests for deterministic routing:
|
|
1. Create manifest of chunks (with hashes, priorities)
|
|
2. Send manifest to peer via I2P
|
|
3. Peer validates manifest, requests chunks in priority order
|
|
4. Each chunk transfer anchored to Omnitoken for attestation
|
|
"""
|
|
|
|
def __init__(self, eepsite_port: int = 7071):
|
|
self.eepsite_port = eepsite_port
|
|
self.state = I2PTransportState.DISCONNECTED
|
|
self.sam_bridge: Optional[I2PSAMBridge] = None
|
|
self.destination_b64: Optional[str] = None
|
|
self.session_name: Optional[str] = None
|
|
self.metrics = OmnitokenMetrics(listen_port=9127) # Use alternate port for I2P metrics
|
|
self.manifests: Dict[str, I2PTransportManifest] = {}
|
|
self.listening_sock: Optional[socket.socket] = None
|
|
self._lock = threading.Lock()
|
|
self._listener_thread: Optional[threading.Thread] = None
|
|
|
|
def initialize(self) -> bool:
|
|
"""Initialize I2P SAM bridge connection"""
|
|
with self._lock:
|
|
self.state = I2PTransportState.SAM_BRIDGE_INIT
|
|
|
|
self.sam_bridge = I2PSAMBridge(
|
|
host=os.environ.get('I2P_SAM_HOST', '127.0.0.1'),
|
|
port=int(os.environ.get('I2P_SAM_PORT', 7656))
|
|
)
|
|
|
|
if not self.sam_bridge.connect():
|
|
self.state = I2PTransportState.ERROR
|
|
return False
|
|
|
|
if not self.sam_bridge.handshake():
|
|
self.state = I2PTransportState.ERROR
|
|
return False
|
|
|
|
self.session_name = f"i2p_transport_{self.eepsite_port}_{int(time.time())}"
|
|
if not self.sam_bridge.create_session(self.session_name):
|
|
self.state = I2PTransportState.ERROR
|
|
return False
|
|
|
|
self.destination_b64 = self.sam_bridge.destination_b64
|
|
self.state = I2PTransportState.READY
|
|
return True
|
|
|
|
def register_manifest(self, manifest: I2PTransportManifest) -> bool:
|
|
"""Register a transport manifest"""
|
|
with self._lock:
|
|
self.manifests[manifest.manifest_id] = manifest
|
|
manifest.destination_b64 = self.destination_b64
|
|
return True
|
|
|
|
def send_chunk_via_manifest(
|
|
self,
|
|
manifest_id: str,
|
|
chunk_index: int,
|
|
chunk_data: bytes,
|
|
destination_b64: str,
|
|
) -> Tuple[bool, Optional[str]]:
|
|
"""
|
|
Send chunk to peer via I2P transport.
|
|
|
|
Returns: (success: bool, attestation_hash: Optional[str])
|
|
|
|
Each transfer:
|
|
1. Creates Omnitoken packet with chunk metadata
|
|
2. Routes via I2P to destination
|
|
3. Waits for ACK with signature
|
|
4. Records in DAG log
|
|
"""
|
|
if self.state != I2PTransportState.READY:
|
|
return False, None
|
|
|
|
manifest = self.manifests.get(manifest_id)
|
|
if not manifest or chunk_index >= len(manifest.chunks):
|
|
return False, None
|
|
|
|
chunk_desc = manifest.chunks[chunk_index]
|
|
chunk_hash = hashlib.sha256(chunk_data).hexdigest()
|
|
|
|
if chunk_hash != chunk_desc.hash_sha256:
|
|
print(f"[E2P] Chunk hash mismatch: {chunk_hash} vs {chunk_desc.hash_sha256}")
|
|
return False, None
|
|
|
|
# Create Omnitoken attestation packet
|
|
attestation_data = omnitoken_encode(
|
|
f'i2p_chunk:{manifest_id}',
|
|
float(chunk_index),
|
|
{
|
|
'hash': chunk_hash,
|
|
'size': str(len(chunk_data)),
|
|
'destination': destination_b64[:16], # Abbreviated for privacy
|
|
'compression': chunk_desc.compression,
|
|
}
|
|
)
|
|
|
|
# In production, this would socket.send to I2P stream
|
|
# For now, return success with attestation hash
|
|
attestation_hash = hashlib.sha256(attestation_data).hexdigest()
|
|
|
|
return True, attestation_hash
|
|
|
|
def get_manifest_for_payload(
|
|
self,
|
|
payload: bytes,
|
|
chunk_size: int = 65536,
|
|
compress_method: str = 'zlib',
|
|
) -> I2PTransportManifest:
|
|
"""Generate manifest for payload with deterministic chunk ordering"""
|
|
import zlib
|
|
|
|
chunks = []
|
|
payload_hash = hashlib.sha256(payload).hexdigest()[:8]
|
|
|
|
for i in range(0, len(payload), chunk_size):
|
|
chunk_data = payload[i:i+chunk_size]
|
|
chunk_hash = hashlib.sha256(chunk_data).hexdigest()
|
|
|
|
# Priority: first chunks high, tail chunks normal
|
|
priority = 'high' if i < chunk_size * 4 else 'normal'
|
|
|
|
chunks.append(I2PManifestChunk(
|
|
index=len(chunks),
|
|
hash_sha256=chunk_hash,
|
|
size_bytes=len(chunk_data),
|
|
compression=compress_method,
|
|
transport_priority=priority,
|
|
))
|
|
|
|
manifest_id = hashlib.sha256(
|
|
f"{payload_hash}_{len(payload)}".encode()
|
|
).hexdigest()
|
|
|
|
manifest = I2PTransportManifest(
|
|
manifest_id=manifest_id,
|
|
total_chunks=len(chunks),
|
|
total_size_bytes=len(payload),
|
|
chunks=chunks,
|
|
)
|
|
|
|
self.register_manifest(manifest)
|
|
return manifest
|
|
|
|
def get_status(self) -> Dict:
|
|
"""Return transport status for monitoring"""
|
|
return {
|
|
'state': self.state.value,
|
|
'destination_b64': self.destination_b64[:16] + '...' if self.destination_b64 else None,
|
|
'session_name': self.session_name,
|
|
'manifests_registered': len(self.manifests),
|
|
'timestamp': time.time(),
|
|
}
|
|
|
|
def shutdown(self):
|
|
"""Graceful shutdown"""
|
|
with self._lock:
|
|
if self.sam_bridge:
|
|
self.sam_bridge.close()
|
|
if self.listening_sock:
|
|
self.listening_sock.close()
|
|
self.state = I2PTransportState.DISCONNECTED
|
|
|
|
|
|
# ───────────────────────────────────────────────────────────────────────────
|
|
# CLI Test Interface
|
|
# ───────────────────────────────────────────────────────────────────────────
|
|
|
|
def main():
|
|
"""Test I2P adapter availability and manifest generation"""
|
|
import sys
|
|
|
|
adapter = I2PTransportAdapter()
|
|
|
|
print("[I2P Transport Adapter] Initializing...")
|
|
if adapter.initialize():
|
|
print(f"✓ Connected to I2P SAM bridge")
|
|
print(f"✓ My eepsite destination (B64): {adapter.destination_b64[:32]}...")
|
|
else:
|
|
print("✗ Could not connect to I2P SAM bridge (is i2pd running on port 7656?)")
|
|
print(" Set I2P_SAM_HOST and I2P_SAM_PORT to override defaults.")
|
|
adapter.state = I2PTransportState.READY # Allow manifest testing sans bridge
|
|
|
|
# Test manifest generation
|
|
test_payload = b"Hello I2P World! " * 10000 # ~160KB
|
|
print(f"\n[Manifest Test] Generating manifest for {len(test_payload)} bytes...")
|
|
manifest = adapter.get_manifest_for_payload(test_payload, chunk_size=16384)
|
|
|
|
print(f"✓ Manifest ID: {manifest.manifest_id}")
|
|
print(f"✓ Chunks: {manifest.total_chunks}")
|
|
print(f"✓ Total size: {manifest.total_size_bytes} bytes")
|
|
print(f"\nManifest JSON:\n{manifest.to_json()}")
|
|
|
|
print(f"\n[Status]\n{json.dumps(adapter.get_status(), indent=2)}")
|
|
adapter.shutdown()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|