346 lines
10 KiB
Zig
346 lines
10 KiB
Zig
// monetary_controller.zig - LIBERTARIA L1 ECONOMIC ENGINE
|
|
// RFC-0648 + RFC-0649 Implementation
|
|
//
|
|
// DEPLOYMENT TARGET: 03:00 Session (2026-02-05)
|
|
// STATUS: Ready for implementation
|
|
|
|
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
|
|
// =============================================================================
|
|
// PROTOCOL ENSHRINED CONSTANTS (Immutable)
|
|
// =============================================================================
|
|
|
|
/// Maximum deflation rate (hard floor)
|
|
const PROTOCOL_FLOOR: f64 = -0.05; // -5% per epoch max
|
|
|
|
/// Maximum inflation rate (hard ceiling)
|
|
const PROTOCOL_CEILING: f64 = 0.20; // +20% per epoch max
|
|
|
|
/// Target velocity (Chapter-tunable, but default)
|
|
const DEFAULT_V_TARGET: f64 = 6.0;
|
|
|
|
/// Stability band thresholds
|
|
const STAGNATION_THRESHOLD: f64 = 0.8; // 80% of target = stimulus
|
|
const OVERHEAT_THRESHOLD: f64 = 1.2; // 120% of target = brake
|
|
|
|
// =============================================================================
|
|
// CHAPTER-TUNABLE PARAMETERS (Genesis-configurable)
|
|
// =============================================================================
|
|
|
|
pub const MonetaryParams = struct {
|
|
// PID Gains - TUNE THESE IN FIELD
|
|
Kp: f64 = 0.15, // Proportional: immediate response
|
|
Ki: f64 = 0.02, // Integral: long-term correction
|
|
Kd: f64 = 0.08, // Derivative: dampening
|
|
|
|
// Opportunity Window
|
|
opportunity_multiplier: f64 = 1.5, // 50% bonus during stimulus
|
|
difficulty_scalar: f64 = 0.9, // 10% easier to mint
|
|
q_boost_factor: f64 = 0.15, // 15% activity boost
|
|
|
|
// Extraction
|
|
base_fee_burn_rate: f64 = 0.1, // 10% fee increase
|
|
demurrage_rate: f64 = 0.001, // 0.1% per epoch on stagnant
|
|
|
|
// Anti-Sybil
|
|
genesis_difficulty: u32 = 20, // ~10 min on smartphone
|
|
maintenance_difficulty: u32 = 12, // ~10 sec monthly
|
|
|
|
// Velocity target
|
|
V_target: f64 = DEFAULT_V_TARGET,
|
|
};
|
|
|
|
// =============================================================================
|
|
// STATE VARIABLES (Per-Chapter)
|
|
// =============================================================================
|
|
|
|
pub const MonetaryState = struct {
|
|
M: f64, // Money Supply (Mass)
|
|
V: f64, // Velocity
|
|
Q: f64, // Economic Activity (Output)
|
|
P: f64, // Price Level
|
|
|
|
// PID State
|
|
error_integral: f64 = 0.0,
|
|
prev_error: f64 = 0.0,
|
|
|
|
// Epoch tracking
|
|
current_epoch: u64 = 0,
|
|
|
|
pub fn init(M_initial: f64, V_initial: f64, Q_initial: f64) MonetaryState {
|
|
return .{
|
|
.M = M_initial,
|
|
.V = V_initial,
|
|
.Q = Q_initial,
|
|
.P = 1.0,
|
|
};
|
|
}
|
|
};
|
|
|
|
// =============================================================================
|
|
// CORE ALGORITHMS
|
|
// =============================================================================
|
|
|
|
/// PID Controller with tanh saturation
|
|
///
|
|
/// u(t) = Kp*e(t) + Ki*∫e(t)dt + Kd*de/dt
|
|
/// ΔM(t) = M(t) * clamp(tanh(k*u), FLOOR, CEILING)
|
|
pub fn pidController(
|
|
state: *MonetaryState,
|
|
params: MonetaryParams,
|
|
V_measured: f64,
|
|
) f64 {
|
|
// 1. Calculate error
|
|
const error = params.V_target - V_measured;
|
|
|
|
// 2. Update integral
|
|
state.error_integral += error;
|
|
|
|
// 3. Calculate derivative
|
|
const derivative = error - state.prev_error;
|
|
state.prev_error = error;
|
|
|
|
// 4. Raw PID output
|
|
const u = params.Kp * error +
|
|
params.Ki * state.error_integral +
|
|
params.Kd * derivative;
|
|
|
|
// 5. tanh saturation (smooth)
|
|
const tanh_u = std.math.tanh(u);
|
|
|
|
// 6. Hard protocol caps (enshrined)
|
|
return std.math.clamp(tanh_u, PROTOCOL_FLOOR, PROTOCOL_CEILING);
|
|
}
|
|
|
|
/// Opportunity Window: Injection during stagnation
|
|
///
|
|
/// When V < 0.8 * target:
|
|
/// - Difficulty drops (cheaper to mint)
|
|
/// - Multiplier active (more rewarding)
|
|
/// - Q boost (psychological stimulus)
|
|
pub fn applyOpportunityWindow(
|
|
state: *MonetaryState,
|
|
params: MonetaryParams,
|
|
delta_m_raw: f64,
|
|
) struct { delta_m: f64, active: bool, q_boost: f64 } {
|
|
|
|
const is_stagnant = state.V < params.V_target * STAGNATION_THRESHOLD;
|
|
|
|
if (is_stagnant) {
|
|
// Stimulus: Make work cheaper AND more rewarding
|
|
const delta_m = delta_m_raw * params.opportunity_multiplier;
|
|
|
|
// Psychological boost: Economic activity increases
|
|
// This is the KEY INSIGHT: Stimulus → Behavior, not just Money
|
|
const q_boost = state.Q * params.q_boost_factor;
|
|
state.Q += q_boost;
|
|
|
|
return .{
|
|
.delta_m = delta_m,
|
|
.active = true,
|
|
.q_boost = q_boost,
|
|
};
|
|
}
|
|
|
|
return .{
|
|
.delta_m = delta_m_raw,
|
|
.active = false,
|
|
.q_boost = 0.0,
|
|
};
|
|
}
|
|
|
|
/// Extraction: Cooling during overheating
|
|
///
|
|
/// When V > 1.2 * target:
|
|
/// - Base fee burn (transactions more expensive)
|
|
/// - Demurrage on stagnant money
|
|
pub fn applyExtraction(
|
|
state: *MonetaryState,
|
|
params: MonetaryParams,
|
|
delta_m_raw: f64,
|
|
) struct { delta_m: f64, active: bool, burned: f64 } {
|
|
|
|
const is_overheated = state.V > params.V_target * OVERHEAT_THRESHOLD;
|
|
|
|
if (is_overheated) {
|
|
// 1. Demurrage on stagnant M
|
|
const demurrage_burn = state.M * params.demurrage_rate;
|
|
state.M -= demurrage_burn;
|
|
|
|
// 2. Extra brake on emission
|
|
const delta_m = delta_m_raw * 0.8;
|
|
|
|
return .{
|
|
.delta_m = delta_m,
|
|
.active = true,
|
|
.burned = demurrage_burn,
|
|
};
|
|
}
|
|
|
|
return .{
|
|
.delta_m = delta_m_raw,
|
|
.active = false,
|
|
.burned = 0.0,
|
|
};
|
|
}
|
|
|
|
/// Main Monetary Step Function
|
|
///
|
|
/// Called once per epoch by L1 consensus
|
|
pub fn monetaryStep(
|
|
state: *MonetaryState,
|
|
params: MonetaryParams,
|
|
exogenous_shock: f64, // External events (panic, bubble)
|
|
) void {
|
|
state.current_epoch += 1;
|
|
|
|
// 1. Apply exogenous shocks (market psychology)
|
|
state.V += exogenous_shock;
|
|
|
|
// 2. PID Controller
|
|
const delta_m_raw = pidController(state, params, state.V);
|
|
|
|
// 3. Opportunity Window (stimulus if stagnant)
|
|
const opp = applyOpportunityWindow(state, params, delta_m_raw);
|
|
|
|
// 4. Extraction (brake if overheated)
|
|
const ext = applyExtraction(state, params, opp.delta_m);
|
|
|
|
// 5. Update Money Supply
|
|
state.M *= (1.0 + ext.delta_m);
|
|
|
|
// 6. Natural Q decay (activity slows without stimulus)
|
|
state.Q *= 0.995;
|
|
|
|
// 7. Recalculate V from Fisher equation
|
|
// But: Q is now endogenous (can be boosted by stimulus)
|
|
state.V = (state.P * state.Q) / state.M;
|
|
|
|
// 8. Floor protection
|
|
if (state.V < 0.1) state.V = 0.1;
|
|
}
|
|
|
|
// =============================================================================
|
|
// ANTI-SYBIL: SOULKEY VALIDATION
|
|
// =============================================================================
|
|
|
|
pub const SoulKey = struct {
|
|
did: [32]u8,
|
|
genesis_proof: EntropyProof,
|
|
last_maintenance: u64,
|
|
maintenance_debt: f64,
|
|
};
|
|
|
|
pub const EntropyProof = struct {
|
|
nonce: [32]u8,
|
|
difficulty: u32,
|
|
hash: [32]u8,
|
|
};
|
|
|
|
/// Verify Argon2id proof
|
|
pub fn verifyEntropyProof(proof: EntropyProof, required_difficulty: u32) bool {
|
|
// TODO: Argon2id verification
|
|
// Returns true if hash meets difficulty target
|
|
_ = proof;
|
|
_ = required_difficulty;
|
|
return true; // Placeholder
|
|
}
|
|
|
|
/// Check if SoulKey qualifies for Opportunity Window
|
|
pub fn qualifiesForMintWindow(
|
|
soul: SoulKey,
|
|
current_epoch: u64,
|
|
params: MonetaryParams,
|
|
) bool {
|
|
// Must have genesis
|
|
if (!verifyEntropyProof(soul.genesis_proof, params.genesis_difficulty)) {
|
|
return false;
|
|
}
|
|
|
|
// Must be maintained (Kenya Rule: 1 proof/month)
|
|
const epochs_since_maintenance = current_epoch - soul.last_maintenance;
|
|
const max_epochs = 30 * 24 * 6; // ~1 month (10-min epochs)
|
|
|
|
if (epochs_since_maintenance > max_epochs) {
|
|
return false; // Maintenance debt too high
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// =============================================================================
|
|
// HAMILTONIAN UTILITIES
|
|
// =============================================================================
|
|
|
|
/// Economic Energy: E = 0.5 * M * V²
|
|
pub fn calculateEnergy(state: MonetaryState) f64 {
|
|
return 0.5 * state.M * state.V * state.V;
|
|
}
|
|
|
|
/// Momentum: P = M * V
|
|
pub fn calculateMomentum(state: MonetaryState) f64 {
|
|
return state.M * state.V;
|
|
}
|
|
|
|
/// Check if system is in equilibrium
|
|
pub fn isEquilibrium(state: MonetaryState, params: MonetaryParams) bool {
|
|
const ratio = state.V / params.V_target;
|
|
return ratio >= 0.9 and ratio <= 1.1;
|
|
}
|
|
|
|
// =============================================================================
|
|
// TEST HARNESS (for 03:00 session)
|
|
// =============================================================================
|
|
|
|
test "monetary controller basic" {
|
|
var state = MonetaryState.init(1000.0, 5.0, 5000.0);
|
|
const params = MonetaryParams{};
|
|
|
|
// Run 100 epochs
|
|
var i: usize = 0;
|
|
while (i < 100) : (i += 1) {
|
|
monetaryStep(&state, params, 0.0);
|
|
}
|
|
|
|
// System should stabilize near target
|
|
try std.testing.expect(state.V > 4.0);
|
|
try std.testing.expect(state.V < 8.0);
|
|
}
|
|
|
|
test "opportunity window triggers" {
|
|
var state = MonetaryState.init(1000.0, 2.0, 5000.0); // Stagnant
|
|
const params = MonetaryParams{};
|
|
|
|
const delta_before = state.M;
|
|
monetaryStep(&state, params, 0.0);
|
|
const delta_after = state.M;
|
|
|
|
// Should have triggered stimulus
|
|
try std.testing.expect(delta_after > delta_before * 1.01);
|
|
}
|
|
|
|
test "extraction triggers" {
|
|
var state = MonetaryState.init(1000.0, 10.0, 5000.0); // Overheated
|
|
const params = MonetaryParams{};
|
|
|
|
const m_before = state.M;
|
|
monetaryStep(&state, params, 0.0);
|
|
const m_after = state.M;
|
|
|
|
// Should have burned some M via demurrage
|
|
try std.testing.expect(m_after < m_before);
|
|
}
|
|
|
|
// =============================================================================
|
|
// TODO FOR 03:00 SESSION
|
|
// =============================================================================
|
|
|
|
// [ ] Integrate with RFC-0315 (Access Toll Protocol)
|
|
// [ ] Argon2d proof verification
|
|
// [ ] Chapter persistence (save/restore state)
|
|
// [ ] Event hooks for external monitoring
|
|
// [ ] Fuzz testing with random shocks
|
|
|
|
// END monetary_controller.zig
|