From 6d9379179b0ec6086ac53a1e57050eaf4b17e1c6 Mon Sep 17 00:00:00 2001 From: dmitrii-f-t27 Date: Sat, 17 Oct 2026 11:05:31 -0300 Subject: [PATCH] spec(port): port specs/port/trios/crates/trios-server/src/tools/trios-crypto.t27 (Closes #5641) Port gHashTag/trios:crates/trios-server/src/tools/trios-crypto.rs (at 8b229e9489ee, 99 lines) as specs/port/trios/crates/ trios-server/src/tools/trios-crypto.t27 - Roadmap Stage 6, goal #4548, target t27c gen-rust. The crypto mining MCP tool's decisions are ported as pure functions and tested (6 tests, 6 pass, 0 vacuous, 38 runtime asserts, 100% function coverage): the one-tool dispatch table (only crypto_mine_sha256 routes; anything else answers None), the four validation gates with their exact lengths and error strings (data at most 76 required; target exactly 32 required; start_nonce required; nonce required and a valid u64), the composed first-violation-answers rule, the 80-byte header layout - the data bytes in the first slots with shorter data leaving zeros behind, the nonce in the LAST 8 bytes in big-endian at offset 72 - and the four-field mining outcome (nonce, hex hash, computed count, found flag) with the mining-failed error prefix. The SHA-256 FFI and the hex encoding are the callers' plumbing. Verified: parse OK, spec-status IMPLEMENTED, typecheck 0 errors, coverage 19/19, test-report 6/6 pass, 0 BLOCKED, gen 169 lines with 0 'not yet implemented', boundary exactly this one file, dupe_scan: all 17 introduced names unique across 2040 specs (checked before the commit), bare body scan adds no duplicate group (752/221 unchanged). Dmitrii Fedorov ([@dmitrii-f-t27](https://github.com/dmitrii-f-t27)) --- .../trios-server/src/tools/trios-crypto.t27 | 226 ++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 specs/port/trios/crates/trios-server/src/tools/trios-crypto.t27 diff --git a/specs/port/trios/crates/trios-server/src/tools/trios-crypto.t27 b/specs/port/trios/crates/trios-server/src/tools/trios-crypto.t27 new file mode 100644 index 0000000000..fe7f0cab9b --- /dev/null +++ b/specs/port/trios/crates/trios-server/src/tools/trios-crypto.t27 @@ -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"); +}