feat(rfc-0315): Add Zig Verifier PoC for Access Tolls
- TollClearanceProof structure with STARK support - Immediate and lazy (Kenya) verification modes - NonceCache for replay prevention - 6 passing unit tests Refs: RFC-0315 v0.3.0
This commit is contained in:
parent
30fa2693cc
commit
a67f4c43f7
|
|
@ -0,0 +1,82 @@
|
|||
# RFC STATUS UPDATE - 2026-02-04
|
||||
|
||||
## CONCEPTUALLY STABLE (Frozen Protocol Mechanics)
|
||||
|
||||
### RFC-0648: Hamiltonian Economic Dynamics
|
||||
- **Status:** PRE-IMPLEMENTATION / STABLE
|
||||
- **Stability:** Protocol mechanics frozen
|
||||
- **Tuning:** Parameter adjustment only via field testing (Ki, Kp, Kd, bands)
|
||||
- **Changes:** No changes to mathematical framework allowed
|
||||
|
||||
### RFC-0649: Emergent Protocol-Owned Emission (EPOE)
|
||||
- **Status:** PRE-IMPLEMENTATION / STABLE
|
||||
- **Stability:** Core mechanisms frozen
|
||||
- Opportunity Windows (injection)
|
||||
- Demurrage + Burn (extraction)
|
||||
- Enshrined PID with Protocol Caps
|
||||
- Anti-Sybil: Genesis + Maintenance
|
||||
- **Tuning:** Bandwidths, multipliers, costs adjustable
|
||||
- **Changes:** No structural changes allowed
|
||||
|
||||
## DRAFT (Ready for Review)
|
||||
|
||||
### RFC-0130: ZK-STARK Primitive Layer
|
||||
- **Status:** DRAFT v0.1.0
|
||||
- **Scope:** Zero-knowledge proofs without trusted setup
|
||||
- **Circuits:** Membership, Reputation, Trust Distance, Balance, Velocity, Delegation
|
||||
- **Kenya Compliance:** Recursive compression (45-200 KB → 2-5 KB)
|
||||
- **Integration:** W3C Verifiable Credentials
|
||||
- **Next:** Review and freeze
|
||||
|
||||
### RFC-0205: ChapterPassport Protocol
|
||||
- **Status:** DRAFT v0.1.0
|
||||
- **Scope:** Universal credential for Identity + Economics + Governance
|
||||
- **Layers:** Identity Core, Membership, Economic Standing, Attestations, ZK-Proofs
|
||||
- **Integration:** RFC-0130 (ZK-STARK), RFC-0648 (Hamiltonian)
|
||||
- **Next:** Review and freeze
|
||||
|
||||
---
|
||||
|
||||
## PRIORITY 1: RFC-0315 (ACTIVE DEVELOPMENT)
|
||||
|
||||
### RFC-0315: Privacy-Preserving Access Tolls
|
||||
- **Status:** ACTIVE DRAFT v0.3.0
|
||||
- **Layer:** L2 (Economic Strategy)
|
||||
- **Scope:** Dynamic resource allocation with ZK-STARK privacy
|
||||
- **Dependencies:**
|
||||
- ✅ RFC-0130 (ZK-STARK #10 TollClearanceCircuit)
|
||||
- ✅ RFC-0205 (Passport nullifier lifecycle)
|
||||
- ✅ RFC-0648 (Hamiltonian velocity scaling)
|
||||
- **Key Features:**
|
||||
- Gas-less toll verification via STARKs
|
||||
- Kenya-compliant recursive compression (<5KB)
|
||||
- Trust-scaled discounts via QVL
|
||||
- Batch verification for router performance
|
||||
- **Implementation:**
|
||||
- ✅ Zig Verifier PoC complete (`features/access-toll/`)
|
||||
- ✅ Core data structures (TollClearanceProof, Nullifier, LazyBatch)
|
||||
- ✅ Replay prevention (NonceCache)
|
||||
- ✅ Immediate & lazy verification modes
|
||||
- ✅ 6 unit tests passing
|
||||
- **Urgency:** CRITICAL - Blocks L1/L2 integration
|
||||
- **Next:** Winterfell STARK integration, Hamiltonian coupling
|
||||
- **Blocked By:** None - ready for extension
|
||||
|
||||
---
|
||||
|
||||
## PROTOCOL FREEZE POLICY
|
||||
|
||||
**Effective immediately:**
|
||||
- ❌ NO changes to protocol mechanics on stable RFCs
|
||||
- ❌ NO new economic primitives without RFC-0315 foundation
|
||||
- ✅ Parameter tuning via field testing only
|
||||
- ✅ Implementation bugs can be fixed
|
||||
- ✅ RFC-0315 completion has priority
|
||||
|
||||
**Reason:** L1 stability required before L2/L4 development
|
||||
|
||||
---
|
||||
|
||||
**Signed:** Janus + Markus
|
||||
**Date:** 2026-02-04
|
||||
**Epoch:** Pre-Implementation Lock
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
# RFC-0315 Access Toll Protocol - Zig Verifier PoC
|
||||
|
||||
**Status:** IMPLEMENTATION v0.1.0
|
||||
**Target:** Zig 0.13+
|
||||
**License:** EUPL-1.2
|
||||
|
||||
## Overview
|
||||
|
||||
This is a Proof-of-Concept implementation of the **Privacy-Preserving Access Toll** verifier from RFC-0315. It demonstrates:
|
||||
|
||||
- **ZK-STARK Toll Clearance Proofs** (structure only - production uses winterfell/starky)
|
||||
- **Lazy Batch Verification** for Kenya compliance
|
||||
- **Nullifier-based Replay Prevention**
|
||||
- **Trust-Scaled Toll Bands**
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
access-toll/
|
||||
├── toll_verifier.zig # Main implementation
|
||||
├── build.zig # Build configuration
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## Core Components
|
||||
|
||||
### TollClearanceProof
|
||||
ZK-STARK #10 proof structure containing:
|
||||
- `stark_proof`: FRI-based STARK (placeholder in PoC)
|
||||
- `compressed`: Recursive compression for Kenya mode (<5KB)
|
||||
- `commitment_hash`: Opaque toll commitment (blake3)
|
||||
- `nullifier`: Anti-replay nonce
|
||||
- `toll_band`: Acceptable price range
|
||||
|
||||
### TollVerifier
|
||||
Main verification engine:
|
||||
- **Immediate mode**: Full STARK verification (high-resource routers)
|
||||
- **Lazy mode**: Optimistic acceptance with batch verification (Kenya mode)
|
||||
- **Replay prevention**: Nullifier cache with GC
|
||||
|
||||
### LazyBatch
|
||||
Resource-constrained verification queue:
|
||||
- Accumulates proofs for batch processing
|
||||
- Time and size-based flush triggers
|
||||
- Recursive STARK aggregation (placeholder in PoC)
|
||||
|
||||
## Build & Run
|
||||
|
||||
```bash
|
||||
# Build library
|
||||
zig build
|
||||
|
||||
# Run demo
|
||||
zig build run
|
||||
|
||||
# Run tests
|
||||
zig build test
|
||||
```
|
||||
|
||||
## Demo Output
|
||||
|
||||
```
|
||||
=== RFC-0315 Toll Verifier PoC ===
|
||||
|
||||
[1] Verifier initialized
|
||||
[2] Commitment computed: a1b2c3d4...
|
||||
[3] Immediate verification: valid
|
||||
|
||||
[4] Kenya Mode (lazy batching):
|
||||
Toll 1: valid (queued)
|
||||
Toll 2: valid (queued)
|
||||
...
|
||||
[5] Batch processed
|
||||
|
||||
[Stats] Verified: 11, Rejected: 0
|
||||
```
|
||||
|
||||
## Integration Points
|
||||
|
||||
### RFC-0130 (ZK-STARK)
|
||||
Replace placeholder `verifyStarkImmediate()` with actual winterfell/starky verification:
|
||||
|
||||
```zig
|
||||
// Production integration
|
||||
const winterfell = @import("winterfell");
|
||||
|
||||
fn verifyStarkImmediate(proof: StarkProof) !bool {
|
||||
return winterfell.verify(
|
||||
proof.data,
|
||||
TollClearanceAir{}, // Constraint system
|
||||
proof.public_inputs,
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### RFC-0205 (Passport)
|
||||
Nullifiers are derived from Passport soul keys:
|
||||
|
||||
```zig
|
||||
const nullifier = Nullifier.fromCommitment(
|
||||
commitment,
|
||||
passport.soul_key,
|
||||
);
|
||||
```
|
||||
|
||||
### RFC-0648 (Hamiltonian)
|
||||
Toll bands are adjusted by PID controller output:
|
||||
|
||||
```zig
|
||||
const adjusted_band = hamiltonian.scaleTollBand(
|
||||
base_band,
|
||||
velocity_error,
|
||||
);
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Run all tests:
|
||||
```bash
|
||||
zig build test
|
||||
```
|
||||
|
||||
Tests cover:
|
||||
- Toll band range checking
|
||||
- Commitment determinism
|
||||
- Replay prevention
|
||||
- Immediate vs lazy verification
|
||||
- Batch queue mechanics
|
||||
|
||||
## Kenya Compliance
|
||||
|
||||
The implementation supports "Lean Tolls" for low-resource environments:
|
||||
|
||||
1. **Compressed proofs**: <5KB recursive STARKs
|
||||
2. **Lazy verification**: Accept first, verify in batch
|
||||
3. **Memory efficient**: Bounded nullifier cache with GC
|
||||
4. **Bandwidth optimized**: Batch multiple tolls in one verification
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [ ] Integrate winterfell for real STARK verification
|
||||
- [ ] Add recursive proof compression
|
||||
- [ ] Implement QVL trust-scaled discounts
|
||||
- [ ] Hamiltonian velocity coupling
|
||||
- [ ] Wire Frame L0 transport integration
|
||||
|
||||
## References
|
||||
|
||||
- RFC-0315: Privacy-Preserving Access Tolls
|
||||
- RFC-0130: ZK-STARK Primitive Layer
|
||||
- RFC-0205: ChapterPassport Protocol
|
||||
- RFC-0648: Hamiltonian Economic Dynamics
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
// Main library
|
||||
const lib = b.addLibrary(.{
|
||||
.linkage = .static,
|
||||
.name = "access-toll",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("toll_verifier.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
b.installArtifact(lib);
|
||||
|
||||
// Executable for demo
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "toll-verifier-demo",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("toll_verifier.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
|
||||
// Run command
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
const run_step = b.step("run", "Run the demo");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
// Tests
|
||||
const lib_unit_tests = b.addTest(.{
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("toll_verifier.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
}),
|
||||
});
|
||||
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
|
||||
const test_step = b.step("test", "Run unit tests");
|
||||
test_step.dependOn(&run_lib_unit_tests.step);
|
||||
}
|
||||
|
|
@ -0,0 +1,578 @@
|
|||
// RFC-0315: Privacy-Preserving Access Tolls - Zig Verifier PoC
|
||||
// Status: IMPLEMENTATION v0.1.0
|
||||
// License: EUPL-1.2
|
||||
|
||||
const std = @import("std");
|
||||
const crypto = std.crypto;
|
||||
const hash = crypto.hash;
|
||||
|
||||
/// STARK Proof structure (placeholder - real impl uses winterfell/starky)
|
||||
pub const StarkProof = struct {
|
||||
// FRI layers, constraint evaluations, etc.
|
||||
// Simplified for PoC - in production this is ~2-5KB recursive proof
|
||||
data: []const u8,
|
||||
|
||||
pub fn deinit(self: *StarkProof, allocator: std.mem.Allocator) void {
|
||||
allocator.free(self.data);
|
||||
}
|
||||
};
|
||||
|
||||
/// Compressed proof for Kenya compliance (<5KB)
|
||||
pub const CompressedProof = struct {
|
||||
recursive_root: [32]u8,
|
||||
compressed_data: []const u8,
|
||||
|
||||
pub fn deinit(self: *CompressedProof, allocator: std.mem.Allocator) void {
|
||||
allocator.free(self.compressed_data);
|
||||
}
|
||||
};
|
||||
|
||||
/// Toll band defines acceptable price range (range proof)
|
||||
pub const TollBand = struct {
|
||||
min: u64,
|
||||
max: u64,
|
||||
target: u64,
|
||||
|
||||
pub fn contains(self: TollBand, amount: u64) bool {
|
||||
return amount >= self.min and amount <= self.max;
|
||||
}
|
||||
};
|
||||
|
||||
/// Commitment to toll payment (opaque, privacy-preserving)
|
||||
pub const TollCommitment = struct {
|
||||
hash: [32]u8, // blake3(resource_id || amount || nonce)
|
||||
|
||||
pub fn compute(
|
||||
_allocator: std.mem.Allocator, // Reserved for future use
|
||||
resource_id: []const u8,
|
||||
amount: u64,
|
||||
nonce: [16]u8,
|
||||
) ![32]u8 {
|
||||
_ = _allocator; // Explicitly ignore for now
|
||||
|
||||
var hasher = hash.Blake3.init(.{});
|
||||
|
||||
// Hash components
|
||||
hasher.update(resource_id);
|
||||
|
||||
var amount_bytes: [8]u8 = undefined;
|
||||
std.mem.writeInt(u64, &amount_bytes, amount, .little);
|
||||
hasher.update(&amount_bytes);
|
||||
|
||||
hasher.update(&nonce);
|
||||
|
||||
var result: [32]u8 = undefined;
|
||||
hasher.final(&result);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Anti-replay nullifier
|
||||
pub const Nullifier = struct {
|
||||
value: [32]u8,
|
||||
|
||||
pub fn fromCommitment(commitment: [32]u8, secret_key: [32]u8) [32]u8 {
|
||||
var hasher = hash.Blake3.init(.{});
|
||||
hasher.update(&commitment);
|
||||
hasher.update(&secret_key);
|
||||
var result: [32]u8 = undefined;
|
||||
hasher.final(&result);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Toll clearance proof - ZK-STARK #10 from RFC-0130
|
||||
pub const TollClearanceProof = struct {
|
||||
stark_proof: StarkProof,
|
||||
compressed: ?CompressedProof, // Kenya mode
|
||||
commitment_hash: [32]u8,
|
||||
nullifier: [32]u8, // Anti-replay
|
||||
toll_band: TollBand,
|
||||
|
||||
pub fn deinit(self: *TollClearanceProof, allocator: std.mem.Allocator) void {
|
||||
self.stark_proof.deinit(allocator);
|
||||
if (self.compressed) |*comp| {
|
||||
comp.deinit(allocator);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Verification result
|
||||
pub const VerificationResult = enum {
|
||||
valid,
|
||||
invalid_commitment,
|
||||
invalid_stark,
|
||||
replay_detected,
|
||||
band_violation,
|
||||
};
|
||||
|
||||
/// Pending toll for lazy verification (Kenya mode)
|
||||
pub const PendingToll = struct {
|
||||
proof: TollClearanceProof,
|
||||
received_at: i64, // timestamp
|
||||
|
||||
pub fn deinit(self: *PendingToll, allocator: std.mem.Allocator) void {
|
||||
self.proof.deinit(allocator);
|
||||
}
|
||||
};
|
||||
|
||||
/// Lazy batch queue for resource-constrained routers
|
||||
pub const LazyBatch = struct {
|
||||
pending: std.ArrayList(PendingToll),
|
||||
gpa: std.mem.Allocator,
|
||||
deadline: i64,
|
||||
max_size: usize,
|
||||
|
||||
const BATCH_SIZE_DEFAULT = 100;
|
||||
const BATCH_WINDOW_MS = 5000; // 5 seconds
|
||||
|
||||
pub fn init(gpa: std.mem.Allocator, max_size: ?usize) LazyBatch {
|
||||
return .{
|
||||
.pending = .empty,
|
||||
.gpa = gpa,
|
||||
.deadline = std.time.milliTimestamp() + BATCH_WINDOW_MS,
|
||||
.max_size = max_size orelse BATCH_SIZE_DEFAULT,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *LazyBatch) void {
|
||||
for (self.pending.items) |*item| {
|
||||
item.deinit(self.gpa);
|
||||
}
|
||||
self.pending.deinit(self.gpa);
|
||||
}
|
||||
|
||||
/// Add proof to batch (optimistic acceptance)
|
||||
pub fn enqueue(self: *LazyBatch, proof: TollClearanceProof) !void {
|
||||
if (self.pending.items.len >= self.max_size) {
|
||||
return error.BatchFull;
|
||||
}
|
||||
|
||||
const pending_item = PendingToll{
|
||||
.proof = proof,
|
||||
.received_at = std.time.milliTimestamp(),
|
||||
};
|
||||
|
||||
try self.pending.append(self.gpa, pending_item);
|
||||
}
|
||||
|
||||
/// Check if batch should flush
|
||||
pub fn shouldFlush(self: *LazyBatch) bool {
|
||||
const now = std.time.milliTimestamp();
|
||||
return now >= self.deadline or self.pending.items.len >= self.max_size;
|
||||
}
|
||||
|
||||
/// Get pending proofs for batch verification
|
||||
pub fn flush(self: *LazyBatch) []PendingToll {
|
||||
const result = self.pending.toOwnedSlice(self.gpa) catch return &[_]PendingToll{};
|
||||
self.deadline = std.time.milliTimestamp() + BATCH_WINDOW_MS;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Nullifier cache for replay prevention
|
||||
pub const NonceCache = struct {
|
||||
spent: std.AutoHashMap([32]u8, i64),
|
||||
max_age_ms: i64,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, max_age_ms: ?i64) NonceCache {
|
||||
return .{
|
||||
.spent = std.AutoHashMap([32]u8, i64).init(allocator),
|
||||
.max_age_ms = max_age_ms orelse (24 * 60 * 60 * 1000), // 24h default
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *NonceCache) void {
|
||||
self.spent.deinit();
|
||||
}
|
||||
|
||||
pub fn contains(self: *NonceCache, nullifier: [32]u8) bool {
|
||||
return self.spent.contains(nullifier);
|
||||
}
|
||||
|
||||
pub fn markSpent(self: *NonceCache, nullifier: [32]u8) !void {
|
||||
const now = std.time.milliTimestamp();
|
||||
try self.spent.put(nullifier, now);
|
||||
}
|
||||
|
||||
/// Clean old entries (call periodically)
|
||||
pub fn gc(self: *NonceCache) void {
|
||||
const now = std.time.milliTimestamp();
|
||||
var iter = self.spent.iterator();
|
||||
while (iter.next()) |entry| {
|
||||
if (now - entry.value_ptr.* > self.max_age_ms) {
|
||||
_ = self.spent.remove(entry.key_ptr.*);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Router context for verification decisions
|
||||
pub const RouterContext = struct {
|
||||
is_kenya_mode: bool,
|
||||
resource_constrained: bool,
|
||||
current_load: f32, // 0.0 - 1.0
|
||||
|
||||
pub fn shouldLazyVerify(self: RouterContext) bool {
|
||||
return self.is_kenya_mode or self.resource_constrained or self.current_load > 0.8;
|
||||
}
|
||||
};
|
||||
|
||||
/// Main Toll Verifier - RFC-0315 Section 7.2
|
||||
pub const TollVerifier = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
nonce_cache: NonceCache,
|
||||
batch_queue: LazyBatch,
|
||||
verified_count: u64,
|
||||
rejected_count: u64,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) TollVerifier {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.nonce_cache = NonceCache.init(allocator, null),
|
||||
.batch_queue = LazyBatch.init(allocator, null),
|
||||
.verified_count = 0,
|
||||
.rejected_count = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *TollVerifier) void {
|
||||
self.nonce_cache.deinit();
|
||||
self.batch_queue.deinit();
|
||||
}
|
||||
|
||||
/// Verify a toll clearance proof
|
||||
/// Returns true if valid (or optimistically accepted for lazy mode)
|
||||
pub fn verifyToll(
|
||||
self: *TollVerifier,
|
||||
proof: TollClearanceProof,
|
||||
context: RouterContext,
|
||||
) !VerificationResult {
|
||||
// 1. Check nullifier not spent (anti-replay)
|
||||
if (self.nonce_cache.contains(proof.nullifier)) {
|
||||
self.rejected_count += 1;
|
||||
return .replay_detected;
|
||||
}
|
||||
|
||||
// 2. Verify commitment format (basic sanity check)
|
||||
if (!self.verifyCommitmentFormat(proof.commitment_hash)) {
|
||||
self.rejected_count += 1;
|
||||
return .invalid_commitment;
|
||||
}
|
||||
|
||||
// 3. Route based on resource context
|
||||
if (context.shouldLazyVerify()) {
|
||||
// Lazy verification - accept now, verify in batch later
|
||||
try self.batch_queue.enqueue(proof);
|
||||
|
||||
// Mark nullifier as pending (will be finalized on batch verify)
|
||||
try self.nonce_cache.markSpent(proof.nullifier);
|
||||
self.verified_count += 1;
|
||||
return .valid;
|
||||
} else {
|
||||
// Immediate verification
|
||||
const result = try self.verifyStarkImmediate(proof);
|
||||
if (result == .valid) {
|
||||
try self.nonce_cache.markSpent(proof.nullifier);
|
||||
self.verified_count += 1;
|
||||
} else {
|
||||
self.rejected_count += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify commitment hash format (not cryptographic verification)
|
||||
fn verifyCommitmentFormat(_: *TollVerifier, commitment: [32]u8) bool {
|
||||
// Non-zero check
|
||||
var all_zero = true;
|
||||
for (commitment) |b| {
|
||||
if (b != 0) {
|
||||
all_zero = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return !all_zero;
|
||||
}
|
||||
|
||||
/// Immediate STARK verification (production: calls winterfell/starky)
|
||||
fn verifyStarkImmediate(_: *TollVerifier, proof: TollClearanceProof) !VerificationResult {
|
||||
// PoC: Simulate verification
|
||||
// In production: verify FRI layers, constraint satisfaction, etc.
|
||||
|
||||
// Check if proof data exists
|
||||
if (proof.stark_proof.data.len == 0) {
|
||||
return .invalid_stark;
|
||||
}
|
||||
|
||||
// Check compressed proof if Kenya mode
|
||||
if (proof.compressed) |comp| {
|
||||
if (comp.compressed_data.len == 0) {
|
||||
return .invalid_stark;
|
||||
}
|
||||
}
|
||||
|
||||
return .valid;
|
||||
}
|
||||
|
||||
/// Process batch queue if ready
|
||||
pub fn processBatch(self: *TollVerifier) !void {
|
||||
if (!self.batch_queue.shouldFlush()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pending = self.batch_queue.flush();
|
||||
defer self.allocator.free(pending);
|
||||
|
||||
// In production: generate recursive STARK proving all pending
|
||||
// For PoC: just verify individually
|
||||
for (pending) |*item| {
|
||||
_ = try self.verifyStarkImmediate(item.proof);
|
||||
// In production: collect failures for rollback
|
||||
}
|
||||
}
|
||||
|
||||
/// Get verification statistics
|
||||
pub fn getStats(self: *TollVerifier) struct { verified: u64, rejected: u64 } {
|
||||
return .{
|
||||
.verified = self.verified_count,
|
||||
.rejected = self.rejected_count,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// TESTS
|
||||
// ============================================================================
|
||||
|
||||
test "TollBand contains" {
|
||||
const band = TollBand{
|
||||
.min = 100,
|
||||
.max = 500,
|
||||
.target = 250,
|
||||
};
|
||||
|
||||
try std.testing.expect(band.contains(100));
|
||||
try std.testing.expect(band.contains(250));
|
||||
try std.testing.expect(band.contains(500));
|
||||
try std.testing.expect(!band.contains(50));
|
||||
try std.testing.expect(!band.contains(600));
|
||||
}
|
||||
|
||||
test "TollCommitment computation" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
const resource_id = "test-resource-123";
|
||||
const amount: u64 = 250;
|
||||
const nonce = [_]u8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
||||
|
||||
const commitment = try TollCommitment.compute(allocator, resource_id, amount, nonce);
|
||||
|
||||
// Verify deterministic
|
||||
const commitment2 = try TollCommitment.compute(allocator, resource_id, amount, nonce);
|
||||
try std.testing.expectEqual(commitment, commitment2);
|
||||
|
||||
// Different inputs = different outputs
|
||||
const commitment3 = try TollCommitment.compute(allocator, resource_id, amount + 1, nonce);
|
||||
var all_same = true;
|
||||
for (commitment, commitment3) |a, b| {
|
||||
if (a != b) {
|
||||
all_same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
try std.testing.expect(!all_same);
|
||||
}
|
||||
|
||||
test "NonceCache replay prevention" {
|
||||
const allocator = std.testing.allocator;
|
||||
var cache = NonceCache.init(allocator, null);
|
||||
defer cache.deinit();
|
||||
|
||||
const nullifier = [_]u8{1} ** 32;
|
||||
|
||||
try std.testing.expect(!cache.contains(nullifier));
|
||||
try cache.markSpent(nullifier);
|
||||
try std.testing.expect(cache.contains(nullifier));
|
||||
}
|
||||
|
||||
test "TollVerifier immediate verify" {
|
||||
const allocator = std.testing.allocator;
|
||||
var verifier = TollVerifier.init(allocator);
|
||||
defer verifier.deinit();
|
||||
|
||||
// Create a valid proof
|
||||
const proof = TollClearanceProof{
|
||||
.stark_proof = .{ .data = "valid-proof-data" },
|
||||
.compressed = null,
|
||||
.commitment_hash = [_]u8{1} ** 32,
|
||||
.nullifier = [_]u8{2} ** 32,
|
||||
.toll_band = .{ .min = 100, .max = 500, .target = 250 },
|
||||
};
|
||||
|
||||
const context = RouterContext{
|
||||
.is_kenya_mode = false,
|
||||
.resource_constrained = false,
|
||||
.current_load = 0.5,
|
||||
};
|
||||
|
||||
const result = try verifier.verifyToll(proof, context);
|
||||
try std.testing.expectEqual(result, .valid);
|
||||
|
||||
// Verify replay detection
|
||||
const result2 = try verifier.verifyToll(proof, context);
|
||||
try std.testing.expectEqual(result2, .replay_detected);
|
||||
}
|
||||
|
||||
test "TollVerifier lazy batch mode" {
|
||||
const allocator = std.testing.allocator;
|
||||
var verifier = TollVerifier.init(allocator);
|
||||
defer verifier.deinit();
|
||||
|
||||
const context = RouterContext{
|
||||
.is_kenya_mode = true, // Enable lazy mode
|
||||
.resource_constrained = false,
|
||||
.current_load = 0.5,
|
||||
};
|
||||
|
||||
// Enqueue multiple proofs
|
||||
var i: usize = 0;
|
||||
while (i < 5) : (i += 1) {
|
||||
const proof = TollClearanceProof{
|
||||
.stark_proof = .{ .data = try allocator.dupe(u8, "proof-data") },
|
||||
.compressed = null,
|
||||
.commitment_hash = [_]u8{@intCast(i + 1)} ** 32,
|
||||
.nullifier = [_]u8{@intCast(i + 10)} ** 32,
|
||||
.toll_band = .{ .min = 100, .max = 500, .target = 250 },
|
||||
};
|
||||
|
||||
const result = try verifier.verifyToll(proof, context);
|
||||
try std.testing.expectEqual(result, .valid);
|
||||
}
|
||||
|
||||
// Check batch queue has items
|
||||
try std.testing.expect(verifier.batch_queue.pending.items.len > 0);
|
||||
|
||||
// Process batch
|
||||
try verifier.processBatch();
|
||||
|
||||
// Check stats
|
||||
const stats = verifier.getStats();
|
||||
try std.testing.expectEqual(stats.verified, 5);
|
||||
}
|
||||
|
||||
test "Nullifier generation" {
|
||||
const commitment = [_]u8{1, 2, 3} ++ [_]u8{0} ** 29;
|
||||
const secret_key = [_]u8{4, 5, 6} ++ [_]u8{0} ** 29;
|
||||
|
||||
const nullifier = Nullifier.fromCommitment(commitment, secret_key);
|
||||
const nullifier2 = Nullifier.fromCommitment(commitment, secret_key);
|
||||
|
||||
// Deterministic
|
||||
try std.testing.expectEqual(nullifier, nullifier2);
|
||||
|
||||
// Different inputs = different outputs
|
||||
const different_key = [_]u8{7, 8, 9} ++ [_]u8{0} ** 29;
|
||||
const nullifier3 = Nullifier.fromCommitment(commitment, different_key);
|
||||
var all_same = true;
|
||||
for (nullifier, nullifier3) |a, b| {
|
||||
if (a != b) {
|
||||
all_same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
try std.testing.expect(!all_same);
|
||||
}
|
||||
|
||||
// Demo/example usage
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
std.debug.print("=== RFC-0315 Toll Verifier PoC ===\n\n", .{});
|
||||
|
||||
// Initialize verifier
|
||||
var verifier = TollVerifier.init(allocator);
|
||||
defer verifier.deinit();
|
||||
|
||||
std.debug.print("[1] Verifier initialized\n", .{});
|
||||
|
||||
// Create a toll commitment
|
||||
const resource_id = "premium-feed-access";
|
||||
const amount: u64 = 250;
|
||||
const nonce = [_]u8{0xAB} ** 16;
|
||||
|
||||
const commitment = try TollCommitment.compute(allocator, resource_id, amount, nonce);
|
||||
std.debug.print("[2] Commitment computed: ", .{});
|
||||
for (commitment) |b| {
|
||||
std.debug.print("{x:0>2}", .{b});
|
||||
}
|
||||
std.debug.print("\n", .{});
|
||||
|
||||
// Create proof (in production: generated via ZK-STARK)
|
||||
const proof = TollClearanceProof{
|
||||
.stark_proof = .{ .data = "stark-proof-placeholder" },
|
||||
.compressed = null,
|
||||
.commitment_hash = commitment,
|
||||
.nullifier = Nullifier.fromCommitment(commitment, [_]u8{0xCD} ** 32),
|
||||
.toll_band = .{ .min = 100, .max = 500, .target = 250 },
|
||||
};
|
||||
|
||||
// Verify in normal mode
|
||||
const normal_context = RouterContext{
|
||||
.is_kenya_mode = false,
|
||||
.resource_constrained = false,
|
||||
.current_load = 0.5,
|
||||
};
|
||||
|
||||
const result = try verifier.verifyToll(proof, normal_context);
|
||||
std.debug.print("[3] Immediate verification: {s}\n", .{@tagName(result)});
|
||||
|
||||
// Demonstrate Kenya mode (lazy batching)
|
||||
std.debug.print("\n[4] Kenya Mode (lazy batching):\n", .{});
|
||||
|
||||
const kenya_context = RouterContext{
|
||||
.is_kenya_mode = true,
|
||||
.resource_constrained = true,
|
||||
.current_load = 0.9,
|
||||
};
|
||||
|
||||
// Simulate 10 tolls
|
||||
var i: usize = 0;
|
||||
while (i < 10) : (i += 1) {
|
||||
const k_commitment = try TollCommitment.compute(
|
||||
allocator,
|
||||
resource_id,
|
||||
amount + @as(u64, @intCast(i)), // Varying amounts
|
||||
[_]u8{@intCast(i)} ** 16,
|
||||
);
|
||||
|
||||
const k_proof = TollClearanceProof{
|
||||
.stark_proof = .{ .data = try allocator.dupe(u8, "kenya-proof") },
|
||||
.compressed = .{
|
||||
.recursive_root = [_]u8{0xFF} ** 32,
|
||||
.compressed_data = try allocator.dupe(u8, "compressed"),
|
||||
},
|
||||
.commitment_hash = k_commitment,
|
||||
.nullifier = Nullifier.fromCommitment(k_commitment, [_]u8{@intCast(i)} ** 32),
|
||||
.toll_band = .{ .min = 100, .max = 600, .target = 300 },
|
||||
};
|
||||
|
||||
const k_result = try verifier.verifyToll(k_proof, kenya_context);
|
||||
std.debug.print(" Toll {d}: {s} (queued)\n", .{ i + 1, @tagName(k_result) });
|
||||
}
|
||||
|
||||
// Process batch
|
||||
try verifier.processBatch();
|
||||
std.debug.print("[5] Batch processed\n", .{});
|
||||
|
||||
// Final stats
|
||||
const stats = verifier.getStats();
|
||||
std.debug.print("\n[Stats] Verified: {d}, Rejected: {d}\n", .{
|
||||
stats.verified,
|
||||
stats.rejected
|
||||
});
|
||||
|
||||
std.debug.print("\n=== RFC-0315 PoC Complete ===\n", .{});
|
||||
}
|
||||
Loading…
Reference in New Issue