diff --git a/Cargo.lock b/Cargo.lock index 3e445a1..b73a7e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,7 +49,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rand", + "rand 0.8.5", "sha1", "smallvec", "tokio", @@ -207,10 +207,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if 1.0.0", - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -499,7 +499,7 @@ dependencies = [ "alloy-genesis", "alloy-primitives", "k256", - "rand", + "rand 0.8.5", "serde_json", "tempfile", "thiserror 2.0.11", @@ -526,7 +526,7 @@ dependencies = [ "keccak-asm", "paste", "proptest", - "rand", + "rand 0.8.5", "ruint", "rustc-hash", "serde", @@ -738,7 +738,7 @@ dependencies = [ "alloy-signer", "async-trait", "k256", - "rand", + "rand 0.8.5", "thiserror 2.0.11", ] @@ -982,6 +982,15 @@ version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arc-swap" version = "1.7.1" @@ -1099,7 +1108,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", ] [[package]] @@ -1109,7 +1118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", ] [[package]] @@ -1535,6 +1544,12 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "bytemuck" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" + [[package]] name = "byteorder" version = "1.5.0" @@ -1901,7 +1916,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -2291,7 +2306,7 @@ dependencies = [ "generic-array", "group", "pkcs8", - "rand_core", + "rand_core 0.6.4", "sec1", "subtle", "zeroize", @@ -2456,7 +2471,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -2479,7 +2494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand", + "rand 0.8.5", "rustc-hex", "static_assertions", ] @@ -2698,7 +2713,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] @@ -2733,7 +2760,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -3564,6 +3591,16 @@ dependencies = [ "libc", ] +[[package]] +name = "matrixmultiply" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" +dependencies = [ + "autocfg", + "rawpointer", +] + [[package]] name = "memchr" version = "2.7.4" @@ -3619,6 +3656,7 @@ dependencies = [ "lazy_static", "libc", "log", + "nalgebra", "nvml-wrapper", "regex", "reqwest", @@ -3674,7 +3712,7 @@ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -3702,6 +3740,33 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "nalgebra" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.92", +] + [[package]] name = "native-tls" version = "0.2.12" @@ -3827,6 +3892,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -3842,6 +3916,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -4190,7 +4275,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -4294,7 +4379,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -4383,8 +4468,8 @@ dependencies = [ "bitflags 2.6.0", "lazy_static", "num-traits", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "rand_xorshift", "regex-syntax", "rusty-fork", @@ -4429,11 +4514,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", "serde", ] +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.0", + "zerocopy 0.8.15", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -4441,7 +4537,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[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 0.9.0", ] [[package]] @@ -4450,7 +4556,17 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +dependencies = [ + "getrandom 0.3.1", + "zerocopy 0.8.15", ] [[package]] @@ -4459,9 +4575,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "rayon" version = "1.10.0" @@ -4511,7 +4633,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f9edfc3524eee83ea9043734d59b139f24b6974742e2e23b16faa2b8a33fe36" dependencies = [ - "rand", + "rand 0.8.5", "redis", "socket2", "tempfile", @@ -4541,7 +4663,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror 1.0.69", ] @@ -4552,7 +4674,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror 2.0.11", ] @@ -4654,7 +4776,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if 1.0.0", - "getrandom", + "getrandom 0.2.15", "libc", "spin", "untrusted", @@ -4689,7 +4811,7 @@ dependencies = [ "parity-scale-codec", "primitive-types", "proptest", - "rand", + "rand 0.8.5", "rlp", "ruint-macro", "serde", @@ -4816,6 +4938,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + [[package]] name = "same-file" version = "1.0.6" @@ -5082,6 +5213,7 @@ dependencies = [ "dashmap", "futures-util", "hex", + "nalgebra", "redis", "serde", "serde_json", @@ -5118,7 +5250,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "simba" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", ] [[package]] @@ -5727,7 +5872,7 @@ dependencies = [ "http 1.2.0", "httparse", "log", - "rand", + "rand 0.8.5", "rustls", "rustls-pki-types", "sha1", @@ -5862,7 +6007,7 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" dependencies = [ - "getrandom", + "getrandom 0.2.15", "serde", ] @@ -5876,6 +6021,8 @@ dependencies = [ "clap 4.5.27", "env_logger 0.11.6", "log", + "nalgebra", + "rand 0.9.0", "reqwest", "serde", "serde_json", @@ -5993,6 +6140,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.99" @@ -6134,6 +6290,16 @@ dependencies = [ "rustix", ] +[[package]] +name = "wide" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "widestring" version = "1.1.0" @@ -6449,6 +6615,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "wrapcenum-derive" version = "0.4.1" @@ -6615,7 +6790,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e101d4bc320b6f9abb68846837b70e25e380ca2f467ab494bf29fcc435fcc3" +dependencies = [ + "zerocopy-derive 0.8.15", ] [[package]] @@ -6629,6 +6813,17 @@ dependencies = [ "syn 2.0.92", ] +[[package]] +name = "zerocopy-derive" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a73df1008145cd135b3c780d275c57c3e6ba8324a41bd5e0008fe167c3bc7c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.92", +] + [[package]] name = "zerofrom" version = "0.1.5" diff --git a/Makefile b/Makefile index 06c43b9..cd89a59 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,7 @@ watch-discovery: watch-miner: set -a; source ${ENV_FILE}; set +a; \ - cargo watch -w miner/src -x "run --bin miner -- run --private-key-provider $$PROVIDER_PRIVATE_KEY --private-key-node $$NODE_PRIVATE_KEY --port 8091 --external-ip 0.0.0.0 --compute-pool-id 0" + cargo watch -w miner/src -x "run --bin miner -- run --private-key-provider $$PROVIDER_PRIVATE_KEY --private-key-node $$NODE_PRIVATE_KEY --port 8091 --external-ip 0.0.0.0 --compute-pool-id 0 --validator-address $$VALIDATOR_ADDRESS" watch-validator: set -a; source ${ENV_FILE}; set +a; \ @@ -88,7 +88,7 @@ build-miner: run-miner-bin: set -a; source .env; set +a; \ - ./target/release/miner run --private-key-provider $$PROVIDER_PRIVATE_KEY --private-key-node $$NODE_PRIVATE_KEY --port 8091 --external-ip 0.0.0.0 --compute-pool-id 0 + ./target/release/miner run --private-key-provider $$PROVIDER_PRIVATE_KEY --private-key-node $$NODE_PRIVATE_KEY --port 8091 --external-ip 0.0.0.0 --compute-pool-id 0 --validator-address $$VALIDATOR_ADDRESS SSH_CONNECTION ?= your-ssh-conn string EXTERNAL_IP ?= 0.0.0.0 @@ -140,7 +140,8 @@ watch-miner-remote: setup-remote setup-tunnel sync-remote --private-key-node \$$NODE_PRIVATE_KEY \ --port $(PORT) \ --external-ip \$$EXTERNAL_IP \ - --compute-pool-id 0 2>&1 | tee miner.log\"" + --compute-pool-id 0 \ + --validator-address \$$VALIDATOR_ADDRESS 2>&1 | tee miner.log\"" # Kill SSH tunnel .PHONY: kill-tunnel kill-tunnel: diff --git a/miner/Cargo.toml b/miner/Cargo.toml index 3ca0151..fab3f33 100644 --- a/miner/Cargo.toml +++ b/miner/Cargo.toml @@ -41,6 +41,7 @@ chrono = "0.4" serial_test = "0.5.1" directories = "6.0.0" strip-ansi-escapes = "0.2.1" +nalgebra = "0.33.2" [dev-dependencies] tempfile = "=3.14.0" diff --git a/miner/makefile b/miner/makefile index e007a91..59ade05 100644 --- a/miner/makefile +++ b/miner/makefile @@ -11,11 +11,11 @@ run-check: run: @echo "🔨 Building locally..." - cargo run -- run --subnet-id 1 --private-key-provider $$PRIVATE_KEY_PROVIDER --private-key-node $$NODE_KEY --port 8090 --external-ip 0.0.0.0 --compute-pool-id 0 + cargo run -- run --subnet-id 1 --private-key-provider $$PRIVATE_KEY_PROVIDER --private-key-node $$NODE_KEY --port 8090 --external-ip 0.0.0.0 --compute-pool-id 0 --validator-address $$VALIDATOR_ADDRESS watch: @echo "👀 Watching for changes..." - cargo watch -x "run -- run --subnet-id 1 --private-key-provider $$PRIVATE_KEY_PROVIDER --private-key-node $$NODE_KEY --port 8090 --external-ip 0.0.0.0 --compute-pool-id 0" + cargo watch -x "run -- run --subnet-id 1 --private-key-provider $$PRIVATE_KEY_PROVIDER --private-key-node $$NODE_KEY --port 8090 --external-ip 0.0.0.0 --compute-pool-id 0 --validator-address $$VALIDATOR_ADDRESS" # Setup GPU server with required dependencies gpu-setup: @@ -44,4 +44,4 @@ gpu-run: # Watch mode - automatically deploy and run on file changes gpu-watch: - cargo watch -x "make gpu-run" \ No newline at end of file + cargo watch -x "make gpu-run" diff --git a/miner/src/api/routes/challenge.rs b/miner/src/api/routes/challenge.rs new file mode 100644 index 0000000..15ad1d5 --- /dev/null +++ b/miner/src/api/routes/challenge.rs @@ -0,0 +1,23 @@ +use crate::api::server::AppState; +use actix_web::{ + web::{self, post, Data}, + HttpResponse, Scope, +}; +use shared::models::api::ApiResponse; +use shared::models::challenge::calc_matrix; +use shared::models::challenge::ChallengeRequest; + +pub async fn handle_challenge( + challenge: web::Json, + _app_state: Data, +) -> HttpResponse { + let result = calc_matrix(&challenge); + + let response = ApiResponse::new(true, result); + + HttpResponse::Ok().json(response) +} + +pub fn challenge_routes() -> Scope { + web::scope("/challenge").route("/submit", post().to(handle_challenge)) +} diff --git a/miner/src/api/routes/mod.rs b/miner/src/api/routes/mod.rs index 942b4f0..d1e00d3 100644 --- a/miner/src/api/routes/mod.rs +++ b/miner/src/api/routes/mod.rs @@ -1,3 +1,4 @@ +pub mod challenge; pub mod invite; pub mod task; pub mod types; diff --git a/miner/src/api/server.rs b/miner/src/api/server.rs index 1077fc3..0262e4d 100644 --- a/miner/src/api/server.rs +++ b/miner/src/api/server.rs @@ -1,12 +1,15 @@ +use crate::api::routes::challenge::challenge_routes; use crate::api::routes::invite::invite_routes; use crate::api::routes::task::task_routes; use crate::docker::DockerService; use crate::operations::heartbeat::service::HeartbeatService; use actix_web::{middleware, web::Data, App, HttpServer}; +use alloy::primitives::Address; use shared::security::auth_signature_middleware::{ValidateSignature, ValidatorState}; use shared::web3::contracts::core::builder::Contracts; use shared::web3::contracts::structs::compute_pool::PoolInfo; use shared::web3::wallet::Wallet; +use std::str::FromStr; use std::sync::Arc; #[derive(Clone)] @@ -28,6 +31,7 @@ pub async fn start_server( heartbeat_service: Arc, docker_service: Arc, pool_info: Arc, + validator_address: String, ) -> std::io::Result<()> { println!("Starting server at http://{}:{}", host, port); @@ -39,7 +43,9 @@ pub async fn start_server( docker_service, }); - let allowed_addresses = vec![pool_info.creator, pool_info.compute_manager_key]; + let validator = Address::from_str(validator_address.as_str()).unwrap(); + + let allowed_addresses = vec![pool_info.creator, pool_info.compute_manager_key, validator]; let validator_state = Arc::new(ValidatorState::new(allowed_addresses)); HttpServer::new(move || { @@ -49,6 +55,7 @@ pub async fn start_server( .wrap(ValidateSignature::new(validator_state.clone())) .service(invite_routes()) .service(task_routes()) + .service(challenge_routes()) }) .bind((host, port))? .run() diff --git a/miner/src/cli/command.rs b/miner/src/cli/command.rs index f06f512..d2abbcc 100644 --- a/miner/src/cli/command.rs +++ b/miner/src/cli/command.rs @@ -80,6 +80,9 @@ pub enum Commands { // Amount of stake to use when provider is newly registered #[arg(long, default_value = "10")] provider_stake: i32, + + #[arg(long, default_value = "0x0000000000000000000000000000000000000000")] + validator_address: Option, }, /// Run system checks to verify hardware and software compatibility Check {}, @@ -104,6 +107,7 @@ pub async fn execute_command( state_dir_overwrite, disable_state_storing, auto_recover, + validator_address, } => { if *disable_state_storing && *auto_recover { Console::error( @@ -345,6 +349,7 @@ pub async fn execute_command( heartbeat_clone.clone(), docker_service.clone(), pool_info, + validator_address.clone().unwrap_or_default(), ) .await } { diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 69def21..2b7e80c 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -24,3 +24,4 @@ uuid = { version = "1.12.1", features = ["v4", "serde"] } redis = "0.28.1" dashmap = "6.1.0" anyhow = "1.0.95" +nalgebra = "0.33.2" diff --git a/shared/src/models/challenge.rs b/shared/src/models/challenge.rs new file mode 100644 index 0000000..c461264 --- /dev/null +++ b/shared/src/models/challenge.rs @@ -0,0 +1,88 @@ +use nalgebra::DMatrix; +use serde::{ + de::{self, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::fmt; + +#[derive(Debug, Clone)] +pub struct FixedF64(pub f64); + +impl Serialize for FixedF64 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // adjust precision as needed + serializer.serialize_str(&format!("{:.12}", self.0)) + } +} + +impl<'de> Deserialize<'de> for FixedF64 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FixedF64Visitor; + + impl Visitor<'_> for FixedF64Visitor { + type Value = FixedF64; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string representing a fixed precision float") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + value + .parse::() + .map(FixedF64) + .map_err(|_| E::custom(format!("invalid f64: {}", value))) + } + } + + deserializer.deserialize_str(FixedF64Visitor) + } +} + +impl PartialEq for FixedF64 { + fn eq(&self, other: &Self) -> bool { + format!("{:.10}", self.0) == format!("{:.10}", other.0) + } +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct ChallengeRequest { + pub rows_a: usize, + pub cols_a: usize, + pub data_a: Vec, + pub rows_b: usize, + pub cols_b: usize, + pub data_b: Vec, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct ChallengeResponse { + pub result: Vec, + pub rows: usize, + pub cols: usize, +} + +pub fn calc_matrix(req: &ChallengeRequest) -> ChallengeResponse { + // convert FixedF64 to f64 + let data_a: Vec = req.data_a.iter().map(|x| x.0).collect(); + let data_b: Vec = req.data_b.iter().map(|x| x.0).collect(); + let a = DMatrix::from_vec(req.rows_a, req.cols_a, data_a); + let b = DMatrix::from_vec(req.rows_b, req.cols_b, data_b); + let c = a * b; + + let data_c: Vec = c.iter().map(|x| FixedF64(*x)).collect(); + + ChallengeResponse { + rows: c.nrows(), + cols: c.ncols(), + result: data_c, + } +} diff --git a/shared/src/models/mod.rs b/shared/src/models/mod.rs index 1f74f3b..bbb5915 100644 --- a/shared/src/models/mod.rs +++ b/shared/src/models/mod.rs @@ -1,4 +1,5 @@ pub mod api; +pub mod challenge; pub mod heartbeat; pub mod invite; pub mod metric; diff --git a/validator/Cargo.toml b/validator/Cargo.toml index 8750064..5b22a7c 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -10,6 +10,8 @@ anyhow = "1.0.95" clap = { version = "4.5.26", features = ["derive"] } env_logger = "0.11.6" log = "0.4.25" +nalgebra = "0.33.2" +rand = "0.9.0" reqwest = "0.12.12" serde = "1.0.217" serde_json = "1.0.135" diff --git a/validator/src/main.rs b/validator/src/main.rs index 80f78fa..6a39d0b 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -5,9 +5,15 @@ use anyhow::{Context, Result}; use clap::Parser; use log::LevelFilter; use log::{error, info}; +use rand::rng; +use rand::Rng; use serde_json::json; use shared::models::api::ApiResponse; +use shared::models::challenge::calc_matrix; +use shared::models::challenge::FixedF64; +use shared::models::challenge::{ChallengeRequest, ChallengeResponse}; use shared::models::node::DiscoveryNode; +use shared::security::request_signer::sign_request; use shared::web3::contracts::core::builder::ContractBuilder; use shared::web3::wallet::Wallet; use url::Url; @@ -163,6 +169,17 @@ fn main() { } }; + let challenge_route = "/challenge/submit"; + let challenge_result = + runtime.block_on(challenge_node(&node, &validator_wallet, challenge_route)); + if challenge_result.is_err() { + error!( + "Failed to challenge node {}: {:?}", + node.id, challenge_result + ); + continue; + } + if let Err(e) = runtime.block_on( contracts .prime_network @@ -176,3 +193,131 @@ fn main() { std::thread::sleep(std::time::Duration::from_secs(10)); } } + +fn random_challenge( + rows_a: usize, + cols_a: usize, + rows_b: usize, + cols_b: usize, +) -> ChallengeRequest { + let mut rng = rng(); + + let data_a_vec: Vec = (0..(rows_a * cols_a)) + .map(|_| rng.random_range(0.0..1.0)) + .collect(); + + let data_b_vec: Vec = (0..(rows_b * cols_b)) + .map(|_| rng.random_range(0.0..1.0)) + .collect(); + + // convert to FixedF64 + let data_a: Vec = data_a_vec.iter().map(|x| FixedF64(*x)).collect(); + let data_b: Vec = data_b_vec.iter().map(|x| FixedF64(*x)).collect(); + + ChallengeRequest { + rows_a, + cols_a, + data_a, + rows_b, + cols_b, + data_b, + } +} + +pub async fn challenge_node( + node: &DiscoveryNode, + wallet: &Wallet, + challenge_route: &str, +) -> Result> { + let node_url = format!("http://{}:{}", node.node.ip_address, node.node.port); + + let mut headers = reqwest::header::HeaderMap::new(); + + // create random challenge matrix + let challenge_matrix = random_challenge(3, 3, 3, 3); + let challenge_expected = calc_matrix(&challenge_matrix); + + let post_url = format!("{}{}", node_url, challenge_route); + + let address = wallet.wallet.default_signer().address().to_string(); + let challenge_matrix_value = serde_json::to_value(&challenge_matrix)?; + let signature = sign_request(challenge_route, wallet, Some(&challenge_matrix_value)).await?; + + headers.insert("x-address", address.parse().unwrap()); + headers.insert("x-signature", signature.parse().unwrap()); + + let response = reqwest::Client::new() + .post(post_url) + .headers(headers) + .json(&challenge_matrix_value) + .send() + .await?; + + let response_text = response.text().await?; + let parsed_response: ApiResponse = serde_json::from_str(&response_text)?; + + if !parsed_response.success { + Err("Error fetching challenge from node".into()) + } else if challenge_expected.result == parsed_response.data.result { + info!("Challenge successful"); + Ok(0) + } else { + error!("Challenge failed"); + Err("Node failed challenge".into()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use actix_web::{test, App}; + use actix_web::{ + web::{self, post}, + HttpResponse, Scope, + }; + + pub async fn handle_challenge( + challenge: web::Json, + //app_state: Data, + ) -> HttpResponse { + let result = calc_matrix(&challenge); + HttpResponse::Ok().json(result) + } + + pub fn challenge_routes() -> Scope { + web::scope("/challenge") + .route("", post().to(handle_challenge)) + .route("/", post().to(handle_challenge)) + } + + #[actix_web::test] + async fn test_challenge_route() { + let app = test::init_service(App::new().service(challenge_routes())).await; + + let vec_a = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]; + let vec_b = [9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0]; + + // convert vectors to FixedF64 + let data_a: Vec = vec_a.iter().map(|x| FixedF64(*x)).collect(); + let data_b: Vec = vec_b.iter().map(|x| FixedF64(*x)).collect(); + + let challenge_request = ChallengeRequest { + rows_a: 3, + cols_a: 3, + data_a, + rows_b: 3, + cols_b: 3, + data_b, + }; + + let req = test::TestRequest::post() + .uri("/challenge") + .set_json(&challenge_request) + .to_request(); + + let resp: ChallengeResponse = test::call_and_read_body_json(&app, req).await; + let expected_response = calc_matrix(&challenge_request); + + assert_eq!(resp.result, expected_response.result); + } +}