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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions gen/rust/tri_settle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub const SNR_FLOOR: u32 = 3;
pub const SNR_CEIL: u32 = 40;

pub fn round_add(round_total: u32, epoch_bytes: u32) -> u32 {
let sum: u32 = (round_total + epoch_bytes);
let sum: u32 = (round_total).wrapping_add(epoch_bytes);
if (sum < round_total) {
return 0xFFFFFFFF;
} else {
Expand Down Expand Up @@ -65,4 +65,3 @@ pub fn snr_to_quality(snr_db: u32) -> u32 {
}
}
}

147 changes: 147 additions & 0 deletions gen/zig/tri_settle.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Generated from t27 spec: TriSettle (module name)
// DO NOT EDIT — generated by t27c
// phi^2 + 1/phi^2 = 3 | TRINITY

const std = @import("std");

// use types: no references in this module
const PPM: u32 = 1000000;
const SNR_FLOOR: u32 = 3;
const SNR_CEIL: u32 = 40;
fn round_add(round_total: u32, epoch_bytes: u32) u32 {
const sum: u32 = round_total +% epoch_bytes;
if (sum < round_total) {
return 0xFFFFFFFF;
} else {
return sum;
}
}
fn reward_share_ppm(node_bytes: u32, round_bytes: u32) u32 {
if (round_bytes == 0) {
return 0;
} else {
const scaled: u64 = @as(u64, @intCast(node_bytes)) * @as(u64, @intCast(PPM));
return @as(u32, @truncate(scaled / @as(u64, @intCast(round_bytes))));
}
}
fn reward_units(node_bytes: u32, round_bytes: u32, pool: u32) u64 {
if (round_bytes == 0) {
return 0;
} else {
const num: u64 = @as(u64, @intCast(pool)) * @as(u64, @intCast(node_bytes));
return num / @as(u64, @intCast(round_bytes));
}
}
test "share_proportional" {
const half = reward_share_ppm(100, 200);
const quarter = reward_share_ppm(50, 200);
if (!(half == 500000)) @panic("half");
if (!(quarter == 250000)) @panic("quarter");
if (!(half == (quarter + quarter))) @panic("2x bytes = 2x share");
}
test "share_full" {
if (!(reward_share_ppm(200, 200) == PPM)) @panic("full share");
}
test "share_zero_guard" {
if (!(reward_share_ppm(5, 0) == 0)) @panic("empty round pays nobody");
if (!(reward_share_ppm(0, 200) == 0)) @panic("no work no share");
}
test "units_no_free_mint" {
if (!(reward_units(0, 200, 1000) == 0)) @panic("no free mint");
}
test "units_proportional" {
if (!(reward_units(100, 200, 1000) == 500)) @panic("half of pool");
}
test "units_conservation" {
const a = reward_units(100, 300, 1000);
const b = reward_units(200, 300, 1000);
if (!(a == 333)) @panic("node a");
if (!(b == 666)) @panic("node b");
if (!((a + b) <= @as(u64, @intCast(1000)))) @panic("sum <= pool");
}
test "round_monotonic" {
const r0 = round_add(0, 19974);
const r1 = round_add(r0, 40000);
if (!(r1 > r0)) @panic("monotonic");
if (!(r0 == 19974)) @panic("epoch1");
if (!(r1 == 59974)) @panic("epoch1+2");
}
test "round_saturates" {
if (!(round_add(0xFFFFFF00, 0x0000FFFF) == 0xFFFFFFFF)) @panic("saturates");
}
fn weighted_contrib(node_bytes: u32, quality: u32) u64 {
return @as(u64, @intCast(node_bytes)) * @as(u64, @intCast(quality));
}
fn round_add_weighted(wtotal: u64, node_bytes: u32, quality: u32) u64 {
return wtotal + weighted_contrib(node_bytes, quality);
}
fn reward_weighted(node_bytes: u32, quality: u32, weighted_total: u64, pool: u32) u64 {
const hi: u32 = @as(u32, @truncate(weighted_total >> 32));
const lo: u32 = @as(u32, @truncate(weighted_total));
if ((hi == 0) and (lo == 0)) {
return 0;
} else {
const contrib: u64 = @as(u64, @intCast(node_bytes)) * @as(u64, @intCast(quality));
const ppm: u64 = (contrib * 1000000) / weighted_total;
return (@as(u64, @intCast(pool)) * ppm) / 1000000;
}
}
test "weighted_quality_rewards_coverage" {
const wt = round_add_weighted(round_add_weighted(0, 1000, 47), 1000, 10);
const a = reward_weighted(1000, 47, wt, 1000);
const b = reward_weighted(1000, 10, wt, 1000);
if (!(a > b)) @panic("better link earns more per byte");
if (!(wt == @as(u64, @intCast(57000)))) @panic("weighted total = 47000 + 10000");
}
test "weighted_equal_quality_is_proportional" {
const wt = round_add_weighted(round_add_weighted(0, 100, 5), 200, 5);
const a = reward_weighted(100, 5, wt, 1000);
const b = reward_weighted(200, 5, wt, 1000);
if (!(a == 333)) @panic("third");
if (!(b == 666)) @panic("two thirds");
}
test "weighted_conservation" {
const wt = round_add_weighted(round_add_weighted(0, 1000, 47), 500, 30);
const a = reward_weighted(1000, 47, wt, 10000);
const b = reward_weighted(500, 30, wt, 10000);
if (!((a + b) <= @as(u64, @intCast(10000)))) @panic("sum <= pool");
}
test "weighted_zero_quality" {
const wt = round_add_weighted(0, 1000, 47);
if (!(reward_weighted(1000, 0, wt, 1000) == 0)) @panic("dead link no reward");
}
test "weighted_large_total_not_zero" {
const big = @as(u64, @intCast(4294967296));
const r = reward_weighted(1000000, 1000, big, 1000);
if (!(r > @as(u64, @intCast(0)))) @panic("large total not read as zero");
}
fn snr_to_quality(snr_db: u32) u32 {
if (snr_db <= SNR_FLOOR) {
return 0;
} else if (snr_db >= SNR_CEIL) {
return SNR_CEIL - SNR_FLOOR;
} else {
return snr_db - SNR_FLOOR;
}
}
test "snr_quality_floor" {
if (!(snr_to_quality(1) == 0)) @panic("1 dB unusable");
if (!(snr_to_quality(3) == 0)) @panic("at floor unusable");
}
test "snr_quality_measured" {
if (!(snr_to_quality(34) == 31)) @panic("strong link (.13->.12 -10 dB)");
if (!(snr_to_quality(5) == 2)) @panic("weak link (.13->.12 -50 dB)");
if (!(snr_to_quality(34) > snr_to_quality(5))) @panic("stronger link -> higher quality");
}
test "snr_quality_ceiling" {
if (!(snr_to_quality(100) == 37)) @panic("saturates at CEIL-FLOOR");
if (!(snr_to_quality(40) == 37)) @panic("at ceiling");
}
test "snr_drives_reward" {
const qa = snr_to_quality(34);
const qb = snr_to_quality(5);
const wt = round_add_weighted(round_add_weighted(0, 10000, qa), 10000, qb);
const ra = reward_weighted(10000, qa, wt, 1000);
const rb = reward_weighted(10000, qb, wt, 1000);
if (!(ra > rb)) @panic("better coverage earns more $TRI");
}
2 changes: 1 addition & 1 deletion specs/tri_settle.t27
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module TriSettle {
// Fold one epoch's verified byte count into the running round total.
// Saturating so an overflow can never wrap the round total DOWNWARD.
fn round_add(round_total: u32, epoch_bytes: u32) -> u32 {
let sum: u32 = round_total + epoch_bytes;
let sum: u32 = round_total +% epoch_bytes;
if (sum < round_total) {
return 0xFFFFFFFF; // saturate
} else {
Expand Down
Loading