Skip to content
Merged
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
226 changes: 226 additions & 0 deletions specs/port/trios/crates/trios-server/src/tools/trios-crypto.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
// MCP tools for the trios-crypto crate — SHA-256 mining operations.
// Port of gHashTag/trios:crates/trios-server/src/tools/trios-crypto.rs
// (at 8b229e9489ee, 99 lines) — Roadmap Stage 6, goal #4548, target
// t27c gen-rust.
//
// The original exposes one mining tool behind a name dispatcher:
// crypto_mine_sha256 validates its input — the data bytes (at most 76,
// required, must be an array), the target hash (exactly 32 bytes,
// required), the start and max nonce (required u64s), and the nonce
// (required, a valid u64) — then builds the 80-byte header: the data
// bytes in the first 76 slots (shorter data leaves the rest zero),
// the nonce in the LAST 8 bytes in big-endian. The FFI mining call
// answers the nonce, the hex hash, the computed count and the found
// flag, or the mining-failed error. dispatch routes only the
// crypto_mine_sha256 name; anything else answers None.
//
// The SHA-256 FFI and the hex encoding are the callers' plumbing; the
// decisions — the one-tool dispatch table, the four validation gates
// with their exact lengths, the 80-byte header layout with the
// big-endian nonce at the tail, and the four-field success shape —
// are ported here as pure functions and tested (issue #5641, "Port
// the decision, not the plumbing").

module port::trios_server_tools_crypto;

// ---------------------------------------------------------------------------
// Original line 9: the 80-byte block header constant.
// ---------------------------------------------------------------------------

pub fn sha256_header_size() -> usize {
return 80;
}

pub fn header_data_capacity() -> usize {
return 76;
}

pub fn nonce_offset() -> usize {
return 72;
}

pub fn nonce_byte_count() -> usize {
return 8;
}

// ---------------------------------------------------------------------------
// Original lines 12-17: `dispatch` — the one-name routing table.
// ---------------------------------------------------------------------------

pub fn dispatched_tool_count() -> usize {
return 1;
}

pub fn dispatch(name: str) -> bool {
return name == "crypto_mine_sha256";
}

pub fn dispatch_tool_name() -> str {
return "crypto_mine_sha256";
}

// ---------------------------------------------------------------------------
// Original lines 22-53: `crypto_mine_sha256`'s validation gates — each
// input rule answers its own error; all four must pass before the
// header is built.
// ---------------------------------------------------------------------------

pub fn data_gate(data_present: bool, data_len: usize) -> str {
if (!data_present) {
return "data is required and must be an array";
}
if (data_len > 76) {
return "data must be exactly 76 bytes";
}
return "";
}

pub fn target_gate(target_present: bool, target_len: usize) -> str {
if (!target_present) {
return "target is required and must be an array";
}
if (target_len != 32) {
return "target must be exactly 32 bytes";
}
return "";
}

pub fn start_nonce_gate(start_nonce_present: bool) -> str {
if (!start_nonce_present) {
return "start_nonce is required";
}
return "";
}

pub fn nonce_gate(nonce_present: bool, nonce_valid: bool) -> str {
if (!nonce_present) {
return "nonce is required";
}
if (!nonce_valid) {
return "nonce must be a valid u64";
}
return "";
}

// The composed gate: all four inputs pass, or the first violation
// answers. The port takes the violation answers in gate order.
pub fn crypto_mine_sha256(data_error: str, target_error: str, start_nonce_error: str, nonce_error: str) -> str {
if (data_error != "") {
return data_error;
}
if (target_error != "") {
return target_error;
}
if (start_nonce_error != "") {
return start_nonce_error;
}
if (nonce_error != "") {
return nonce_error;
}
return "ok";
}

// ---------------------------------------------------------------------------
// Original lines 56-70: the header build — the data bytes fill the
// first slots (shorter data leaves zeros behind), the nonce rides the
// LAST 8 bytes in big-endian.
// ---------------------------------------------------------------------------

pub fn header_nonce_is_big_endian() -> bool {
return true;
}

pub fn header_nonce_slot(nonce_byte_index: usize) -> usize {
return nonce_offset() + nonce_byte_index;
}

pub fn header_data_slot(data_index: usize) -> usize {
return data_index;
}

pub fn shorter_data_leaves_zeros(data_len: usize) -> bool {
return data_len < header_data_capacity();
}

// ---------------------------------------------------------------------------
// Original lines 81-98: the mining outcome — the four-field success
// shape (nonce, hex hash, computed count, found flag) or the
// mining-failed error.
// ---------------------------------------------------------------------------

pub fn mining_success_field_count() -> usize {
return 4;
}

pub fn mining_success_field(i: usize) -> str {
if (i == 0) { return "nonce"; }
if (i == 1) { return "hash"; }
if (i == 2) { return "hashes_computed"; }
if (i == 3) { return "found"; }
return "";
}

pub fn mining_failed_prefix() -> str {
return "mining failed";
}

// ---------------------------------------------------------------------------
// Tests: every decision above asserted against values read from the
// original.
// ---------------------------------------------------------------------------

test header_layout {
assert(sha256_header_size() == 80);
assert(header_data_capacity() == 76);
assert(nonce_offset() == 72);
assert(nonce_byte_count() == 8);
assert(header_nonce_is_big_endian());
}

test header_slot_arithmetic {
assert(header_data_slot(0) == 0);
assert(header_data_slot(75) == 75);
assert(header_nonce_slot(0) == 72);
assert(header_nonce_slot(7) == 79);
assert(shorter_data_leaves_zeros(10));
assert(!shorter_data_leaves_zeros(76));
}

test dispatch_routes_one_tool {
assert(dispatched_tool_count() == 1);
assert(dispatch("crypto_mine_sha256"));
assert(!dispatch("crypto_unknown"));
assert(dispatch_tool_name() == "crypto_mine_sha256");
}

test validation_gates {
assert(data_gate(false, 0) == "data is required and must be an array");
assert(data_gate(true, 77) == "data must be exactly 76 bytes");
assert(data_gate(true, 76) == "");
assert(target_gate(false, 0) == "target is required and must be an array");
assert(target_gate(true, 31) == "target must be exactly 32 bytes");
assert(target_gate(true, 33) == "target must be exactly 32 bytes");
assert(target_gate(true, 32) == "");
assert(start_nonce_gate(false) == "start_nonce is required");
assert(start_nonce_gate(true) == "");
assert(nonce_gate(false, true) == "nonce is required");
assert(nonce_gate(true, false) == "nonce must be a valid u64");
assert(nonce_gate(true, true) == "");
}

test gate_composition_first_violation_answers {
assert(crypto_mine_sha256("data is required and must be an array", "", "", "") == "data is required and must be an array");
assert(crypto_mine_sha256("", "target must be exactly 32 bytes", "", "") == "target must be exactly 32 bytes");
assert(crypto_mine_sha256("", "", "start_nonce is required", "") == "start_nonce is required");
assert(crypto_mine_sha256("", "", "", "nonce must be a valid u64") == "nonce must be a valid u64");
assert(crypto_mine_sha256("", "", "", "") == "ok");
}

test mining_outcome_shape {
assert(mining_success_field_count() == 4);
assert(mining_success_field(0) == "nonce");
assert(mining_success_field(1) == "hash");
assert(mining_success_field(2) == "hashes_computed");
assert(mining_success_field(3) == "found");
assert(mining_failed_prefix() == "mining failed");
}
Loading