Merge branch 'dht-testing' into 'main'

Many bug fixes, colors update for CLI

See merge request veilid/veilid!31
This commit is contained in:
John Smith 2023-06-21 05:37:54 +00:00
commit 231099fa9e
25 changed files with 768 additions and 261 deletions

307
Cargo.lock generated
View File

@ -193,6 +193,26 @@ version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "arboard"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6041616acea41d67c4a984709ddab1587fd0b10efe5cc563fee954d2f011854"
dependencies = [
"clipboard-win",
"core-graphics",
"image",
"log",
"objc",
"objc-foundation",
"objc_id",
"once_cell",
"parking_lot 0.12.1",
"thiserror",
"winapi 0.3.9",
"x11rb",
]
[[package]] [[package]]
name = "argon2" name = "argon2"
version = "0.5.0" version = "0.5.0"
@ -727,6 +747,12 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "block"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.9.0" version = "0.9.0"
@ -827,6 +853,12 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "bytemuck"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.3" version = "1.4.3"
@ -1081,6 +1113,17 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]]
name = "clipboard-win"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362"
dependencies = [
"error-code",
"str-buf",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "cmake" name = "cmake"
version = "0.1.50" version = "0.1.50"
@ -1103,6 +1146,12 @@ dependencies = [
"owo-colors", "owo-colors",
] ]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]] [[package]]
name = "combine" name = "combine"
version = "4.6.6" version = "4.6.6"
@ -1241,6 +1290,30 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "core-graphics"
version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
dependencies = [
"bitflags 1.3.2",
"core-foundation 0.9.3",
"core-graphics-types",
"foreign-types",
"libc",
]
[[package]]
name = "core-graphics-types"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33"
dependencies = [
"bitflags 1.3.2",
"core-foundation 0.9.3",
"libc",
]
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.8" version = "0.2.8"
@ -1413,8 +1486,6 @@ dependencies = [
"lazy_static", "lazy_static",
"libc", "libc",
"log", "log",
"maplit",
"ncurses",
"signal-hook", "signal-hook",
"tokio 1.28.2", "tokio 1.28.2",
"unicode-segmentation", "unicode-segmentation",
@ -1431,7 +1502,7 @@ dependencies = [
"flexi_logger", "flexi_logger",
"lazy_static", "lazy_static",
"log", "log",
"time 0.3.22", "time 0.3.9",
"unicode-width", "unicode-width",
] ]
@ -1473,7 +1544,7 @@ dependencies = [
"owning_ref", "owning_ref",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"time 0.3.22", "time 0.3.9",
"tokio 1.28.2", "tokio 1.28.2",
"toml 0.7.4", "toml 0.7.4",
"unicode-segmentation", "unicode-segmentation",
@ -1826,6 +1897,16 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "error-code"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21"
dependencies = [
"libc",
"str-buf",
]
[[package]] [[package]]
name = "ethbloom" name = "ethbloom"
version = "0.13.0" version = "0.13.0"
@ -1912,6 +1993,15 @@ dependencies = [
"instant", "instant",
] ]
[[package]]
name = "fdeflate"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10"
dependencies = [
"simd-adler32",
]
[[package]] [[package]]
name = "ffi-support" name = "ffi-support"
version = "0.4.4" version = "0.4.4"
@ -1965,7 +2055,7 @@ dependencies = [
"regex", "regex",
"rustversion", "rustversion",
"thiserror", "thiserror",
"time 0.3.22", "time 0.3.9",
] ]
[[package]] [[package]]
@ -1987,6 +2077,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.0" version = "1.2.0"
@ -2176,6 +2281,16 @@ dependencies = [
"version_check 0.9.4", "version_check 0.9.4",
] ]
[[package]]
name = "gethostname"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e"
dependencies = [
"libc",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.1.16" version = "0.1.16"
@ -2593,6 +2708,21 @@ dependencies = [
"xmltree", "xmltree",
] ]
[[package]]
name = "image"
version = "0.24.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"num-rational",
"num-traits",
"png",
"tiff",
]
[[package]] [[package]]
name = "impl-codec" name = "impl-codec"
version = "0.6.0" version = "0.6.0"
@ -2778,6 +2908,12 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jpeg-decoder"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.64" version = "0.3.64"
@ -3029,6 +3165,15 @@ dependencies = [
"linked-hash-map", "linked-hash-map",
] ]
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "maplit" name = "maplit"
version = "1.0.2" version = "1.0.2"
@ -3129,6 +3274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [ dependencies = [
"adler", "adler",
"simd-adler32",
] ]
[[package]] [[package]]
@ -3232,17 +3378,6 @@ dependencies = [
"socket2 0.4.9", "socket2 0.4.9",
] ]
[[package]]
name = "ncurses"
version = "5.101.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e2c5d34d72657dc4b638a1c25d40aae81e4f1c699062f72f467237920752032"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]] [[package]]
name = "ndk" name = "ndk"
version = "0.7.0" version = "0.7.0"
@ -3402,6 +3537,18 @@ dependencies = [
"memoffset 0.6.5", "memoffset 0.6.5",
] ]
[[package]]
name = "nix"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
dependencies = [
"bitflags 1.3.2",
"cfg-if 1.0.0",
"libc",
"memoffset 0.6.5",
]
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.26.2" version = "0.26.2"
@ -3590,6 +3737,35 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
]
[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
"block",
"objc",
"objc_id",
]
[[package]]
name = "objc_id"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
dependencies = [
"objc",
]
[[package]] [[package]]
name = "object" name = "object"
version = "0.30.4" version = "0.30.4"
@ -4044,6 +4220,19 @@ dependencies = [
"plotters-backend", "plotters-backend",
] ]
[[package]]
name = "png"
version = "0.17.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide 0.7.1",
]
[[package]] [[package]]
name = "polling" name = "polling"
version = "2.8.0" version = "2.8.0"
@ -5091,6 +5280,12 @@ version = "1.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
[[package]]
name = "simd-adler32"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f"
[[package]] [[package]]
name = "simdutf8" name = "simdutf8"
version = "0.1.4" version = "0.1.4"
@ -5116,7 +5311,7 @@ checksum = "acee08041c5de3d5048c8b3f6f13fafb3026b24ba43c6a695a0c76179b844369"
dependencies = [ dependencies = [
"log", "log",
"termcolor", "termcolor",
"time 0.3.22", "time 0.3.9",
] ]
[[package]] [[package]]
@ -5261,6 +5456,12 @@ dependencies = [
"pin-project-lite 0.2.9", "pin-project-lite 0.2.9",
] ]
[[package]]
name = "str-buf"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.8.0" version = "0.8.0"
@ -5422,6 +5623,17 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "tiff"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471"
dependencies = [
"flate2",
"jpeg-decoder",
"weezl",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.45" version = "0.1.45"
@ -5450,24 +5662,16 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.22" version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd"
dependencies = [ dependencies = [
"itoa", "itoa",
"libc", "libc",
"num_threads", "num_threads",
"serde", "time-macros 0.2.4",
"time-core",
"time-macros 0.2.9",
] ]
[[package]]
name = "time-core"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.1.1" version = "0.1.1"
@ -5480,12 +5684,9 @@ dependencies = [
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.2.9" version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
dependencies = [
"time-core",
]
[[package]] [[package]]
name = "time-macros-impl" name = "time-macros-impl"
@ -5797,7 +5998,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e" checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e"
dependencies = [ dependencies = [
"crossbeam-channel", "crossbeam-channel",
"time 0.3.22", "time 0.3.9",
"tracing-subscriber", "tracing-subscriber",
] ]
@ -6164,6 +6365,7 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
name = "veilid-cli" name = "veilid-cli"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"arboard",
"async-std", "async-std",
"async-tungstenite 0.8.0", "async-tungstenite 0.8.0",
"bugsalot", "bugsalot",
@ -6683,6 +6885,12 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "weezl"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
[[package]] [[package]]
name = "wg" name = "wg"
version = "0.3.2" version = "0.3.2"
@ -6754,6 +6962,15 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "winapi-wsapoll"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e"
dependencies = [
"winapi 0.3.9",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"
@ -7022,6 +7239,28 @@ dependencies = [
"tap", "tap",
] ]
[[package]]
name = "x11rb"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507"
dependencies = [
"gethostname",
"nix 0.24.3",
"winapi 0.3.9",
"winapi-wsapoll",
"x11rb-protocol",
]
[[package]]
name = "x11rb-protocol"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67"
dependencies = [
"nix 0.24.3",
]
[[package]] [[package]]
name = "x25519-dalek" name = "x25519-dalek"
version = "1.2.0" version = "1.2.0"

@ -1 +1 @@
Subproject commit effa60cea24e99f294865ed325ffc57612d72785 Subproject commit 1eaa0814e0dd7e585b50c5d4e6e7cb9474d7a758

View File

@ -11,7 +11,6 @@ path = "src/main.rs"
[features] [features]
default = [ "rt-tokio" ] default = [ "rt-tokio" ]
macos = [ "cursive/ncurses-backend" ]
rt-async-std = [ "async-std", "veilid-tools/rt-async-std", "cursive/rt-async-std" ] rt-async-std = [ "async-std", "veilid-tools/rt-async-std", "cursive/rt-async-std" ]
rt-tokio = [ "tokio", "tokio-util", "veilid-tools/rt-tokio", "cursive/rt-tokio" ] rt-tokio = [ "tokio", "tokio-util", "veilid-tools/rt-tokio", "cursive/rt-tokio" ]
@ -26,6 +25,7 @@ cursive_buffered_backend = { path = "../external/cursive_buffered_backend" }
# cursive-multiplex = "0.6.0" # cursive-multiplex = "0.6.0"
# cursive_tree_view = "0.6.0" # cursive_tree_view = "0.6.0"
cursive_table_view = "0.14.0" cursive_table_view = "0.14.0"
arboard = "3.2.0"
# cursive-tabs = "0.5.0" # cursive-tabs = "0.5.0"
clap = "^3" clap = "^3"
directories = "^4" directories = "^4"

View File

@ -83,7 +83,7 @@ impl ClientApiConnection {
async fn process_veilid_update(&self, update: json::JsonValue) { async fn process_veilid_update(&self, update: json::JsonValue) {
let comproc = self.inner.lock().comproc.clone(); let comproc = self.inner.lock().comproc.clone();
let Some(kind) = update["kind"].as_str() else { let Some(kind) = update["kind"].as_str() else {
comproc.log_message(format!("missing update kind: {}", update)); comproc.log_message(Level::Error, format!("missing update kind: {}", update));
return; return;
}; };
match kind { match kind {
@ -113,7 +113,7 @@ impl ClientApiConnection {
comproc.update_value_change(&update); comproc.update_value_change(&update);
} }
_ => { _ => {
comproc.log_message(format!("unknown update kind: {}", update)); comproc.log_message(Level::Error, format!("unknown update kind: {}", update));
} }
} }
} }

View File

@ -105,6 +105,7 @@ impl CommandProcessor {
pub fn cmd_help(&self, _rest: Option<String>, callback: UICallback) -> Result<(), String> { pub fn cmd_help(&self, _rest: Option<String>, callback: UICallback) -> Result<(), String> {
trace!("CommandProcessor::cmd_help"); trace!("CommandProcessor::cmd_help");
self.ui_sender().add_node_event( self.ui_sender().add_node_event(
Level::Info,
r#"Commands: r#"Commands:
exit/quit - exit the client exit/quit - exit the client
disconnect - disconnect the client from the Veilid node disconnect - disconnect the client from the Veilid node
@ -186,8 +187,14 @@ reply - reply to an AppCall not handled directly by the server
let ui = self.ui_sender(); let ui = self.ui_sender();
spawn_detached_local(async move { spawn_detached_local(async move {
match capi.server_debug(rest.unwrap_or_default()).await { match capi.server_debug(rest.unwrap_or_default()).await {
Ok(output) => ui.display_string_dialog("Debug Output", output, callback), Ok(output) => {
Err(e) => ui.display_string_dialog("Debug Error", e.to_string(), callback), ui.add_node_event(Level::Debug, output);
ui.send_callback(callback);
}
Err(e) => {
ui.add_node_event(Level::Error, e.to_string());
ui.send_callback(callback);
}
} }
}); });
Ok(()) Ok(())
@ -206,7 +213,7 @@ reply - reply to an AppCall not handled directly by the server
let log_level = match convert_loglevel(&rest.unwrap_or_default()) { let log_level = match convert_loglevel(&rest.unwrap_or_default()) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
ui.add_node_event(format!("Failed to change log level: {}", e)); ui.add_node_event(Level::Error, format!("Failed to change log level: {}", e));
ui.send_callback(callback); ui.send_callback(callback);
return; return;
} }
@ -239,7 +246,7 @@ reply - reply to an AppCall not handled directly by the server
let (id, msg) = if let Some(second) = second { let (id, msg) = if let Some(second) = second {
let id = match u64::from_str(&first) { let id = match u64::from_str(&first) {
Err(e) => { Err(e) => {
ui.add_node_event(format!("invalid appcall id: {}", e)); ui.add_node_event(Level::Error, format!("invalid appcall id: {}", e));
ui.send_callback(callback); ui.send_callback(callback);
return; return;
} }
@ -249,7 +256,7 @@ reply - reply to an AppCall not handled directly by the server
} else { } else {
let id = match some_last_id { let id = match some_last_id {
None => { None => {
ui.add_node_event("must specify last call id".to_owned()); ui.add_node_event(Level::Error, "must specify last call id".to_owned());
ui.send_callback(callback); ui.send_callback(callback);
return; return;
} }
@ -260,7 +267,7 @@ reply - reply to an AppCall not handled directly by the server
let msg = if msg[0..1] == "#".to_owned() { let msg = if msg[0..1] == "#".to_owned() {
match hex::decode(msg[1..].as_bytes().to_vec()) { match hex::decode(msg[1..].as_bytes().to_vec()) {
Err(e) => { Err(e) => {
ui.add_node_event(format!("invalid hex message: {}", e)); ui.add_node_event(Level::Error, format!("invalid hex message: {}", e));
ui.send_callback(callback); ui.send_callback(callback);
return; return;
} }
@ -272,7 +279,10 @@ reply - reply to an AppCall not handled directly by the server
let msglen = msg.len(); let msglen = msg.len();
match capi.server_appcall_reply(id, msg).await { match capi.server_appcall_reply(id, msg).await {
Ok(()) => { Ok(()) => {
ui.add_node_event(format!("reply sent to {} : {} bytes", id, msglen)); ui.add_node_event(
Level::Info,
format!("reply sent to {} : {} bytes", id, msglen),
);
ui.send_callback(callback); ui.send_callback(callback);
return; return;
} }
@ -383,8 +393,8 @@ reply - reply to an AppCall not handled directly by the server
// calls into ui // calls into ui
//////////////////////////////////////////// ////////////////////////////////////////////
pub fn log_message(&self, message: String) { pub fn log_message(&self, log_level: Level, message: String) {
self.inner().ui_sender.add_node_event(message); self.inner().ui_sender.add_node_event(log_level, message);
} }
pub fn update_attachment(&self, attachment: &json::JsonValue) { pub fn update_attachment(&self, attachment: &json::JsonValue) {
@ -428,25 +438,30 @@ reply - reply to an AppCall not handled directly by the server
)); ));
} }
if !out.is_empty() { if !out.is_empty() {
self.inner().ui_sender.add_node_event(out); self.inner().ui_sender.add_node_event(Level::Info, out);
} }
} }
pub fn update_value_change(&self, value_change: &json::JsonValue) { pub fn update_value_change(&self, value_change: &json::JsonValue) {
let out = format!("Value change: {:?}", value_change.as_str().unwrap_or("???")); let out = format!("Value change: {:?}", value_change.as_str().unwrap_or("???"));
self.inner().ui_sender.add_node_event(out); self.inner().ui_sender.add_node_event(Level::Info, out);
} }
pub fn update_log(&self, log: &json::JsonValue) { pub fn update_log(&self, log: &json::JsonValue) {
self.inner().ui_sender.add_node_event(format!( let log_level =
"{}: {}{}", Level::from_str(log["log_level"].as_str().unwrap_or("error")).unwrap_or(Level::Error);
log["log_level"].as_str().unwrap_or("???"), self.inner().ui_sender.add_node_event(
log["message"].as_str().unwrap_or("???"), log_level,
if let Some(bt) = log["backtrace"].as_str() { format!(
format!("\nBacktrace:\n{}", bt) "{}: {}{}",
} else { log["log_level"].as_str().unwrap_or("???"),
"".to_owned() log["message"].as_str().unwrap_or("???"),
} if let Some(bt) = log["backtrace"].as_str() {
)); format!("\nBacktrace:\n{}", bt)
} else {
"".to_owned()
}
),
);
} }
pub fn update_app_message(&self, msg: &json::JsonValue) { pub fn update_app_message(&self, msg: &json::JsonValue) {
@ -466,9 +481,10 @@ reply - reply to an AppCall not handled directly by the server
hex::encode(message) hex::encode(message)
}; };
self.inner() self.inner().ui_sender.add_node_event(
.ui_sender Level::Info,
.add_node_event(format!("AppMessage ({:?}): {}", msg["sender"], strmsg)); format!("AppMessage ({:?}): {}", msg["sender"], strmsg),
);
} }
pub fn update_app_call(&self, call: &json::JsonValue) { pub fn update_app_call(&self, call: &json::JsonValue) {
@ -490,10 +506,13 @@ reply - reply to an AppCall not handled directly by the server
let id = json_str_u64(&call["call_id"]); let id = json_str_u64(&call["call_id"]);
self.inner().ui_sender.add_node_event(format!( self.inner().ui_sender.add_node_event(
"AppCall ({:?}) id = {:016x} : {}", Level::Info,
call["sender"], id, strmsg format!(
)); "AppCall ({:?}) id = {:016x} : {}",
call["sender"], id, strmsg
),
);
self.inner_mut().last_call_id = Some(id); self.inner_mut().last_call_id = Some(id);
} }

View File

@ -74,8 +74,14 @@ impl TableViewItem<PeerTableColumn> for json::JsonValue {
Self: Sized, Self: Sized,
{ {
match column { match column {
PeerTableColumn::NodeId => self.to_column(column).cmp(&other.to_column(column)), PeerTableColumn::NodeId => self
PeerTableColumn::Address => self.to_column(column).cmp(&other.to_column(column)), .to_column(column)
.to_ascii_lowercase()
.cmp(&other.to_column(column).to_ascii_lowercase()),
PeerTableColumn::Address => self
.to_column(column)
.to_ascii_lowercase()
.cmp(&other.to_column(column).to_ascii_lowercase()),
PeerTableColumn::LatencyAvg => json_str_u64(&self["peer_stats"]["latency"]["average"]) PeerTableColumn::LatencyAvg => json_str_u64(&self["peer_stats"]["latency"]["average"])
.cmp(&json_str_u64(&other["peer_stats"]["latency"]["average"])), .cmp(&json_str_u64(&other["peer_stats"]["latency"]["average"])),
PeerTableColumn::TransferDownAvg => { PeerTableColumn::TransferDownAvg => {

View File

@ -27,23 +27,23 @@ interface:
shadow: false shadow: false
borders: "simple" borders: "simple"
colors: colors:
background : "#333D3D" background : "black"
shadow : "#000000" shadow : "black"
view : "#1c2323" view : "black"
primary : "#a6d8d3" primary : "light cyan"
secondary : "#8cb4b7" secondary : "cyan"
tertiary : "#eeeeee" tertiary : "green"
title_primary : "#f93fbd" title_primary : "light magenta"
title_secondary : "#ff0000" title_secondary : "magenta"
highlight : "#f93fbd" highlight : "light white"
highlight_inactive : "#a6d8d3" highlight_inactive : "white"
highlight_text : "#333333" highlight_text : "black"
log_colors: log_colors:
trace : "#707070" trace : "light blue"
debug : "#a0a0a0" debug : "light green"
info : "#5cd3c6" info : "white"
warn : "#fedc50" warn : "light yellow"
error : "#ff4a15" error : "light red"
"### "###
.replace( .replace(
"%LOGGING_FILE_DIRECTORY%", "%LOGGING_FILE_DIRECTORY%",

View File

@ -12,10 +12,10 @@ use cursive::views::*;
use cursive::Cursive; use cursive::Cursive;
use cursive::CursiveRunnable; use cursive::CursiveRunnable;
use cursive_flexi_logger_view::{CursiveLogWriter, FlexiLoggerView}; use cursive_flexi_logger_view::{CursiveLogWriter, FlexiLoggerView};
//use cursive_multiplex::*; // use cursive_multiplex::*;
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
use std::io::Write;
use thiserror::Error; use thiserror::Error;
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
/// ///
struct Dirty<T> { struct Dirty<T> {
@ -311,8 +311,7 @@ impl UI {
.button("Close", move |s| { .button("Close", move |s| {
s.pop_layer(); s.pop_layer();
close_cb(s); close_cb(s);
}), //.wrap_with(CircularFocus::new) }),
//.wrap_tab(),
); );
s.set_global_callback(cursive::event::Event::Key(Key::Esc), move |s| { s.set_global_callback(cursive::event::Event::Key(Key::Esc), move |s| {
s.set_global_callback(cursive::event::Event::Key(Key::Esc), UI::quit_handler); s.set_global_callback(cursive::event::Event::Key(Key::Esc), UI::quit_handler);
@ -346,18 +345,19 @@ impl UI {
return; return;
} }
// run command // run command
cursive_flexi_logger_view::push_to_log(StyledString::styled(
cursive_flexi_logger_view::parse_lines_to_log(
ColorStyle::primary().into(),
format!("> {}", text), format!("> {}", text),
ColorStyle::primary(), );
));
match Self::run_command(s, text) { match Self::run_command(s, text) {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
let color = *Self::inner_mut(s).log_colors.get(&Level::Error).unwrap(); let color = *Self::inner_mut(s).log_colors.get(&Level::Error).unwrap();
cursive_flexi_logger_view::push_to_log(StyledString::styled( cursive_flexi_logger_view::parse_lines_to_log(
color.into(),
format!(" Error: {}", e), format!(" Error: {}", e),
color, );
));
} }
} }
// save to history unless it's a duplicate // save to history unless it's a duplicate
@ -455,6 +455,55 @@ impl UI {
Self::command_processor(s).start_connection(); Self::command_processor(s).start_connection();
} }
fn copy_to_clipboard<S: AsRef<str>>(s: &mut Cursive, text: S) {
if let Ok(mut clipboard) = arboard::Clipboard::new() {
// X11/Wayland/other system copy
if clipboard.set_text(text.as_ref()).is_ok() {
let color = *Self::inner_mut(s).log_colors.get(&Level::Info).unwrap();
cursive_flexi_logger_view::parse_lines_to_log(
color.into(),
format!(">> Copied: {}", text.as_ref()),
);
} else {
let color = *Self::inner_mut(s).log_colors.get(&Level::Warn).unwrap();
cursive_flexi_logger_view::parse_lines_to_log(
color.into(),
format!(">> Could not copy to clipboard"),
);
}
} else {
// OSC52 clipboard copy for terminals
if std::io::stdout()
.write_all(
format!(
"\x1B]52;c;{}\x07",
data_encoding::BASE64.encode(text.as_ref().as_bytes()),
)
.as_bytes(),
)
.is_ok()
{
if std::io::stdout().flush().is_ok() {
let color = *Self::inner_mut(s).log_colors.get(&Level::Info).unwrap();
cursive_flexi_logger_view::parse_lines_to_log(
color.into(),
format!(">> Copied: {}", text.as_ref()),
);
}
}
}
}
fn on_submit_peers_table_view(s: &mut Cursive, _row: usize, index: usize) {
let peers_table_view = UI::peers(s);
let node_id = peers_table_view
.borrow_item(index)
.map(|j| j["node_ids"][0].to_string());
if let Some(node_id) = node_id {
Self::copy_to_clipboard(s, node_id);
}
}
fn show_connection_dialog(s: &mut Cursive, state: ConnectionState) -> bool { fn show_connection_dialog(s: &mut Cursive, state: ConnectionState) -> bool {
let mut inner = Self::inner_mut(s); let mut inner = Self::inner_mut(s);
@ -644,7 +693,28 @@ impl UI {
fn refresh_peers(s: &mut Cursive) { fn refresh_peers(s: &mut Cursive) {
let mut peers = UI::peers(s); let mut peers = UI::peers(s);
let inner = Self::inner_mut(s); let inner = Self::inner_mut(s);
let sel_item = peers.item();
let sel_item_text = peers
.item()
.map(|x| peers.borrow_items()[x]["node_ids"][0].clone());
peers.set_items_stable(inner.ui_state.peers_state.get().clone()); peers.set_items_stable(inner.ui_state.peers_state.get().clone());
let mut selected = false;
if let Some(sel_item_text) = sel_item_text {
// First select by name
for n in 0..peers.borrow_items().len() {
if peers.borrow_items()[n]["node_ids"][0] == sel_item_text {
peers.set_selected_item(n);
selected = true;
}
}
}
if !selected {
if let Some(sel_item) = sel_item {
peers.set_selected_item(sel_item);
}
}
} }
fn update_cb(s: &mut Cursive) { fn update_cb(s: &mut Cursive) {
@ -707,9 +777,6 @@ impl UI {
// Instantiate the cursive runnable // Instantiate the cursive runnable
let runnable = CursiveRunnable::new( let runnable = CursiveRunnable::new(
|| -> Result<Box<dyn cursive::backend::Backend>, Box<DumbError>> { || -> Result<Box<dyn cursive::backend::Backend>, Box<DumbError>> {
#[cfg(feature = "macos")]
let backend = cursive::backends::curses::n::Backend::init().unwrap();
#[cfg(not(feature = "macos"))]
let backend = cursive::backends::crossterm::Backend::init().unwrap(); let backend = cursive::backends::crossterm::Backend::init().unwrap();
let buffered_backend = cursive_buffered_backend::BufferedBackend::new(backend); let buffered_backend = cursive_buffered_backend::BufferedBackend::new(backend);
Ok(Box::new(buffered_backend)) Ok(Box::new(buffered_backend))
@ -737,6 +804,11 @@ impl UI {
})), })),
}; };
let ui_sender = UISender {
inner: this.inner.clone(),
cb_sink,
};
let mut inner = this.inner.lock(); let mut inner = this.inner.lock();
// Make the inner object accessible in callbacks easily // Make the inner object accessible in callbacks easily
@ -750,12 +822,14 @@ impl UI {
.with_name("node-events-panel") .with_name("node-events-panel")
.full_screen(); .full_screen();
let peers_table_view = PeersTableView::new() let mut peers_table_view = PeersTableView::new()
.column(PeerTableColumn::NodeId, "Node Id", |c| c.width(48)) .column(PeerTableColumn::NodeId, "Node Id", |c| c.width(48))
.column(PeerTableColumn::Address, "Address", |c| c) .column(PeerTableColumn::Address, "Address", |c| c)
.column(PeerTableColumn::LatencyAvg, "Ping", |c| c.width(8)) .column(PeerTableColumn::LatencyAvg, "Ping", |c| c.width(8))
.column(PeerTableColumn::TransferDownAvg, "Down", |c| c.width(8)) .column(PeerTableColumn::TransferDownAvg, "Down", |c| c.width(8))
.column(PeerTableColumn::TransferUpAvg, "Up", |c| c.width(8)) .column(PeerTableColumn::TransferUpAvg, "Up", |c| c.width(8));
peers_table_view.set_on_submit(UI::on_submit_peers_table_view);
let peers_table_view = peers_table_view
.with_name("peers") .with_name("peers")
.full_width() .full_width()
.min_height(8); .min_height(8);
@ -832,8 +906,7 @@ impl UI {
drop(inner); drop(inner);
let inner = this.inner.clone(); (this, ui_sender)
(this, UISender { inner, cb_sink })
} }
pub fn cursive_flexi_logger(&self) -> Box<CursiveLogWriter> { pub fn cursive_flexi_logger(&self) -> Box<CursiveLogWriter> {
let mut flv = cursive_flexi_logger_view::cursive_flexi_logger(self.siv.cb_sink().clone()); let mut flv = cursive_flexi_logger_view::cursive_flexi_logger(self.siv.cb_sink().clone());
@ -906,7 +979,7 @@ impl UISender {
started: bool, started: bool,
bps_down: u64, bps_down: u64,
bps_up: u64, bps_up: u64,
peers: Vec<json::JsonValue>, mut peers: Vec<json::JsonValue>,
) { ) {
{ {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
@ -915,6 +988,11 @@ impl UISender {
((bps_down as f64) / 1000.0f64) as f32, ((bps_down as f64) / 1000.0f64) as f32,
((bps_up as f64) / 1000.0f64) as f32, ((bps_up as f64) / 1000.0f64) as f32,
)); ));
peers.sort_by(|a, b| {
a["node_ids"][0]
.to_string()
.cmp(&b["node_ids"][0].to_string())
});
inner.ui_state.peers_state.set(peers); inner.ui_state.peers_state.set(peers);
} }
let _ = self.cb_sink.send(Box::new(UI::update_cb)); let _ = self.cb_sink.send(Box::new(UI::update_cb));
@ -922,10 +1000,18 @@ impl UISender {
pub fn set_config(&mut self, config: &json::JsonValue) { pub fn set_config(&mut self, config: &json::JsonValue) {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
inner let node_ids = &config["network"]["routing_table"]["node_id"];
.ui_state
.node_id let mut node_id_str = String::new();
.set(config["network"]["routing_table"]["node_id"].to_string()); for l in 0..node_ids.len() {
let nid = &node_ids[l];
if !node_id_str.is_empty() {
node_id_str.push_str(" ");
}
node_id_str.push_str(nid.to_string().as_ref());
}
inner.ui_state.node_id.set(node_id_str);
} }
pub fn set_connection_state(&mut self, state: ConnectionState) { pub fn set_connection_state(&mut self, state: ConnectionState) {
{ {
@ -935,17 +1021,11 @@ impl UISender {
let _ = self.cb_sink.send(Box::new(UI::update_cb)); let _ = self.cb_sink.send(Box::new(UI::update_cb));
} }
pub fn add_node_event(&self, event: String) { pub fn add_node_event(&self, log_color: Level, event: String) {
{ {
let inner = self.inner.lock(); let inner = self.inner.lock();
let color = *inner.log_colors.get(&Level::Info).unwrap(); let color = *inner.log_colors.get(&log_color).unwrap();
let mut starting_style: Style = color.into(); cursive_flexi_logger_view::parse_lines_to_log(color.into(), event);
for line in event.lines() {
let (spanned_string, end_style) =
cursive::utils::markup::ansi::parse_with_starting_style(starting_style, line);
cursive_flexi_logger_view::push_to_log(spanned_string);
starting_style = end_style;
}
} }
let _ = self.cb_sink.send(Box::new(UI::update_cb)); let _ = self.cb_sink.send(Box::new(UI::update_cb));
} }

View File

@ -130,6 +130,7 @@ macro_rules! byte_array_type {
Self { bytes } Self { bytes }
} }
// Big endian bit ordering
pub fn bit(&self, index: usize) -> bool { pub fn bit(&self, index: usize) -> bool {
assert!(index < ($size * 8)); assert!(index < ($size * 8));
let bi = index / 8; let bi = index / 8;
@ -152,6 +153,7 @@ macro_rules! byte_array_type {
None None
} }
// Big endian nibble ordering
pub fn nibble(&self, index: usize) -> u8 { pub fn nibble(&self, index: usize) -> u8 {
assert!(index < ($size * 2)); assert!(index < ($size * 2));
let bi = index / 2; let bi = index / 2;

View File

@ -353,10 +353,40 @@ async fn test_operations(vcrypto: CryptoSystemVersion) {
assert_eq!(d4.first_nonzero_bit(), Some(0)); assert_eq!(d4.first_nonzero_bit(), Some(0));
} }
pub async fn test_crypto_key_ordering() {
let k1 = CryptoKey::new([
128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,
]);
let k2 = CryptoKey::new([
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,
]);
let k3 = CryptoKey::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 128,
]);
let k4 = CryptoKey::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1,
]);
let k5 = CryptoKey::new([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,
]);
assert!(k2 < k1);
assert!(k3 < k2);
assert!(k4 < k3);
assert!(k5 < k4);
}
pub async fn test_all() { pub async fn test_all() {
let api = crypto_tests_startup().await; let api = crypto_tests_startup().await;
let crypto = api.crypto().unwrap(); let crypto = api.crypto().unwrap();
test_crypto_key_ordering().await;
// Test versions // Test versions
for v in VALID_CRYPTO_KINDS { for v in VALID_CRYPTO_KINDS {
let vcrypto = crypto.get(v).unwrap(); let vcrypto = crypto.get(v).unwrap();

View File

@ -51,6 +51,7 @@ impl RoutingTable {
return NetworkResult::invalid_message("unsupported cryptosystem"); return NetworkResult::invalid_message("unsupported cryptosystem");
}; };
let own_distance = vcrypto.distance(&own_node_id.value, &key.value); let own_distance = vcrypto.distance(&own_node_id.value, &key.value);
let vcrypto2 = vcrypto.clone();
let filter = Box::new( let filter = Box::new(
move |rti: &RoutingTableInner, opt_entry: Option<Arc<BucketEntry>>| { move |rti: &RoutingTableInner, opt_entry: Option<Arc<BucketEntry>>| {
@ -98,6 +99,46 @@ impl RoutingTable {
}, },
); );
// xxx test
// Validate peers returned are, in fact, closer to the key than the node we sent this to
let valid = match Self::verify_peers_closer(vcrypto2, own_node_id, key, &closest_nodes) {
Ok(v) => v,
Err(e) => {
panic!("missing cryptosystem in peers node ids: {}", e);
}
};
if !valid {
panic!("non-closer peers returned");
}
NetworkResult::value(closest_nodes) NetworkResult::value(closest_nodes)
} }
/// Determine if set of peers is closer to key_near than key_far
pub(crate) fn verify_peers_closer(
vcrypto: CryptoSystemVersion,
key_far: TypedKey,
key_near: TypedKey,
peers: &[PeerInfo],
) -> EyreResult<bool> {
let kind = vcrypto.kind();
if key_far.kind != kind || key_near.kind != kind {
bail!("keys all need the same cryptosystem");
}
let mut closer = true;
for peer in peers {
let Some(key_peer) = peer.node_ids().get(kind) else {
bail!("peers need to have a key with the same cryptosystem");
};
let d_near = vcrypto.distance(&key_near.value, &key_peer.value);
let d_far = vcrypto.distance(&key_far.value, &key_peer.value);
if d_far < d_near {
closer = false;
}
}
Ok(closer)
}
} }

View File

@ -29,6 +29,13 @@ pub enum Destination {
} }
impl Destination { impl Destination {
pub fn target(&self) -> Option<NodeRef> {
match self {
Destination::Direct { target, safety_selection: _ } => Some(target.clone()),
Destination::Relay { relay:_, target, safety_selection: _ } => Some(target.clone()),
Destination::PrivateRoute { private_route:_, safety_selection:_ } => None,
}
}
pub fn direct(target: NodeRef) -> Self { pub fn direct(target: NodeRef) -> Self {
let sequencing = target.sequencing(); let sequencing = target.sequencing();
Self::Direct { Self::Direct {

View File

@ -88,6 +88,7 @@ where
for cn in &ctx.closest_nodes { for cn in &ctx.closest_nodes {
if cn.same_entry(&nn) { if cn.same_entry(&nn) {
dup = true; dup = true;
break;
} }
} }
if !dup { if !dup {
@ -125,6 +126,7 @@ where
// New fanout call candidate found // New fanout call candidate found
next_node = Some(cn.clone()); next_node = Some(cn.clone());
ctx.called_nodes.add(key); ctx.called_nodes.add(key);
break;
} }
} }
} }

View File

@ -395,7 +395,7 @@ impl RPCProcessor {
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
/// Determine if a SignedNodeInfo can be placed into the specified routing domain /// Determine if a SignedNodeInfo can be placed into the specified routing domain
fn filter_node_info( fn verify_node_info(
&self, &self,
routing_domain: RoutingDomain, routing_domain: RoutingDomain,
signed_node_info: &SignedNodeInfo, signed_node_info: &SignedNodeInfo,
@ -1348,7 +1348,7 @@ impl RPCProcessor {
// Ensure the sender peer info is for the actual sender specified in the envelope // Ensure the sender peer info is for the actual sender specified in the envelope
// Sender PeerInfo was specified, update our routing table with it // Sender PeerInfo was specified, update our routing table with it
if !self.filter_node_info(routing_domain, sender_peer_info.signed_node_info()) { if !self.verify_node_info(routing_domain, sender_peer_info.signed_node_info()) {
return Ok(NetworkResult::invalid_message( return Ok(NetworkResult::invalid_message(
"sender peerinfo has invalid peer scope", "sender peerinfo has invalid peer scope",
)); ));

View File

@ -29,9 +29,9 @@ impl RPCProcessor {
let app_call_a = match kind { let app_call_a = match kind {
RPCOperationKind::Answer(a) => match a.destructure() { RPCOperationKind::Answer(a) => match a.destructure() {
RPCAnswerDetail::AppCallA(a) => a, RPCAnswerDetail::AppCallA(a) => a,
_ => return Err(RPCError::invalid_format("not an appcall answer")), _ => return Ok(NetworkResult::invalid_message("not an appcall answer")),
}, },
_ => return Err(RPCError::invalid_format("not an answer")), _ => return Ok(NetworkResult::invalid_message("not an answer")),
}; };
let a_message = app_call_a.destructure(); let a_message = app_call_a.destructure();

View File

@ -46,17 +46,17 @@ impl RPCProcessor {
let find_node_a = match kind { let find_node_a = match kind {
RPCOperationKind::Answer(a) => match a.destructure() { RPCOperationKind::Answer(a) => match a.destructure() {
RPCAnswerDetail::FindNodeA(a) => a, RPCAnswerDetail::FindNodeA(a) => a,
_ => return Err(RPCError::invalid_format("not a find_node answer")), _ => return Ok(NetworkResult::invalid_message("not a find_node answer")),
}, },
_ => return Err(RPCError::invalid_format("not an answer")), _ => return Ok(NetworkResult::invalid_message("not an answer")),
}; };
// Verify peers are in the correct peer scope // Verify peers are in the correct peer scope
let peers = find_node_a.destructure(); let peers = find_node_a.destructure();
for peer_info in &peers { for peer_info in &peers {
if !self.filter_node_info(RoutingDomain::PublicInternet, peer_info.signed_node_info()) { if !self.verify_node_info(RoutingDomain::PublicInternet, peer_info.signed_node_info()) {
return Err(RPCError::invalid_format( return Ok(NetworkResult::invalid_message(
"find_node response has invalid peer scope", "find_node response has invalid peer scope",
)); ));
} }

View File

@ -24,32 +24,32 @@ impl RPCProcessor {
last_descriptor: Option<SignedValueDescriptor>, last_descriptor: Option<SignedValueDescriptor>,
) -> Result<NetworkResult<Answer<GetValueAnswer>>, RPCError> { ) -> Result<NetworkResult<Answer<GetValueAnswer>>, RPCError> {
// Ensure destination never has a private route // Ensure destination never has a private route
if matches!( // and get the target noderef so we can validate the response
dest, let Some(target) = dest.target() else {
Destination::PrivateRoute {
private_route: _,
safety_selection: _
}
) {
return Err(RPCError::internal( return Err(RPCError::internal(
"Never send get value requests over private routes", "Never send set value requests over private routes",
)); ));
} };
// Get the target node id
let Some(vcrypto) = self.crypto.get(key.kind) else {
return Err(RPCError::internal("unsupported cryptosystem"));
};
let Some(target_node_id) = target.node_ids().get(key.kind) else {
return Err(RPCError::internal("No node id for crypto kind"));
};
// Send the getvalue question
let get_value_q = RPCOperationGetValueQ::new(key, subkey, last_descriptor.is_none()); let get_value_q = RPCOperationGetValueQ::new(key, subkey, last_descriptor.is_none());
let question = RPCQuestion::new( let question = RPCQuestion::new(
network_result_try!(self.get_destination_respond_to(&dest)?), network_result_try!(self.get_destination_respond_to(&dest)?),
RPCQuestionDetail::GetValueQ(get_value_q), RPCQuestionDetail::GetValueQ(get_value_q),
); );
let Some(vcrypto) = self.crypto.get(key.kind) else {
return Err(RPCError::internal("unsupported cryptosystem"));
};
// Send the getvalue question
let question_context = QuestionContext::GetValue(ValidateGetValueContext { let question_context = QuestionContext::GetValue(ValidateGetValueContext {
last_descriptor, last_descriptor,
subkey, subkey,
vcrypto, vcrypto: vcrypto.clone(),
}); });
let waitable_reply = network_result_try!( let waitable_reply = network_result_try!(
@ -68,13 +68,27 @@ impl RPCProcessor {
let get_value_a = match kind { let get_value_a = match kind {
RPCOperationKind::Answer(a) => match a.destructure() { RPCOperationKind::Answer(a) => match a.destructure() {
RPCAnswerDetail::GetValueA(a) => a, RPCAnswerDetail::GetValueA(a) => a,
_ => return Err(RPCError::invalid_format("not a getvalue answer")), _ => return Ok(NetworkResult::invalid_message("not a getvalue answer")),
}, },
_ => return Err(RPCError::invalid_format("not an answer")), _ => return Ok(NetworkResult::invalid_message("not an answer")),
}; };
let (value, peers, descriptor) = get_value_a.destructure(); let (value, peers, descriptor) = get_value_a.destructure();
// Validate peers returned are, in fact, closer to the key than the node we sent this to
let valid = match RoutingTable::verify_peers_closer(vcrypto, target_node_id, key, &peers) {
Ok(v) => v,
Err(e) => {
return Ok(NetworkResult::invalid_message(format!(
"missing cryptosystem in peers node ids: {}",
e
)));
}
};
if !valid {
return Ok(NetworkResult::invalid_message("non-closer peers returned"));
}
Ok(NetworkResult::value(Answer::new( Ok(NetworkResult::value(Answer::new(
latency, latency,
GetValueAnswer { GetValueAnswer {

View File

@ -62,7 +62,7 @@ impl RPCProcessor {
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Make sure hop count makes sense // Make sure hop count makes sense
if next_private_route.hop_count as usize > self.unlocked_inner.max_route_hop_count { if next_private_route.hop_count as usize > self.unlocked_inner.max_route_hop_count {
return Err(RPCError::protocol( return Ok(NetworkResult::invalid_message(
"Private route hop count too high to process", "Private route hop count too high to process",
)); ));
} }
@ -110,9 +110,9 @@ impl RPCProcessor {
// Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret) // Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret)
// xxx: punish nodes that send messages that fail to decrypt eventually? How to do this for safety routes? // xxx: punish nodes that send messages that fail to decrypt eventually? How to do this for safety routes?
let node_id_secret = self.routing_table.node_id_secret_key(remote_sr_pubkey.kind); let node_id_secret = self.routing_table.node_id_secret_key(remote_sr_pubkey.kind);
let dh_secret = vcrypto let Ok(dh_secret) = vcrypto.cached_dh(&remote_sr_pubkey.value, &node_id_secret) else {
.cached_dh(&remote_sr_pubkey.value, &node_id_secret) return Ok(NetworkResult::invalid_message("dh failed for remote safety route for safety routed operation"));
.map_err(RPCError::protocol)?; };
let body = match vcrypto.decrypt_aead( let body = match vcrypto.decrypt_aead(
routed_operation.data(), routed_operation.data(),
routed_operation.nonce(), routed_operation.nonce(),
@ -183,19 +183,18 @@ impl RPCProcessor {
// Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret) // Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret)
// xxx: punish nodes that send messages that fail to decrypt eventually. How to do this for private routes? // xxx: punish nodes that send messages that fail to decrypt eventually. How to do this for private routes?
let dh_secret = vcrypto let Ok(dh_secret) = vcrypto.cached_dh(&remote_sr_pubkey.value, &secret_key) else {
.cached_dh(&remote_sr_pubkey.value, &secret_key) return Ok(NetworkResult::invalid_message("dh failed for remote safety route for private routed operation"));
.map_err(RPCError::protocol)?; };
let body = vcrypto let Ok(body) = vcrypto
.decrypt_aead( .decrypt_aead(
routed_operation.data(), routed_operation.data(),
routed_operation.nonce(), routed_operation.nonce(),
&dh_secret, &dh_secret,
None, None,
) ) else {
.map_err(RPCError::map_internal( return Ok(NetworkResult::invalid_message("decryption of routed operation failed"));
"decryption of routed operation failed", };
))?;
// Pass message to RPC system // Pass message to RPC system
self.enqueue_private_routed_message( self.enqueue_private_routed_message(
@ -401,37 +400,48 @@ impl RPCProcessor {
SafetyRouteHops::Data(ref route_hop_data) => { SafetyRouteHops::Data(ref route_hop_data) => {
// Decrypt the blob with DEC(nonce, DH(the SR's public key, this hop's secret) // Decrypt the blob with DEC(nonce, DH(the SR's public key, this hop's secret)
let node_id_secret = self.routing_table.node_id_secret_key(crypto_kind); let node_id_secret = self.routing_table.node_id_secret_key(crypto_kind);
let dh_secret = vcrypto let Ok(dh_secret) = vcrypto
.cached_dh(&safety_route.public_key.value, &node_id_secret) .cached_dh(&safety_route.public_key.value, &node_id_secret) else {
.map_err(RPCError::protocol)?; return Ok(NetworkResult::invalid_message("dh failed for safety route hop"));
let mut dec_blob_data = vcrypto };
let Ok(mut dec_blob_data) = vcrypto
.decrypt_aead( .decrypt_aead(
&route_hop_data.blob, &route_hop_data.blob,
&route_hop_data.nonce, &route_hop_data.nonce,
&dh_secret, &dh_secret,
None, None,
) )
.map_err(RPCError::protocol)?; else {
return Ok(NetworkResult::invalid_message("failed to decrypt route hop data for safety route hop"));
};
// See if this is last hop in safety route, if so, we're decoding a PrivateRoute not a RouteHop // See if this is last hop in safety route, if so, we're decoding a PrivateRoute not a RouteHop
let Some(dec_blob_tag) = dec_blob_data.pop() else { let Some(dec_blob_tag) = dec_blob_data.pop() else {
return Ok(NetworkResult::invalid_message("no bytes in blob")); return Ok(NetworkResult::invalid_message("no bytes in blob"));
}; };
let dec_blob_reader = RPCMessageData::new(dec_blob_data).get_reader()?; let Ok(dec_blob_reader) = RPCMessageData::new(dec_blob_data).get_reader() else {
return Ok(NetworkResult::invalid_message("Failed to decode RPCMessageData from blob"));
};
// Decode the blob appropriately // Decode the blob appropriately
if dec_blob_tag == 1 { if dec_blob_tag == 1 {
// PrivateRoute // PrivateRoute
let private_route = { let private_route = {
let pr_reader = dec_blob_reader let Ok(pr_reader) = dec_blob_reader
.get_root::<veilid_capnp::private_route::Reader>() .get_root::<veilid_capnp::private_route::Reader>() else {
.map_err(RPCError::protocol)?; return Ok(NetworkResult::invalid_message("failed to get private route reader for blob"));
decode_private_route(&pr_reader)? };
let Ok(private_route) = decode_private_route(&pr_reader) else {
return Ok(NetworkResult::invalid_message("failed to decode private route"));
};
private_route
}; };
// Validate the private route // Validate the private route
private_route.validate(self.crypto.clone()).map_err(RPCError::protocol)?; if let Err(_) = private_route.validate(self.crypto.clone()) {
return Ok(NetworkResult::invalid_message("failed to validate private route"));
}
// Switching from full safety route to private route first hop // Switching from full safety route to private route first hop
network_result_try!( network_result_try!(
@ -445,14 +455,20 @@ impl RPCProcessor {
} else if dec_blob_tag == 0 { } else if dec_blob_tag == 0 {
// RouteHop // RouteHop
let route_hop = { let route_hop = {
let rh_reader = dec_blob_reader let Ok(rh_reader) = dec_blob_reader
.get_root::<veilid_capnp::route_hop::Reader>() .get_root::<veilid_capnp::route_hop::Reader>() else {
.map_err(RPCError::protocol)?; return Ok(NetworkResult::invalid_message("failed to get route hop reader for blob"));
decode_route_hop(&rh_reader)? };
let Ok(route_hop) = decode_route_hop(&rh_reader) else {
return Ok(NetworkResult::invalid_message("failed to decode route hop"));
};
route_hop
}; };
// Validate the route hop // Validate the route hop
route_hop.validate(self.crypto.clone()).map_err(RPCError::protocol)?; if let Err(_) = route_hop.validate(self.crypto.clone()) {
return Ok(NetworkResult::invalid_message("failed to validate route hop"));
}
// Continue the full safety route with another hop // Continue the full safety route with another hop
network_result_try!( network_result_try!(

View File

@ -14,7 +14,6 @@ impl RPCProcessor {
/// Because this leaks information about the identity of the node itself, /// Because this leaks information about the identity of the node itself,
/// replying to this request received over a private route will leak /// replying to this request received over a private route will leak
/// the identity of the node and defeat the private route. /// the identity of the node and defeat the private route.
#[instrument(level = "trace", skip(self), ret, err)]
pub async fn rpc_call_set_value( pub async fn rpc_call_set_value(
self, self,
dest: Destination, dest: Destination,
@ -25,18 +24,22 @@ impl RPCProcessor {
send_descriptor: bool, send_descriptor: bool,
) -> Result<NetworkResult<Answer<SetValueAnswer>>, RPCError> { ) -> Result<NetworkResult<Answer<SetValueAnswer>>, RPCError> {
// Ensure destination never has a private route // Ensure destination never has a private route
if matches!( // and get the target noderef so we can validate the response
dest, let Some(target) = dest.target() else {
Destination::PrivateRoute {
private_route: _,
safety_selection: _
}
) {
return Err(RPCError::internal( return Err(RPCError::internal(
"Never send set value requests over private routes", "Never send set value requests over private routes",
)); ));
} };
// Get the target node id
let Some(vcrypto) = self.crypto.get(key.kind) else {
return Err(RPCError::internal("unsupported cryptosystem"));
};
let Some(target_node_id) = target.node_ids().get(key.kind) else {
return Err(RPCError::internal("No node id for crypto kind"));
};
// Send the setvalue question
let set_value_q = RPCOperationSetValueQ::new( let set_value_q = RPCOperationSetValueQ::new(
key, key,
subkey, subkey,
@ -51,17 +54,11 @@ impl RPCProcessor {
network_result_try!(self.get_destination_respond_to(&dest)?), network_result_try!(self.get_destination_respond_to(&dest)?),
RPCQuestionDetail::SetValueQ(set_value_q), RPCQuestionDetail::SetValueQ(set_value_q),
); );
let Some(vcrypto) = self.crypto.get(key.kind) else {
return Err(RPCError::internal("unsupported cryptosystem"));
};
// Send the setvalue question
let question_context = QuestionContext::SetValue(ValidateSetValueContext { let question_context = QuestionContext::SetValue(ValidateSetValueContext {
descriptor, descriptor,
subkey, subkey,
vcrypto, vcrypto: vcrypto.clone(),
}); });
let waitable_reply = network_result_try!( let waitable_reply = network_result_try!(
self.question(dest, question, Some(question_context)) self.question(dest, question, Some(question_context))
.await? .await?
@ -78,13 +75,27 @@ impl RPCProcessor {
let set_value_a = match kind { let set_value_a = match kind {
RPCOperationKind::Answer(a) => match a.destructure() { RPCOperationKind::Answer(a) => match a.destructure() {
RPCAnswerDetail::SetValueA(a) => a, RPCAnswerDetail::SetValueA(a) => a,
_ => return Err(RPCError::invalid_format("not a setvalue answer")), _ => return Ok(NetworkResult::invalid_message("not a setvalue answer")),
}, },
_ => return Err(RPCError::invalid_format("not an answer")), _ => return Ok(NetworkResult::invalid_message("not an answer")),
}; };
let (set, value, peers) = set_value_a.destructure(); let (set, value, peers) = set_value_a.destructure();
// Validate peers returned are, in fact, closer to the key than the node we sent this to
let valid = match RoutingTable::verify_peers_closer(vcrypto, target_node_id, key, &peers) {
Ok(v) => v,
Err(e) => {
return Ok(NetworkResult::invalid_message(format!(
"missing cryptosystem in peers node ids: {}",
e
)));
}
};
if !valid {
return Ok(NetworkResult::invalid_message("non-closer peers returned"));
}
Ok(NetworkResult::value(Answer::new( Ok(NetworkResult::value(Answer::new(
latency, latency,
SetValueAnswer { set, value, peers }, SetValueAnswer { set, value, peers },

View File

@ -119,9 +119,9 @@ impl RPCProcessor {
let status_a = match kind { let status_a = match kind {
RPCOperationKind::Answer(a) => match a.destructure() { RPCOperationKind::Answer(a) => match a.destructure() {
RPCAnswerDetail::StatusA(a) => a, RPCAnswerDetail::StatusA(a) => a,
_ => return Err(RPCError::invalid_format("not a status answer")), _ => return Ok(NetworkResult::invalid_message("not a status answer")),
}, },
_ => return Err(RPCError::invalid_format("not an answer")), _ => return Ok(NetworkResult::invalid_message("not an answer")),
}; };
let (a_node_status, sender_info) = status_a.destructure(); let (a_node_status, sender_info) = status_a.destructure();

View File

@ -1,7 +1,7 @@
use super::*; use super::*;
/// The context of the do_get_value operation /// The context of the outbound_get_value operation
struct DoGetValueContext { struct OutboundGetValueContext {
/// The latest value of the subkey, may be the value passed in /// The latest value of the subkey, may be the value passed in
pub value: Option<SignedValueData>, pub value: Option<SignedValueData>,
/// The consensus count for the value we have received /// The consensus count for the value we have received
@ -42,7 +42,7 @@ impl StorageManager {
} else { } else {
None None
}; };
let context = Arc::new(Mutex::new(DoGetValueContext { let context = Arc::new(Mutex::new(OutboundGetValueContext {
value: last_subkey_result.value, value: last_subkey_result.value,
value_count: 0, value_count: 0,
descriptor: last_subkey_result.descriptor.clone(), descriptor: last_subkey_result.descriptor.clone(),

View File

@ -341,7 +341,6 @@ impl StorageManager {
} else { } else {
ValueData::new(data, writer.key) ValueData::new(data, writer.key)
}; };
let seq = value_data.seq();
// Validate with schema // Validate with schema
if !schema.check_subkey_value_data(descriptor.owner(), subkey, &value_data) { if !schema.check_subkey_value_data(descriptor.owner(), subkey, &value_data) {
@ -374,7 +373,6 @@ impl StorageManager {
drop(inner); drop(inner);
// Use the safety selection we opened the record with // Use the safety selection we opened the record with
let final_signed_value_data = self let final_signed_value_data = self
.outbound_set_value( .outbound_set_value(
rpc_processor, rpc_processor,
@ -386,13 +384,12 @@ impl StorageManager {
) )
.await?; .await?;
// If we got a new value back then write it to the opened record // Whatever record we got back, store it locally, might be newer than the one we asked to save
if final_signed_value_data.value_data().seq() != seq { let mut inner = self.lock().await?;
let mut inner = self.lock().await?; inner
inner .handle_set_local_value(key, subkey, final_signed_value_data.clone())
.handle_set_local_value(key, subkey, final_signed_value_data.clone()) .await?;
.await?;
}
Ok(Some(final_signed_value_data.into_value_data())) Ok(Some(final_signed_value_data.into_value_data()))
} }

View File

@ -1,7 +1,7 @@
use super::*; use super::*;
/// The context of the do_get_value operation /// The context of the outbound_set_value operation
struct DoSetValueContext { struct OutboundSetValueContext {
/// The latest value of the subkey, may be the value passed in /// The latest value of the subkey, may be the value passed in
pub value: SignedValueData, pub value: SignedValueData,
/// The consensus count for the value we have received /// The consensus count for the value we have received
@ -37,7 +37,7 @@ impl StorageManager {
// Make do-set-value answer context // Make do-set-value answer context
let schema = descriptor.schema()?; let schema = descriptor.schema()?;
let context = Arc::new(Mutex::new(DoSetValueContext { let context = Arc::new(Mutex::new(OutboundSetValueContext {
value, value,
value_count: 0, value_count: 0,
schema, schema,

View File

@ -1,74 +1,77 @@
# # Routing context veilid tests # Routing context veilid tests
# import veilid import veilid
# import pytest import pytest
# import asyncio import asyncio
# import json import json
# from . import * from . import *
# ################################################################## ##################################################################
# BOGUS_KEY = veilid.TypedKey.from_value(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.PublicKey.from_bytes(b' ')) BOGUS_KEY = veilid.TypedKey.from_value(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.PublicKey.from_bytes(b' '))
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_get_dht_value_unopened(api_connection: veilid.VeilidAPI): async def test_get_dht_value_unopened(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
# out = await rc.get_dht_value(BOGUS_KEY, veilid.ValueSubkey(0), False) out = await rc.get_dht_value(BOGUS_KEY, veilid.ValueSubkey(0), False)
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_open_dht_record_nonexistent_no_writer(api_connection: veilid.VeilidAPI): async def test_open_dht_record_nonexistent_no_writer(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
# out = await rc.open_dht_record(BOGUS_KEY, None) out = await rc.open_dht_record(BOGUS_KEY, None)
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_close_dht_record_nonexistent(api_connection: veilid.VeilidAPI): async def test_close_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
# await rc.close_dht_record(BOGUS_KEY) await rc.close_dht_record(BOGUS_KEY)
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_delete_dht_record_nonexistent(api_connection: veilid.VeilidAPI): async def test_delete_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
# await rc.delete_dht_record(BOGUS_KEY) await rc.delete_dht_record(BOGUS_KEY)
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_create_delete_dht_record_simple(api_connection: veilid.VeilidAPI): async def test_create_delete_dht_record_simple(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1)) rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1))
# await rc.close_dht_record(rec.key) await rc.close_dht_record(rec.key)
# await rc.delete_dht_record(rec.key) await rc.delete_dht_record(rec.key)
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_get_dht_value_nonexistent(api_connection: veilid.VeilidAPI): async def test_get_dht_value_nonexistent(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1)) rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1))
# assert await rc.get_dht_value(rec.key, 0, False) == None assert await rc.get_dht_value(rec.key, 0, False) == None
# await rc.close_dht_record(rec.key) await rc.close_dht_record(rec.key)
# await rc.delete_dht_record(rec.key) await rc.delete_dht_record(rec.key)
# @pytest.mark.asyncio @pytest.mark.asyncio
# async def test_set_get_dht_value(api_connection: veilid.VeilidAPI): async def test_set_get_dht_value(api_connection: veilid.VeilidAPI):
# rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
# async with rc: async with rc:
# rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1)) rec = await rc.create_dht_record(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.DHTSchema.dflt(1))
# vd = await rc.set_dht_value(rec.key, 0, b"BLAH BLAH BLAH") vd = await rc.set_dht_value(rec.key, 0, b"BLAH BLAH BLAH")
# assert vd != None assert vd != None
# vd2 = await rc.get_dht_value(rec.key, 0, False) vd2 = await rc.get_dht_value(rec.key, 0, False)
# assert vd2 != None assert vd2 != None
# assert vd == vd2 print("vd: {}", vd.__dict__)
print("vd2: {}", vd2.__dict__)
# await rc.close_dht_record(rec.key) assert vd == vd2
# await rc.delete_dht_record(rec.key)
await rc.close_dht_record(rec.key)
await rc.delete_dht_record(rec.key)

View File

@ -2,6 +2,7 @@ import base64
import json import json
from enum import StrEnum from enum import StrEnum
from typing import Any, Optional, Self, Tuple from typing import Any, Optional, Self, Tuple
from functools import total_ordering
#################################################################### ####################################################################
@ -206,6 +207,7 @@ class ValueSeqNum(int):
#################################################################### ####################################################################
@total_ordering
class VeilidVersion: class VeilidVersion:
_major: int _major: int
_minor: int _minor: int
@ -216,6 +218,25 @@ class VeilidVersion:
self._minor = minor self._minor = minor
self._patch = patch self._patch = patch
def __lt__(self, other):
if other is None:
return False
if self._major < other._major:
return True
if self._major > other._major:
return False
if self._minor < other._minor:
return True
if self._minor > other._minor:
return False
if self._patch < other._patch:
return True
return False
def __eq__(self, other):
return isinstance(other, VeilidVersion) and self.data == other.data and self.seq == other.seq and self.writer == other.writer
@property @property
def major(self): def major(self):
return self._major return self._major
@ -323,6 +344,7 @@ class DHTRecordDescriptor:
return self.__dict__ return self.__dict__
@total_ordering
class ValueData: class ValueData:
seq: ValueSeqNum seq: ValueSeqNum
data: bytes data: bytes
@ -333,6 +355,24 @@ class ValueData:
self.data = data self.data = data
self.writer = writer self.writer = writer
def __lt__(self, other):
if other is None:
return true
if self.data < other.data:
return True
if self.data > other.data:
return False
if self.seq < other.seq:
return True
if self.seq > other.seq:
return False
if self.writer < other.writer:
return True
return False
def __eq__(self, other):
return isinstance(other, ValueData) and self.data == other.data and self.seq == other.seq and self.writer == other.writer
@classmethod @classmethod
def from_json(cls, j: dict) -> Self: def from_json(cls, j: dict) -> Self:
return cls( return cls(