From cfb151246eb7fa06591c6bd50b8ea3d5c2f0c422 Mon Sep 17 00:00:00 2001 From: echobt Date: Wed, 18 Feb 2026 14:58:39 +0000 Subject: [PATCH 1/3] feat(wasm): add agent storage, logging types, and route definitions - Create agent_storage module with store/retrieve functions for agent code, hashes, and evaluation logs using host storage - Add AgentLogEntry and AgentLogs types to types.rs - Add 1MB package_zip size validation in validate() - Store agent code, hash, and logs during evaluate() - Add GET /agent/:hotkey/code and /agent/:hotkey/logs routes --- wasm/src/agent_storage.rs | 68 +++++++++++++++++++++++++++++++++++++++ wasm/src/lib.rs | 44 +++++++++++++++++++++++-- wasm/src/routes.rs | 10 ++++++ wasm/src/types.rs | 19 +++++++++++ 4 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 wasm/src/agent_storage.rs diff --git a/wasm/src/agent_storage.rs b/wasm/src/agent_storage.rs new file mode 100644 index 00000000..dcaaff4b --- /dev/null +++ b/wasm/src/agent_storage.rs @@ -0,0 +1,68 @@ +use alloc::string::String; +use alloc::vec::Vec; +use platform_challenge_sdk_wasm::host_functions::{host_storage_get, host_storage_set}; + +use crate::types::AgentLogs; + +pub const MAX_AGENT_PACKAGE_SIZE: usize = 1_048_576; +const MAX_LOG_SIZE: usize = 262_144; +pub const MAX_TASK_OUTPUT_PREVIEW: usize = 4_096; + +fn make_key(prefix: &[u8], miner_hotkey: &str, epoch: u64) -> Vec { + let mut key = Vec::from(prefix); + key.extend_from_slice(miner_hotkey.as_bytes()); + key.push(b':'); + key.extend_from_slice(&epoch.to_le_bytes()); + key +} + +pub fn store_agent_code(miner_hotkey: &str, epoch: u64, package_zip: &[u8]) -> bool { + if package_zip.len() > MAX_AGENT_PACKAGE_SIZE { + return false; + } + let key = make_key(b"agent_code:", miner_hotkey, epoch); + host_storage_set(&key, package_zip).is_ok() +} + +pub fn store_agent_hash(miner_hotkey: &str, epoch: u64, agent_hash: &str) -> bool { + let key = make_key(b"agent_hash:", miner_hotkey, epoch); + host_storage_set(&key, agent_hash.as_bytes()).is_ok() +} + +pub fn store_agent_logs(miner_hotkey: &str, epoch: u64, logs: &AgentLogs) -> bool { + let data = match bincode::serialize(logs) { + Ok(d) => d, + Err(_) => return false, + }; + if data.len() > MAX_LOG_SIZE { + return false; + } + let key = make_key(b"agent_logs:", miner_hotkey, epoch); + host_storage_set(&key, &data).is_ok() +} + +pub fn get_agent_code(miner_hotkey: &str, epoch: u64) -> Option> { + let key = make_key(b"agent_code:", miner_hotkey, epoch); + let data = host_storage_get(&key).ok()?; + if data.is_empty() { + return None; + } + Some(data) +} + +pub fn get_agent_logs(miner_hotkey: &str, epoch: u64) -> Option { + let key = make_key(b"agent_logs:", miner_hotkey, epoch); + let data = host_storage_get(&key).ok()?; + if data.is_empty() { + return None; + } + bincode::deserialize(&data).ok() +} + +pub fn truncate_output(output: &str, max_len: usize) -> String { + if output.len() <= max_len { + return String::from(output); + } + let truncated = &output[..max_len]; + String::from(truncated) +} diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 12c4309f..37936434 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -2,6 +2,7 @@ extern crate alloc; +mod agent_storage; mod dataset; mod routes; mod scoring; @@ -18,7 +19,8 @@ use platform_challenge_sdk_wasm::{Challenge, EvaluationInput, EvaluationOutput}; use crate::scoring::{calculate_aggregate, format_summary, to_weight}; use crate::types::{ - ChallengeParams, DatasetSelection, LlmJudgeRequest, LlmJudgeResponse, Submission, TaskResult, + AgentLogEntry, AgentLogs, ChallengeParams, DatasetSelection, LlmJudgeRequest, + LlmJudgeResponse, Submission, TaskResult, }; const MAX_SUBMISSION_SIZE: u64 = 64 * 1024 * 1024; @@ -167,6 +169,10 @@ impl Challenge for TermChallengeWasm { } } + let miner_hotkey = submission.miner_hotkey; + let epoch = submission.epoch; + let agent_hash = submission.agent_hash; + let package_zip = submission.package_zip; let mut results: Vec = submission.task_results; if let Some(ref url) = params.llm_judge_url { @@ -188,7 +194,37 @@ impl Challenge for TermChallengeWasm { let score = (weight * 10_000.0) as i64; let message = format_summary(&aggregate); - set_last_submission_epoch(&submission.miner_hotkey, submission.epoch); + let _ = agent_storage::store_agent_code(&miner_hotkey, epoch, &package_zip); + let _ = agent_storage::store_agent_hash(&miner_hotkey, epoch, &agent_hash); + + let mut entries = Vec::with_capacity(results.len()); + let mut total_size_bytes: u64 = 0; + for r in &results { + let output_preview = agent_storage::truncate_output( + &r.agent_output, + agent_storage::MAX_TASK_OUTPUT_PREVIEW, + ); + total_size_bytes = total_size_bytes.saturating_add(output_preview.len() as u64); + entries.push(AgentLogEntry { + task_id: r.task_id.clone(), + passed: r.passed, + score: r.score, + execution_time_ms: r.execution_time_ms, + output_preview, + error: r.error.clone(), + }); + } + + let logs = AgentLogs { + miner_hotkey: miner_hotkey.clone(), + epoch, + agent_hash: agent_hash.clone(), + entries, + total_size_bytes, + }; + let _ = agent_storage::store_agent_logs(&miner_hotkey, epoch, &logs); + + set_last_submission_epoch(&miner_hotkey, epoch); EvaluationOutput::success(score, &message) } @@ -217,6 +253,10 @@ impl Challenge for TermChallengeWasm { return false; } + if submission.package_zip.len() > 1_048_576 { + return false; + } + if submission.basilica_instance.is_empty() || submission.executor_url.is_empty() || submission.executor_token.is_empty() diff --git a/wasm/src/routes.rs b/wasm/src/routes.rs index 33b2fa24..d5d884b4 100644 --- a/wasm/src/routes.rs +++ b/wasm/src/routes.rs @@ -48,5 +48,15 @@ pub fn get_route_definitions() -> Vec { path: String::from("/stats"), description: String::from("Challenge statistics: total submissions, active miners"), }, + RouteDefinition { + method: String::from("GET"), + path: String::from("/agent/:hotkey/code"), + description: String::from("Returns stored agent code package for a miner"), + }, + RouteDefinition { + method: String::from("GET"), + path: String::from("/agent/:hotkey/logs"), + description: String::from("Returns evaluation logs for a miner"), + }, ] } diff --git a/wasm/src/types.rs b/wasm/src/types.rs index e1547326..3c4e0520 100644 --- a/wasm/src/types.rs +++ b/wasm/src/types.rs @@ -95,6 +95,25 @@ pub struct DatasetSelection { pub dataset_hash: String, } +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AgentLogEntry { + pub task_id: String, + pub passed: bool, + pub score: f64, + pub execution_time_ms: u64, + pub output_preview: String, + pub error: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AgentLogs { + pub miner_hotkey: String, + pub epoch: u64, + pub agent_hash: String, + pub entries: Vec, + pub total_size_bytes: u64, +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RouteDefinition { pub method: String, From 7741f7b94b00d36de4e0accd3b07947d27a14894 Mon Sep 17 00:00:00 2001 From: echobt Date: Wed, 18 Feb 2026 15:02:47 +0000 Subject: [PATCH 2/3] feat(cli): add ratatui TUI binary for monitoring Add a new native binary crate 'cli/' with a ratatui-based terminal UI for monitoring the term-challenge. Features include: - Four tabs: Leaderboard, Evaluation, Submission, Network - JSON-RPC 2.0 client for fetching data from chain endpoint - Auto-refresh every 10 seconds with manual refresh via 'r' key - Tab/Shift+Tab navigation, Up/Down scrolling, 'q' to quit - Status bar showing epoch, phase, block height, validators - Graceful error handling with TUI error display - CLI args via clap: --rpc-url, --hotkey, --challenge-id, --tab The workspace uses default-members to prevent the CLI from being built when targeting wasm32-unknown-unknown. --- AGENTS.md | 95 ++- Cargo.lock | 2155 ++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 3 +- README.md | 151 +++- cli/Cargo.toml | 24 + cli/src/app.rs | 206 +++++ cli/src/main.rs | 105 +++ cli/src/rpc.rs | 217 +++++ cli/src/ui.rs | 384 +++++++++ wasm/src/lib.rs | 14 +- 10 files changed, 3287 insertions(+), 67 deletions(-) create mode 100644 cli/Cargo.toml create mode 100644 cli/src/app.rs create mode 100644 cli/src/main.rs create mode 100644 cli/src/rpc.rs create mode 100644 cli/src/ui.rs diff --git a/AGENTS.md b/AGENTS.md index 49def6b4..618bd079 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,22 +2,30 @@ ## Project Purpose -Term Challenge is a WASM evaluation module for AI agents on the Bittensor network via platform-v2. Miners submit Python agent packages (as zip files) that solve SWE-bench tasks. The WASM module runs inside platform-v2 validators to validate submissions, evaluate task results, and compute scores. +Term Challenge is a WASM evaluation module for AI agents on the Bittensor network via platform-v2. Miners submit Python agent packages (as zip files) that solve SWE-bench tasks. The WASM module runs inside platform-v2 validators to validate submissions, evaluate task results, and compute scores. A companion native CLI (`term-cli`) provides a TUI for monitoring leaderboards, evaluation progress, and network health. ## Architecture Overview ``` term-challenge/ -├── Cargo.toml # workspace with members = ["wasm"] +├── Cargo.toml # workspace with members = ["wasm", "cli"] ├── wasm/ │ ├── Cargo.toml # cdylib, depends on platform-challenge-sdk-wasm │ └── src/ -│ ├── lib.rs # Challenge impl + register_challenge! -│ ├── types.rs # Submission, TaskDefinition, DecayParams, etc. -│ ├── scoring.rs # Aggregate scoring, decay, weight calculation -│ ├── tasks.rs # Active dataset storage (SWE-bench tasks) -│ ├── dataset.rs # Dataset selection and consensus logic -│ └── routes.rs # Challenge route definitions for RPC +│ ├── lib.rs # Challenge impl + register_challenge! +│ ├── types.rs # Submission, TaskDefinition, AgentLogs, etc. +│ ├── scoring.rs # Aggregate scoring, decay, weight calculation +│ ├── tasks.rs # Active dataset storage (SWE-bench tasks) +│ ├── dataset.rs # Dataset selection and consensus logic +│ ├── routes.rs # Challenge route definitions for RPC +│ └── agent_storage.rs # Agent code & log storage functions +├── cli/ +│ ├── Cargo.toml # native binary, ratatui TUI +│ └── src/ +│ ├── main.rs # Entry point, event loop +│ ├── app.rs # Application state +│ ├── ui.rs # Ratatui UI rendering +│ └── rpc.rs # JSON-RPC 2.0 client ├── AGENTS.md ├── README.md ├── LICENSE @@ -32,7 +40,9 @@ term-challenge/ 3. **Validators** run WASM `validate()` — checks signature, epoch rate limit, Basilica metadata 4. **50% validator approval** → submission stored in blockchain 5. **Validators** run WASM `evaluate()` — scores task results, applies LLM judge -6. **Consensus** aggregates scores, applies decay, submits weights to Bittensor +6. **Agent code & logs** stored on-chain for auditability (code ≤ 1MB, logs ≤ 256KB) +7. **Log consensus** — validators propose logs, >50% hash agreement required +8. **Consensus** aggregates scores, applies decay, submits weights to Bittensor ### Key Concepts @@ -42,9 +52,63 @@ term-challenge/ - **Epoch rate limiting**: 1 submission per 3 epochs per miner - **Top agent decay**: 72h grace period, then 50% daily decay to 0 weight +## Agent Code Storage + +Agent submissions are stored on-chain for auditability and retrieval. The `agent_storage` module manages three storage categories: + +| Storage Key Format | Content | Max Size | +|---|---|---| +| `agent_code::` | Raw zip package bytes | 1 MB (1,048,576 bytes) | +| `agent_hash::` | Hash of the agent package | — | +| `agent_logs::` | Serialized `AgentLogs` struct | 256 KB (262,144 bytes) | + +- **Package size limit**: Submissions with `package_zip` exceeding 1 MB are rejected at the storage layer. +- **Log size limit**: Serialized logs exceeding 256 KB are rejected. Individual task output previews are truncated to 4 KB (4,096 bytes) before storage. +- **Key format**: Keys are constructed as `:` using little-endian encoding for the epoch. + +## CLI + +The `term-cli` crate is a **native binary** (NOT `no_std`) that provides a terminal user interface for monitoring the term-challenge network. + +### Design + +- **Framework**: Built with [ratatui](https://ratatui.rs/) for TUI rendering +- **Transport**: Connects to validators via JSON-RPC 2.0 over HTTP +- **Target**: Standard `x86_64` / `aarch64` native targets (not WASM) + +### Available Tabs + +| Tab | Description | +|---|---| +| Leaderboard | Current scores, ranks, and miner hotkeys | +| Evaluation | Live evaluation progress for pending submissions | +| Submission | Recent submission history and status | +| Network | Validator count, epoch info, system health | + +### Keyboard Shortcuts + +| Key | Action | +|---|---| +| `Tab` / `Shift+Tab` | Switch between tabs | +| `↑` / `↓` / `j` / `k` | Navigate rows | +| `r` | Refresh data | +| `q` / `Esc` | Quit | + +### RPC Methods Used + +- `epoch_current` — Current epoch number +- `challenge_call /leaderboard` — Leaderboard data +- `evaluation_getProgress` — Evaluation progress for a submission +- `agent_getLogs` — Validated evaluation logs +- `system_health` — Node health status +- `validator_count` — Number of active validators + ## Build Commands ```bash +# Build CLI (native) +cargo build --release -p term-cli + # Build WASM module cargo build --release --target wasm32-unknown-unknown -p term-challenge-wasm @@ -70,17 +134,20 @@ Git hooks live in `.githooks/` and are activated with `git config core.hooksPath 5. **Host functions are the ONLY external interface.** No direct HTTP, no filesystem, no std::net. 6. **Do NOT add `#[allow(dead_code)]` broadly.** Fix unused code or remove it. +> **Note:** The `cli/` crate is exempt from the `no_std` rule (rule 1) and the host-functions-only rule (rule 5) since it is a native binary that runs outside the WASM sandbox. Rules 2, 3, 4, and 6 still apply to CLI code. + ## DO / DO NOT ### DO -- Use `alloc::string::String`, `alloc::vec::Vec`, `alloc::collections::BTreeMap` -- Use `serde` with `default-features = false, features = ["derive", "alloc"]` -- Use `bincode` with `default-features = false` for serialization -- Use host functions for all I/O: `host_storage_get/set`, `host_http_post`, `host_consensus_get_epoch` +- Use `alloc::string::String`, `alloc::vec::Vec`, `alloc::collections::BTreeMap` (WASM code) +- Use `serde` with `default-features = false, features = ["derive", "alloc"]` (WASM code) +- Use `bincode` with `default-features = false` for serialization (WASM code) +- Use host functions for all I/O: `host_storage_get/set`, `host_http_post`, `host_consensus_get_epoch` (WASM code) - Keep the `register_challenge!` macro ABI contract intact +- Use standard `std` library features in the `cli/` crate (it is a native binary) ### DO NOT -- Do NOT use `std::`, `println!`, `std::collections::HashMap` +- Do NOT use `std::`, `println!`, `std::collections::HashMap` in WASM code - Do NOT add heavy dependencies — the WASM module must stay minimal - Do NOT break the WASM ABI (evaluate, validate, get_name, get_version, get_tasks, configure, alloc) - Do NOT store sensitive data in plain text in blockchain storage diff --git a/Cargo.lock b/Cargo.lock index b22566b5..71bc5197 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,104 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bincode" version = "1.3.3" @@ -12,84 +110,2071 @@ dependencies = [ ] [[package]] -name = "platform-challenge-sdk-wasm" -version = "0.1.0" -source = "git+https://github.com/PlatformNetwork/platform-v2?branch=main#ae9b049a3abbaf9808aca38fdc8a782f813ce734" +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" dependencies = [ - "bincode", + "rustversion", +] + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", "serde", + "wasm-bindgen", + "windows-link", ] [[package]] -name = "proc-macro2" -version = "1.0.106" +name = "clap" +version = "4.5.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "c5caf74d17c3aec5495110c34cc3f78644bfa89af6c8993ed4de2790e49b6499" dependencies = [ - "unicode-ident", + "clap_builder", + "clap_derive", ] [[package]] -name = "quote" -version = "1.0.44" +name = "clap_builder" +version = "4.5.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "370daa45065b80218950227371916a1633217ae42b2715b2287b606dcd618e24" dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", "proc-macro2", + "quote", + "syn", ] [[package]] -name = "serde" -version = "1.0.228" +name = "clap_lex" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "compact_str" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" dependencies = [ - "serde_core", - "serde_derive", + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", ] [[package]] -name = "serde_core" -version = "1.0.228" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "serde_derive", + "bitflags", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", ] [[package]] -name = "serde_derive" -version = "1.0.228" +name = "crossterm_winapi" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ + "ident_case", "proc-macro2", "quote", + "strsim", "syn", ] [[package]] -name = "syn" -version = "2.0.116" +name = "darling_macro" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn", ] [[package]] -name = "term-challenge-wasm" -version = "0.1.0" +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ - "bincode", - "platform-challenge-sdk-wasm", - "serde", + "libc", + "windows-sys 0.61.2", ] [[package]] -name = "unicode-ident" -version = "1.0.24" +name = "find-msvc-tools" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "instability" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "platform-challenge-sdk-wasm" +version = "0.1.0" +source = "git+https://github.com/PlatformNetwork/platform-v2?branch=main#ae9b049a3abbaf9808aca38fdc8a782f813ce734" +dependencies = [ + "bincode", + "serde", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "ratatui" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +dependencies = [ + "bitflags", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "instability", + "itertools", + "lru", + "paste", + "strum", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "term-challenge-wasm" +version = "0.1.0" +dependencies = [ + "bincode", + "platform-challenge-sdk-wasm", + "serde", +] + +[[package]] +name = "term-cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "crossterm", + "ratatui", + "reqwest", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index f8971519..f34f0296 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] resolver = "2" -members = ["wasm"] +members = ["wasm", "cli"] +default-members = ["wasm"] [workspace.package] version = "0.1.0" diff --git a/README.md b/README.md index 4f82c6b6..8c583c30 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,94 @@ Term Challenge is a WASM evaluation module for AI agents on the Bittensor network. It runs inside [platform-v2](https://github.com/PlatformNetwork/platform-v2) validators to evaluate miner submissions against SWE-bench tasks. +--- + +## System Architecture + +```mermaid +flowchart LR + Miner[Miner] -->|Submit Agent ZIP| RPC[Validator RPC] + RPC --> Validators[Validator Network] + Validators --> WASM[term-challenge WASM] + Validators --> Executor[term-executor] + Executor -->|Task Results| Validators + Validators -->|Scores + Weights| BT[Bittensor Chain] + CLI[term-cli TUI] -->|JSON-RPC| RPC + CLI -->|Display| Monitor[Leaderboard / Progress / Logs] +``` + +--- + +## Evaluation Flow + +```mermaid +sequenceDiagram + participant M as Miner + participant V as Validators + participant W as WASM Module + participant E as term-executor + participant BT as Bittensor + + M->>V: Submit agent zip + metadata + V->>W: validate(submission) + W-->>V: approved (>50% consensus) + V->>E: Execute agent on SWE-bench tasks + E-->>V: Task results + scores + V->>W: evaluate(results) + W-->>V: Aggregate score + weight + V->>V: Store agent code & logs + V->>V: Log consensus (>50% agreement) + V->>BT: Submit weights at epoch boundary +``` + +--- + +## CLI Data Flow + +```mermaid +flowchart TB + CLI[term-cli] -->|epoch_current| RPC[Validator RPC] + CLI -->|challenge_call /leaderboard| RPC + CLI -->|evaluation_getProgress| RPC + CLI -->|agent_getLogs| RPC + CLI -->|system_health| RPC + CLI -->|validator_count| RPC + RPC --> State[Chain State] + State --> LB[Leaderboard Data] + State --> Eval[Evaluation Progress] + State --> Logs[Validated Logs] +``` + +--- + +## Agent Log Consensus + +```mermaid +flowchart LR + V1[Validator 1] -->|Log Proposal| P2P[(P2P Network)] + V2[Validator 2] -->|Log Proposal| P2P + V3[Validator 3] -->|Log Proposal| P2P + P2P --> Consensus{Hash Match >50%?} + Consensus -->|Yes| Store[Validated Logs] + Consensus -->|No| Reject[Rejected] +``` + +--- + +## Agent Code Storage + +```mermaid +flowchart TB + Submit[Agent Submission] --> Validate{package_zip ≤ 1MB?} + Validate -->|Yes| Store[Blockchain Storage] + Validate -->|No| Reject[Rejected] + Store --> Code[agent_code:hotkey:epoch] + Store --> Hash[agent_hash:hotkey:epoch] + Store --> Logs[agent_logs:hotkey:epoch ≤ 256KB] +``` + +--- + ## Features - **WASM Module**: Compiles to `wasm32-unknown-unknown`, loaded by platform-v2 validators @@ -20,6 +108,11 @@ Term Challenge is a WASM evaluation module for AI agents on the Bittensor networ - **Top Agent Decay**: 72h grace period, 50% daily decay to 0 weight - **P2P Dataset Consensus**: Validators collectively select 50 evaluation tasks - **Zip Package Submissions**: Agents submitted as zip packages (no compilation step) +- **Agent Code Storage**: Submitted agent packages (≤ 1MB) stored on-chain with hash verification +- **Log Consensus**: Evaluation logs validated across validators with >50% hash agreement +- **CLI (term-cli)**: Native TUI for monitoring leaderboards, evaluation progress, submissions, and network health + +--- ## Building @@ -29,33 +122,71 @@ cargo build --release --target wasm32-unknown-unknown -p term-challenge-wasm # The output .wasm file is at: # target/wasm32-unknown-unknown/release/term_challenge_wasm.wasm + +# Build CLI (native) +cargo build --release -p term-cli ``` +--- + ## Architecture -This repository contains ONLY the WASM evaluation module. All infrastructure (P2P networking, RPC server, blockchain storage, validator coordination) is provided by [platform-v2](https://github.com/PlatformNetwork/platform-v2). +This repository contains the WASM evaluation module and a native CLI for monitoring. All infrastructure (P2P networking, RPC server, blockchain storage, validator coordination) is provided by [platform-v2](https://github.com/PlatformNetwork/platform-v2). ``` term-challenge/ -├── wasm/ # WASM evaluation module +├── wasm/ # WASM evaluation module +│ └── src/ +│ ├── lib.rs # Challenge trait implementation +│ ├── types.rs # Submission, task, and config types +│ ├── scoring.rs # Score aggregation and decay +│ ├── tasks.rs # Active dataset management +│ ├── dataset.rs # Dataset selection consensus +│ ├── routes.rs # RPC route definitions +│ └── agent_storage.rs # Agent code & log storage functions +├── cli/ # Native TUI monitoring tool │ └── src/ -│ ├── lib.rs # Challenge trait implementation -│ ├── types.rs # Submission, task, and config types -│ ├── scoring.rs # Score aggregation and decay -│ ├── tasks.rs # Active dataset management -│ ├── dataset.rs # Dataset selection consensus -│ └── routes.rs # RPC route definitions -├── AGENTS.md # Development guide +│ ├── main.rs # Entry point, event loop +│ ├── app.rs # Application state +│ ├── ui.rs # Ratatui UI rendering +│ └── rpc.rs # JSON-RPC 2.0 client +├── AGENTS.md # Development guide └── README.md ``` +--- + ## How It Works 1. Miners submit zip packages with agent code and SWE-bench task results 2. Platform-v2 validators load this WASM module 3. `validate()` checks signatures, epoch rate limits, and Basilica metadata 4. `evaluate()` scores task results and applies LLM judge scoring -5. Scores are aggregated via P2P consensus and submitted to Bittensor +5. Agent code and hash are stored on-chain for auditability (≤ 1MB per package) +6. Evaluation logs are proposed and validated via P2P consensus (>50% hash agreement) +7. Scores are aggregated via P2P consensus and submitted to Bittensor + +--- + +## CLI Usage + +```bash +# Install via platform CLI +platform download term-challenge + +# Or build from source +cargo build --release -p term-cli + +# Run the TUI +term-cli --rpc-url http://chain.platform.network:9944 + +# With miner hotkey filter +term-cli --hotkey 5GrwvaEF... --tab leaderboard + +# Available tabs: leaderboard, evaluation, submission, network +``` + +--- ## License diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 00000000..16798f40 --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "term-cli" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +description = "Terminal Benchmark Challenge CLI — ratatui TUI for monitoring" + +[[bin]] +name = "term-cli" +path = "src/main.rs" + +[dependencies] +ratatui = "0.29" +crossterm = "0.28" +tokio = { version = "1.40", features = ["full"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +clap = { version = "4.5", features = ["derive"] } +anyhow = "1.0" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +chrono = { version = "0.4", features = ["serde"] } diff --git a/cli/src/app.rs b/cli/src/app.rs new file mode 100644 index 00000000..00c5a449 --- /dev/null +++ b/cli/src/app.rs @@ -0,0 +1,206 @@ +use crate::rpc::RpcClient; +use chrono::{DateTime, Utc}; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Tab { + Leaderboard, + Evaluation, + Submission, + Network, +} + +impl Tab { + pub const ALL: [Tab; 4] = [ + Tab::Leaderboard, + Tab::Evaluation, + Tab::Submission, + Tab::Network, + ]; + + pub fn label(self) -> &'static str { + match self { + Tab::Leaderboard => "Leaderboard", + Tab::Evaluation => "Evaluation", + Tab::Submission => "Submission", + Tab::Network => "Network", + } + } + + pub fn index(self) -> usize { + match self { + Tab::Leaderboard => 0, + Tab::Evaluation => 1, + Tab::Submission => 2, + Tab::Network => 3, + } + } +} + +pub struct LeaderboardRow { + pub rank: u32, + pub miner_hotkey: String, + pub score: f64, + pub pass_rate: f64, + pub submissions: u32, + pub last_submission: String, +} + +pub struct EvalTaskRow { + pub task_id: String, + pub status: String, + pub score: f64, + pub duration_ms: u64, + pub error: Option, +} + +pub struct NetworkStatus { + pub epoch: u64, + pub phase: String, + pub block_height: u64, + pub validators: usize, + pub connected: bool, +} + +impl Default for NetworkStatus { + fn default() -> Self { + Self { + epoch: 0, + phase: "unknown".to_string(), + block_height: 0, + validators: 0, + connected: false, + } + } +} + +pub struct App { + pub tab: Tab, + pub rpc_url: String, + pub hotkey: Option, + pub challenge_id: Option, + pub leaderboard: Vec, + pub evaluation_progress: Vec, + pub network_status: NetworkStatus, + pub scroll_offset: usize, + pub last_refresh: Option>, + pub error_message: Option, + pub should_quit: bool, +} + +impl App { + pub fn new(rpc_url: String, hotkey: Option, challenge_id: Option) -> Self { + Self { + tab: Tab::Leaderboard, + rpc_url, + hotkey, + challenge_id, + leaderboard: Vec::new(), + evaluation_progress: Vec::new(), + network_status: NetworkStatus::default(), + scroll_offset: 0, + last_refresh: None, + error_message: None, + should_quit: false, + } + } + + pub fn set_tab_from_str(&mut self, s: &str) { + self.tab = match s.to_lowercase().as_str() { + "leaderboard" => Tab::Leaderboard, + "evaluation" => Tab::Evaluation, + "submission" => Tab::Submission, + "network" => Tab::Network, + _ => Tab::Leaderboard, + }; + self.scroll_offset = 0; + } + + pub fn next_tab(&mut self) { + let idx = self.tab.index(); + let next = (idx + 1) % Tab::ALL.len(); + self.tab = Tab::ALL[next]; + self.scroll_offset = 0; + } + + pub fn prev_tab(&mut self) { + let idx = self.tab.index(); + let prev = if idx == 0 { + Tab::ALL.len() - 1 + } else { + idx - 1 + }; + self.tab = Tab::ALL[prev]; + self.scroll_offset = 0; + } + + pub fn scroll_up(&mut self) { + self.scroll_offset = self.scroll_offset.saturating_sub(1); + } + + pub fn scroll_down(&mut self) { + self.scroll_offset = self.scroll_offset.saturating_add(1); + } + + pub async fn refresh(&mut self, rpc: &RpcClient) { + self.error_message = None; + + if let Err(e) = self.refresh_network(rpc).await { + self.error_message = Some(format!("Network: {e}")); + self.network_status.connected = false; + return; + } + self.network_status.connected = true; + + if self.challenge_id.is_none() { + match rpc.fetch_challenge_list().await { + Ok(challenges) if challenges.len() == 1 => { + self.challenge_id = Some(challenges[0].id.clone()); + } + Ok(_) => {} + Err(e) => { + self.error_message = Some(format!("Challenges: {e}")); + } + } + } + + if let Some(cid) = &self.challenge_id { + let cid = cid.clone(); + match rpc.fetch_leaderboard(&cid).await { + Ok(rows) => self.leaderboard = rows, + Err(e) => { + self.error_message = Some(format!("Leaderboard: {e}")); + } + } + } + + if let Some(hotkey) = &self.hotkey { + let hotkey = hotkey.clone(); + match rpc.fetch_evaluation_progress(&hotkey).await { + Ok(tasks) => self.evaluation_progress = tasks, + Err(e) => { + tracing::debug!("Evaluation progress: {e}"); + } + } + } + + self.last_refresh = Some(Utc::now()); + } + + async fn refresh_network(&mut self, rpc: &RpcClient) -> anyhow::Result<()> { + let _ = rpc.fetch_system_health().await?; + + let epoch_info = rpc.fetch_epoch_info().await?; + self.network_status.epoch = epoch_info.epoch; + self.network_status.phase = epoch_info.phase; + self.network_status.block_height = epoch_info.block_height; + + match rpc.fetch_validator_count().await { + Ok(count) => self.network_status.validators = count, + Err(e) => { + tracing::warn!("Failed to fetch validator count: {e}"); + } + } + + Ok(()) + } +} diff --git a/cli/src/main.rs b/cli/src/main.rs new file mode 100644 index 00000000..cc419a42 --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,105 @@ +mod app; +mod rpc; +mod ui; + +use std::time::{Duration, Instant}; + +use anyhow::Result; +use clap::Parser; +use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use tracing_subscriber::EnvFilter; + +use crate::app::App; +use crate::rpc::RpcClient; + +#[derive(Parser)] +#[command(name = "term-cli", about = "Terminal Benchmark Challenge Monitor")] +struct Cli { + /// RPC endpoint URL + #[arg(long, default_value = "http://chain.platform.network:9944")] + rpc_url: String, + + /// Your miner hotkey for filtered views + #[arg(long)] + hotkey: Option, + + /// Challenge ID (auto-detected if single challenge) + #[arg(long)] + challenge_id: Option, + + /// Initial tab to display + #[arg(long, default_value = "leaderboard")] + tab: String, +} + +#[tokio::main] +async fn main() -> Result<()> { + let cli = Cli::parse(); + + tracing_subscriber::fmt() + .with_env_filter( + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn")), + ) + .with_writer(std::io::stderr) + .init(); + + let mut terminal = ratatui::try_init()?; + + let result = run(&mut terminal, cli).await; + + ratatui::try_restore()?; + + result +} + +async fn run(terminal: &mut ratatui::DefaultTerminal, cli: Cli) -> Result<()> { + let mut app = App::new(cli.rpc_url.clone(), cli.hotkey, cli.challenge_id); + app.set_tab_from_str(&cli.tab); + + let rpc = RpcClient::new(&cli.rpc_url); + + app.refresh(&rpc).await; + + let tick_rate = Duration::from_secs(10); + let mut last_tick = Instant::now(); + + loop { + terminal.draw(|f| ui::draw(f, &app))?; + + let timeout = tick_rate + .checked_sub(last_tick.elapsed()) + .unwrap_or_default(); + + if event::poll(timeout)? { + if let Event::Key(key) = event::read()? { + if key.kind == KeyEventKind::Press { + match key.code { + KeyCode::Char('q') => { + app.should_quit = true; + } + KeyCode::Tab => app.next_tab(), + KeyCode::BackTab => app.prev_tab(), + KeyCode::Up => app.scroll_up(), + KeyCode::Down => app.scroll_down(), + KeyCode::Char('r') => { + app.refresh(&rpc).await; + last_tick = Instant::now(); + } + _ => {} + } + } + } + } + + if app.should_quit { + break; + } + + if last_tick.elapsed() >= tick_rate { + app.refresh(&rpc).await; + last_tick = Instant::now(); + } + } + + Ok(()) +} diff --git a/cli/src/rpc.rs b/cli/src/rpc.rs new file mode 100644 index 00000000..350cd15c --- /dev/null +++ b/cli/src/rpc.rs @@ -0,0 +1,217 @@ +use std::sync::atomic::{AtomicU64, Ordering}; + +use anyhow::{anyhow, Context}; +use serde::{Deserialize, Serialize}; + +use crate::app::{EvalTaskRow, LeaderboardRow}; + +pub struct RpcClient { + url: String, + client: reqwest::Client, + request_id: AtomicU64, +} + +#[derive(Serialize)] +struct JsonRpcRequest<'a> { + jsonrpc: &'a str, + id: u64, + method: &'a str, + params: serde_json::Value, +} + +#[derive(Deserialize)] +struct JsonRpcResponse { + result: Option, + error: Option, + #[allow(dead_code)] + id: Option, +} + +#[derive(Deserialize)] +struct JsonRpcError { + code: i64, + message: String, +} + +pub struct EpochInfo { + pub epoch: u64, + pub phase: String, + pub block_height: u64, +} + +#[derive(Deserialize)] +struct EpochInfoRaw { + #[serde(default)] + epoch: u64, + #[serde(default)] + phase: String, + #[serde(default)] + block_height: u64, +} + +pub struct ChallengeInfo { + pub id: String, +} + +#[derive(Deserialize)] +struct ChallengeInfoRaw { + #[serde(default)] + id: String, +} + +#[derive(Deserialize)] +struct LeaderboardRowRaw { + #[serde(default)] + rank: u32, + #[serde(default)] + miner_hotkey: String, + #[serde(default)] + score: f64, + #[serde(default)] + pass_rate: f64, + #[serde(default)] + submissions: u32, + #[serde(default)] + last_submission: String, +} + +#[derive(Deserialize)] +struct EvalTaskRowRaw { + #[serde(default)] + task_id: String, + #[serde(default)] + status: String, + #[serde(default)] + score: f64, + #[serde(default)] + duration_ms: u64, + #[serde(default)] + error: Option, +} + +impl RpcClient { + pub fn new(url: &str) -> Self { + Self { + url: url.to_string(), + client: reqwest::Client::new(), + request_id: AtomicU64::new(1), + } + } + + pub async fn call( + &self, + method: &str, + params: serde_json::Value, + ) -> anyhow::Result { + let id = self.request_id.fetch_add(1, Ordering::Relaxed); + let request = JsonRpcRequest { + jsonrpc: "2.0", + id, + method, + params, + }; + + let response = self + .client + .post(&self.url) + .json(&request) + .send() + .await + .context("Failed to send RPC request")?; + + let status = response.status(); + if !status.is_success() { + return Err(anyhow!("RPC HTTP error: {status}")); + } + + let rpc_response: JsonRpcResponse = response + .json() + .await + .context("Failed to parse RPC response")?; + + if let Some(err) = rpc_response.error { + return Err(anyhow!("RPC error {}: {}", err.code, err.message)); + } + + rpc_response + .result + .ok_or_else(|| anyhow!("RPC response missing result")) + } + + pub async fn fetch_leaderboard( + &self, + challenge_id: &str, + ) -> anyhow::Result> { + let params = serde_json::json!({ + "challenge_id": challenge_id, + "path": "/leaderboard" + }); + let result = self.call("challenge_call", params).await?; + let raw: Vec = + serde_json::from_value(result).context("Failed to parse leaderboard data")?; + Ok(raw + .into_iter() + .map(|r| LeaderboardRow { + rank: r.rank, + miner_hotkey: r.miner_hotkey, + score: r.score, + pass_rate: r.pass_rate, + submissions: r.submissions, + last_submission: r.last_submission, + }) + .collect()) + } + + pub async fn fetch_epoch_info(&self) -> anyhow::Result { + let result = self.call("epoch_current", serde_json::json!({})).await?; + let raw: EpochInfoRaw = + serde_json::from_value(result).context("Failed to parse epoch info")?; + Ok(EpochInfo { + epoch: raw.epoch, + phase: raw.phase, + block_height: raw.block_height, + }) + } + + pub async fn fetch_system_health(&self) -> anyhow::Result { + self.call("system_health", serde_json::json!({})).await + } + + pub async fn fetch_validator_count(&self) -> anyhow::Result { + let result = self.call("validator_count", serde_json::json!({})).await?; + let count = result.as_u64().unwrap_or_default() as usize; + Ok(count) + } + + pub async fn fetch_evaluation_progress( + &self, + submission_id: &str, + ) -> anyhow::Result> { + let params = serde_json::json!({ + "submission_id": submission_id + }); + let result = self.call("evaluation_getProgress", params).await?; + let raw: Vec = + serde_json::from_value(result).context("Failed to parse evaluation progress")?; + Ok(raw + .into_iter() + .map(|r| EvalTaskRow { + task_id: r.task_id, + status: r.status, + score: r.score, + duration_ms: r.duration_ms, + error: r.error, + }) + .collect()) + } + + pub async fn fetch_challenge_list(&self) -> anyhow::Result> { + let result = self.call("challenge_list", serde_json::json!({})).await?; + let raw: Vec = + serde_json::from_value(result).context("Failed to parse challenge list")?; + Ok(raw + .into_iter() + .map(|r| ChallengeInfo { id: r.id }) + .collect()) + } +} diff --git a/cli/src/ui.rs b/cli/src/ui.rs new file mode 100644 index 00000000..54fea87c --- /dev/null +++ b/cli/src/ui.rs @@ -0,0 +1,384 @@ +use ratatui::{ + layout::{Constraint, Direction, Layout, Rect}, + style::{Color, Modifier, Style, Stylize}, + text::{Line, Span}, + widgets::{Block, Borders, Cell, Paragraph, Row, Table, Tabs}, + Frame, +}; + +use crate::app::{App, Tab}; + +pub fn draw(frame: &mut Frame, app: &App) { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(3), + Constraint::Min(0), + Constraint::Length(3), + ]) + .split(frame.area()); + + draw_tabs(frame, chunks[0], app); + + match app.tab { + Tab::Leaderboard => draw_leaderboard(frame, chunks[1], app), + Tab::Evaluation => draw_evaluation(frame, chunks[1], app), + Tab::Submission => draw_submission(frame, chunks[1], app), + Tab::Network => draw_network(frame, chunks[1], app), + } + + draw_status_bar(frame, chunks[2], app); +} + +fn draw_tabs(frame: &mut Frame, area: Rect, app: &App) { + let titles: Vec<&str> = Tab::ALL.iter().map(|t| t.label()).collect(); + let tabs = Tabs::new(titles) + .block(Block::default().borders(Borders::ALL).title("Term CLI")) + .select(app.tab.index()) + .style(Style::default().fg(Color::Gray)) + .highlight_style( + Style::default() + .fg(Color::Yellow) + .add_modifier(Modifier::BOLD), + ); + frame.render_widget(tabs, area); +} + +fn draw_leaderboard(frame: &mut Frame, area: Rect, app: &App) { + let header = Row::new(vec![ + Cell::from("Rank"), + Cell::from("Miner"), + Cell::from("Score"), + Cell::from("Pass Rate"), + Cell::from("Submissions"), + Cell::from("Last Submission"), + ]) + .style( + Style::default() + .fg(Color::Yellow) + .add_modifier(Modifier::BOLD), + ); + + let visible_rows = visible_row_count(area); + let rows: Vec = app + .leaderboard + .iter() + .skip(app.scroll_offset) + .take(visible_rows) + .map(|entry| { + let hotkey_display = truncate_hotkey(&entry.miner_hotkey, 8); + Row::new(vec![ + Cell::from(entry.rank.to_string()), + Cell::from(hotkey_display), + Cell::from(format!("{:.4}", entry.score)), + Cell::from(format!("{:.1}%", entry.pass_rate * 100.0)), + Cell::from(entry.submissions.to_string()), + Cell::from(entry.last_submission.clone()), + ]) + }) + .collect(); + + let widths = [ + Constraint::Length(6), + Constraint::Length(14), + Constraint::Length(10), + Constraint::Length(12), + Constraint::Length(12), + Constraint::Min(20), + ]; + + let table = Table::new(rows, widths) + .header(header) + .block(Block::default().borders(Borders::ALL).title("Leaderboard")) + .row_highlight_style(Style::default().add_modifier(Modifier::BOLD)); + + frame.render_widget(table, area); + + if app.leaderboard.is_empty() { + draw_empty_message(frame, area, "No leaderboard data available"); + } +} + +fn draw_evaluation(frame: &mut Frame, area: Rect, app: &App) { + let inner_chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Length(3), Constraint::Min(0)]) + .split(area); + + let total = app.evaluation_progress.len(); + let completed = app + .evaluation_progress + .iter() + .filter(|t| t.status == "completed") + .count(); + let progress_text = if total > 0 { + format!( + "Progress: {completed}/{total} ({:.0}%)", + (completed as f64 / total as f64) * 100.0 + ) + } else { + "No evaluation tasks".to_string() + }; + let progress_bar = Paragraph::new(progress_text).block( + Block::default() + .borders(Borders::ALL) + .title("Overall Progress"), + ); + frame.render_widget(progress_bar, inner_chunks[0]); + + let header = Row::new(vec![ + Cell::from("Task ID"), + Cell::from("Status"), + Cell::from("Score"), + Cell::from("Duration (ms)"), + Cell::from("Error"), + ]) + .style( + Style::default() + .fg(Color::Yellow) + .add_modifier(Modifier::BOLD), + ); + + let visible_rows = visible_row_count(inner_chunks[1]); + let rows: Vec = app + .evaluation_progress + .iter() + .skip(app.scroll_offset) + .take(visible_rows) + .map(|task| { + let status_style = match task.status.as_str() { + "completed" => Style::default().fg(Color::Green), + "failed" => Style::default().fg(Color::Red), + "running" => Style::default().fg(Color::Cyan), + _ => Style::default().fg(Color::Gray), + }; + Row::new(vec![ + Cell::from(task.task_id.clone()), + Cell::from(Span::styled(task.status.clone(), status_style)), + Cell::from(format!("{:.4}", task.score)), + Cell::from(task.duration_ms.to_string()), + Cell::from(task.error.clone().unwrap_or_default()), + ]) + }) + .collect(); + + let widths = [ + Constraint::Length(20), + Constraint::Length(12), + Constraint::Length(10), + Constraint::Length(14), + Constraint::Min(20), + ]; + + let table = Table::new(rows, widths).header(header).block( + Block::default() + .borders(Borders::ALL) + .title("Evaluation Tasks"), + ); + + frame.render_widget(table, inner_chunks[1]); + + if app.evaluation_progress.is_empty() { + draw_empty_message(frame, inner_chunks[1], "No evaluation data available"); + } +} + +fn draw_submission(frame: &mut Frame, area: Rect, app: &App) { + let block = Block::default().borders(Borders::ALL).title("Submissions"); + + match &app.hotkey { + Some(hotkey) => { + let filtered: Vec<&crate::app::LeaderboardRow> = app + .leaderboard + .iter() + .filter(|r| r.miner_hotkey == *hotkey) + .collect(); + + if filtered.is_empty() { + let text = Paragraph::new(format!( + "No submissions found for hotkey: {}", + truncate_hotkey(hotkey, 16) + )) + .block(block); + frame.render_widget(text, area); + return; + } + + let mut lines = Vec::new(); + for entry in &filtered { + lines.push(Line::from(vec![ + Span::styled("Rank: ", Style::default().fg(Color::Yellow)), + Span::raw(entry.rank.to_string()), + ])); + lines.push(Line::from(vec![ + Span::styled("Hotkey: ", Style::default().fg(Color::Yellow)), + Span::raw(entry.miner_hotkey.clone()), + ])); + lines.push(Line::from(vec![ + Span::styled("Score: ", Style::default().fg(Color::Yellow)), + Span::raw(format!("{:.4}", entry.score)), + ])); + lines.push(Line::from(vec![ + Span::styled("Pass Rate: ", Style::default().fg(Color::Yellow)), + Span::raw(format!("{:.1}%", entry.pass_rate * 100.0)), + ])); + lines.push(Line::from(vec![ + Span::styled("Submissions: ", Style::default().fg(Color::Yellow)), + Span::raw(entry.submissions.to_string()), + ])); + lines.push(Line::from(vec![ + Span::styled("Last Submission: ", Style::default().fg(Color::Yellow)), + Span::raw(entry.last_submission.clone()), + ])); + lines.push(Line::from("")); + } + + let paragraph = Paragraph::new(lines).block(block); + frame.render_widget(paragraph, area); + } + None => { + let text = Paragraph::new("No hotkey specified. Use --hotkey to filter submissions.") + .block(block); + frame.render_widget(text, area); + } + } +} + +fn draw_network(frame: &mut Frame, area: Rect, app: &App) { + let ns = &app.network_status; + let connected_style = if ns.connected { + Style::default().fg(Color::Green) + } else { + Style::default().fg(Color::Red) + }; + let connected_text = if ns.connected { + "Connected" + } else { + "Disconnected" + }; + + let lines = vec![ + Line::from(vec![ + Span::styled("Status: ", Style::default().fg(Color::Yellow).bold()), + Span::styled(connected_text, connected_style), + ]), + Line::from(""), + Line::from(vec![ + Span::styled("Epoch: ", Style::default().fg(Color::Yellow).bold()), + Span::raw(ns.epoch.to_string()), + ]), + Line::from(vec![ + Span::styled("Phase: ", Style::default().fg(Color::Yellow).bold()), + Span::raw(ns.phase.clone()), + ]), + Line::from(vec![ + Span::styled("Block Height:", Style::default().fg(Color::Yellow).bold()), + Span::raw(format!(" {}", ns.block_height)), + ]), + Line::from(vec![ + Span::styled("Validators: ", Style::default().fg(Color::Yellow).bold()), + Span::raw(ns.validators.to_string()), + ]), + Line::from(""), + Line::from(vec![ + Span::styled("RPC URL: ", Style::default().fg(Color::Yellow).bold()), + Span::raw(app.rpc_url.clone()), + ]), + ]; + + if let Some(cid) = &app.challenge_id { + let mut all_lines = lines; + all_lines.push(Line::from(vec![ + Span::styled("Challenge: ", Style::default().fg(Color::Yellow).bold()), + Span::raw(cid.clone()), + ])); + let paragraph = Paragraph::new(all_lines).block( + Block::default() + .borders(Borders::ALL) + .title("Network Status"), + ); + frame.render_widget(paragraph, area); + } else { + let paragraph = Paragraph::new(lines).block( + Block::default() + .borders(Borders::ALL) + .title("Network Status"), + ); + frame.render_widget(paragraph, area); + } +} + +fn draw_status_bar(frame: &mut Frame, area: Rect, app: &App) { + let ns = &app.network_status; + let refresh_str = app + .last_refresh + .map(|t| t.format("%H:%M:%S UTC").to_string()) + .unwrap_or_else(|| "never".to_string()); + + let mut spans = vec![ + Span::styled(" Epoch: ", Style::default().fg(Color::Yellow)), + Span::raw(ns.epoch.to_string()), + Span::raw(" | "), + Span::styled("Phase: ", Style::default().fg(Color::Yellow)), + Span::raw(ns.phase.clone()), + Span::raw(" | "), + Span::styled("Block: ", Style::default().fg(Color::Yellow)), + Span::raw(ns.block_height.to_string()), + Span::raw(" | "), + Span::styled("Validators: ", Style::default().fg(Color::Yellow)), + Span::raw(ns.validators.to_string()), + Span::raw(" | "), + Span::styled("Refresh: ", Style::default().fg(Color::Yellow)), + Span::raw(refresh_str), + ]; + + if let Some(err) = &app.error_message { + spans.push(Span::raw(" | ")); + spans.push(Span::styled( + format!("Error: {err}"), + Style::default().fg(Color::Red), + )); + } + + let status = Paragraph::new(Line::from(spans)) + .block(Block::default().borders(Borders::ALL).title("Status")); + frame.render_widget(status, area); +} + +fn truncate_hotkey(hotkey: &str, max_len: usize) -> String { + if hotkey.len() > max_len { + format!("{}...", &hotkey[..max_len]) + } else { + hotkey.to_string() + } +} + +fn visible_row_count(area: Rect) -> usize { + area.height.saturating_sub(4) as usize +} + +fn draw_empty_message(frame: &mut Frame, area: Rect, message: &str) { + let inner = centered_rect(60, 20, area); + let text = Paragraph::new(message).style(Style::default().fg(Color::DarkGray)); + frame.render_widget(text, inner); +} + +fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { + let popup_layout = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Percentage((100 - percent_y) / 2), + Constraint::Percentage(percent_y), + Constraint::Percentage((100 - percent_y) / 2), + ]) + .split(r); + + Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Percentage((100 - percent_x) / 2), + Constraint::Percentage(percent_x), + Constraint::Percentage((100 - percent_x) / 2), + ]) + .split(popup_layout[1])[1] +} diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 37936434..0f4fb615 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -19,8 +19,8 @@ use platform_challenge_sdk_wasm::{Challenge, EvaluationInput, EvaluationOutput}; use crate::scoring::{calculate_aggregate, format_summary, to_weight}; use crate::types::{ - AgentLogEntry, AgentLogs, ChallengeParams, DatasetSelection, LlmJudgeRequest, - LlmJudgeResponse, Submission, TaskResult, + AgentLogEntry, AgentLogs, ChallengeParams, DatasetSelection, LlmJudgeRequest, LlmJudgeResponse, + Submission, TaskResult, }; const MAX_SUBMISSION_SIZE: u64 = 64 * 1024 * 1024; @@ -114,11 +114,11 @@ impl TermChallengeWasm { Err(_) => return None, }; - let judge_resp: LlmJudgeResponse = - match bincode_options_llm().deserialize(&response_bytes) { - Ok(r) => r, - Err(_) => return None, - }; + let judge_resp: LlmJudgeResponse = match bincode_options_llm().deserialize(&response_bytes) + { + Ok(r) => r, + Err(_) => return None, + }; if !judge_resp.score.is_finite() { return None; From 165bd5efb974d399c4020060a482380980940b01 Mon Sep 17 00:00:00 2001 From: echobt Date: Wed, 18 Feb 2026 15:23:39 +0000 Subject: [PATCH 3/3] refactor(wasm): clean up unused code and add documentation Remove unused dataset consensus functions from dataset.rs that were not yet integrated with the P2P consensus system. The active dataset is currently managed via ChallengeParams passed to evaluate(), making these functions dead code. The module now contains documentation notes for future reimplementation. Remove unused get_dataset_history() from tasks.rs which also contained an .unwrap_or_default() pattern on deserialization. Add doc comments and #[allow(dead_code)] annotations to agent_storage retrieval functions (get_agent_code, get_agent_logs), route definitions (get_route_definitions), and scoring utilities (to_weight, apply_decay) that are defined for future platform-v2 integration but not yet called internally. Add module-level documentation to routes.rs explaining the challenge_call RPC integration path. --- wasm/src/agent_storage.rs | 6 +++ wasm/src/dataset.rs | 107 +++----------------------------------- wasm/src/routes.rs | 12 +++++ wasm/src/scoring.rs | 6 +++ wasm/src/tasks.rs | 13 ----- 5 files changed, 32 insertions(+), 112 deletions(-) diff --git a/wasm/src/agent_storage.rs b/wasm/src/agent_storage.rs index dcaaff4b..59fd4e1a 100644 --- a/wasm/src/agent_storage.rs +++ b/wasm/src/agent_storage.rs @@ -41,6 +41,9 @@ pub fn store_agent_logs(miner_hotkey: &str, epoch: u64, logs: &AgentLogs) -> boo host_storage_set(&key, &data).is_ok() } +/// Retrieve stored agent code for a miner/epoch. +/// Called by platform-v2's challenge route handler for `/agent/:hotkey/code` requests. +#[allow(dead_code)] pub fn get_agent_code(miner_hotkey: &str, epoch: u64) -> Option> { let key = make_key(b"agent_code:", miner_hotkey, epoch); let data = host_storage_get(&key).ok()?; @@ -50,6 +53,9 @@ pub fn get_agent_code(miner_hotkey: &str, epoch: u64) -> Option> { Some(data) } +/// Retrieve stored agent logs for a miner/epoch. +/// Called by platform-v2's challenge route handler for `/agent/:hotkey/logs` requests. +#[allow(dead_code)] pub fn get_agent_logs(miner_hotkey: &str, epoch: u64) -> Option { let key = make_key(b"agent_logs:", miner_hotkey, epoch); let data = host_storage_get(&key).ok()?; diff --git a/wasm/src/dataset.rs b/wasm/src/dataset.rs index df4fb5ea..0f741485 100644 --- a/wasm/src/dataset.rs +++ b/wasm/src/dataset.rs @@ -1,101 +1,10 @@ -use alloc::string::String; -use alloc::vec::Vec; -use core::fmt::Write as _; -use platform_challenge_sdk_wasm::host_functions::{ - host_consensus_get_epoch, host_random_seed, host_storage_set, -}; +//! Dataset selection and consensus logic (reserved for future P2P consensus implementation) -use crate::types::{DatasetSelection, TaskDefinition}; +// Note: Dataset consensus functions removed as the feature is not yet integrated. +// The active dataset is currently managed via the ChallengeParams passed to evaluate(). -const DATASET_SELECTION_PREFIX: &[u8] = b"dataset_selection:"; -const TOTAL_SWE_BENCH_TASKS: usize = 2294; -const TASKS_TO_SELECT: usize = 100; -const CONSENSUS_DATASET_SIZE: usize = 50; - -pub fn select_random_task_indices() -> Vec { - let mut seed = [0u8; 32]; - if host_random_seed(&mut seed).is_err() { - return Vec::new(); - } - - let mut indices = Vec::with_capacity(TASKS_TO_SELECT); - let mut state: u64 = u64::from_le_bytes([ - seed[0], seed[1], seed[2], seed[3], seed[4], seed[5], seed[6], seed[7], - ]); - - while indices.len() < TASKS_TO_SELECT { - state = state - .wrapping_mul(6364136223846793005) - .wrapping_add(1442695040888963407); - let idx = (state >> 33) as usize % TOTAL_SWE_BENCH_TASKS; - if !indices.contains(&idx) { - indices.push(idx); - } - } - - indices -} - -pub fn store_my_selection(indices: &[usize]) -> bool { - let epoch = host_consensus_get_epoch(); - if epoch < 0 { - return false; - } - - let mut key = Vec::with_capacity(DATASET_SELECTION_PREFIX.len() + 8); - key.extend_from_slice(DATASET_SELECTION_PREFIX); - key.extend_from_slice(&(epoch as u64).to_le_bytes()); - - let data = match bincode::serialize(indices) { - Ok(d) => d, - Err(_) => return false, - }; - host_storage_set(&key, &data).is_ok() -} - -pub fn build_consensus_dataset( - all_tasks: &[TaskDefinition], - validator_selections: &[Vec], -) -> Vec { - if validator_selections.is_empty() || all_tasks.is_empty() { - return Vec::new(); - } - - let threshold = validator_selections.len().div_ceil(2); - let mut counts = alloc::collections::BTreeMap::new(); - - for selection in validator_selections { - for &idx in selection { - *counts.entry(idx).or_insert(0usize) += 1; - } - } - - let mut consensus_indices: Vec = counts - .into_iter() - .filter(|(_, count)| *count >= threshold) - .map(|(idx, _)| idx) - .collect(); - - consensus_indices.sort_unstable(); - consensus_indices.truncate(CONSENSUS_DATASET_SIZE); - - consensus_indices - .iter() - .filter_map(|&idx| all_tasks.get(idx).cloned()) - .collect() -} - -pub fn create_dataset_selection(tasks: Vec) -> DatasetSelection { - let epoch = host_consensus_get_epoch(); - - let mut hash_input = String::new(); - for task in &tasks { - let _ = write!(hash_input, "{}:{};", task.id, task.name); - } - - DatasetSelection { - tasks, - selected_at_epoch: if epoch >= 0 { epoch as u64 } else { 0 }, - dataset_hash: hash_input, - } -} +// When P2P dataset consensus is implemented, this module will contain: +// - Random task index selection using host_random_seed +// - Validator selection storage via host_storage_set +// - Consensus building (>50% agreement) on task indices +// - Dataset selection serialization with hash verification diff --git a/wasm/src/routes.rs b/wasm/src/routes.rs index d5d884b4..ab548ed8 100644 --- a/wasm/src/routes.rs +++ b/wasm/src/routes.rs @@ -1,9 +1,21 @@ +//! Route definitions for the term-challenge module +//! +//! These routes are designed to be queried via platform-v2's `challenge_call` RPC method. +//! When the challenge SDK's route integration is complete, these routes will be registered +//! automatically. Until then, validators can access this data via direct storage queries. + use alloc::string::String; use alloc::vec; use alloc::vec::Vec; use crate::types::RouteDefinition; +/// Returns route definitions for the term-challenge module. +/// +/// Note: This function is currently unused pending integration with platform-v2's +/// challenge route registration system. The routes are defined here for documentation +/// and future automatic registration. +#[allow(dead_code)] pub fn get_route_definitions() -> Vec { vec![ RouteDefinition { diff --git a/wasm/src/scoring.rs b/wasm/src/scoring.rs index 989fb1df..515efad0 100644 --- a/wasm/src/scoring.rs +++ b/wasm/src/scoring.rs @@ -74,10 +74,16 @@ pub fn calculate_aggregate(tasks: &[TaskDefinition], results: &[TaskResult]) -> } } +/// Convert aggregate score to weight (normalized 0.0-1.0). pub fn to_weight(score: &AggregateScore) -> f64 { score.pass_rate.clamp(0.0, 1.0) } +/// Apply decay to weight based on hours since top score. +/// +/// Note: This function is reserved for future use when decay mechanics are +/// integrated with the scoring system via challenge configuration. +#[allow(dead_code)] pub fn apply_decay(weight: f64, hours_since_top: f64, params: &DecayParams) -> f64 { let grace = params.grace_period_hours as f64; if hours_since_top <= grace { diff --git a/wasm/src/tasks.rs b/wasm/src/tasks.rs index f03e5e99..5552268b 100644 --- a/wasm/src/tasks.rs +++ b/wasm/src/tasks.rs @@ -50,16 +50,3 @@ fn append_dataset_history(selection: &DatasetSelection) -> bool { }; host_storage_set(DATASET_HISTORY_KEY, &data).is_ok() } - -pub fn get_dataset_history() -> Vec { - host_storage_get(DATASET_HISTORY_KEY) - .ok() - .and_then(|d| { - if d.is_empty() { - None - } else { - bincode::deserialize(&d).ok() - } - }) - .unwrap_or_default() -}