This commit is contained in:
Victor Vobis 2025-08-27 21:51:14 +02:00
parent 4b6801644b
commit c2f189ccb9
84 changed files with 819 additions and 547 deletions

13
Cargo.lock generated
View File

@ -144,6 +144,7 @@ dependencies = [
"clap",
"crossterm 0.29.0",
"hex",
"once_cell",
"ratatui",
"serde",
"serde_json",
@ -260,6 +261,15 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "colored"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "compact_str"
version = "0.8.1"
@ -1500,6 +1510,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vlogger"
version = "0.1.0"
dependencies = [
"colored",
]
[[package]]
name = "warp"

View File

@ -20,3 +20,4 @@ web-sys = { version = "0.3.77", features = ["WebSocket"] }
vlogger = { path = "./lib/logger-rs" }
ratatui = "0.29.0"
crossterm = "0.29.0"
once_cell = "1.21.3"

View File

@ -2,6 +2,91 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "colored"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
dependencies = [
"windows-sys",
]
[[package]]
name = "vlogger"
version = "0.1.0"
dependencies = [
"colored",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[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_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[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_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[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_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View File

@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2024"
[dependencies]
colored = "3.0.0"

View File

@ -14,22 +14,6 @@ pub const LOG_LEVEL: [&str; 5] = [
"FATAL",
];
pub fn colored(text: &str, color: &str) -> String {
// For terminals: use ANSI escape codes
let ansi_code = match color {
"black" => "30",
"red" => "31",
"green" => "32",
"yellow" => "33",
"blue" => "34",
"purple" => "35",
"cyan" => "36",
"white" => "37",
_ => "0", // default no color
};
format!("\x1b[{ansi_code}m{text}\x1b[0m")
}
pub fn current_timestamp() -> String {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
@ -63,15 +47,16 @@ macro_rules! msg {
$crate::INFO => {
format!(
"[{}] {} | {}\n",
$crate::colored($crate::LOG_LEVEL[$level], "green"),
//$crate::colored($crate::LOG_LEVEL[$level], "green"),
$crate::LOG_LEVEL[$level].to_string(),
$crate::current_timestamp(),
formatted_msg
)
},
$crate::DEBUG => {
format!(
"[{}] [{}:{}] {} | {}\n",
$crate::LOG_LEVEL[$level],
"[{}]\t[{}:{}] {} | {}\n",
$crate::LOG_LEVEL[$level].to_string(),
file!(),
line!(),
$crate::current_timestamp(),
@ -81,7 +66,7 @@ macro_rules! msg {
$crate::WARNING => {
format!(
"[{}] {} | {}\n",
$crate::colored($crate::LOG_LEVEL[$level], "yellow"),
$crate::LOG_LEVEL[$level].to_string(),
$crate::current_timestamp(),
formatted_msg
)
@ -89,7 +74,7 @@ macro_rules! msg {
$crate::ERROR => {
format!(
"[{}] {} | {}\n",
$crate::colored($crate::LOG_LEVEL[$level], "red"),
$crate::LOG_LEVEL[$level].to_string(),
$crate::current_timestamp(),
formatted_msg
)
@ -97,7 +82,7 @@ macro_rules! msg {
$crate::FATAL => {
format!(
"[{}] [{}:{}] {} | {}\n",
$crate::colored($crate::LOG_LEVEL[$level], "red"),
$crate::LOG_LEVEL[$level].to_string(),
file!(),
line!(),
$crate::current_timestamp(),
@ -154,14 +139,6 @@ mod tests {
assert!(error_msg.contains("Test error: 42"));
}
#[test]
fn test_colored() {
let red_text = colored("error", "red");
assert!(red_text.contains("\x1b[31m"));
assert!(red_text.contains("error"));
assert!(red_text.contains("\x1b[0m"));
}
#[test]
fn test_timestamp() {
let ts = current_timestamp();

View File

@ -0,0 +1 @@
{"rustc_fingerprint":9331962960305057120,"outputs":{"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/sgoinfre/vvobis/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"x87\"\ntarget_has_atomic\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"unknown\"\nub_checks\nunix\n","stderr":""},"11857020428658561806":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/sgoinfre/vvobis/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\nfmt_debug=\"full\"\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"x87\"\ntarget_has_atomic\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"unknown\"\nub_checks\nunix\n","stderr":""},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.87.0-nightly (1aeb99d24 2025-03-19)\nbinary: rustc\ncommit-hash: 1aeb99d248e1b0069110cb03c6f1dcc7b36fd7f3\ncommit-date: 2025-03-19\nhost: x86_64-unknown-linux-gnu\nrelease: 1.87.0-nightly\nLLVM version: 20.1.0\n","stderr":""}},"successes":{}}

View File

@ -0,0 +1,3 @@
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by cargo.
# For information about cache directory tags see https://bford.info/cachedir/

View File

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
2cdcc8159c269916

View File

@ -0,0 +1 @@
{"rustc":9200240233716061564,"features":"[]","declared_features":"[\"no-color\"]","target":10635017557502881088,"profile":15657897354478470176,"path":11251442337508459999,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/colored-5b26b8bcd167138a/dep-lib-colored","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
b7d5ee472865d234

View File

@ -0,0 +1 @@
{"rustc":9200240233716061564,"features":"[]","declared_features":"[\"no-color\"]","target":10635017557502881088,"profile":2241668132362809309,"path":11251442337508459999,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/colored-be62070d26ff5634/dep-lib-colored","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
12fd561ccccbf38d

View File

@ -0,0 +1 @@
{"rustc":9200240233716061564,"features":"[]","declared_features":"[]","target":3130845551982539986,"profile":8731458305071235362,"path":10763286916239946207,"deps":[[9114801806035203750,"colored",false,1628375192093383724]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/vlogger-5e31d9758c815091/dep-lib-vlogger","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
aeff26b79a20f245

View File

@ -0,0 +1 @@
{"rustc":9200240233716061564,"features":"[]","declared_features":"[]","target":3130845551982539986,"profile":17672942494452627365,"path":10763286916239946207,"deps":[[9114801806035203750,"colored",false,3806215858761422263]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/vlogger-dada301cb6b4723c/dep-lib-vlogger","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
af88079a39808c51

View File

@ -0,0 +1 @@
{"rustc":9200240233716061564,"features":"[]","declared_features":"[]","target":3130845551982539986,"profile":3316208278650011218,"path":10763286916239946207,"deps":[[9114801806035203750,"colored",false,3806215858761422263]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/vlogger-ee9eba351348c096/dep-test-lib-vlogger","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1,12 @@
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/deps/libcolored-5b26b8bcd167138a.rmeta: /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/lib.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/color.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/control.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/error.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/style.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/customcolors.rs
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/deps/libcolored-5b26b8bcd167138a.rlib: /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/lib.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/color.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/control.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/error.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/style.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/customcolors.rs
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/deps/colored-5b26b8bcd167138a.d: /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/lib.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/color.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/control.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/error.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/style.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/customcolors.rs
/sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/lib.rs:
/sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/color.rs:
/sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/control.rs:
/sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/error.rs:
/sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/style.rs:
/sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/customcolors.rs:

View File

@ -0,0 +1,10 @@
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/deps/libcolored-be62070d26ff5634.rmeta: /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/lib.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/color.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/control.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/error.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/style.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/customcolors.rs
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/deps/colored-be62070d26ff5634.d: /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/lib.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/color.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/control.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/error.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/style.rs /sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/customcolors.rs
/sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/lib.rs:
/sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/color.rs:
/sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/control.rs:
/sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/error.rs:
/sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/style.rs:
/sgoinfre/vvobis/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/colored-3.0.0/src/customcolors.rs:

View File

@ -0,0 +1,7 @@
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/deps/libvlogger-5e31d9758c815091.rmeta: src/lib.rs
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/deps/libvlogger-5e31d9758c815091.rlib: src/lib.rs
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/deps/vlogger-5e31d9758c815091.d: src/lib.rs
src/lib.rs:

View File

@ -0,0 +1,5 @@
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/deps/libvlogger-dada301cb6b4723c.rmeta: src/lib.rs
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/deps/vlogger-dada301cb6b4723c.d: src/lib.rs
src/lib.rs:

View File

@ -0,0 +1,5 @@
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/deps/libvlogger-ee9eba351348c096.rmeta: src/lib.rs
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/deps/vlogger-ee9eba351348c096.d: src/lib.rs
src/lib.rs:

View File

@ -0,0 +1 @@
/sgoinfre/vvobis/git/blockchain/lib/logger-rs/target/debug/libvlogger.rlib: /sgoinfre/vvobis/git/blockchain/lib/logger-rs/src/lib.rs

Binary file not shown.

View File

@ -2,10 +2,115 @@ use std::net::SocketAddr;
use clap::{Parser, Subcommand};
use crate::core;
use crate::watcher::RenderPane;
use clap::*;
#[derive(Parser)]
pub struct Cli {
#[command(subcommand)]
pub command: CliCommand
}
#[derive(Subcommand)]
pub enum CliCommand{
#[command(name = "node")]
Node {
#[command(subcommand)]
command: CliNodeCommand
},
#[command(name = "clear", aliases = ["c"])]
Clear{
pane: RenderPane
}
}
#[derive(Subcommand)]
pub enum CliPeerCommand {
/// Connect To Peer With IpAddr
#[command(name = "connect", aliases = ["c", "con"])]
Connect{addr: String},
/// Remove Peer Connection
#[command(name = "remove", aliases = ["rm"])]
Remove{id: String},
/// List Connected Peers
#[command(name = "list", aliases = ["ls", "l"])]
List,
}
#[derive(Subcommand)]
pub enum CliSeedCommand {
/// Connect to Seed nodes
#[command(name = "connect", aliases = ["c", "con"])]
Connect
}
#[derive(Subcommand)]
pub enum CliBlockCommand {
/// List Blocks in Chain
#[command(name = "list", aliases = ["ls", "l"])]
List,
/// Create and Broadcast new Block
#[command(name = "create", aliases = ["c", "new"])]
Create,
/// Export Blocks to file
#[command(name = "dump", aliases = ["export"])]
Dump {
/// Output file
#[arg(short, long)]
output: String
}
}
#[derive(Subcommand)]
#[command(name = "node")]
#[command(about = "A blockchain node CLI tool")]
#[command(version = "1.0")]
#[command(long_about = "A comprehensive CLI tool for managing blockchain nodes, peers, and transactions")]
pub enum CliNodeCommand {
/// Peer related Cmd
#[command(name = "peer")]
Peer {
#[command(subcommand)]
peer_cmd: CliPeerCommand
},
/// Block related Cmd
#[command(name = "block")]
Block {
#[command(subcommand)]
block_cmd: CliBlockCommand
},
/// Make a Transaction
#[command(name = "tx")]
Transaction(core::Tx),
/// Start new TcpListner on Addr
#[command(name = "listen")]
StartListner{addr: String},
/// Display Node id
#[command(name = "id")]
DebugShowId,
/// Connect to Seed Nodes
#[command(name = "seed")]
Seeds {
#[command(subcommand)]
seed_cmd: CliSeedCommand
},
}
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
pub struct Args {
pub struct CliArgs {
/// Provide address on which node will listen
#[arg(short = 'a', long)]
pub addr: Option<SocketAddr>,
@ -26,6 +131,6 @@ pub enum TxCmd {
Add(core::Tx)
}
pub fn get_args() -> Args {
Args::parse()
pub fn get_args() -> CliArgs {
CliArgs::parse()
}

50
src/cli.rs Normal file
View File

@ -0,0 +1,50 @@
use crate::args::*;
use crate::native_node::node::*;
use clap::Parser;
use crate::watcher::ExecutorCommand;
pub fn handle_peer_command(cmd: CliPeerCommand) -> NodeCommand {
match cmd {
CliPeerCommand::List => NodeCommand::ListPeers,
CliPeerCommand::Remove { id } => NodeCommand::RemovePeer{ peer_id: id.parse::<uuid::Uuid>().unwrap() },
CliPeerCommand::Connect { addr } => NodeCommand::ConnectToPeer(addr),
}
}
pub fn handle_block_command(cmd: CliBlockCommand) -> NodeCommand {
match cmd {
CliBlockCommand::List => NodeCommand::ListBlocks,
CliBlockCommand::Dump { output } => NodeCommand::DumpBlocks(output),
CliBlockCommand::Create => NodeCommand::CreateBlock,
}
}
fn handle_seed_command(cmd: CliSeedCommand) -> NodeCommand {
match cmd {
CliSeedCommand::Connect => NodeCommand::ConnectToSeeds
}
}
fn node_cli(cmd: CliNodeCommand) -> ExecutorCommand {
match cmd {
CliNodeCommand::Peer { peer_cmd } => ExecutorCommand::Node(handle_peer_command(peer_cmd)),
CliNodeCommand::Block { block_cmd } => ExecutorCommand::Node(handle_block_command(block_cmd)),
CliNodeCommand::Transaction(tx)=> ExecutorCommand::Node(NodeCommand::Transaction{tx}),
CliNodeCommand::DebugShowId => ExecutorCommand::Node(NodeCommand::ShowId),
CliNodeCommand::StartListner { addr } => {
ExecutorCommand::Node(NodeCommand::StartListner(addr.parse().unwrap()))
},
CliNodeCommand::Seeds { seed_cmd } => ExecutorCommand::Node(handle_seed_command(seed_cmd)),
}
}
pub fn cli(input: &[&str]) -> ExecutorCommand {
match Cli::try_parse_from(input) {
Ok(cmd) => match cmd.command {
CliCommand::Node{ command } => node_cli(command),
CliCommand::Clear { pane } => ExecutorCommand::Clear(pane),
}
Err(e) => ExecutorCommand::InvalidCommand(format!("{e}"))
}
}

View File

@ -9,6 +9,8 @@ use std::collections::HashMap;
use std::io::Write;
use std::time::UNIX_EPOCH;
const BLOCKCHAIN_ID: &str = "victors-first-blockchain";
pub type Account = String;
#[derive(Debug)]
@ -19,6 +21,7 @@ pub enum ValidationError {
#[derive(Debug, Default)]
pub struct Blockchain {
id: String,
balances: std::collections::HashMap<Account, u32>,
blocks: Vec<core::Block>,
tx_mempool: Vec<core::Tx>,
@ -99,10 +102,16 @@ impl Blockchain {
hex::encode(hasher.finalize())
}
pub fn dump_blocks(&self, db_file: &mut std::fs::File) {
fn acc_exists(&self, acc: &Account) -> bool {
self.balances.iter().find(|(k, _)| *k == acc).is_some()
}
pub fn dump_blocks(&self, path: String) {
let block_json = serde_json::to_string_pretty(&self.blocks).unwrap();
if let Ok(mut db_file) = std::fs::OpenOptions::new().truncate(true).create(true).write(true).open(path) {
db_file.write_all(&block_json.as_bytes()).unwrap();
}
}
pub fn create_block(&mut self) -> core::Block {
let previous_hash = if self.blocks().len() > 0 {
@ -137,7 +146,6 @@ impl Blockchain {
pub fn apply(&mut self, tx: &core::Tx) -> Result<(), BlockchainError> {
self.tx_mempool.push(tx.clone());
return Ok(());
match tx.validate() {
Ok(_) => {},
Err(e) => return Err(BlockchainError::Tx(e))
@ -156,10 +164,10 @@ impl Blockchain {
if let Some(to_balance) = self.balances.get_mut(&tx.to().to_string()) {
*to_balance += tx.value()
} else {
if tx.is_new_account() {
self.balances.insert(tx.to().to_string(), tx.value());
if self.acc_exists(tx.to()) {
*self.balances.get_mut(tx.to()).unwrap() += tx.value();
} else {
return Err(BlockchainError::Tx(TxError::UnknownAccount(tx.to().to_string())))
self.balances.insert(tx.to().clone(), tx.value());
}
}
@ -168,6 +176,7 @@ impl Blockchain {
pub fn new(balances: HashMap<Account, u32>, blocks: Vec<core::Block>, tx_mempool: Vec<core::Tx>) -> Blockchain {
return Self {
id: BLOCKCHAIN_ID.to_string(),
balances,
blocks,
tx_mempool
@ -247,7 +256,8 @@ impl Blockchain {
let chain = Blockchain {
blocks,
balances: HashMap::new(),
tx_mempool: vec![]
tx_mempool: vec![],
id: BLOCKCHAIN_ID.to_string(),
};
chain.validate_chain()?;

View File

@ -30,10 +30,10 @@ impl Tx {
pub fn is_reward(&self) -> bool {
return self.data == "reward";
}
pub fn from(&self) -> &str {
pub fn from(&self) -> &Account {
&self.from
}
pub fn to(&self) -> &str {
pub fn to(&self) -> &Account {
&self.to
}
pub fn value(&self) -> u32 {

View File

@ -4,6 +4,7 @@ pub mod core;
pub mod native_node;
pub mod seeds_constants;
pub mod watcher;
pub mod cli;
use crate::{args::get_args, watcher::watcher::Watcher};
@ -15,7 +16,7 @@ async fn main() {
let mut watcher = Watcher::build().file(args.seed_file).addr(args.addr).start().await;
loop {
if watcher.poll().await.is_ok_and(|b| b) {
if !watcher.poll().await.is_ok_and(|b| b) {
break ;
}
}

View File

@ -2,5 +2,3 @@ pub mod node;
pub mod network;
pub mod message;
pub mod error;
pub mod cli;

View File

@ -1,36 +0,0 @@
use crate::native_node::{node};
use vlogger::*;
use tokio::sync::mpsc;
use crate::core;
use std::io::{self, Write};
impl node::NativeNode {
pub async fn cli(command_sender: mpsc::Sender<node::NodeCommand>) {
loop {
print!("\n> ");
io::stdout().flush().unwrap();
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(_) => {
let input = input.trim();
if input.is_empty() {
continue ;
}
let parts: Vec<&str> = input.split_whitespace().collect();
let command = parts[0];
let args = &parts[1..];
match command {
_ => {
log!(ERROR, "Unkown command {command}");
continue;
}
}
},
Err(_) => {}
}
}
}
}

View File

@ -11,7 +11,7 @@ use vlogger::*;
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub enum ProtocolMessage {
BootstrapRequest {
node_id: uuid::Uuid,
peer_id: uuid::Uuid,
version: String
},
BootstrapResponse {
@ -23,7 +23,7 @@ pub enum ProtocolMessage {
GetPeersResponse {
peer_addresses: Vec<SocketAddr>
},
Handshake { node_id: uuid::Uuid, version: String },
Handshake { peer_id: uuid::Uuid, version: String },
Block { peer_id: uuid::Uuid, height: u64, block: core::Block },
Transaction{ peer_id: uuid::Uuid, tx: core::Tx },
Ping { peer_id: uuid::Uuid },

View File

@ -9,29 +9,86 @@ use tokio::select;
use tokio::sync::mpsc;
impl node::NativeNode {
pub async fn connect_to_seeds(&mut self, sender: mpsc::Sender<node::NodeCommand>) {
for seed in SEED_NODES.iter() {
pub async fn connect_to_seeds(&mut self) {
for addr in SEED_NODES.iter() {
if let Some(a)= self.addr {
if *seed != a {
if let Ok(mut stream) = tokio::net::TcpStream::connect(seed).await {
if let Ok(peer_id) = node::NativeNode::send_handshake(self.id.clone(), &mut stream).await {
let sender = sender.clone();
node::NativeNode::establish_connection(peer_id, *seed, stream, sender).await;
if *addr != a {
self.connect_to_peer(*addr).await;
}
}
}
}
pub async fn connect_to_peer(
&self,
addr: SocketAddr,
) {
if let Ok(stream) = tokio::net::TcpStream::connect(addr).await {
Self::establish_connection(self.id, None, addr, stream, self.tx().clone()).await;
}
}
pub async fn accept_connections(
listner: tokio::net::TcpListener,
request_sender: tokio::sync::mpsc::Sender<node::NodeCommand>,
node_id: uuid::Uuid
) {
log!(INFO, "Starting to accept connections");
while let Ok((mut stream, addr)) = listner.accept().await {
if let Ok(message) = node::NativeNode::receive_message(&mut stream).await {
match message {
message::ProtocolMessage::Handshake { peer_id, .. } => {
node::NativeNode::establish_connection(node_id, Some(peer_id), addr, stream, request_sender.clone()).await;
},
_ => {
log!(WARNING, "Invalid Response! expected Handshake, got {:?}", message);
}
}
}
}
}
pub async fn establish_connection(
peer_id: uuid::Uuid,
node_id: uuid::Uuid,
peer_id: Option<uuid::Uuid>,
addr: SocketAddr,
stream: tokio::net::TcpStream,
mut stream: tokio::net::TcpStream,
request_sender: tokio::sync::mpsc::Sender<node::NodeCommand>
) {
let handshake_response = message::ProtocolMessage::Handshake {
peer_id: node_id.clone(),
version: "".to_string()
};
match peer_id {
Some(id) => {
if let Ok(()) = Self::send_message(&mut stream, &handshake_response).await {
let (response_sender, response_receiver) = mpsc::channel::<message::ProtocolMessage>(100);
let add_peer = node::NodeCommand::AddPeer {
peer_id: id,
addr: addr.clone(),
sender: response_sender
};
if let Err(_) = request_sender.send(add_peer).await {
log!(ERROR, "Failed to send AddPeer to {}", addr);
}
log!(INFO, "Established Connection with {}", addr);
node::NativeNode::start_peer_handler(stream, id, request_sender.clone(), response_receiver).await;
}
}
None => {
if let Ok(mes) = Self::receive_message(&mut stream).await {
let (response_sender, response_receiver) = mpsc::channel::<message::ProtocolMessage>(100);
match mes {
message::ProtocolMessage::Handshake { peer_id, .. } => {
let add_peer = node::NodeCommand::AddPeer {
peer_id,
addr: addr.clone(),
@ -45,35 +102,13 @@ impl node::NativeNode {
log!(INFO, "Established Connection with {}", addr);
node::NativeNode::start_peer_handler(stream, peer_id, request_sender.clone(), response_receiver).await;
}
pub async fn accept_connections(
listner: tokio::net::TcpListener,
request_sender: tokio::sync::mpsc::Sender<node::NodeCommand>,
node_id: uuid::Uuid
) {
log!(INFO, "Starting to accept connections");
while let Ok((mut stream, addr)) = listner.accept().await {
let handshake_response = message::ProtocolMessage::Handshake {
node_id: node_id.clone(),
version: "".to_string()
};
if let Ok(_) = node::NativeNode::send_message(&mut stream, &handshake_response).await {
if let Ok(message) = node::NativeNode::receive_message(&mut stream).await {
match message {
message::ProtocolMessage::Handshake { node_id, .. } => {
node::NativeNode::establish_connection(node_id, addr, stream, request_sender.clone()).await;
},
_ => {
log!(WARNING, "Invalid Response! expected Handshake, got {:?}", message);
}
}
}
}
_ => { }
}
}
}
}
}
async fn start_peer_handler(
mut stream: tokio::net::TcpStream,

View File

@ -16,6 +16,7 @@ pub struct TcpPeer {
pub sender: tokio::sync::mpsc::Sender<ProtocolMessage>
}
#[allow(dead_code)]
pub struct NativeNode {
pub id: Uuid,
pub addr: Option<SocketAddr>,
@ -35,11 +36,12 @@ pub enum NodeCommand {
Transaction { tx: core::Tx },
StartListner(SocketAddr),
CreateBlock,
DebugListBlocks,
DebugListPeers,
DebugShowId,
DebugDumpBlocks,
ListBlocks,
ListPeers,
ShowId,
DumpBlocks(String),
ConnectToSeeds,
ConnectToPeer(String),
Exit,
}
@ -72,7 +74,7 @@ impl NativeNode {
fn add_tcp_peer(&mut self, id: Uuid, addr: SocketAddr, sender: tokio::sync::mpsc::Sender<ProtocolMessage>) {
let peer = TcpPeer {
id: id,
id,
addr,
sender
};
@ -132,12 +134,12 @@ impl NativeNode {
}
pub async fn send_handshake(id: uuid::Uuid, stream: &mut tokio::net::TcpStream) -> Result<uuid::Uuid, ValidationError> {
let handshake = ProtocolMessage::Handshake { node_id: id.clone(), version: "".to_string() };
let handshake = ProtocolMessage::Handshake { peer_id: id.clone(), version: "".to_string() };
NativeNode::send_message(stream, &handshake).await.unwrap();
if let Ok(response) = NativeNode::receive_message(stream).await {
match response {
message::ProtocolMessage::Handshake { node_id, version: _ } => {
Ok(node_id)
message::ProtocolMessage::Handshake { peer_id, version: _ } => {
Ok(peer_id)
},
_ => {
log!(ERROR, "Invalid response on Handshake");
@ -150,25 +152,25 @@ impl NativeNode {
}
pub async fn bootstrap(&mut self) -> Result<(), ValidationError> {
log!(INFO, "Running As Native Node");
self.log(msg!(INFO, "Running As Native Node")).await;
let mut stream = tokio::net::TcpStream::connect(SEED_NODES[0]).await.unwrap();
let id = uuid::Uuid::new_v4();
if let Ok(_) = NativeNode::send_handshake(id, &mut stream).await {
let message = message::ProtocolMessage::BootstrapRequest { node_id: id.clone(), version: "".to_string() };
let message = message::ProtocolMessage::BootstrapRequest { peer_id: id.clone(), version: "".to_string() };
NativeNode::send_message(&mut stream, &message).await.unwrap();
log!(INFO, "Sent BootstrapRequest to seed");
self.log(msg!(INFO, "Sent BootstrapRequest to seed")).await;
if let Ok(response) = NativeNode::receive_message(&mut stream).await {
match response {
ProtocolMessage::BootstrapResponse { blocks } => {
log!(INFO, "Received BootstrapResponse from seed");
self.log(msg!(INFO, "Received BootstrapResponse from seed")).await;
self.chain = core::Blockchain::build(blocks).unwrap();
Ok(())
},
_ => {
log!(ERROR, "Invalid Response from BootstrapRequest: {:?}", &response);
self.log(msg!(ERROR, "Invalid Response from BootstrapRequest: {:?}", &response)).await;
Err(ValidationError::InvalidBlockHash)
}
}
@ -184,7 +186,7 @@ impl NativeNode {
for (id, peer) in &self.tcp_peers {
let message = ProtocolMessage::Transaction{peer_id: self.id, tx: tx.clone()};
peer.sender.send(message).await.unwrap();
log!(DEBUG, "Send Transaction message to {id}");
self.log(msg!(DEBUG, "Send Transaction message to {id}")).await;
}
}
@ -196,7 +198,7 @@ impl NativeNode {
block: block.clone()
};
peer.sender.send(message).await.unwrap();
log!(DEBUG, "Send Block message to {id}");
self.log(msg!(DEBUG, "Send Block message to {id}")).await;
}
}
@ -220,6 +222,10 @@ impl NativeNode {
};
}
async fn log(&self, msg: String) {
let _ = self.exec_tx.send(ExecutorCommand::Print(msg));
}
pub async fn run_native(&mut self) {
if let Some(a) = self.addr {
@ -232,8 +238,13 @@ impl NativeNode {
self.start_connection_listner(addr).await;
}
NodeCommand::ConnectToSeeds => {
self.connect_to_seeds(self.tx()).await;
self.connect_to_seeds().await;
},
NodeCommand::ConnectToPeer(addr) => {
if let Ok(addr_sock) = addr.parse::<SocketAddr>() {
self.connect_to_peer(addr_sock).await;
}
}
NodeCommand::AddPeer { peer_id, addr, sender } => {
self.add_tcp_peer(peer_id, addr, sender);
},
@ -248,27 +259,27 @@ impl NativeNode {
self.broadcast_transaction(&tx).await;
},
NodeCommand::CreateBlock => {
log!(INFO, "Received CreateBlock Command");
self.log(msg!(INFO, "Received CreateBlock Command")).await;
let block = self.chain.create_block();
self.broadcast_block(&block).await;
},
NodeCommand::DebugListBlocks => {
log!(INFO, "Received DebugListBlocks command");
NodeCommand::ListBlocks => {
self.log(msg!(INFO, "Received DebugListBlocks command")).await;
self.chain.print_blocks();
},
NodeCommand::DebugListPeers => {
log!(INFO, "Received DebugListPeers command");
NodeCommand::ListPeers => {
self.log(msg!(INFO, "Received DebugListPeers command")).await;
self.list_peers();
},
NodeCommand::DebugShowId => {
log!(INFO, "Received DebugListBlocks command");
NodeCommand::ShowId => {
self.log(msg!(INFO, "Received DebugListBlocks command")).await;
self.show_id();
},
NodeCommand::DebugDumpBlocks => {
// self.chain.dump_blocks(&mut self.db_file);
NodeCommand::DumpBlocks(s) => {
self.chain.dump_blocks(s);
}
NodeCommand::Exit => {
log!(DEBUG, "Node Exit");
self.log(msg!(DEBUG, "Node Exit")).await;
break ;
}
}

View File

@ -1,10 +1,11 @@
use crate::{native_node::node::NodeCommand, watcher::renderer::*};
use crate::{native_node::node::NodeCommand, watcher::{ watcher::Watcher, renderer::* }};
use tokio::sync::mpsc;
use vlogger::*;
pub enum ExecutorCommand {
NodeResponse(String),
Echo(Vec<String>),
Print(String),
InvalidCommand(String),
Node(NodeCommand),
Clear(RenderPane),
@ -28,14 +29,14 @@ impl Executor {
}
}
fn exit(&mut self) {
log!(DEBUG, "Executor Exit");
async fn exit(&mut self) {
self.render_string(msg!(DEBUG, "Executor Exit")).await;
self.exit = true
}
async fn listen(&mut self) {
if let Some(cmd) = self.rx.recv().await {
let _ = self.execute(cmd);
let _ = self.execute(cmd).await;
}
}
@ -60,7 +61,7 @@ impl Executor {
// }
}
fn render_string(&self, str: String) {
async fn render_string(&self, str: String) {
let rd_cmd = RenderCommand::RenderStringToPane{
str,
pane: RenderPane::CliOutput
@ -68,37 +69,39 @@ impl Executor {
let _ = self.render_tx.send(rd_cmd);
}
fn echo(&self, s: Vec<String>) {
async fn echo(&self, s: Vec<String>) {
Watcher::log(&self.render_tx, msg!(INFO, "Exec: Recieved echo command")).await;
let mut str = s.join(" ");
str.push_str("\n");
let rd_cmd = RenderCommand::RenderStringToPane{
str,
pane: RenderPane::CliOutput
};
let _ = self.render_tx.send(rd_cmd);
let _ = self.render_tx.send(rd_cmd).await;
}
fn clear(&self, p: RenderPane) {
async fn clear(&self, p: RenderPane) {
let rd_cmd = RenderCommand::ClearPane(p);
let _ = self.render_tx.send(rd_cmd);
let _ = self.render_tx.send(rd_cmd).await;
}
fn invalid_command(&self, str: String) {
async fn invalid_command(&self, str: String) {
let rd_cmd = RenderCommand::RenderStringToPane{
str,
pane: RenderPane::CliOutput
};
let _ = self.render_tx.send(rd_cmd);
let _ = self.render_tx.send(rd_cmd).await;
}
async fn execute(&mut self, cmd: ExecutorCommand) {
match cmd {
ExecutorCommand::NodeResponse(resp) => self.render_string(resp),
ExecutorCommand::NodeResponse(resp) => self.render_string(resp).await,
ExecutorCommand::Node(n) => self.handle_node_cmd(n).await,
ExecutorCommand::Clear(p) => self.clear(p),
ExecutorCommand::Echo(s) => self.echo(s),
ExecutorCommand::InvalidCommand(str) => self.invalid_command(str),
ExecutorCommand::Exit => self.exit(),
ExecutorCommand::Clear(p) => self.clear(p).await,
ExecutorCommand::Echo(s) => self.echo(s).await,
ExecutorCommand::Print(s) => self.render_string(s).await,
ExecutorCommand::InvalidCommand(str) => self.invalid_command(str).await,
ExecutorCommand::Exit => self.exit().await,
}
}
@ -108,4 +111,3 @@ impl Executor {
}
}
}

View File

@ -1,10 +1,6 @@
use std::net::SocketAddr;
use crate::native_node::node::NodeCommand;
use crate::watcher::executor::{ExecutorCommand};
use crate::watcher::executor::ExecutorCommand;
use vlogger::*;
use crate::core;
use crate::cli::cli;
use tokio::time::{timeout, Duration};
@ -40,7 +36,8 @@ impl Parser {
}
}
fn exit(&mut self) {
async fn exit(&mut self) {
self.print(msg!(DEBUG, "Parser Exit")).await;
self.exit = true;
}
@ -50,108 +47,49 @@ impl Parser {
}
}
pub async fn print(&self, msg: String) {
if let Err(e) = self.exec_tx.send(ExecutorCommand::Echo(vec![msg])).await {
log!(ERROR, "Error response from exec: {e}");
}
}
async fn listen(&mut self) {
if let Ok(Some(mes)) = timeout(Duration::from_millis(400), self.rx.recv()).await {
log!(DEBUG, "Parser: Received message from watcher");
self.print(msg!(INFO, "Parser: Received message from watcher")).await;
match mes {
ParserCommand::ParseCmdString(s) => {
let s_split: Vec<&str> = s.split(" ").collect();
if s_split.len() != 0 {
let cmd = &s_split[0];
let args = &s_split[1..];
let exec_cmd = match *cmd {
CMD_NODE => {
if !args.is_empty() {
match args[0] {
"id" => {
ExecutorCommand::Node(NodeCommand::DebugShowId)
let argv: Vec<&str> = std::iter::once(" ")
.chain(s.split_whitespace())
.collect();
let cmd = cli(&argv);
let _ = self.exec_tx.send(cmd).await;
},
"tx" => {
if args.len() != 4 {
log!(ERROR, "Invalid arg count! Expected {}, got {}", 4, args.len());
}
let from = args[0];
let to = args[1];
let value = args[2].parse::<u32>().unwrap();
let data = args[3];
let tx = core::Tx::new(
from.to_string(),
to.to_string(),
value,
data.to_string()
);
ExecutorCommand::Node(NodeCommand::Transaction { tx })
},
"block" => {
ExecutorCommand::Node(NodeCommand::CreateBlock)
},
"list" => {
if args.len() != 1 {
ExecutorCommand::InvalidCommand(msg!(ERROR, "node list: expects 1 argument"))
} else {
match args[0] {
"blocks" => ExecutorCommand::Node(NodeCommand::DebugListBlocks),
"peers" => ExecutorCommand::Node(NodeCommand::DebugListPeers),
_ => ExecutorCommand::InvalidCommand(msg!(ERROR, "Unkown arg: {}", args[0])),
}
}
},
"dump_blocks" => {
ExecutorCommand::Node(NodeCommand::DebugDumpBlocks)
},
"connect" => {
ExecutorCommand::Node(NodeCommand::ConnectToSeeds)
},
"listen" => {
if args.len() != 1 {
ExecutorCommand::InvalidCommand(msg!(ERROR, "node listen: expects 1 argument"))
} else {
if let Ok(addr) = args[0].parse::<SocketAddr>() {
ExecutorCommand::Node(NodeCommand::StartListner(addr))
} else {
ExecutorCommand::InvalidCommand(msg!(ERROR, "node listen: invalid address {}", args[0]))
}
}
}
_ => {
ExecutorCommand::InvalidCommand(msg!(ERROR, "node: unknown argument {}", args[0]))
}
}
} else {
ExecutorCommand::InvalidCommand(msg!(ERROR, "node: expected arguments"))
}
}
CMD_ECHO => {
if args.is_empty() {
ExecutorCommand::InvalidCommand(msg!(ERROR, "print expects args"))
} else {
ExecutorCommand::Echo(args.iter().map(|a| a.to_string()).collect())
}
}
CMD_CLEAR => {
if args.is_empty() {
ExecutorCommand::Clear(RenderPane::All)
} else if args[0] == "in" {
ExecutorCommand::Clear(RenderPane::CliInput)
} else if args[0] == "out" {
ExecutorCommand::Clear(RenderPane::CliOutput)
} else {
ExecutorCommand::InvalidCommand(msg!(ERROR, "clear: Unknown arg {}", args[0]))
}
}
_ => {
ExecutorCommand::InvalidCommand(msg!(ERROR, "Unknown Command {cmd}"))
}
};
let _ = self.exec_tx.send(exec_cmd).await;
}
}
//match *cmd {
// CMD_NODE => { cli(&s_split) }
// CMD_ECHO => {
// if args.is_empty() {
// ExecutorCommand::InvalidCommand(msg!(ERROR, "print expects args"))
// } else {
// ExecutorCommand::Echo(args.iter().map(|a| a.to_string()).collect())
// }
// }
// CMD_CLEAR => {
// if args.is_empty() {
// ExecutorCommand::Clear(RenderPane::All)
// } else if args[0] == "in" {
// ExecutorCommand::Clear(RenderPane::CliInput)
// } else if args[0] == "out" {
// ExecutorCommand::Clear(RenderPane::CliOutput)
// } else {
// ExecutorCommand::InvalidCommand(msg!(ERROR, "clear: Unknown arg {}", args[0]))
// }
// }
// _ => {
// ExecutorCommand::InvalidCommand(msg!(ERROR, "Unknown Command {cmd}"))
// }
// };
ParserCommand::Exit => {
log!(DEBUG, "Parser Exit");
self.exit();
self.exit().await;
}
}
}

View File

@ -31,7 +31,7 @@ pub struct Pane {
buffer: String,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone, clap::ValueEnum)]
pub enum RenderPane {
All,
CliInput,
@ -200,13 +200,32 @@ impl Widget for &Renderer {
}
})
.border_set(border::THICK);
let inner_area = block.inner(layout[p.layout_index as usize]);
let content_width = inner_area.width as usize;
let content_height = inner_area.height as usize;
let wrapped_lines = p.buffer
.lines()
.map(|line| {
if line.is_empty() {
1
} else {
(line.len() + content_width - 1) / content_width
}
})
.sum::<usize>();
let scroll_offset = if wrapped_lines > content_height {
wrapped_lines - content_height
} else {
0
};
Paragraph::new(p.buffer.clone())
.wrap(Wrap::default())
.left_aligned()
.block(block)
.scroll((scroll_offset as u16, 0))
.render(layout[p.layout_index as usize], buf);
}
}
}

View File

@ -1,12 +1,13 @@
use crate::watcher::*;
use crossterm::{event::{self, Event, KeyCode, KeyEventKind}};
use crossterm::event::{self, Event, KeyCode, KeyEventKind};
use tokio::sync::mpsc;
use std::{io::{self}, net::SocketAddr};
use crate::{native_node::node::{NativeNode, NodeCommand}};
use crate::native_node::node::{NativeNode, NodeCommand};
use vlogger::*;
#[allow(dead_code)]
pub struct Watcher {
render_tx: mpsc::Sender<RenderCommand>,
parser_tx: mpsc::Sender<ParserCommand>,
@ -27,9 +28,11 @@ impl Watcher {
WatcherBuilder::new()
}
pub async fn log(&self, msg: String) -> Result<(), mpsc::error::SendError<RenderCommand>> {
pub async fn log(render_tx: &mpsc::Sender<RenderCommand>, msg: String) {
let rendermsg = RenderCommand::RenderStringToPane { str: msg, pane: RenderPane::CliOutput };
self.render_tx.send(rendermsg).await
if let Err(e) = render_tx.send(rendermsg).await {
log!(FATAL, "Failed to send render command: {e}");
}
}
pub async fn exit(self) {
@ -57,8 +60,8 @@ impl Watcher {
KeyCode::Enter => {
let rd_mes = RenderCommand::RenderInput(k.code);
let pr_mes = ParserCommand::ParseCmdString(self.cmd_buffer.clone());
let _ = self.render_tx.send(rd_mes).await;
let _ = self.parser_tx.send(pr_mes).await;
let _ = self.render_tx.send(rd_mes).await;
self.cmd_buffer.clear();
}
KeyCode::Esc => {
@ -96,9 +99,9 @@ impl WatcherBuilder {
self
}
pub async fn log(render_tx: &mpsc::Sender<RenderCommand>, msg: String) -> Result<(), mpsc::error::SendError<RenderCommand>> {
pub async fn log(render_tx: &mpsc::Sender<RenderCommand>, msg: String) {
let rendermsg = RenderCommand::RenderStringToPane { str: msg, pane: RenderPane::CliOutput };
render_tx.send(rendermsg).await
let _ = render_tx.send(rendermsg).await;
}
@ -129,7 +132,7 @@ impl WatcherBuilder {
}
});
let _ = Self::log(&render_tx, msg!(INFO, "Started Parser")).await;
Watcher::log(&render_tx, msg!(INFO, "Started Parser")).await;
let executor_handle = tokio::spawn({
let rend_tx = render_tx.clone();
@ -139,7 +142,7 @@ impl WatcherBuilder {
}
});
let _ = Self::log(&render_tx, msg!(INFO, "Started Executor")).await;
Watcher::log(&render_tx, msg!(INFO, "Started Executor")).await;
let node_tx = node.tx();
@ -149,7 +152,7 @@ impl WatcherBuilder {
}
});
let _ = Self::log(&render_tx, msg!(INFO, "Started Node")).await;
Watcher::log(&render_tx, msg!(INFO, "Started Node")).await;
Watcher {
render_tx,