mirror of
https://gitlab.com/veilid/veilid.git
synced 2024-12-17 19:54:46 -05:00
Merge branch 'dht-work' into 'main'
DHT WatchValue and InspectRecord Support See merge request veilid/veilid!261
This commit is contained in:
commit
ebd4c0070d
167
.gitignore
vendored
167
.gitignore
vendored
@ -66,172 +66,7 @@ flamegraph.svg
|
|||||||
perf.data
|
perf.data
|
||||||
perf.data.old
|
perf.data.old
|
||||||
|
|
||||||
##############################################################################
|
###################################
|
||||||
### Python
|
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Distribution / packaging
|
|
||||||
.Python
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
share/python-wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# PyInstaller
|
|
||||||
# Usually these files are written by a python script from a template
|
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
||||||
*.manifest
|
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
htmlcov/
|
|
||||||
.tox/
|
|
||||||
.nox/
|
|
||||||
.coverage
|
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
|
||||||
*.cover
|
|
||||||
*.py,cover
|
|
||||||
.hypothesis/
|
|
||||||
.pytest_cache/
|
|
||||||
cover/
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
*.pot
|
|
||||||
|
|
||||||
# Django stuff:
|
|
||||||
*.log
|
|
||||||
local_settings.py
|
|
||||||
db.sqlite3
|
|
||||||
db.sqlite3-journal
|
|
||||||
|
|
||||||
# Flask stuff:
|
|
||||||
instance/
|
|
||||||
.webassets-cache
|
|
||||||
|
|
||||||
# Scrapy stuff:
|
|
||||||
.scrapy
|
|
||||||
|
|
||||||
# Sphinx documentation
|
|
||||||
docs/_build/
|
|
||||||
|
|
||||||
# PyBuilder
|
|
||||||
.pybuilder/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
|
||||||
.ipynb_checkpoints
|
|
||||||
|
|
||||||
# IPython
|
|
||||||
profile_default/
|
|
||||||
ipython_config.py
|
|
||||||
|
|
||||||
# pyenv
|
|
||||||
# For a library or package, you might want to ignore these files since the code is
|
|
||||||
# intended to run in multiple environments; otherwise, check them in:
|
|
||||||
# .python-version
|
|
||||||
|
|
||||||
# pipenv
|
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
||||||
# install all needed dependencies.
|
|
||||||
#Pipfile.lock
|
|
||||||
|
|
||||||
# poetry
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
||||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
||||||
# commonly ignored for libraries.
|
|
||||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
||||||
#poetry.lock
|
|
||||||
|
|
||||||
# pdm
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
||||||
#pdm.lock
|
|
||||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
||||||
# in version control.
|
|
||||||
# https://pdm.fming.dev/#use-with-ide
|
|
||||||
.pdm.toml
|
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
||||||
__pypackages__/
|
|
||||||
|
|
||||||
# Celery stuff
|
|
||||||
celerybeat-schedule
|
|
||||||
celerybeat.pid
|
|
||||||
|
|
||||||
# SageMath parsed files
|
|
||||||
*.sage.py
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Spyder project settings
|
|
||||||
.spyderproject
|
|
||||||
.spyproject
|
|
||||||
|
|
||||||
# Rope project settings
|
|
||||||
.ropeproject
|
|
||||||
|
|
||||||
# mkdocs documentation
|
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
|
||||||
.mypy_cache/
|
|
||||||
.dmypy.json
|
|
||||||
dmypy.json
|
|
||||||
|
|
||||||
# Pyre type checker
|
|
||||||
.pyre/
|
|
||||||
|
|
||||||
# pytype static type analyzer
|
|
||||||
.pytype/
|
|
||||||
|
|
||||||
# Cython debug symbols
|
|
||||||
cython_debug/
|
|
||||||
|
|
||||||
# PyCharm
|
|
||||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
||||||
#.idea/
|
|
||||||
|
|
||||||
## Custom for veilid-python
|
|
||||||
veilid-python/demo/.demokeys
|
|
||||||
|
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
1181
Cargo.lock
generated
1181
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@ logging:
|
|||||||
enabled: false
|
enabled: false
|
||||||
core:
|
core:
|
||||||
capabilities:
|
capabilities:
|
||||||
disable: ['TUNL','SGNL','RLAY','DIAL','DHTV','APPM','ROUT']
|
disable: ['TUNL','SGNL','RLAY','DIAL','DHTV','DHTW','APPM','ROUT']
|
||||||
network:
|
network:
|
||||||
upnp: false
|
upnp: false
|
||||||
dht:
|
dht:
|
||||||
|
@ -22,7 +22,7 @@ logging:
|
|||||||
enabled: false
|
enabled: false
|
||||||
core:
|
core:
|
||||||
capabilities:
|
capabilities:
|
||||||
disable: ['TUNL','SGNL','RLAY','DIAL','DHTV','APPM']
|
disable: ['TUNL','SGNL','RLAY','DIAL','DHTV','DHTW','APPM']
|
||||||
network:
|
network:
|
||||||
upnp: false
|
upnp: false
|
||||||
dht:
|
dht:
|
||||||
|
@ -38,7 +38,7 @@ cursive = { git = "https://gitlab.com/veilid/cursive.git", default-features = fa
|
|||||||
cursive_buffered_backend = { git = "https://gitlab.com/veilid/cursive-buffered-backend.git" }
|
cursive_buffered_backend = { git = "https://gitlab.com/veilid/cursive-buffered-backend.git" }
|
||||||
# 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 = { git = "https://gitlab.com/veilid/cursive-table-view.git" }
|
||||||
arboard = "3.3.0"
|
arboard = "3.3.0"
|
||||||
# cursive-tabs = "0.5.0"
|
# cursive-tabs = "0.5.0"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
@ -50,12 +50,12 @@ serde_derive = "^1"
|
|||||||
parking_lot = "^0"
|
parking_lot = "^0"
|
||||||
cfg-if = "^1"
|
cfg-if = "^1"
|
||||||
config = { version = "^0", features = ["yaml"] }
|
config = { version = "^0", features = ["yaml"] }
|
||||||
bugsalot = { package = "veilid-bugsalot", version = "0.1.0" }
|
bugsalot = { package = "veilid-bugsalot", version = "0.2.0" }
|
||||||
flexi_logger = { version = "^0", features = ["use_chrono_for_offset"] }
|
flexi_logger = { version = "^0", features = ["use_chrono_for_offset"] }
|
||||||
thiserror = "^1"
|
thiserror = "^1"
|
||||||
crossbeam-channel = "^0"
|
crossbeam-channel = "^0"
|
||||||
hex = "^0"
|
hex = "^0"
|
||||||
veilid-tools = { version = "0.2.5", path = "../veilid-tools", default-features = false}
|
veilid-tools = { version = "0.2.5", path = "../veilid-tools", default-features = false }
|
||||||
|
|
||||||
json = "^0"
|
json = "^0"
|
||||||
stop-token = { version = "^0", default-features = false }
|
stop-token = { version = "^0", default-features = false }
|
||||||
@ -67,6 +67,8 @@ chrono = "0.4.31"
|
|||||||
owning_ref = "0.4.1"
|
owning_ref = "0.4.1"
|
||||||
unicode-width = "0.1.11"
|
unicode-width = "0.1.11"
|
||||||
lru = "0.10.1"
|
lru = "0.10.1"
|
||||||
|
rustyline-async = "0.4.2"
|
||||||
|
console = "0.15.8"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = "^2"
|
serial_test = "^2"
|
||||||
|
@ -80,7 +80,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(Level::Error, format!("missing update kind: {}", update));
|
comproc.log_message(Level::Error, &format!("missing update kind: {}", update));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match kind {
|
match kind {
|
||||||
@ -110,7 +110,7 @@ impl ClientApiConnection {
|
|||||||
comproc.update_value_change(&update);
|
comproc.update_value_change(&update);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
comproc.log_message(Level::Error, format!("unknown update kind: {}", update));
|
comproc.log_message(Level::Error, &format!("unknown update kind: {}", update));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -395,6 +395,27 @@ impl ClientApiConnection {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn server_change_log_ignore(
|
||||||
|
&self,
|
||||||
|
layer: String,
|
||||||
|
log_ignore: String,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
trace!("ClientApiConnection::change_log_ignore");
|
||||||
|
let mut req = json::JsonValue::new_object();
|
||||||
|
req["op"] = "Control".into();
|
||||||
|
req["args"] = json::JsonValue::new_array();
|
||||||
|
req["args"].push("ChangeLogIgnore").unwrap();
|
||||||
|
req["args"].push(layer).unwrap();
|
||||||
|
req["args"].push(log_ignore).unwrap();
|
||||||
|
let Some(resp) = self.perform_request(req).await else {
|
||||||
|
return Err("Cancelled".to_owned());
|
||||||
|
};
|
||||||
|
if resp.has_key("error") {
|
||||||
|
return Err(resp["error"].to_string());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Start Client API connection
|
// Start Client API connection
|
||||||
pub async fn ipc_connect(&self, ipc_path: PathBuf) -> Result<(), String> {
|
pub async fn ipc_connect(&self, ipc_path: PathBuf) -> Result<(), String> {
|
||||||
trace!("ClientApiConnection::ipc_connect");
|
trace!("ClientApiConnection::ipc_connect");
|
||||||
|
@ -41,7 +41,7 @@ impl ConnectionState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct CommandProcessorInner {
|
struct CommandProcessorInner {
|
||||||
ui_sender: UISender,
|
ui_sender: Box<dyn UISender>,
|
||||||
capi: Option<ClientApiConnection>,
|
capi: Option<ClientApiConnection>,
|
||||||
reconnect: bool,
|
reconnect: bool,
|
||||||
finished: bool,
|
finished: bool,
|
||||||
@ -60,7 +60,7 @@ pub struct CommandProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CommandProcessor {
|
impl CommandProcessor {
|
||||||
pub fn new(ui_sender: UISender, settings: &Settings) -> Self {
|
pub fn new(ui_sender: Box<dyn UISender>, settings: &Settings) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Arc::new(Mutex::new(CommandProcessorInner {
|
inner: Arc::new(Mutex::new(CommandProcessorInner {
|
||||||
ui_sender,
|
ui_sender,
|
||||||
@ -86,8 +86,8 @@ impl CommandProcessor {
|
|||||||
fn inner_mut(&self) -> MutexGuard<CommandProcessorInner> {
|
fn inner_mut(&self) -> MutexGuard<CommandProcessorInner> {
|
||||||
self.inner.lock()
|
self.inner.lock()
|
||||||
}
|
}
|
||||||
fn ui_sender(&self) -> UISender {
|
fn ui_sender(&self) -> Box<dyn UISender> {
|
||||||
self.inner.lock().ui_sender.clone()
|
self.inner.lock().ui_sender.clone_uisender()
|
||||||
}
|
}
|
||||||
fn capi(&self) -> ClientApiConnection {
|
fn capi(&self) -> ClientApiConnection {
|
||||||
self.inner.lock().capi.as_ref().unwrap().clone()
|
self.inner.lock().capi.as_ref().unwrap().clone()
|
||||||
@ -126,7 +126,7 @@ impl CommandProcessor {
|
|||||||
|
|
||||||
ui.add_node_event(
|
ui.add_node_event(
|
||||||
Level::Info,
|
Level::Info,
|
||||||
format!(
|
&format!(
|
||||||
r#"Client Commands:
|
r#"Client 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
|
||||||
@ -136,6 +136,9 @@ impl CommandProcessor {
|
|||||||
all, terminal, system, api, file, otlp
|
all, terminal, system, api, file, otlp
|
||||||
levels include:
|
levels include:
|
||||||
error, warn, info, debug, trace
|
error, warn, info, debug, trace
|
||||||
|
change_log_ignore <layer> <changes> change the log target ignore list for a tracing layer
|
||||||
|
targets to add to the ignore list can be separated by a comma.
|
||||||
|
to remove a target from the ignore list, prepend it with a minus.
|
||||||
enable [flag] set a flag
|
enable [flag] set a flag
|
||||||
disable [flag] unset a flag
|
disable [flag] unset a flag
|
||||||
valid flags in include:
|
valid flags in include:
|
||||||
@ -190,11 +193,11 @@ Server Debug Commands:
|
|||||||
spawn_detached_local(async move {
|
spawn_detached_local(async move {
|
||||||
match capi.server_debug(command_line).await {
|
match capi.server_debug(command_line).await {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
ui.add_node_event(Level::Info, output);
|
ui.add_node_event(Level::Info, &output);
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
ui.add_node_event(Level::Error, e.to_string());
|
ui.add_node_event(Level::Error, &e);
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,20 +218,59 @@ Server Debug Commands:
|
|||||||
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(Level::Error, 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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match capi.server_change_log_level(layer, log_level).await {
|
match capi.server_change_log_level(layer, log_level.clone()).await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
ui.display_string_dialog("Success", "Log level changed", callback);
|
ui.display_string_dialog(
|
||||||
|
"Log level changed",
|
||||||
|
&format!("Log level set to '{}'", log_level),
|
||||||
|
callback,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
ui.display_string_dialog(
|
ui.display_string_dialog(
|
||||||
"Server command 'change_log_level' failed",
|
"Server command 'change_log_level' failed",
|
||||||
e.to_string(),
|
&e,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmd_change_log_ignore(
|
||||||
|
&self,
|
||||||
|
rest: Option<String>,
|
||||||
|
callback: UICallback,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
trace!("CommandProcessor::cmd_change_log_ignore");
|
||||||
|
let capi = self.capi();
|
||||||
|
let ui = self.ui_sender();
|
||||||
|
spawn_detached_local(async move {
|
||||||
|
let (layer, rest) = Self::word_split(&rest.unwrap_or_default());
|
||||||
|
let log_ignore = rest.unwrap_or_default();
|
||||||
|
|
||||||
|
match capi
|
||||||
|
.server_change_log_ignore(layer, log_ignore.clone())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => {
|
||||||
|
ui.display_string_dialog(
|
||||||
|
"Log ignore changed",
|
||||||
|
&format!("Log ignore changed '{}'", log_ignore),
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
ui.display_string_dialog(
|
||||||
|
"Server command 'change_log_ignore' failed",
|
||||||
|
&e,
|
||||||
callback,
|
callback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -247,11 +289,11 @@ Server Debug Commands:
|
|||||||
match flag.as_str() {
|
match flag.as_str() {
|
||||||
"app_messages" => {
|
"app_messages" => {
|
||||||
this.inner.lock().enable_app_messages = true;
|
this.inner.lock().enable_app_messages = true;
|
||||||
ui.add_node_event(Level::Info, format!("flag enabled: {}", flag));
|
ui.add_node_event(Level::Info, &format!("flag enabled: {}", flag));
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
ui.add_node_event(Level::Error, format!("unknown flag: {}", flag));
|
ui.add_node_event(Level::Error, &format!("unknown flag: {}", flag));
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,11 +311,11 @@ Server Debug Commands:
|
|||||||
match flag.as_str() {
|
match flag.as_str() {
|
||||||
"app_messages" => {
|
"app_messages" => {
|
||||||
this.inner.lock().enable_app_messages = false;
|
this.inner.lock().enable_app_messages = false;
|
||||||
ui.add_node_event(Level::Info, format!("flag disabled: {}", flag));
|
ui.add_node_event(Level::Info, &format!("flag disabled: {}", flag));
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
ui.add_node_event(Level::Error, format!("unknown flag: {}", flag));
|
ui.add_node_event(Level::Error, &format!("unknown flag: {}", flag));
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -291,6 +333,7 @@ Server Debug Commands:
|
|||||||
"disconnect" => self.cmd_disconnect(callback),
|
"disconnect" => self.cmd_disconnect(callback),
|
||||||
"shutdown" => self.cmd_shutdown(callback),
|
"shutdown" => self.cmd_shutdown(callback),
|
||||||
"change_log_level" => self.cmd_change_log_level(rest, callback),
|
"change_log_level" => self.cmd_change_log_level(rest, callback),
|
||||||
|
"change_log_ignore" => self.cmd_change_log_ignore(rest, callback),
|
||||||
"enable" => self.cmd_enable(rest, callback),
|
"enable" => self.cmd_enable(rest, callback),
|
||||||
"disable" => self.cmd_disable(rest, callback),
|
"disable" => self.cmd_disable(rest, callback),
|
||||||
_ => self.cmd_debug(command_line.to_owned(), callback),
|
_ => self.cmd_debug(command_line.to_owned(), callback),
|
||||||
@ -413,13 +456,13 @@ Server Debug Commands:
|
|||||||
// calls into ui
|
// calls into ui
|
||||||
////////////////////////////////////////////
|
////////////////////////////////////////////
|
||||||
|
|
||||||
pub fn log_message(&self, log_level: Level, message: String) {
|
pub fn log_message(&self, log_level: Level, message: &str) {
|
||||||
self.inner().ui_sender.add_node_event(log_level, message);
|
self.inner().ui_sender.add_log_event(log_level, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_attachment(&self, attachment: &json::JsonValue) {
|
pub fn update_attachment(&self, attachment: &json::JsonValue) {
|
||||||
self.inner_mut().ui_sender.set_attachment_state(
|
self.inner_mut().ui_sender.set_attachment_state(
|
||||||
attachment["state"].as_str().unwrap_or_default().to_owned(),
|
attachment["state"].as_str().unwrap_or_default(),
|
||||||
attachment["public_internet_ready"]
|
attachment["public_internet_ready"]
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
@ -458,7 +501,7 @@ Server Debug Commands:
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
if !out.is_empty() {
|
if !out.is_empty() {
|
||||||
self.inner().ui_sender.add_node_event(Level::Info, 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) {
|
||||||
@ -475,15 +518,15 @@ Server Debug Commands:
|
|||||||
datastr,
|
datastr,
|
||||||
if truncated { "..." } else { "" }
|
if truncated { "..." } else { "" }
|
||||||
);
|
);
|
||||||
self.inner().ui_sender.add_node_event(Level::Info, 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) {
|
||||||
let log_level =
|
let log_level =
|
||||||
Level::from_str(log["log_level"].as_str().unwrap_or("error")).unwrap_or(Level::Error);
|
Level::from_str(log["log_level"].as_str().unwrap_or("error")).unwrap_or(Level::Error);
|
||||||
self.inner().ui_sender.add_node_event(
|
self.inner().ui_sender.add_log_event(
|
||||||
log_level,
|
log_level,
|
||||||
format!(
|
&format!(
|
||||||
"{}: {}{}",
|
"{}: {}{}",
|
||||||
log["log_level"].as_str().unwrap_or("???"),
|
log["log_level"].as_str().unwrap_or("???"),
|
||||||
log["message"].as_str().unwrap_or("???"),
|
log["message"].as_str().unwrap_or("???"),
|
||||||
@ -530,7 +573,7 @@ Server Debug Commands:
|
|||||||
|
|
||||||
self.inner().ui_sender.add_node_event(
|
self.inner().ui_sender.add_node_event(
|
||||||
Level::Info,
|
Level::Info,
|
||||||
format!(
|
&format!(
|
||||||
"AppMessage ({:?}): {}{}",
|
"AppMessage ({:?}): {}{}",
|
||||||
msg["sender"],
|
msg["sender"],
|
||||||
strmsg,
|
strmsg,
|
||||||
@ -570,7 +613,7 @@ Server Debug Commands:
|
|||||||
|
|
||||||
self.inner().ui_sender.add_node_event(
|
self.inner().ui_sender.add_node_event(
|
||||||
Level::Info,
|
Level::Info,
|
||||||
format!(
|
&format!(
|
||||||
"AppCall ({:?}) id = {:016x} : {}{}",
|
"AppCall ({:?}) id = {:016x} : {}{}",
|
||||||
call["sender"],
|
call["sender"],
|
||||||
id,
|
id,
|
||||||
|
1448
veilid-cli/src/cursive_ui.rs
Normal file
1448
veilid-cli/src/cursive_ui.rs
Normal file
File diff suppressed because it is too large
Load Diff
350
veilid-cli/src/interactive_ui.rs
Normal file
350
veilid-cli/src/interactive_ui.rs
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use crate::command_processor::*;
|
||||||
|
use crate::cursive_ui::CursiveUI;
|
||||||
|
use crate::settings::*;
|
||||||
|
use crate::tools::*;
|
||||||
|
use crate::ui::*;
|
||||||
|
|
||||||
|
use console::{style, Term};
|
||||||
|
use flexi_logger::writers::LogWriter;
|
||||||
|
use rustyline_async::SharedWriter;
|
||||||
|
use rustyline_async::{Readline, ReadlineError, ReadlineEvent};
|
||||||
|
use stop_token::future::FutureExt as StopTokenFutureExt;
|
||||||
|
use stop_token::*;
|
||||||
|
|
||||||
|
pub type InteractiveUICallback = Box<dyn FnMut() + Send>;
|
||||||
|
|
||||||
|
pub struct InteractiveUIInner {
|
||||||
|
cmdproc: Option<CommandProcessor>,
|
||||||
|
stdout: Option<SharedWriter>,
|
||||||
|
error: Option<String>,
|
||||||
|
done: Option<StopSource>,
|
||||||
|
connection_state_receiver: flume::Receiver<ConnectionState>,
|
||||||
|
log_enabled: bool,
|
||||||
|
enable_color: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct InteractiveUI {
|
||||||
|
inner: Arc<Mutex<InteractiveUIInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InteractiveUI {
|
||||||
|
pub fn new(_settings: &Settings) -> (Self, InteractiveUISender) {
|
||||||
|
let (cssender, csreceiver) = flume::unbounded::<ConnectionState>();
|
||||||
|
|
||||||
|
let term = Term::stdout();
|
||||||
|
let enable_color = console::colors_enabled() && term.features().colors_supported();
|
||||||
|
|
||||||
|
// Create the UI object
|
||||||
|
let this = Self {
|
||||||
|
inner: Arc::new(Mutex::new(InteractiveUIInner {
|
||||||
|
cmdproc: None,
|
||||||
|
stdout: None,
|
||||||
|
error: None,
|
||||||
|
done: Some(StopSource::new()),
|
||||||
|
connection_state_receiver: csreceiver,
|
||||||
|
log_enabled: false,
|
||||||
|
enable_color,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ui_sender = InteractiveUISender {
|
||||||
|
inner: this.inner.clone(),
|
||||||
|
connection_state_sender: cssender,
|
||||||
|
};
|
||||||
|
|
||||||
|
(this, ui_sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn command_loop(&self) {
|
||||||
|
let (mut readline, mut stdout) =
|
||||||
|
match Readline::new("> ".to_owned()).map_err(|e| e.to_string()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (connection_state_receiver, done) = {
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
(
|
||||||
|
inner.connection_state_receiver.clone(),
|
||||||
|
inner.done.as_ref().unwrap().token(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.inner.lock().stdout = Some(stdout.clone());
|
||||||
|
|
||||||
|
CursiveUI::set_start_time();
|
||||||
|
|
||||||
|
// Wait for connection to be established
|
||||||
|
loop {
|
||||||
|
match connection_state_receiver.recv_async().await {
|
||||||
|
Ok(ConnectionState::ConnectedTCP(_, _))
|
||||||
|
| Ok(ConnectionState::ConnectedIPC(_, _)) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(ConnectionState::RetryingTCP(_, _)) | Ok(ConnectionState::RetryingIPC(_, _)) => {
|
||||||
|
}
|
||||||
|
Ok(ConnectionState::Disconnected) => {}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Some(e) = self.inner.lock().error.clone() {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
match readline.readline().timeout_at(done.clone()).await {
|
||||||
|
Ok(Ok(ReadlineEvent::Line(line))) => {
|
||||||
|
let line = line.trim();
|
||||||
|
if line == "clear" {
|
||||||
|
if let Err(e) = readline.clear() {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
}
|
||||||
|
} else if line == "log error" {
|
||||||
|
let opt_cmdproc = self.inner.lock().cmdproc.clone();
|
||||||
|
if let Some(cmdproc) = opt_cmdproc {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
"change_log_level api error",
|
||||||
|
UICallback::Interactive(Box::new(|| {})),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
self.inner.lock().log_enabled = true;
|
||||||
|
}
|
||||||
|
} else if line == "log warn" {
|
||||||
|
let opt_cmdproc = self.inner.lock().cmdproc.clone();
|
||||||
|
if let Some(cmdproc) = opt_cmdproc {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
"change_log_level api warn",
|
||||||
|
UICallback::Interactive(Box::new(|| {})),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
self.inner.lock().log_enabled = true;
|
||||||
|
}
|
||||||
|
} else if line == "log info" {
|
||||||
|
let opt_cmdproc = self.inner.lock().cmdproc.clone();
|
||||||
|
if let Some(cmdproc) = opt_cmdproc {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
"change_log_level api info",
|
||||||
|
UICallback::Interactive(Box::new(|| {})),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
self.inner.lock().log_enabled = true;
|
||||||
|
}
|
||||||
|
} else if line == "log debug" || line == "log" {
|
||||||
|
let opt_cmdproc = self.inner.lock().cmdproc.clone();
|
||||||
|
if let Some(cmdproc) = opt_cmdproc {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
"change_log_level api debug",
|
||||||
|
UICallback::Interactive(Box::new(|| {})),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
self.inner.lock().log_enabled = true;
|
||||||
|
}
|
||||||
|
} else if line == "log trace" {
|
||||||
|
let opt_cmdproc = self.inner.lock().cmdproc.clone();
|
||||||
|
if let Some(cmdproc) = opt_cmdproc {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
"change_log_level api trace",
|
||||||
|
UICallback::Interactive(Box::new(|| {})),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
self.inner.lock().log_enabled = true;
|
||||||
|
}
|
||||||
|
} else if line == "log off" {
|
||||||
|
let opt_cmdproc = self.inner.lock().cmdproc.clone();
|
||||||
|
if let Some(cmdproc) = opt_cmdproc {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
"change_log_level api off",
|
||||||
|
UICallback::Interactive(Box::new(|| {})),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
self.inner.lock().log_enabled = false;
|
||||||
|
}
|
||||||
|
} else if !line.is_empty() {
|
||||||
|
readline.add_history_entry(line.to_string());
|
||||||
|
let cmdproc = self.inner.lock().cmdproc.clone();
|
||||||
|
if let Some(cmdproc) = &cmdproc {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
line,
|
||||||
|
UICallback::Interactive(Box::new({
|
||||||
|
//let mut stdout = stdout.clone();
|
||||||
|
move || {
|
||||||
|
// if let Err(e) = writeln!(stdout) {
|
||||||
|
// println!("Error: {:?}", e);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
) {
|
||||||
|
if let Err(e) = writeln!(stdout, "Error: {}", e) {
|
||||||
|
println!("Error: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Ok(ReadlineEvent::Interrupted)) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(Ok(ReadlineEvent::Eof)) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(Err(ReadlineError::Closed)) => {}
|
||||||
|
Ok(Err(ReadlineError::IO(e))) => {
|
||||||
|
println!("IO Error: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let _ = readline.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UI for InteractiveUI {
|
||||||
|
fn set_command_processor(&mut self, cmdproc: CommandProcessor) {
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
inner.cmdproc = Some(cmdproc);
|
||||||
|
}
|
||||||
|
fn run_async(&mut self) -> Pin<Box<dyn core::future::Future<Output = ()>>> {
|
||||||
|
let this = self.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
this.command_loop().await;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct InteractiveUISender {
|
||||||
|
inner: Arc<Mutex<InteractiveUIInner>>,
|
||||||
|
connection_state_sender: flume::Sender<ConnectionState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UISender for InteractiveUISender {
|
||||||
|
fn clone_uisender(&self) -> Box<dyn UISender> {
|
||||||
|
Box::new(InteractiveUISender {
|
||||||
|
inner: self.inner.clone(),
|
||||||
|
connection_state_sender: self.connection_state_sender.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn as_logwriter(&self) -> Option<Box<dyn LogWriter>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_string_dialog(&self, title: &str, text: &str, close_cb: UICallback) {
|
||||||
|
let Some(mut stdout) = self.inner.lock().stdout.clone() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Err(e) = writeln!(stdout, "{}: {}", title, text) {
|
||||||
|
self.inner.lock().error = Some(e.to_string());
|
||||||
|
}
|
||||||
|
if let UICallback::Interactive(mut close_cb) = close_cb {
|
||||||
|
close_cb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quit(&self) {
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_callback(&self, callback: UICallback) {
|
||||||
|
if let UICallback::Interactive(mut callback) = callback {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn set_attachment_state(
|
||||||
|
&mut self,
|
||||||
|
_state: &str,
|
||||||
|
_public_internet_ready: bool,
|
||||||
|
_local_network_ready: bool,
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
fn set_network_status(
|
||||||
|
&mut self,
|
||||||
|
_started: bool,
|
||||||
|
_bps_down: u64,
|
||||||
|
_bps_up: u64,
|
||||||
|
mut _peers: Vec<json::JsonValue>,
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
fn set_config(&mut self, _config: &json::JsonValue) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
fn set_connection_state(&mut self, state: ConnectionState) {
|
||||||
|
if let Err(e) = self.connection_state_sender.send(state) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_node_event(&self, _log_color: Level, event: &str) {
|
||||||
|
let Some(mut stdout) = self.inner.lock().stdout.clone() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Err(e) = writeln!(stdout, "{}", event) {
|
||||||
|
self.inner.lock().error = Some(e.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn add_log_event(&self, log_color: Level, event: &str) {
|
||||||
|
let (enable_color, mut stdout) = {
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
if !inner.log_enabled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(stdout) = inner.stdout.clone() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
(inner.enable_color, stdout)
|
||||||
|
};
|
||||||
|
|
||||||
|
let log_line = format!(
|
||||||
|
"{}: {}",
|
||||||
|
CursiveUI::cli_ts(CursiveUI::get_start_time()),
|
||||||
|
event
|
||||||
|
);
|
||||||
|
if enable_color {
|
||||||
|
let log_line = match log_color {
|
||||||
|
Level::Error => style(log_line).red().bright().to_string(),
|
||||||
|
Level::Warn => style(log_line).yellow().bright().to_string(),
|
||||||
|
Level::Info => log_line,
|
||||||
|
Level::Debug => style(log_line).green().bright().to_string(),
|
||||||
|
Level::Trace => style(log_line).blue().bright().to_string(),
|
||||||
|
};
|
||||||
|
if let Err(e) = writeln!(stdout, "{}", log_line) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("{}", log_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
268
veilid-cli/src/io_read_write_ui.rs
Normal file
268
veilid-cli/src/io_read_write_ui.rs
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
use crate::command_processor::*;
|
||||||
|
use crate::settings::*;
|
||||||
|
use crate::tools::*;
|
||||||
|
use crate::ui::*;
|
||||||
|
|
||||||
|
use futures::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, BufWriter};
|
||||||
|
use stop_token::future::FutureExt as StopTokenFutureExt;
|
||||||
|
use stop_token::*;
|
||||||
|
use veilid_tools::AsyncMutex;
|
||||||
|
|
||||||
|
use flexi_logger::writers::LogWriter;
|
||||||
|
|
||||||
|
static FINISHED_LINE: &str = "\x7F ===FINISHED=== \x7F";
|
||||||
|
|
||||||
|
pub type IOReadWriteUICallback = Box<dyn FnMut() + Send>;
|
||||||
|
|
||||||
|
pub struct IOReadWriteUIInner<R: AsyncRead + Unpin + Send, W: AsyncWrite + Unpin + Send> {
|
||||||
|
cmdproc: Option<CommandProcessor>,
|
||||||
|
in_io: Arc<AsyncMutex<BufReader<R>>>,
|
||||||
|
out_io: Arc<AsyncMutex<BufWriter<W>>>,
|
||||||
|
out_receiver: flume::Receiver<String>,
|
||||||
|
out_sender: flume::Sender<String>,
|
||||||
|
done: Option<StopSource>,
|
||||||
|
connection_state_receiver: flume::Receiver<ConnectionState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IOReadWriteUI<R: AsyncRead + Unpin + Send, W: AsyncWrite + Unpin + Send> {
|
||||||
|
inner: Arc<Mutex<IOReadWriteUIInner<R, W>>>,
|
||||||
|
}
|
||||||
|
impl<R: AsyncRead + Unpin + Send, W: AsyncWrite + Unpin + Send> Clone for IOReadWriteUI<R, W> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
IOReadWriteUI {
|
||||||
|
inner: self.inner.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: AsyncRead + Unpin + Send, W: AsyncWrite + Unpin + Send> IOReadWriteUI<R, W> {
|
||||||
|
pub fn new(_settings: &Settings, in_io: R, out_io: W) -> (Self, IOReadWriteUISender<R, W>) {
|
||||||
|
// Create the UI object
|
||||||
|
let (sender, receiver) = flume::unbounded::<String>();
|
||||||
|
let (cssender, csreceiver) = flume::unbounded::<ConnectionState>();
|
||||||
|
let this = Self {
|
||||||
|
inner: Arc::new(Mutex::new(IOReadWriteUIInner {
|
||||||
|
cmdproc: None,
|
||||||
|
in_io: Arc::new(AsyncMutex::new(BufReader::new(in_io))),
|
||||||
|
out_io: Arc::new(AsyncMutex::new(BufWriter::new(out_io))),
|
||||||
|
out_receiver: receiver,
|
||||||
|
out_sender: sender.clone(),
|
||||||
|
connection_state_receiver: csreceiver,
|
||||||
|
done: Some(StopSource::new()),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ui_sender = IOReadWriteUISender {
|
||||||
|
inner: this.inner.clone(),
|
||||||
|
out_sender: sender,
|
||||||
|
connection_state_sender: cssender,
|
||||||
|
};
|
||||||
|
|
||||||
|
(this, ui_sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn output_loop(&self) {
|
||||||
|
let out_receiver = self.inner.lock().out_receiver.clone();
|
||||||
|
let out_io = self.inner.lock().out_io.clone();
|
||||||
|
|
||||||
|
let mut out = out_io.lock().await;
|
||||||
|
let done = self.inner.lock().done.as_ref().unwrap().token();
|
||||||
|
|
||||||
|
while let Ok(Ok(line)) = out_receiver.recv_async().timeout_at(done.clone()).await {
|
||||||
|
if line == FINISHED_LINE {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let line = format!("{}\n", line);
|
||||||
|
if let Err(e) = out.write_all(line.as_bytes()).await {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if let Err(e) = out.flush().await {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn command_loop(&self) {
|
||||||
|
let (in_io, out_sender, connection_state_receiver, done) = {
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
(
|
||||||
|
inner.in_io.clone(),
|
||||||
|
inner.out_sender.clone(),
|
||||||
|
inner.connection_state_receiver.clone(),
|
||||||
|
inner.done.as_ref().unwrap().token(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let mut in_io = in_io.lock().await;
|
||||||
|
|
||||||
|
let (exec_sender, exec_receiver) = flume::bounded(1);
|
||||||
|
|
||||||
|
// Wait for connection to be established
|
||||||
|
loop {
|
||||||
|
match connection_state_receiver.recv_async().await {
|
||||||
|
Ok(ConnectionState::ConnectedTCP(_, _))
|
||||||
|
| Ok(ConnectionState::ConnectedIPC(_, _)) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(ConnectionState::RetryingTCP(_, _)) | Ok(ConnectionState::RetryingIPC(_, _)) => {
|
||||||
|
}
|
||||||
|
Ok(ConnectionState::Disconnected) => {}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the input
|
||||||
|
loop {
|
||||||
|
let mut line = String::new();
|
||||||
|
match in_io.read_line(&mut line).timeout_at(done.clone()).await {
|
||||||
|
Ok(Ok(bytes)) => {
|
||||||
|
if bytes == 0 {
|
||||||
|
// Clean exit after everything else is sent
|
||||||
|
if let Err(e) = out_sender.send(FINISHED_LINE.to_string()) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let line = line.trim();
|
||||||
|
if !line.is_empty() {
|
||||||
|
let cmdproc = self.inner.lock().cmdproc.clone();
|
||||||
|
if let Some(cmdproc) = &cmdproc {
|
||||||
|
// Run command
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
line,
|
||||||
|
UICallback::IOReadWrite(Box::new({
|
||||||
|
let exec_sender = exec_sender.clone();
|
||||||
|
move || {
|
||||||
|
// Let the next command execute
|
||||||
|
if let Err(e) = exec_sender.send(()) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Wait until command is done executing before running the next line
|
||||||
|
if let Err(e) = exec_receiver.recv_async().await {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
eprintln!("IO Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: AsyncRead + Unpin + Send + 'static, W: AsyncWrite + Unpin + Send + 'static> UI
|
||||||
|
for IOReadWriteUI<R, W>
|
||||||
|
{
|
||||||
|
fn set_command_processor(&mut self, cmdproc: CommandProcessor) {
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
inner.cmdproc = Some(cmdproc);
|
||||||
|
}
|
||||||
|
fn run_async(&mut self) -> Pin<Box<dyn core::future::Future<Output = ()>>> {
|
||||||
|
let this = self.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
let out_fut = this.output_loop();
|
||||||
|
let cmd_fut = this.command_loop();
|
||||||
|
futures::join!(out_fut, cmd_fut);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct IOReadWriteUISender<R: AsyncRead + Unpin + Send, W: AsyncWrite + Unpin + Send> {
|
||||||
|
inner: Arc<Mutex<IOReadWriteUIInner<R, W>>>,
|
||||||
|
out_sender: flume::Sender<String>,
|
||||||
|
connection_state_sender: flume::Sender<ConnectionState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: AsyncRead + Unpin + Send + 'static, W: AsyncWrite + Unpin + Send + 'static> UISender
|
||||||
|
for IOReadWriteUISender<R, W>
|
||||||
|
{
|
||||||
|
fn clone_uisender(&self) -> Box<dyn UISender> {
|
||||||
|
Box::new(IOReadWriteUISender {
|
||||||
|
inner: self.inner.clone(),
|
||||||
|
out_sender: self.out_sender.clone(),
|
||||||
|
connection_state_sender: self.connection_state_sender.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn as_logwriter(&self) -> Option<Box<dyn LogWriter>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_string_dialog(&self, title: &str, text: &str, close_cb: UICallback) {
|
||||||
|
if let Err(e) = self.out_sender.send(format!("{}: {}", title, text)) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
if let UICallback::IOReadWrite(mut close_cb) = close_cb {
|
||||||
|
close_cb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quit(&self) {
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_callback(&self, callback: UICallback) {
|
||||||
|
if let UICallback::IOReadWrite(mut callback) = callback {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn set_attachment_state(
|
||||||
|
&mut self,
|
||||||
|
_state: &str,
|
||||||
|
_public_internet_ready: bool,
|
||||||
|
_local_network_ready: bool,
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
fn set_network_status(
|
||||||
|
&mut self,
|
||||||
|
_started: bool,
|
||||||
|
_bps_down: u64,
|
||||||
|
_bps_up: u64,
|
||||||
|
mut _peers: Vec<json::JsonValue>,
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
fn set_config(&mut self, _config: &json::JsonValue) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
fn set_connection_state(&mut self, state: ConnectionState) {
|
||||||
|
if let Err(e) = self.connection_state_sender.send(state) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_node_event(&self, _log_color: Level, event: &str) {
|
||||||
|
if let Err(e) = self.out_sender.send(format!("{}\n", event)) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn add_log_event(&self, _log_color: Level, _event: &str) {}
|
||||||
|
}
|
274
veilid-cli/src/log_viewer_ui.rs
Normal file
274
veilid-cli/src/log_viewer_ui.rs
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
use crate::command_processor::*;
|
||||||
|
use crate::cursive_ui::CursiveUI;
|
||||||
|
use crate::settings::*;
|
||||||
|
use crate::tools::*;
|
||||||
|
use crate::ui::*;
|
||||||
|
|
||||||
|
use console::{style, Term};
|
||||||
|
use flexi_logger::writers::LogWriter;
|
||||||
|
use stop_token::future::FutureExt as StopTokenFutureExt;
|
||||||
|
use stop_token::*;
|
||||||
|
|
||||||
|
pub type LogViewerUICallback = Box<dyn FnMut() + Send>;
|
||||||
|
|
||||||
|
pub struct LogViewerUIInner {
|
||||||
|
cmdproc: Option<CommandProcessor>,
|
||||||
|
done: Option<StopSource>,
|
||||||
|
term: Term,
|
||||||
|
connection_state_receiver: flume::Receiver<ConnectionState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LogViewerUI {
|
||||||
|
inner: Arc<Mutex<LogViewerUIInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogViewerUI {
|
||||||
|
pub fn new(_settings: &Settings) -> (Self, LogViewerUISender) {
|
||||||
|
let (cssender, csreceiver) = flume::unbounded::<ConnectionState>();
|
||||||
|
|
||||||
|
let term = Term::stdout();
|
||||||
|
let enable_color = console::colors_enabled() && term.features().colors_supported();
|
||||||
|
|
||||||
|
// Create the UI object
|
||||||
|
let this = Self {
|
||||||
|
inner: Arc::new(Mutex::new(LogViewerUIInner {
|
||||||
|
cmdproc: None,
|
||||||
|
done: Some(StopSource::new()),
|
||||||
|
term: term.clone(),
|
||||||
|
connection_state_receiver: csreceiver,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ui_sender = LogViewerUISender {
|
||||||
|
inner: this.inner.clone(),
|
||||||
|
connection_state_sender: cssender,
|
||||||
|
term,
|
||||||
|
enable_color,
|
||||||
|
};
|
||||||
|
|
||||||
|
(this, ui_sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn command_loop(&self) {
|
||||||
|
let (connection_state_receiver, term, done) = {
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
(
|
||||||
|
inner.connection_state_receiver.clone(),
|
||||||
|
inner.term.clone(),
|
||||||
|
inner.done.as_ref().unwrap().token(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
CursiveUI::set_start_time();
|
||||||
|
|
||||||
|
// Wait for connection to be established
|
||||||
|
loop {
|
||||||
|
match connection_state_receiver.recv_async().await {
|
||||||
|
Ok(ConnectionState::ConnectedTCP(_, _))
|
||||||
|
| Ok(ConnectionState::ConnectedIPC(_, _)) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(ConnectionState::RetryingTCP(_, _)) | Ok(ConnectionState::RetryingIPC(_, _)) => {
|
||||||
|
}
|
||||||
|
Ok(ConnectionState::Disconnected) => {}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cmdproc = self.inner.lock().cmdproc.clone().unwrap();
|
||||||
|
|
||||||
|
if !term.features().is_attended() {
|
||||||
|
done.await;
|
||||||
|
} else {
|
||||||
|
while let Ok(Ok(c)) = blocking_wrapper(
|
||||||
|
{
|
||||||
|
let term = term.clone();
|
||||||
|
move || term.read_char()
|
||||||
|
},
|
||||||
|
Err(std::io::Error::other("failed")),
|
||||||
|
)
|
||||||
|
.timeout_at(done.clone())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
match c {
|
||||||
|
'q' | 'Q' => {
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
'e' | 'E' => {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
"change_log_level api error",
|
||||||
|
UICallback::LogViewer(Box::new(|| {})),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'w' | 'W' => {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
"change_log_level api warn",
|
||||||
|
UICallback::LogViewer(Box::new(|| {})),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'i' | 'I' => {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
"change_log_level api info",
|
||||||
|
UICallback::LogViewer(Box::new(|| {})),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'd' | 'D' => {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
"change_log_level api debug",
|
||||||
|
UICallback::LogViewer(Box::new(|| {})),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
't' | 'T' => {
|
||||||
|
if let Err(e) = cmdproc.run_command(
|
||||||
|
"change_log_level api trace",
|
||||||
|
UICallback::LogViewer(Box::new(|| {})),
|
||||||
|
) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'h' | 'H' => {
|
||||||
|
println!(
|
||||||
|
r"Help:
|
||||||
|
h - This help
|
||||||
|
e - Change log level to 'error'
|
||||||
|
w - Change log level to 'warn'
|
||||||
|
i - Change log level to 'info'
|
||||||
|
d - Change log level to 'debug'
|
||||||
|
t - Change log level to 'trace'
|
||||||
|
q - Quit
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UI for LogViewerUI {
|
||||||
|
fn set_command_processor(&mut self, cmdproc: CommandProcessor) {
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
inner.cmdproc = Some(cmdproc);
|
||||||
|
}
|
||||||
|
fn run_async(&mut self) -> Pin<Box<dyn core::future::Future<Output = ()>>> {
|
||||||
|
let this = self.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
this.command_loop().await;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LogViewerUISender {
|
||||||
|
inner: Arc<Mutex<LogViewerUIInner>>,
|
||||||
|
connection_state_sender: flume::Sender<ConnectionState>,
|
||||||
|
term: Term,
|
||||||
|
enable_color: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UISender for LogViewerUISender {
|
||||||
|
fn clone_uisender(&self) -> Box<dyn UISender> {
|
||||||
|
Box::new(LogViewerUISender {
|
||||||
|
inner: self.inner.clone(),
|
||||||
|
connection_state_sender: self.connection_state_sender.clone(),
|
||||||
|
term: self.term.clone(),
|
||||||
|
enable_color: self.enable_color,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn as_logwriter(&self) -> Option<Box<dyn LogWriter>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_string_dialog(&self, title: &str, text: &str, close_cb: UICallback) {
|
||||||
|
println!("{}: {}", title, text);
|
||||||
|
if let UICallback::Interactive(mut close_cb) = close_cb {
|
||||||
|
close_cb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn quit(&self) {
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_callback(&self, callback: UICallback) {
|
||||||
|
if let UICallback::Interactive(mut callback) = callback {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn set_attachment_state(
|
||||||
|
&mut self,
|
||||||
|
_state: &str,
|
||||||
|
_public_internet_ready: bool,
|
||||||
|
_local_network_ready: bool,
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
fn set_network_status(
|
||||||
|
&mut self,
|
||||||
|
_started: bool,
|
||||||
|
_bps_down: u64,
|
||||||
|
_bps_up: u64,
|
||||||
|
mut _peers: Vec<json::JsonValue>,
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
fn set_config(&mut self, _config: &json::JsonValue) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
fn set_connection_state(&mut self, state: ConnectionState) {
|
||||||
|
if let Err(e) = self.connection_state_sender.send(state) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_node_event(&self, _log_color: Level, event: &str) {
|
||||||
|
println!("{}", event);
|
||||||
|
}
|
||||||
|
fn add_log_event(&self, log_color: Level, event: &str) {
|
||||||
|
let log_line = format!(
|
||||||
|
"{}: {}",
|
||||||
|
CursiveUI::cli_ts(CursiveUI::get_start_time()),
|
||||||
|
event
|
||||||
|
);
|
||||||
|
if self.enable_color {
|
||||||
|
let log_line = match log_color {
|
||||||
|
Level::Error => style(log_line).red().bright().to_string(),
|
||||||
|
Level::Warn => style(log_line).yellow().bright().to_string(),
|
||||||
|
Level::Info => log_line,
|
||||||
|
Level::Debug => style(log_line).green().bright().to_string(),
|
||||||
|
Level::Trace => style(log_line).blue().bright().to_string(),
|
||||||
|
};
|
||||||
|
if let Err(e) = self.term.write_line(&log_line) {
|
||||||
|
eprintln!("Error: {:?}", e);
|
||||||
|
self.inner.lock().done.take();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("{}", log_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,14 +3,19 @@
|
|||||||
#![deny(unused_must_use)]
|
#![deny(unused_must_use)]
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
use crate::{settings::NamedSocketAddrs, tools::*};
|
use crate::{settings::NamedSocketAddrs, tools::*, ui::*};
|
||||||
|
|
||||||
use clap::{Parser, ValueEnum};
|
use clap::{Parser, ValueEnum};
|
||||||
use flexi_logger::*;
|
use flexi_logger::*;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
mod cached_text_view;
|
mod cached_text_view;
|
||||||
mod client_api_connection;
|
mod client_api_connection;
|
||||||
mod command_processor;
|
mod command_processor;
|
||||||
|
mod cursive_ui;
|
||||||
|
mod interactive_ui;
|
||||||
|
mod io_read_write_ui;
|
||||||
|
mod log_viewer_ui;
|
||||||
mod peers_table_view;
|
mod peers_table_view;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod tools;
|
mod tools;
|
||||||
@ -31,7 +36,7 @@ struct CmdlineArgs {
|
|||||||
#[arg(long, short = 'p')]
|
#[arg(long, short = 'p')]
|
||||||
ipc_path: Option<PathBuf>,
|
ipc_path: Option<PathBuf>,
|
||||||
/// Subnode index to use when connecting
|
/// Subnode index to use when connecting
|
||||||
#[arg(long, short = 'i', default_value = "0")]
|
#[arg(long, default_value = "0")]
|
||||||
subnode_index: usize,
|
subnode_index: usize,
|
||||||
/// Address to connect to
|
/// Address to connect to
|
||||||
#[arg(long, short = 'a')]
|
#[arg(long, short = 'a')]
|
||||||
@ -45,9 +50,28 @@ struct CmdlineArgs {
|
|||||||
/// log level
|
/// log level
|
||||||
#[arg(value_enum)]
|
#[arg(value_enum)]
|
||||||
log_level: Option<LogLevel>,
|
log_level: Option<LogLevel>,
|
||||||
|
/// interactive
|
||||||
|
#[arg(long, short = 'i', group = "execution_mode")]
|
||||||
|
interactive: bool,
|
||||||
|
/// evaluate
|
||||||
|
#[arg(long, short = 'e', group = "execution_mode")]
|
||||||
|
evaluate: Option<String>,
|
||||||
|
/// show log only
|
||||||
|
#[arg(long, short = 'l', group = "execution_mode")]
|
||||||
|
log: bool,
|
||||||
|
/// read commands from file
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
short = 'f',
|
||||||
|
group = "execution_mode",
|
||||||
|
value_name = "COMMAND_FILE"
|
||||||
|
)]
|
||||||
|
command_file: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), String> {
|
fn main() -> Result<(), String> {
|
||||||
|
// Start async
|
||||||
|
block_on(async move {
|
||||||
// Get command line options
|
// Get command line options
|
||||||
let default_config_path = settings::Settings::get_default_config_path();
|
let default_config_path = settings::Settings::get_default_config_path();
|
||||||
let args = CmdlineArgs::parse();
|
let args = CmdlineArgs::parse();
|
||||||
@ -78,8 +102,93 @@ fn main() -> Result<(), String> {
|
|||||||
settings.logging.terminal.enabled = true;
|
settings.logging.terminal.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we are running in interactive mode disable some things
|
||||||
|
let mut enable_cursive = true;
|
||||||
|
if args.interactive || args.log || args.command_file.is_some() || args.evaluate.is_some() {
|
||||||
|
settings.logging.terminal.enabled = false;
|
||||||
|
enable_cursive = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Create UI object
|
// Create UI object
|
||||||
let (mut sivui, uisender) = ui::UI::new(settings.interface.node_log.scrollback, &settings);
|
let (mut ui, uisender) = if enable_cursive {
|
||||||
|
let (ui, uisender) = cursive_ui::CursiveUI::new(&settings);
|
||||||
|
(
|
||||||
|
Box::new(ui) as Box<dyn UI>,
|
||||||
|
Box::new(uisender) as Box<dyn UISender>,
|
||||||
|
)
|
||||||
|
} else if args.interactive {
|
||||||
|
let (ui, uisender) = interactive_ui::InteractiveUI::new(&settings);
|
||||||
|
(
|
||||||
|
Box::new(ui) as Box<dyn UI>,
|
||||||
|
Box::new(uisender) as Box<dyn UISender>,
|
||||||
|
)
|
||||||
|
} else if let Some(command_file) = args.command_file {
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature="rt-async-std")] {
|
||||||
|
let (in_obj, out_obj) =
|
||||||
|
if command_file.to_string_lossy() == "-" {
|
||||||
|
(Box::pin(async_std::io::stdin()) as Pin<Box<dyn futures::AsyncRead + Send>>, async_std::io::stdout())
|
||||||
|
} else {
|
||||||
|
let f = match async_std::fs::File::open(command_file).await {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e.to_string());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(Box::pin(f) as Pin<Box<dyn futures::AsyncRead + Send>>, async_std::io::stdout())
|
||||||
|
};
|
||||||
|
} else if #[cfg(feature="rt-tokio")] {
|
||||||
|
use tokio_util::compat::{TokioAsyncWriteCompatExt, TokioAsyncReadCompatExt};
|
||||||
|
let (in_obj, out_obj) =
|
||||||
|
if command_file.to_string_lossy() == "-" {
|
||||||
|
(Box::pin(tokio::io::stdin().compat()) as Pin<Box<dyn futures::AsyncRead + Send>>, tokio::io::stdout().compat_write())
|
||||||
|
} else {
|
||||||
|
let f = match tokio::fs::File::open(command_file).await {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e.to_string());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(Box::pin(f.compat()) as Pin<Box<dyn futures::AsyncRead + Send>>, tokio::io::stdout().compat_write())
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
compile_error!("needs executor implementation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (ui, uisender) = io_read_write_ui::IOReadWriteUI::new(&settings, in_obj, out_obj);
|
||||||
|
(
|
||||||
|
Box::new(ui) as Box<dyn UI>,
|
||||||
|
Box::new(uisender) as Box<dyn UISender>,
|
||||||
|
)
|
||||||
|
} else if let Some(evaluate) = args.evaluate {
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature="rt-async-std")] {
|
||||||
|
let in_str = format!("{}\n", evaluate);
|
||||||
|
let (in_obj, out_obj) = (futures::io::Cursor::new(in_str), async_std::io::stdout());
|
||||||
|
} else if #[cfg(feature="rt-tokio")] {
|
||||||
|
use tokio_util::compat::{TokioAsyncWriteCompatExt};
|
||||||
|
let in_str = format!("{}\n", evaluate);
|
||||||
|
let (in_obj, out_obj) = (futures::io::Cursor::new(in_str), tokio::io::stdout().compat_write());
|
||||||
|
} else {
|
||||||
|
compile_error!("needs executor implementation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (ui, uisender) = io_read_write_ui::IOReadWriteUI::new(&settings, in_obj, out_obj);
|
||||||
|
(
|
||||||
|
Box::new(ui) as Box<dyn UI>,
|
||||||
|
Box::new(uisender) as Box<dyn UISender>,
|
||||||
|
)
|
||||||
|
} else if args.log {
|
||||||
|
let (ui, uisender) = log_viewer_ui::LogViewerUI::new(&settings);
|
||||||
|
(
|
||||||
|
Box::new(ui) as Box<dyn UI>,
|
||||||
|
Box::new(uisender) as Box<dyn UISender>,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
panic!("unknown ui mode");
|
||||||
|
};
|
||||||
|
|
||||||
// Set up loggers
|
// Set up loggers
|
||||||
{
|
{
|
||||||
@ -105,13 +214,13 @@ fn main() -> Result<(), String> {
|
|||||||
FileSpec::default()
|
FileSpec::default()
|
||||||
.directory(settings.logging.file.directory.clone())
|
.directory(settings.logging.file.directory.clone())
|
||||||
.suppress_timestamp(),
|
.suppress_timestamp(),
|
||||||
Box::new(uisender.clone()),
|
uisender.as_logwriter().unwrap(),
|
||||||
)
|
)
|
||||||
.start()
|
.start()
|
||||||
.expect("failed to initialize logger!");
|
.expect("failed to initialize logger!");
|
||||||
} else {
|
} else {
|
||||||
logger
|
logger
|
||||||
.log_to_writer(Box::new(uisender.clone()))
|
.log_to_writer(uisender.as_logwriter().unwrap())
|
||||||
.start()
|
.start()
|
||||||
.expect("failed to initialize logger!");
|
.expect("failed to initialize logger!");
|
||||||
}
|
}
|
||||||
@ -195,7 +304,8 @@ fn main() -> Result<(), String> {
|
|||||||
// Create command processor
|
// Create command processor
|
||||||
debug!("Creating Command Processor ");
|
debug!("Creating Command Processor ");
|
||||||
let comproc = command_processor::CommandProcessor::new(uisender, &settings);
|
let comproc = command_processor::CommandProcessor::new(uisender, &settings);
|
||||||
sivui.set_command_processor(comproc.clone());
|
|
||||||
|
ui.set_command_processor(comproc.clone());
|
||||||
|
|
||||||
// Create client api client side
|
// Create client api client side
|
||||||
info!("Starting API connection");
|
info!("Starting API connection");
|
||||||
@ -217,11 +327,9 @@ fn main() -> Result<(), String> {
|
|||||||
let comproc2 = comproc.clone();
|
let comproc2 = comproc.clone();
|
||||||
let connection_future = comproc.connection_manager();
|
let connection_future = comproc.connection_manager();
|
||||||
|
|
||||||
// Start async
|
|
||||||
block_on(async move {
|
|
||||||
// Start UI
|
// Start UI
|
||||||
let ui_future = async move {
|
let ui_future = async move {
|
||||||
sivui.run_async().await;
|
ui.run_async().await;
|
||||||
|
|
||||||
// When UI quits, close connection and command processor cleanly
|
// When UI quits, close connection and command processor cleanly
|
||||||
comproc2.quit();
|
comproc2.quit();
|
||||||
@ -240,7 +348,6 @@ fn main() -> Result<(), String> {
|
|||||||
compile_error!("needs executor implementation")
|
compile_error!("needs executor implementation")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
1401
veilid-cli/src/ui.rs
1401
veilid-cli/src/ui.rs
File diff suppressed because it is too large
Load Diff
@ -47,12 +47,10 @@ enable-crypto-none = []
|
|||||||
# Debugging and testing features
|
# Debugging and testing features
|
||||||
verbose-tracing = []
|
verbose-tracing = []
|
||||||
tracking = []
|
tracking = []
|
||||||
debug-dht = []
|
|
||||||
crypto-test = ["enable-crypto-vld0", "enable-crypto-none"]
|
crypto-test = ["enable-crypto-vld0", "enable-crypto-none"]
|
||||||
crypto-test-none = ["enable-crypto-none"]
|
crypto-test-none = ["enable-crypto-none"]
|
||||||
veilid_core_android_tests = ["dep:paranoid-android"]
|
veilid_core_android_tests = ["dep:paranoid-android"]
|
||||||
veilid_core_ios_tests = ["dep:tracing-oslog"]
|
veilid_core_ios_tests = ["dep:tracing-oslog"]
|
||||||
network-result-extra = ["veilid-tools/network-result-extra"]
|
|
||||||
|
|
||||||
### DEPENDENCIES
|
### DEPENDENCIES
|
||||||
|
|
||||||
@ -122,7 +120,7 @@ chacha20 = "0.9.1"
|
|||||||
argon2 = "0.5.2"
|
argon2 = "0.5.2"
|
||||||
|
|
||||||
# Network
|
# Network
|
||||||
async-std-resolver = { version = "0.23.2", optional = true }
|
async-std-resolver = { version = "0.24.0", optional = true }
|
||||||
hickory-resolver = { version = "0.24.0", optional = true }
|
hickory-resolver = { version = "0.24.0", optional = true }
|
||||||
|
|
||||||
# Serialization
|
# Serialization
|
||||||
@ -144,13 +142,14 @@ lz4_flex = { version = "0.11.1", default-features = false, features = [
|
|||||||
|
|
||||||
# Tools
|
# Tools
|
||||||
config = { version = "0.13.4", features = ["yaml"] }
|
config = { version = "0.13.4", features = ["yaml"] }
|
||||||
bugsalot = { package = "veilid-bugsalot", version = "0.1.0" }
|
bugsalot = { package = "veilid-bugsalot", version = "0.2.0" }
|
||||||
chrono = "0.4.31"
|
chrono = "0.4.31"
|
||||||
libc = "0.2.151"
|
libc = "0.2.151"
|
||||||
nix = "0.27.1"
|
nix = "0.27.1"
|
||||||
|
|
||||||
# System
|
# System
|
||||||
async-std = { version = "1.12.0", features = ["unstable"], optional = true }
|
async-std = { version = "1.12.0", features = ["unstable"], optional = true }
|
||||||
|
sysinfo = { version = "^0.30.6" }
|
||||||
tokio = { version = "1.35.0", features = ["full"], optional = true }
|
tokio = { version = "1.35.0", features = ["full"], optional = true }
|
||||||
tokio-util = { version = "0.7.10", features = ["compat"], optional = true }
|
tokio-util = { version = "0.7.10", features = ["compat"], optional = true }
|
||||||
tokio-stream = { version = "0.1.14", features = ["net"], optional = true }
|
tokio-stream = { version = "0.1.14", features = ["net"], optional = true }
|
||||||
|
@ -81,11 +81,7 @@ fn append_hash<P: AsRef<Path>, Q: AsRef<Path>>(input_path: P, output_path: Q) {
|
|||||||
let output_path = output_path.as_ref();
|
let output_path = output_path.as_ref();
|
||||||
let lines = std::io::BufReader::new(std::fs::File::open(input_path).unwrap()).lines();
|
let lines = std::io::BufReader::new(std::fs::File::open(input_path).unwrap()).lines();
|
||||||
let h = calculate_hash(lines);
|
let h = calculate_hash(lines);
|
||||||
let mut out_file = OpenOptions::new()
|
let mut out_file = OpenOptions::new().append(true).open(output_path).unwrap();
|
||||||
.write(true)
|
|
||||||
.append(true)
|
|
||||||
.open(output_path)
|
|
||||||
.unwrap();
|
|
||||||
writeln!(out_file, "\n//BUILDHASH:{}", hex::encode(h)).unwrap();
|
writeln!(out_file, "\n//BUILDHASH:{}", hex::encode(h)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,30 +346,46 @@ struct OperationSetValueQ @0xbac06191ff8bdbc5 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct OperationSetValueA @0x9378d0732dc95be2 {
|
struct OperationSetValueA @0x9378d0732dc95be2 {
|
||||||
set @0 :Bool; # true if the set was close enough to be set
|
set @0 :Bool; # true if the set was accepted
|
||||||
value @1 :SignedValueData; # optional: the current value at the key if the set seq number was lower or equal to what was there before
|
value @1 :SignedValueData; # optional: the current value at the key if the set seq number was lower or equal to what was there before
|
||||||
peers @2 :List(PeerInfo); # returned 'closer peer' information on either success or failure
|
peers @2 :List(PeerInfo); # returned 'closer peer' information on either success or failure
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OperationWatchValueQ @0xf9a5a6c547b9b228 {
|
struct OperationWatchValueQ @0xf9a5a6c547b9b228 {
|
||||||
key @0 :TypedKey; # key for value to watch
|
key @0 :TypedKey; # key for value to watch
|
||||||
subkeys @1 :List(SubkeyRange); # subkey range to watch (up to 512 subranges), if empty, watch everything
|
subkeys @1 :List(SubkeyRange); # subkey range to watch (up to 512 subranges), if empty this implies 0..=UINT32_MAX
|
||||||
expiration @2 :UInt64; # requested timestamp when this watch will expire in usec since epoch (can be return less, 0 for max)
|
expiration @2 :UInt64; # requested timestamp when this watch will expire in usec since epoch (can be return less, 0 for max)
|
||||||
count @3 :UInt32; # requested number of changes to watch for (0 = cancel, 1 = single shot, 2+ = counter, UINT32_MAX = continuous)
|
count @3 :UInt32; # requested number of changes to watch for (0 = cancel, 1 = single shot, 2+ = counter, UINT32_MAX = continuous)
|
||||||
watcher @4 :PublicKey; # optional: the watcher performing the watch, can be the owner or a schema member
|
watchId @4 :UInt64; # if 0, request a new watch. if >0, existing watch id
|
||||||
signature @5 :Signature; # optional: signature of the watcher, must be one of the schema members or the key owner. signature covers: key, subkeys, expiration, count
|
watcher @5 :PublicKey; # the watcher performing the watch, can be the owner or a schema member, or a generated anonymous watch keypair
|
||||||
|
signature @6 :Signature; # signature of the watcher, signature covers: key, subkeys, expiration, count, watchId
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OperationWatchValueA @0xa726cab7064ba893 {
|
struct OperationWatchValueA @0xa726cab7064ba893 {
|
||||||
expiration @0 :UInt64; # timestamp when this watch will expire in usec since epoch (0 if watch was rejected). if watch is being cancelled (with count = 0), this will be the non-zero former expiration time.
|
accepted @0 :Bool; # true if the watch was close enough to be accepted
|
||||||
peers @1 :List(PeerInfo); # returned list of other nodes to ask that could propagate watches
|
expiration @1 :UInt64; # timestamp when this watch will expire in usec since epoch (0 if watch was cancelled/dropped)
|
||||||
|
peers @2 :List(PeerInfo); # returned list of other nodes to ask that could propagate watches
|
||||||
|
watchId @3 :UInt64; # (0 = id not allocated if rejecting new watch) random id for watch instance on this node
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OperationInspectValueQ @0xdef712d2fd16f55a {
|
||||||
|
key @0 :TypedKey; # DHT Key = Hash(ownerKeyKind) of: [ ownerKeyValue, schema ]
|
||||||
|
subkeys @1 :List(SubkeyRange); # subkey range to inspect (up to 512 total subkeys), if empty this implies 0..=511
|
||||||
|
wantDescriptor @2 :Bool; # whether or not to include the descriptor for the key
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OperationInspectValueA @0xb8b57faf960ee102 {
|
||||||
|
seqs @0 :List(ValueSeqNum); # the list of subkey value sequence numbers in ascending order for each subkey in the requested range. if a subkey has not been written to, it is given a value of UINT32_MAX. these are not signed, and may be immediately out of date, and must be verified by a GetValueQ request.
|
||||||
|
peers @1 :List(PeerInfo); # returned 'closer peer' information on either success or failure
|
||||||
|
descriptor @2 :SignedValueDescriptor; # optional: the descriptor if requested if the value is also returned
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OperationValueChanged @0xd1c59ebdd8cc1bf6 {
|
struct OperationValueChanged @0xd1c59ebdd8cc1bf6 {
|
||||||
key @0 :TypedKey; # key for value that changed
|
key @0 :TypedKey; # key for value that changed
|
||||||
subkeys @1 :List(SubkeyRange); # subkey range that changed (up to 512 ranges at a time)
|
subkeys @1 :List(SubkeyRange); # subkey range that changed (up to 512 ranges at a time, if empty this is a watch expiration notice)
|
||||||
count @2 :UInt32; # remaining changes left (0 means watch has expired)
|
count @2 :UInt32; # remaining changes left (0 means watch has expired)
|
||||||
value @3 :SignedValueData; # first value that changed (the rest can be gotten with getvalue)
|
watchId @3 :UInt64; # watch id this value change came from
|
||||||
|
value @4 :SignedValueData; # first value that changed (the rest can be gotten with getvalue)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OperationSupplyBlockQ @0xadbf4c542d749971 {
|
struct OperationSupplyBlockQ @0xadbf4c542d749971 {
|
||||||
@ -483,15 +499,17 @@ struct Question @0xd8510bc33492ef70 {
|
|||||||
getValueQ @5 :OperationGetValueQ;
|
getValueQ @5 :OperationGetValueQ;
|
||||||
setValueQ @6 :OperationSetValueQ;
|
setValueQ @6 :OperationSetValueQ;
|
||||||
watchValueQ @7 :OperationWatchValueQ;
|
watchValueQ @7 :OperationWatchValueQ;
|
||||||
|
inspectValueQ @8 :OperationInspectValueQ;
|
||||||
|
|
||||||
# #[cfg(feature="unstable-blockstore")]
|
# #[cfg(feature="unstable-blockstore")]
|
||||||
# supplyBlockQ @8 :OperationSupplyBlockQ;
|
# supplyBlockQ @9 :OperationSupplyBlockQ;
|
||||||
# findBlockQ @9 :OperationFindBlockQ;
|
# findBlockQ @10 :OperationFindBlockQ;
|
||||||
|
|
||||||
# Tunnel operations
|
# Tunnel operations
|
||||||
# #[cfg(feature="unstable-tunnels")]
|
# #[cfg(feature="unstable-tunnels")]
|
||||||
# startTunnelQ @10 :OperationStartTunnelQ;
|
# startTunnelQ @11 :OperationStartTunnelQ;
|
||||||
# completeTunnelQ @11 :OperationCompleteTunnelQ;
|
# completeTunnelQ @12 :OperationCompleteTunnelQ;
|
||||||
# cancelTunnelQ @12 :OperationCancelTunnelQ;
|
# cancelTunnelQ @13 :OperationCancelTunnelQ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,16 +540,17 @@ struct Answer @0xacacb8b6988c1058 {
|
|||||||
getValueA @3 :OperationGetValueA;
|
getValueA @3 :OperationGetValueA;
|
||||||
setValueA @4 :OperationSetValueA;
|
setValueA @4 :OperationSetValueA;
|
||||||
watchValueA @5 :OperationWatchValueA;
|
watchValueA @5 :OperationWatchValueA;
|
||||||
|
inspectValueA @6 :OperationInspectValueA;
|
||||||
|
|
||||||
# #[cfg(feature="unstable-blockstore")]
|
# #[cfg(feature="unstable-blockstore")]
|
||||||
#supplyBlockA @6 :OperationSupplyBlockA;
|
#supplyBlockA @7 :OperationSupplyBlockA;
|
||||||
#findBlockA @7 :OperationFindBlockA;
|
#findBlockA @8 :OperationFindBlockA;
|
||||||
|
|
||||||
# Tunnel operations
|
# Tunnel operations
|
||||||
# #[cfg(feature="unstable-tunnels")]
|
# #[cfg(feature="unstable-tunnels")]
|
||||||
# startTunnelA @8 :OperationStartTunnelA;
|
# startTunnelA @9 :OperationStartTunnelA;
|
||||||
# completeTunnelA @9 :OperationCompleteTunnelA;
|
# completeTunnelA @10 :OperationCompleteTunnelA;
|
||||||
# cancelTunnelA @10 :OperationCancelTunnelA;
|
# cancelTunnelA @11 :OperationCancelTunnelA;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -179,7 +179,14 @@ impl AttachmentManager {
|
|||||||
fn update_attaching_detaching_state(&self, state: AttachmentState) {
|
fn update_attaching_detaching_state(&self, state: AttachmentState) {
|
||||||
let update_callback = {
|
let update_callback = {
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
|
|
||||||
|
// Clear routing table health so when we start measuring it we start from scratch
|
||||||
|
inner.last_routing_table_health = None;
|
||||||
|
|
||||||
|
// Set attachment state directly
|
||||||
inner.last_attachment_state = state;
|
inner.last_attachment_state = state;
|
||||||
|
|
||||||
|
// Set timestamps
|
||||||
if state == AttachmentState::Attaching {
|
if state == AttachmentState::Attaching {
|
||||||
inner.attach_ts = Some(get_aligned_timestamp());
|
inner.attach_ts = Some(get_aligned_timestamp());
|
||||||
} else if state == AttachmentState::Detached {
|
} else if state == AttachmentState::Detached {
|
||||||
@ -189,9 +196,12 @@ impl AttachmentManager {
|
|||||||
} else {
|
} else {
|
||||||
unreachable!("don't use this for attached states, use update_attachment()");
|
unreachable!("don't use this for attached states, use update_attachment()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get callback
|
||||||
inner.update_callback.clone()
|
inner.update_callback.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Send update
|
||||||
if let Some(update_callback) = update_callback {
|
if let Some(update_callback) = update_callback {
|
||||||
update_callback(VeilidUpdate::Attachment(Box::new(VeilidStateAttachment {
|
update_callback(VeilidUpdate::Attachment(Box::new(VeilidStateAttachment {
|
||||||
state,
|
state,
|
||||||
@ -203,7 +213,7 @@ impl AttachmentManager {
|
|||||||
|
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
async fn attachment_maintainer(self) {
|
async fn attachment_maintainer(self) {
|
||||||
debug!("attachment starting");
|
log_net!(debug "attachment starting");
|
||||||
self.update_attaching_detaching_state(AttachmentState::Attaching);
|
self.update_attaching_detaching_state(AttachmentState::Attaching);
|
||||||
|
|
||||||
let netman = self.network_manager();
|
let netman = self.network_manager();
|
||||||
@ -217,7 +227,7 @@ impl AttachmentManager {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("started maintaining peers");
|
log_net!(debug "started maintaining peers");
|
||||||
while self.inner.lock().maintain_peers {
|
while self.inner.lock().maintain_peers {
|
||||||
// tick network manager
|
// tick network manager
|
||||||
if let Err(err) = netman.tick().await {
|
if let Err(err) = netman.tick().await {
|
||||||
@ -241,32 +251,31 @@ impl AttachmentManager {
|
|||||||
// sleep should be at the end in case maintain_peers changes state
|
// sleep should be at the end in case maintain_peers changes state
|
||||||
sleep(1000).await;
|
sleep(1000).await;
|
||||||
}
|
}
|
||||||
debug!("stopped maintaining peers");
|
log_net!(debug "stopped maintaining peers");
|
||||||
|
|
||||||
if !restart {
|
if !restart {
|
||||||
self.update_attaching_detaching_state(AttachmentState::Detaching);
|
self.update_attaching_detaching_state(AttachmentState::Detaching);
|
||||||
debug!("attachment stopping");
|
log_net!(debug "attachment stopping");
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("stopping network");
|
log_net!(debug "stopping network");
|
||||||
netman.shutdown().await;
|
netman.shutdown().await;
|
||||||
|
|
||||||
if !restart {
|
if !restart {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("completely restarting attachment");
|
log_net!(debug "completely restarting attachment");
|
||||||
// chill out for a second first, give network stack time to settle out
|
// chill out for a second first, give network stack time to settle out
|
||||||
sleep(1000).await;
|
sleep(1000).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_attaching_detaching_state(AttachmentState::Detached);
|
self.update_attaching_detaching_state(AttachmentState::Detached);
|
||||||
debug!("attachment stopped");
|
log_net!(debug "attachment stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip_all, err)]
|
#[instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn init(&self, update_callback: UpdateCallback) -> EyreResult<()> {
|
pub async fn init(&self, update_callback: UpdateCallback) -> EyreResult<()> {
|
||||||
trace!("init");
|
|
||||||
{
|
{
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
inner.update_callback = Some(update_callback.clone());
|
inner.update_callback = Some(update_callback.clone());
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::api_tracing_layer::*;
|
|
||||||
use crate::attachment_manager::*;
|
use crate::attachment_manager::*;
|
||||||
use crate::crypto::Crypto;
|
use crate::crypto::Crypto;
|
||||||
|
use crate::logging::*;
|
||||||
use crate::storage_manager::*;
|
use crate::storage_manager::*;
|
||||||
use crate::veilid_api::*;
|
use crate::veilid_api::*;
|
||||||
use crate::veilid_config::*;
|
use crate::veilid_config::*;
|
||||||
@ -70,7 +70,6 @@ impl ServicesContext {
|
|||||||
ApiTracingLayer::init(self.update_callback.clone()).await;
|
ApiTracingLayer::init(self.update_callback.clone()).await;
|
||||||
|
|
||||||
// Set up protected store
|
// Set up protected store
|
||||||
trace!("init protected store");
|
|
||||||
let protected_store = ProtectedStore::new(self.config.clone());
|
let protected_store = ProtectedStore::new(self.config.clone());
|
||||||
if let Err(e) = protected_store.init().await {
|
if let Err(e) = protected_store.init().await {
|
||||||
error!("failed to init protected store: {}", e);
|
error!("failed to init protected store: {}", e);
|
||||||
@ -80,7 +79,6 @@ impl ServicesContext {
|
|||||||
self.protected_store = Some(protected_store.clone());
|
self.protected_store = Some(protected_store.clone());
|
||||||
|
|
||||||
// Set up tablestore and crypto system
|
// Set up tablestore and crypto system
|
||||||
trace!("create table store and crypto system");
|
|
||||||
let table_store = TableStore::new(self.config.clone(), protected_store.clone());
|
let table_store = TableStore::new(self.config.clone(), protected_store.clone());
|
||||||
let crypto = Crypto::new(self.config.clone(), table_store.clone());
|
let crypto = Crypto::new(self.config.clone(), table_store.clone());
|
||||||
table_store.set_crypto(crypto.clone());
|
table_store.set_crypto(crypto.clone());
|
||||||
@ -88,7 +86,6 @@ impl ServicesContext {
|
|||||||
// Initialize table store first, so crypto code can load caches
|
// Initialize table store first, so crypto code can load caches
|
||||||
// Tablestore can use crypto during init, just not any cached operations or things
|
// Tablestore can use crypto during init, just not any cached operations or things
|
||||||
// that require flushing back to the tablestore
|
// that require flushing back to the tablestore
|
||||||
trace!("init table store");
|
|
||||||
if let Err(e) = table_store.init().await {
|
if let Err(e) = table_store.init().await {
|
||||||
error!("failed to init table store: {}", e);
|
error!("failed to init table store: {}", e);
|
||||||
self.shutdown().await;
|
self.shutdown().await;
|
||||||
@ -97,7 +94,6 @@ impl ServicesContext {
|
|||||||
self.table_store = Some(table_store.clone());
|
self.table_store = Some(table_store.clone());
|
||||||
|
|
||||||
// Set up crypto
|
// Set up crypto
|
||||||
trace!("init crypto");
|
|
||||||
if let Err(e) = crypto.init().await {
|
if let Err(e) = crypto.init().await {
|
||||||
error!("failed to init crypto: {}", e);
|
error!("failed to init crypto: {}", e);
|
||||||
self.shutdown().await;
|
self.shutdown().await;
|
||||||
@ -108,7 +104,6 @@ impl ServicesContext {
|
|||||||
// Set up block store
|
// Set up block store
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
{
|
{
|
||||||
trace!("init block store");
|
|
||||||
let block_store = BlockStore::new(self.config.clone());
|
let block_store = BlockStore::new(self.config.clone());
|
||||||
if let Err(e) = block_store.init().await {
|
if let Err(e) = block_store.init().await {
|
||||||
error!("failed to init block store: {}", e);
|
error!("failed to init block store: {}", e);
|
||||||
@ -119,7 +114,6 @@ impl ServicesContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up storage manager
|
// Set up storage manager
|
||||||
trace!("init storage manager");
|
|
||||||
let update_callback = self.update_callback.clone();
|
let update_callback = self.update_callback.clone();
|
||||||
|
|
||||||
let storage_manager = StorageManager::new(
|
let storage_manager = StorageManager::new(
|
||||||
@ -137,7 +131,6 @@ impl ServicesContext {
|
|||||||
self.storage_manager = Some(storage_manager.clone());
|
self.storage_manager = Some(storage_manager.clone());
|
||||||
|
|
||||||
// Set up attachment manager
|
// Set up attachment manager
|
||||||
trace!("init attachment manager");
|
|
||||||
let update_callback = self.update_callback.clone();
|
let update_callback = self.update_callback.clone();
|
||||||
let attachment_manager = AttachmentManager::new(
|
let attachment_manager = AttachmentManager::new(
|
||||||
self.config.clone(),
|
self.config.clone(),
|
||||||
@ -163,28 +156,22 @@ impl ServicesContext {
|
|||||||
info!("Veilid API shutting down");
|
info!("Veilid API shutting down");
|
||||||
|
|
||||||
if let Some(attachment_manager) = &mut self.attachment_manager {
|
if let Some(attachment_manager) = &mut self.attachment_manager {
|
||||||
trace!("terminate attachment manager");
|
|
||||||
attachment_manager.terminate().await;
|
attachment_manager.terminate().await;
|
||||||
}
|
}
|
||||||
if let Some(storage_manager) = &mut self.storage_manager {
|
if let Some(storage_manager) = &mut self.storage_manager {
|
||||||
trace!("terminate storage manager");
|
|
||||||
storage_manager.terminate().await;
|
storage_manager.terminate().await;
|
||||||
}
|
}
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
if let Some(block_store) = &mut self.block_store {
|
if let Some(block_store) = &mut self.block_store {
|
||||||
trace!("terminate block store");
|
|
||||||
block_store.terminate().await;
|
block_store.terminate().await;
|
||||||
}
|
}
|
||||||
if let Some(crypto) = &mut self.crypto {
|
if let Some(crypto) = &mut self.crypto {
|
||||||
trace!("terminate crypto");
|
|
||||||
crypto.terminate().await;
|
crypto.terminate().await;
|
||||||
}
|
}
|
||||||
if let Some(table_store) = &mut self.table_store {
|
if let Some(table_store) = &mut self.table_store {
|
||||||
trace!("terminate table store");
|
|
||||||
table_store.terminate().await;
|
table_store.terminate().await;
|
||||||
}
|
}
|
||||||
if let Some(protected_store) = &mut self.protected_store {
|
if let Some(protected_store) = &mut self.protected_store {
|
||||||
trace!("terminate protected store");
|
|
||||||
protected_store.terminate().await;
|
protected_store.terminate().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +207,6 @@ impl VeilidCoreContext {
|
|||||||
config_callback: ConfigCallback,
|
config_callback: ConfigCallback,
|
||||||
) -> VeilidAPIResult<VeilidCoreContext> {
|
) -> VeilidAPIResult<VeilidCoreContext> {
|
||||||
// Set up config from callback
|
// Set up config from callback
|
||||||
trace!("setup config with callback");
|
|
||||||
let mut config = VeilidConfig::new();
|
let mut config = VeilidConfig::new();
|
||||||
config.setup(config_callback, update_callback.clone())?;
|
config.setup(config_callback, update_callback.clone())?;
|
||||||
|
|
||||||
@ -233,20 +219,17 @@ impl VeilidCoreContext {
|
|||||||
config_json: String,
|
config_json: String,
|
||||||
) -> VeilidAPIResult<VeilidCoreContext> {
|
) -> VeilidAPIResult<VeilidCoreContext> {
|
||||||
// Set up config from json
|
// Set up config from json
|
||||||
trace!("setup config with json");
|
|
||||||
let mut config = VeilidConfig::new();
|
let mut config = VeilidConfig::new();
|
||||||
config.setup_from_json(config_json, update_callback.clone())?;
|
config.setup_from_json(config_json, update_callback.clone())?;
|
||||||
Self::new_common(update_callback, config).await
|
Self::new_common(update_callback, config).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[instrument(err, skip_all)]
|
#[instrument(err, skip_all)]
|
||||||
async fn new_with_config(
|
async fn new_with_config(
|
||||||
update_callback: UpdateCallback,
|
update_callback: UpdateCallback,
|
||||||
config_inner: VeilidConfigInner,
|
config_inner: VeilidConfigInner,
|
||||||
) -> VeilidAPIResult<VeilidCoreContext> {
|
) -> VeilidAPIResult<VeilidCoreContext> {
|
||||||
// Set up config from json
|
// Set up config from json
|
||||||
trace!("setup config with json");
|
|
||||||
let mut config = VeilidConfig::new();
|
let mut config = VeilidConfig::new();
|
||||||
config.setup_from_config(config_inner, update_callback.clone())?;
|
config.setup_from_config(config_inner, update_callback.clone())?;
|
||||||
Self::new_common(update_callback, config).await
|
Self::new_common(update_callback, config).await
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
const VEILID_DOMAIN_API: &[u8] = b"VEILID_API";
|
||||||
|
|
||||||
pub trait CryptoSystem {
|
pub trait CryptoSystem {
|
||||||
// Accessors
|
// Accessors
|
||||||
fn kind(&self) -> CryptoKind;
|
fn kind(&self) -> CryptoKind;
|
||||||
@ -17,6 +19,15 @@ pub trait CryptoSystem {
|
|||||||
fn random_nonce(&self) -> Nonce;
|
fn random_nonce(&self) -> Nonce;
|
||||||
fn random_shared_secret(&self) -> SharedSecret;
|
fn random_shared_secret(&self) -> SharedSecret;
|
||||||
fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret>;
|
fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret>;
|
||||||
|
fn generate_shared_secret(
|
||||||
|
&self,
|
||||||
|
key: &PublicKey,
|
||||||
|
secret: &SecretKey,
|
||||||
|
domain: &[u8],
|
||||||
|
) -> VeilidAPIResult<SharedSecret> {
|
||||||
|
let dh = self.compute_dh(key, secret)?;
|
||||||
|
Ok(self.generate_hash(&[&dh.bytes, domain, VEILID_DOMAIN_API].concat()))
|
||||||
|
}
|
||||||
fn generate_keypair(&self) -> KeyPair;
|
fn generate_keypair(&self) -> KeyPair;
|
||||||
fn generate_hash(&self, data: &[u8]) -> HashDigest;
|
fn generate_hash(&self, data: &[u8]) -> HashDigest;
|
||||||
fn generate_hash_reader(&self, reader: &mut dyn std::io::Read) -> VeilidAPIResult<HashDigest>;
|
fn generate_hash_reader(&self, reader: &mut dyn std::io::Read) -> VeilidAPIResult<HashDigest>;
|
||||||
|
@ -128,8 +128,8 @@ impl Crypto {
|
|||||||
self.unlocked_inner.config.clone()
|
self.unlocked_inner.config.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all, err)]
|
||||||
pub async fn init(&self) -> EyreResult<()> {
|
pub async fn init(&self) -> EyreResult<()> {
|
||||||
trace!("Crypto::init");
|
|
||||||
let table_store = self.unlocked_inner.table_store.clone();
|
let table_store = self.unlocked_inner.table_store.clone();
|
||||||
// Init node id from config
|
// Init node id from config
|
||||||
if let Err(e) = self
|
if let Err(e) = self
|
||||||
@ -190,7 +190,6 @@ impl Crypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn flush(&self) -> EyreResult<()> {
|
pub async fn flush(&self) -> EyreResult<()> {
|
||||||
//trace!("Crypto::flush");
|
|
||||||
let cache_bytes = {
|
let cache_bytes = {
|
||||||
let inner = self.inner.lock();
|
let inner = self.inner.lock();
|
||||||
cache_to_bytes(&inner.dh_cache)
|
cache_to_bytes(&inner.dh_cache)
|
||||||
@ -206,15 +205,14 @@ impl Crypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn terminate(&self) {
|
pub async fn terminate(&self) {
|
||||||
trace!("Crypto::terminate");
|
|
||||||
let flush_future = self.inner.lock().flush_future.take();
|
let flush_future = self.inner.lock().flush_future.take();
|
||||||
if let Some(f) = flush_future {
|
if let Some(f) = flush_future {
|
||||||
f.await;
|
f.await;
|
||||||
}
|
}
|
||||||
trace!("starting termination flush");
|
log_crypto!("starting termination flush");
|
||||||
match self.flush().await {
|
match self.flush().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
trace!("finished termination flush");
|
log_crypto!("finished termination flush");
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("failed termination flush: {}", e);
|
error!("failed termination flush: {}", e);
|
||||||
|
@ -33,7 +33,7 @@ impl ProtectedStore {
|
|||||||
if let Err(e) = self.remove_user_secret(kpsk).await {
|
if let Err(e) = self.remove_user_secret(kpsk).await {
|
||||||
error!("failed to delete '{}': {}", kpsk, e);
|
error!("failed to delete '{}': {}", kpsk, e);
|
||||||
} else {
|
} else {
|
||||||
debug!("deleted table '{}'", kpsk);
|
log_pstore!(debug "deleted table '{}'", kpsk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -19,7 +19,7 @@ impl ProtectedStore {
|
|||||||
if let Err(e) = self.remove_user_secret(kpsk).await {
|
if let Err(e) = self.remove_user_secret(kpsk).await {
|
||||||
error!("failed to delete '{}': {}", kpsk, e);
|
error!("failed to delete '{}': {}", kpsk, e);
|
||||||
} else {
|
} else {
|
||||||
debug!("deleted table '{}'", kpsk);
|
log_pstore!(debug "deleted table '{}'", kpsk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -44,11 +44,11 @@ cfg_if::cfg_if! {
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
mod api_tracing_layer;
|
|
||||||
mod attachment_manager;
|
mod attachment_manager;
|
||||||
mod core_context;
|
mod core_context;
|
||||||
mod crypto;
|
mod crypto;
|
||||||
mod intf;
|
mod intf;
|
||||||
|
mod logging;
|
||||||
mod network_manager;
|
mod network_manager;
|
||||||
mod routing_table;
|
mod routing_table;
|
||||||
mod rpc_processor;
|
mod rpc_processor;
|
||||||
@ -56,14 +56,12 @@ mod storage_manager;
|
|||||||
mod table_store;
|
mod table_store;
|
||||||
mod veilid_api;
|
mod veilid_api;
|
||||||
mod veilid_config;
|
mod veilid_config;
|
||||||
mod veilid_layer_filter;
|
|
||||||
mod wasm_helpers;
|
mod wasm_helpers;
|
||||||
|
|
||||||
pub use self::api_tracing_layer::ApiTracingLayer;
|
pub use self::core_context::{api_startup, api_startup_config, api_startup_json, UpdateCallback};
|
||||||
pub use self::core_context::{api_startup, api_startup_json, api_startup_config, UpdateCallback};
|
pub use self::logging::{ApiTracingLayer, VeilidLayerFilter};
|
||||||
pub use self::veilid_api::*;
|
pub use self::veilid_api::*;
|
||||||
pub use self::veilid_config::*;
|
pub use self::veilid_config::*;
|
||||||
pub use self::veilid_layer_filter::*;
|
|
||||||
pub use veilid_tools as tools;
|
pub use veilid_tools as tools;
|
||||||
|
|
||||||
/// The on-the-wire serialization format for Veilid RPC
|
/// The on-the-wire serialization format for Veilid RPC
|
||||||
@ -88,10 +86,15 @@ pub fn veilid_version() -> (u32, u32, u32) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the default veilid config as a json object
|
||||||
|
pub fn default_veilid_config() -> String {
|
||||||
|
serialize_json(VeilidConfigInner::default())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
pub use intf::android::veilid_core_setup_android;
|
pub use intf::android::veilid_core_setup_android;
|
||||||
|
|
||||||
pub static DEFAULT_LOG_IGNORE_LIST: [&str; 23] = [
|
pub static DEFAULT_LOG_IGNORE_LIST: [&str; 26] = [
|
||||||
"mio",
|
"mio",
|
||||||
"h2",
|
"h2",
|
||||||
"hyper",
|
"hyper",
|
||||||
@ -110,11 +113,14 @@ pub static DEFAULT_LOG_IGNORE_LIST: [&str; 23] = [
|
|||||||
"tungstenite",
|
"tungstenite",
|
||||||
"netlink_proto",
|
"netlink_proto",
|
||||||
"netlink_sys",
|
"netlink_sys",
|
||||||
"trust_dns_resolver",
|
"hickory_resolver",
|
||||||
"trust_dns_proto",
|
"hickory_proto",
|
||||||
"attohttpc",
|
"attohttpc",
|
||||||
"ws_stream_wasm",
|
"ws_stream_wasm",
|
||||||
"keyvaluedb_web",
|
"keyvaluedb_web",
|
||||||
|
"veilid_api",
|
||||||
|
"network_result",
|
||||||
|
"dht",
|
||||||
];
|
];
|
||||||
|
|
||||||
use cfg_if::*;
|
use cfg_if::*;
|
||||||
|
@ -47,6 +47,19 @@ impl ApiTracingLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn simplify_file(file: &str) -> String {
|
||||||
|
let path = std::path::Path::new(file);
|
||||||
|
let path_component_count = path.iter().count();
|
||||||
|
if path.ends_with("mod.rs") && path_component_count >= 2 {
|
||||||
|
let outpath: std::path::PathBuf = path.iter().skip(path_component_count - 2).collect();
|
||||||
|
outpath.to_string_lossy().to_string()
|
||||||
|
} else if let Some(filename) = path.file_name() {
|
||||||
|
filename.to_string_lossy().to_string()
|
||||||
|
} else {
|
||||||
|
file.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<S: Subscriber + for<'a> registry::LookupSpan<'a>> Layer<S> for ApiTracingLayer {
|
impl<S: Subscriber + for<'a> registry::LookupSpan<'a>> Layer<S> for ApiTracingLayer {
|
||||||
fn on_new_span(
|
fn on_new_span(
|
||||||
&self,
|
&self,
|
||||||
@ -86,15 +99,39 @@ impl<S: Subscriber + for<'a> registry::LookupSpan<'a>> Layer<S> for ApiTracingLa
|
|||||||
let mut recorder = StringRecorder::new();
|
let mut recorder = StringRecorder::new();
|
||||||
event.record(&mut recorder);
|
event.record(&mut recorder);
|
||||||
let meta = event.metadata();
|
let meta = event.metadata();
|
||||||
let level = meta.level();
|
let level = *meta.level();
|
||||||
let log_level = VeilidLogLevel::from_tracing_level(*level);
|
let target = meta.target();
|
||||||
|
let log_level = VeilidLogLevel::from_tracing_level(level);
|
||||||
|
|
||||||
let origin = meta
|
let origin = match level {
|
||||||
|
Level::ERROR | Level::WARN => meta
|
||||||
.file()
|
.file()
|
||||||
.and_then(|file| meta.line().map(|ln| format!("{}:{}", file, ln)))
|
.and_then(|file| {
|
||||||
.unwrap_or_default();
|
meta.line()
|
||||||
|
.map(|ln| format!("{}:{}", simplify_file(file), ln))
|
||||||
|
})
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Level::INFO => "".to_owned(),
|
||||||
|
Level::DEBUG | Level::TRACE => meta
|
||||||
|
.file()
|
||||||
|
.and_then(|file| {
|
||||||
|
meta.line().map(|ln| {
|
||||||
|
format!(
|
||||||
|
"{}{}:{}",
|
||||||
|
if target.is_empty() {
|
||||||
|
"".to_owned()
|
||||||
|
} else {
|
||||||
|
format!("[{}]", target)
|
||||||
|
},
|
||||||
|
simplify_file(file),
|
||||||
|
ln
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_default(),
|
||||||
|
};
|
||||||
|
|
||||||
let message = format!("{} {}", origin, recorder);
|
let message = format!("{}{}", origin, recorder).trim().to_owned();
|
||||||
|
|
||||||
let backtrace = if log_level <= VeilidLogLevel::Error {
|
let backtrace = if log_level <= VeilidLogLevel::Error {
|
||||||
let bt = backtrace::Backtrace::new();
|
let bt = backtrace::Backtrace::new();
|
@ -1,12 +1,10 @@
|
|||||||
// LogThru
|
mod api_tracing_layer;
|
||||||
// Pass errors through and log them simultaneously via map_err()
|
mod veilid_layer_filter;
|
||||||
// Also contains common log facilities (net, rpc, rtab, stor, pstore, crypto, etc )
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn map_to_string<X: ToString>(arg: X) -> String {
|
pub use api_tracing_layer::*;
|
||||||
arg.to_string()
|
pub use veilid_layer_filter::*;
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! fn_string {
|
macro_rules! fn_string {
|
||||||
@ -51,6 +49,78 @@ macro_rules! log_net {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log_client_api {
|
||||||
|
(error $text:expr) => {error!(
|
||||||
|
target: "client_api",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
(error $fmt:literal, $($arg:expr),+) => {
|
||||||
|
error!(target:"client_api", $fmt, $($arg),+);
|
||||||
|
};
|
||||||
|
(warn $text:expr) => {warn!(
|
||||||
|
target: "client_api",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
(warn $fmt:literal, $($arg:expr),+) => {
|
||||||
|
warn!(target:"client_api", $fmt, $($arg),+);
|
||||||
|
};
|
||||||
|
(debug $text:expr) => {debug!(
|
||||||
|
target: "client_api",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
(debug $fmt:literal, $($arg:expr),+) => {
|
||||||
|
debug!(target:"client_api", $fmt, $($arg),+);
|
||||||
|
};
|
||||||
|
($text:expr) => {trace!(
|
||||||
|
target: "client_api",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
($fmt:literal, $($arg:expr),+) => {
|
||||||
|
trace!(target:"client_api", $fmt, $($arg),+);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log_network_result {
|
||||||
|
(error $text:expr) => {error!(
|
||||||
|
target: "network_result",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
(error $fmt:literal, $($arg:expr),+) => {
|
||||||
|
error!(target: "network_result", $fmt, $($arg),+);
|
||||||
|
};
|
||||||
|
(warn $text:expr) => {warn!(
|
||||||
|
target: "network_result",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
(warn $fmt:literal, $($arg:expr),+) => {
|
||||||
|
warn!(target:"network_result", $fmt, $($arg),+);
|
||||||
|
};
|
||||||
|
(debug $text:expr) => {debug!(
|
||||||
|
target: "network_result",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
(debug $fmt:literal, $($arg:expr),+) => {
|
||||||
|
debug!(target:"network_result", $fmt, $($arg),+);
|
||||||
|
};
|
||||||
|
($text:expr) => {trace!(
|
||||||
|
target: "network_result",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
($fmt:literal, $($arg:expr),+) => {
|
||||||
|
trace!(target:"network_result", $fmt, $($arg),+);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! log_rpc {
|
macro_rules! log_rpc {
|
||||||
(error $text:expr) => { error!(
|
(error $text:expr) => { error!(
|
||||||
@ -69,7 +139,7 @@ macro_rules! log_rpc {
|
|||||||
(warn $fmt:literal, $($arg:expr),+) => {
|
(warn $fmt:literal, $($arg:expr),+) => {
|
||||||
warn!(target:"rpc", $fmt, $($arg),+);
|
warn!(target:"rpc", $fmt, $($arg),+);
|
||||||
};
|
};
|
||||||
(debug $text:expr) => { error!(
|
(debug $text:expr) => { debug!(
|
||||||
target: "rpc",
|
target: "rpc",
|
||||||
"{}",
|
"{}",
|
||||||
$text,
|
$text,
|
||||||
@ -87,6 +157,42 @@ macro_rules! log_rpc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log_dht {
|
||||||
|
(error $text:expr) => { error!(
|
||||||
|
target: "dht",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
(error $fmt:literal, $($arg:expr),+) => {
|
||||||
|
error!(target:"dht", $fmt, $($arg),+);
|
||||||
|
};
|
||||||
|
(warn $text:expr) => { warn!(
|
||||||
|
target: "dht",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
(warn $fmt:literal, $($arg:expr),+) => {
|
||||||
|
warn!(target:"dht", $fmt, $($arg),+);
|
||||||
|
};
|
||||||
|
(debug $text:expr) => { debug!(
|
||||||
|
target: "dht",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
(debug $fmt:literal, $($arg:expr),+) => {
|
||||||
|
debug!(target:"dht", $fmt, $($arg),+);
|
||||||
|
};
|
||||||
|
($text:expr) => {trace!(
|
||||||
|
target: "dht",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
($fmt:literal, $($arg:expr),+) => {
|
||||||
|
trace!(target:"dht", $fmt, $($arg),+);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! log_rtab {
|
macro_rules! log_rtab {
|
||||||
(error $text:expr) => { error!(
|
(error $text:expr) => { error!(
|
||||||
@ -195,6 +301,42 @@ macro_rules! log_pstore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log_tstore {
|
||||||
|
(error $text:expr) => { error!(
|
||||||
|
target: "tstore",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
(error $fmt:literal, $($arg:expr),+) => {
|
||||||
|
error!(target:"tstore", $fmt, $($arg),+);
|
||||||
|
};
|
||||||
|
(warn $text:expr) => { warn!(
|
||||||
|
target: "tstore",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
(warn $fmt:literal, $($arg:expr),+) => {
|
||||||
|
warn!(target:"tstore", $fmt, $($arg),+);
|
||||||
|
};
|
||||||
|
(debug $text:expr) => { debug!(
|
||||||
|
target: "tstore",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
(debug $fmt:literal, $($arg:expr),+) => {
|
||||||
|
debug!(target:"tstore", $fmt, $($arg),+);
|
||||||
|
};
|
||||||
|
($text:expr) => {trace!(
|
||||||
|
target: "tstore",
|
||||||
|
"{}",
|
||||||
|
$text,
|
||||||
|
)};
|
||||||
|
($fmt:literal, $($arg:expr),+) => {
|
||||||
|
trace!(target:"tstore", $fmt, $($arg),+);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! log_crypto {
|
macro_rules! log_crypto {
|
||||||
(error $text:expr) => { error!(
|
(error $text:expr) => { error!(
|
||||||
@ -222,188 +364,3 @@ macro_rules! log_crypto {
|
|||||||
trace!(target:"crypto", $fmt, $($arg),+);
|
trace!(target:"crypto", $fmt, $($arg),+);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! logthru_net {
|
|
||||||
($($level:ident)?) => {
|
|
||||||
logthru!($($level)? "net")
|
|
||||||
};
|
|
||||||
($($level:ident)? $text:literal) => {
|
|
||||||
logthru!($($level)? "net", $text)
|
|
||||||
};
|
|
||||||
($($level:ident)? $fmt:literal, $($arg:expr),+) => {
|
|
||||||
logthru!($($level)? "net", $fmt, $($arg),+)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! logthru_rpc {
|
|
||||||
($($level:ident)?) => {
|
|
||||||
logthru!($($level)? "rpc")
|
|
||||||
};
|
|
||||||
($($level:ident)? $text:literal) => {
|
|
||||||
logthru!($($level)? "rpc", $text)
|
|
||||||
};
|
|
||||||
($($level:ident)? $fmt:literal, $($arg:expr),+) => {
|
|
||||||
logthru!($($level)? "rpc", $fmt, $($arg),+)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! logthru_rtab {
|
|
||||||
($($level:ident)?) => {
|
|
||||||
logthru!($($level)? "rtab")
|
|
||||||
};
|
|
||||||
($($level:ident)? $text:literal) => {
|
|
||||||
logthru!($($level)? "rtab", $text)
|
|
||||||
};
|
|
||||||
($($level:ident)? $fmt:literal, $($arg:expr),+) => {
|
|
||||||
logthru!($($level)? "rtab", $fmt, $($arg),+)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! logthru_stor {
|
|
||||||
($($level:ident)?) => {
|
|
||||||
logthru!($($level)? "stor")
|
|
||||||
};
|
|
||||||
($($level:ident)? $text:literal) => {
|
|
||||||
logthru!($($level)? "stor", $text)
|
|
||||||
};
|
|
||||||
($($level:ident)? $fmt:literal, $($arg:expr),+) => {
|
|
||||||
logthru!($($level)? "stor", $fmt, $($arg),+)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! logthru_pstore {
|
|
||||||
($($level:ident)?) => {
|
|
||||||
logthru!($($level)? "pstore")
|
|
||||||
};
|
|
||||||
($($level:ident)? $text:literal) => {
|
|
||||||
logthru!($($level)? "pstore", $text)
|
|
||||||
};
|
|
||||||
($($level:ident)? $fmt:literal, $($arg:expr),+) => {
|
|
||||||
logthru!($($level)? "pstore", $fmt, $($arg),+)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! logthru_crypto {
|
|
||||||
($($level:ident)?) => {
|
|
||||||
logthru!($($level)? "crypto")
|
|
||||||
};
|
|
||||||
($($level:ident)? $text:literal) => {
|
|
||||||
logthru!($($level)? "crypto", $text)
|
|
||||||
};
|
|
||||||
($($level:ident)? $fmt:literal, $($arg:expr),+) => {
|
|
||||||
logthru!($($level)? "crypto", $fmt, $($arg),+)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! logthru {
|
|
||||||
// error
|
|
||||||
(error $target:literal) => (|e__| {
|
|
||||||
error!(
|
|
||||||
target: $target,
|
|
||||||
"[{:?}]",
|
|
||||||
e__,
|
|
||||||
);
|
|
||||||
e__
|
|
||||||
});
|
|
||||||
(error $target:literal, $text:literal) => (|e__| {
|
|
||||||
error!(
|
|
||||||
target: $target,
|
|
||||||
"[{:?}] {}",
|
|
||||||
e__,
|
|
||||||
$text
|
|
||||||
);
|
|
||||||
e__
|
|
||||||
});
|
|
||||||
(error $target:literal, $fmt:literal, $($arg:expr),+) => (|e__| {
|
|
||||||
error!(
|
|
||||||
target: $target,
|
|
||||||
concat!("[{:?}] ", $fmt),
|
|
||||||
e__,
|
|
||||||
$($arg),+
|
|
||||||
);
|
|
||||||
e__
|
|
||||||
});
|
|
||||||
// warn
|
|
||||||
(warn $target:literal) => (|e__| {
|
|
||||||
warn!(
|
|
||||||
target: $target,
|
|
||||||
"[{:?}]",
|
|
||||||
e__,
|
|
||||||
);
|
|
||||||
e__
|
|
||||||
});
|
|
||||||
(warn $target:literal, $text:literal) => (|e__| {
|
|
||||||
warn!(
|
|
||||||
target: $target,
|
|
||||||
"[{:?}] {}",
|
|
||||||
e__,
|
|
||||||
$text
|
|
||||||
);
|
|
||||||
e__
|
|
||||||
});
|
|
||||||
(warn $target:literal, $fmt:literal, $($arg:expr),+) => (|e__| {
|
|
||||||
warn!(
|
|
||||||
target: $target,
|
|
||||||
concat!("[{:?}] ", $fmt),
|
|
||||||
e__,
|
|
||||||
$($arg),+
|
|
||||||
);
|
|
||||||
e__
|
|
||||||
});
|
|
||||||
// debug
|
|
||||||
(debug $target:literal) => (|e__| {
|
|
||||||
debug!(
|
|
||||||
target: $target,
|
|
||||||
"[{:?}]",
|
|
||||||
e__,
|
|
||||||
);
|
|
||||||
e__
|
|
||||||
});
|
|
||||||
(debug $target:literal, $text:literal) => (|e__| {
|
|
||||||
debug!(
|
|
||||||
target: $target,
|
|
||||||
"[{:?}] {}",
|
|
||||||
e__,
|
|
||||||
$text
|
|
||||||
);
|
|
||||||
e__
|
|
||||||
});
|
|
||||||
(debug $target:literal, $fmt:literal, $($arg:expr),+) => (|e__| {
|
|
||||||
debug!(
|
|
||||||
target: $target,
|
|
||||||
concat!("[{:?}] ", $fmt),
|
|
||||||
e__,
|
|
||||||
$($arg),+
|
|
||||||
);
|
|
||||||
e__
|
|
||||||
});
|
|
||||||
// trace
|
|
||||||
($target:literal) => (|e__| {
|
|
||||||
trace!(
|
|
||||||
target: $target,
|
|
||||||
"[{:?}]",
|
|
||||||
e__,
|
|
||||||
);
|
|
||||||
e__
|
|
||||||
});
|
|
||||||
($target:literal, $text:literal) => (|e__| {
|
|
||||||
trace!(
|
|
||||||
target: $target,
|
|
||||||
"[{:?}] {}",
|
|
||||||
e__,
|
|
||||||
$text
|
|
||||||
);
|
|
||||||
e__
|
|
||||||
});
|
|
||||||
($target:literal, $fmt:literal, $($arg:expr),+) => (|e__| {
|
|
||||||
trace!(
|
|
||||||
target: $target,
|
|
||||||
concat!("[{:?}] ", $fmt),
|
|
||||||
e__,
|
|
||||||
$($arg),+
|
|
||||||
);
|
|
||||||
e__
|
|
||||||
})
|
|
||||||
}
|
|
@ -16,13 +16,25 @@ pub struct VeilidLayerFilter {
|
|||||||
impl VeilidLayerFilter {
|
impl VeilidLayerFilter {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
max_level: VeilidConfigLogLevel,
|
max_level: VeilidConfigLogLevel,
|
||||||
ignore_list: Option<Vec<String>>,
|
ignore_log_targets: &[String],
|
||||||
) -> VeilidLayerFilter {
|
) -> VeilidLayerFilter {
|
||||||
|
let mut ignore_list = DEFAULT_LOG_IGNORE_LIST.map(|x| x.to_owned()).to_vec();
|
||||||
|
for igedit in ignore_log_targets {
|
||||||
|
if let Some(rest) = igedit.strip_prefix('-') {
|
||||||
|
for i in 0..ignore_list.len() {
|
||||||
|
if ignore_list[i] == rest {
|
||||||
|
ignore_list.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ignore_list.push(igedit.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
Self {
|
Self {
|
||||||
inner: Arc::new(RwLock::new(VeilidLayerFilterInner {
|
inner: Arc::new(RwLock::new(VeilidLayerFilterInner {
|
||||||
max_level: max_level.to_tracing_level_filter(),
|
max_level: max_level.to_tracing_level_filter(),
|
||||||
ignore_list: ignore_list
|
ignore_list,
|
||||||
.unwrap_or_else(|| DEFAULT_LOG_IGNORE_LIST.map(|x| x.to_owned()).to_vec()),
|
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -115,7 +115,7 @@ impl ConnectionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn startup(&self) {
|
pub async fn startup(&self) {
|
||||||
trace!("startup connection manager");
|
log_net!(debug "startup connection manager");
|
||||||
let mut inner = self.arc.inner.lock();
|
let mut inner = self.arc.inner.lock();
|
||||||
if inner.is_some() {
|
if inner.is_some() {
|
||||||
panic!("shouldn't start connection manager twice without shutting it down first");
|
panic!("shouldn't start connection manager twice without shutting it down first");
|
||||||
@ -135,7 +135,7 @@ impl ConnectionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn shutdown(&self) {
|
pub async fn shutdown(&self) {
|
||||||
debug!("starting connection manager shutdown");
|
log_net!(debug "starting connection manager shutdown");
|
||||||
// Remove the inner from the lock
|
// Remove the inner from the lock
|
||||||
let mut inner = {
|
let mut inner = {
|
||||||
let mut inner_lock = self.arc.inner.lock();
|
let mut inner_lock = self.arc.inner.lock();
|
||||||
@ -148,16 +148,16 @@ impl ConnectionManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Stop all the connections and the async processor
|
// Stop all the connections and the async processor
|
||||||
debug!("stopping async processor task");
|
log_net!(debug "stopping async processor task");
|
||||||
drop(inner.stop_source.take());
|
drop(inner.stop_source.take());
|
||||||
let async_processor_jh = inner.async_processor_jh.take().unwrap();
|
let async_processor_jh = inner.async_processor_jh.take().unwrap();
|
||||||
// wait for the async processor to stop
|
// wait for the async processor to stop
|
||||||
debug!("waiting for async processor to stop");
|
log_net!(debug "waiting for async processor to stop");
|
||||||
async_processor_jh.await;
|
async_processor_jh.await;
|
||||||
// Wait for the connections to complete
|
// Wait for the connections to complete
|
||||||
debug!("waiting for connection handlers to complete");
|
log_net!(debug "waiting for connection handlers to complete");
|
||||||
self.arc.connection_table.join().await;
|
self.arc.connection_table.join().await;
|
||||||
debug!("finished connection manager shutdown");
|
log_net!(debug "finished connection manager shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal routine to see if we should keep this connection
|
// Internal routine to see if we should keep this connection
|
||||||
|
@ -99,7 +99,7 @@ impl ConnectionTable {
|
|||||||
let unord = FuturesUnordered::new();
|
let unord = FuturesUnordered::new();
|
||||||
for table in &mut inner.conn_by_id {
|
for table in &mut inner.conn_by_id {
|
||||||
for (_, mut v) in table.drain() {
|
for (_, mut v) in table.drain() {
|
||||||
trace!("connection table join: {:?}", v);
|
log_net!("connection table join: {:?}", v);
|
||||||
v.close();
|
v.close();
|
||||||
unord.push(v);
|
unord.push(v);
|
||||||
}
|
}
|
||||||
|
@ -227,7 +227,7 @@ impl NetworkManager {
|
|||||||
Some(
|
Some(
|
||||||
bcs.derive_shared_secret(
|
bcs.derive_shared_secret(
|
||||||
network_key_password.as_bytes(),
|
network_key_password.as_bytes(),
|
||||||
network_key_password.as_bytes(),
|
&bcs.generate_hash(network_key_password.as_bytes()).bytes,
|
||||||
)
|
)
|
||||||
.expect("failed to derive network key"),
|
.expect("failed to derive network key"),
|
||||||
)
|
)
|
||||||
@ -363,9 +363,8 @@ impl NetworkManager {
|
|||||||
|
|
||||||
#[instrument(level = "debug", skip_all, err)]
|
#[instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn internal_startup(&self) -> EyreResult<()> {
|
pub async fn internal_startup(&self) -> EyreResult<()> {
|
||||||
trace!("NetworkManager::internal_startup begin");
|
|
||||||
if self.unlocked_inner.components.read().is_some() {
|
if self.unlocked_inner.components.read().is_some() {
|
||||||
debug!("NetworkManager::internal_startup already started");
|
log_net!(debug "NetworkManager::internal_startup already started");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,7 +401,7 @@ impl NetworkManager {
|
|||||||
rpc_processor.startup().await?;
|
rpc_processor.startup().await?;
|
||||||
receipt_manager.startup().await?;
|
receipt_manager.startup().await?;
|
||||||
|
|
||||||
trace!("NetworkManager::internal_startup end");
|
log_net!("NetworkManager::internal_startup end");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -422,13 +421,13 @@ impl NetworkManager {
|
|||||||
|
|
||||||
#[instrument(level = "debug", skip_all)]
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub async fn shutdown(&self) {
|
pub async fn shutdown(&self) {
|
||||||
debug!("starting network manager shutdown");
|
log_net!(debug "starting network manager shutdown");
|
||||||
|
|
||||||
// Cancel all tasks
|
// Cancel all tasks
|
||||||
self.cancel_tasks().await;
|
self.cancel_tasks().await;
|
||||||
|
|
||||||
// Shutdown network components if they started up
|
// Shutdown network components if they started up
|
||||||
debug!("shutting down network components");
|
log_net!(debug "shutting down network components");
|
||||||
|
|
||||||
let components = self.unlocked_inner.components.read().clone();
|
let components = self.unlocked_inner.components.read().clone();
|
||||||
if let Some(components) = components {
|
if let Some(components) = components {
|
||||||
@ -441,16 +440,16 @@ impl NetworkManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reset the state
|
// reset the state
|
||||||
debug!("resetting network manager state");
|
log_net!(debug "resetting network manager state");
|
||||||
{
|
{
|
||||||
*self.inner.lock() = NetworkManager::new_inner();
|
*self.inner.lock() = NetworkManager::new_inner();
|
||||||
}
|
}
|
||||||
|
|
||||||
// send update
|
// send update
|
||||||
debug!("sending network state update to api clients");
|
log_net!(debug "sending network state update to api clients");
|
||||||
self.send_network_update();
|
self.send_network_update();
|
||||||
|
|
||||||
debug!("finished network manager shutdown");
|
log_net!(debug "finished network manager shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_client_allowlist(&self, client: TypedKey) {
|
pub fn update_client_allowlist(&self, client: TypedKey) {
|
||||||
@ -493,7 +492,7 @@ impl NetworkManager {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
{
|
{
|
||||||
let (k, v) = inner.client_allowlist.remove_lru().unwrap();
|
let (k, v) = inner.client_allowlist.remove_lru().unwrap();
|
||||||
trace!(key=?k, value=?v, "purge_client_allowlist: remove_lru")
|
trace!(target: "net", key=?k, value=?v, "purge_client_allowlist: remove_lru")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,14 +310,16 @@ impl DiscoveryContext {
|
|||||||
|
|
||||||
// ask the node to send us a dial info validation receipt
|
// ask the node to send us a dial info validation receipt
|
||||||
|
|
||||||
rpc_processor
|
match rpc_processor
|
||||||
.rpc_call_validate_dial_info(node_ref.clone(), dial_info, redirect)
|
.rpc_call_validate_dial_info(node_ref.clone(), dial_info, redirect)
|
||||||
.await
|
.await
|
||||||
.map_err(logthru_net!(
|
{
|
||||||
"failed to send validate_dial_info to {:?}",
|
Err(e) => {
|
||||||
node_ref
|
log_net!("failed to send validate_dial_info to {:?}: {}", node_ref, e);
|
||||||
))
|
false
|
||||||
.unwrap_or(false)
|
}
|
||||||
|
Ok(v) => v,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "trace", skip(self), ret)]
|
#[instrument(level = "trace", skip(self), ret)]
|
||||||
|
@ -279,22 +279,22 @@ impl Network {
|
|||||||
fn load_server_config(&self) -> io::Result<ServerConfig> {
|
fn load_server_config(&self) -> io::Result<ServerConfig> {
|
||||||
let c = self.config.get();
|
let c = self.config.get();
|
||||||
//
|
//
|
||||||
trace!(
|
log_net!(
|
||||||
"loading certificate from {}",
|
"loading certificate from {}",
|
||||||
c.network.tls.certificate_path
|
c.network.tls.certificate_path
|
||||||
);
|
);
|
||||||
let certs = Self::load_certs(&PathBuf::from(&c.network.tls.certificate_path))?;
|
let certs = Self::load_certs(&PathBuf::from(&c.network.tls.certificate_path))?;
|
||||||
trace!("loaded {} certificates", certs.len());
|
log_net!("loaded {} certificates", certs.len());
|
||||||
if certs.is_empty() {
|
if certs.is_empty() {
|
||||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, format!("Certificates at {} could not be loaded.\nEnsure it is in PEM format, beginning with '-----BEGIN CERTIFICATE-----'",c.network.tls.certificate_path)));
|
return Err(io::Error::new(io::ErrorKind::InvalidInput, format!("Certificates at {} could not be loaded.\nEnsure it is in PEM format, beginning with '-----BEGIN CERTIFICATE-----'",c.network.tls.certificate_path)));
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
trace!(
|
log_net!(
|
||||||
"loading private key from {}",
|
"loading private key from {}",
|
||||||
c.network.tls.private_key_path
|
c.network.tls.private_key_path
|
||||||
);
|
);
|
||||||
let mut keys = Self::load_keys(&PathBuf::from(&c.network.tls.private_key_path))?;
|
let mut keys = Self::load_keys(&PathBuf::from(&c.network.tls.private_key_path))?;
|
||||||
trace!("loaded {} keys", keys.len());
|
log_net!("loaded {} keys", keys.len());
|
||||||
if keys.is_empty() {
|
if keys.is_empty() {
|
||||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, format!("Private key at {} could not be loaded.\nEnsure it is unencrypted and in RSA or PKCS8 format, beginning with '-----BEGIN RSA PRIVATE KEY-----' or '-----BEGIN PRIVATE KEY-----'",c.network.tls.private_key_path)));
|
return Err(io::Error::new(io::ErrorKind::InvalidInput, format!("Private key at {} could not be loaded.\nEnsure it is unencrypted and in RSA or PKCS8 format, beginning with '-----BEGIN RSA PRIVATE KEY-----' or '-----BEGIN PRIVATE KEY-----'",c.network.tls.private_key_path)));
|
||||||
}
|
}
|
||||||
@ -710,7 +710,7 @@ impl Network {
|
|||||||
self.unlocked_inner
|
self.unlocked_inner
|
||||||
.interfaces
|
.interfaces
|
||||||
.with_interfaces(|interfaces| {
|
.with_interfaces(|interfaces| {
|
||||||
debug!("interfaces: {:#?}", interfaces);
|
log_net!(debug "interfaces: {:#?}", interfaces);
|
||||||
|
|
||||||
for intf in interfaces.values() {
|
for intf in interfaces.values() {
|
||||||
// Skip networks that we should never encounter
|
// Skip networks that we should never encounter
|
||||||
@ -915,12 +915,12 @@ impl Network {
|
|||||||
|
|
||||||
#[instrument(level = "debug", skip_all)]
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub async fn shutdown(&self) {
|
pub async fn shutdown(&self) {
|
||||||
debug!("starting low level network shutdown");
|
log_net!(debug "starting low level network shutdown");
|
||||||
|
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
|
|
||||||
// Stop all tasks
|
// Stop all tasks
|
||||||
debug!("stopping update network class task");
|
log_net!(debug "stopping update network class task");
|
||||||
if let Err(e) = self.unlocked_inner.update_network_class_task.stop().await {
|
if let Err(e) = self.unlocked_inner.update_network_class_task.stop().await {
|
||||||
error!("update_network_class_task not cancelled: {}", e);
|
error!("update_network_class_task not cancelled: {}", e);
|
||||||
}
|
}
|
||||||
@ -930,17 +930,17 @@ impl Network {
|
|||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
// take the join handles out
|
// take the join handles out
|
||||||
for h in inner.join_handles.drain(..) {
|
for h in inner.join_handles.drain(..) {
|
||||||
trace!("joining: {:?}", h);
|
log_net!("joining: {:?}", h);
|
||||||
unord.push(h);
|
unord.push(h);
|
||||||
}
|
}
|
||||||
// Drop the stop
|
// Drop the stop
|
||||||
drop(inner.stop_source.take());
|
drop(inner.stop_source.take());
|
||||||
}
|
}
|
||||||
debug!("stopping {} low level network tasks", unord.len());
|
log_net!(debug "stopping {} low level network tasks", unord.len());
|
||||||
// Wait for everything to stop
|
// Wait for everything to stop
|
||||||
while unord.next().await.is_some() {}
|
while unord.next().await.is_some() {}
|
||||||
|
|
||||||
debug!("clearing dial info");
|
log_net!(debug "clearing dial info");
|
||||||
|
|
||||||
routing_table
|
routing_table
|
||||||
.edit_routing_domain(RoutingDomain::PublicInternet)
|
.edit_routing_domain(RoutingDomain::PublicInternet)
|
||||||
@ -961,7 +961,7 @@ impl Network {
|
|||||||
// Reset state including network class
|
// Reset state including network class
|
||||||
*self.inner.lock() = Self::new_inner();
|
*self.inner.lock() = Self::new_inner();
|
||||||
|
|
||||||
debug!("finished low level network shutdown");
|
log_net!(debug "finished low level network shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
|
@ -268,7 +268,7 @@ impl Network {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("spawn_socket_listener: binding successful to {}", addr);
|
log_net!(debug "spawn_socket_listener: binding successful to {}", addr);
|
||||||
|
|
||||||
// Create protocol handler records
|
// Create protocol handler records
|
||||||
let listener_state = Arc::new(RwLock::new(ListenerState::new()));
|
let listener_state = Arc::new(RwLock::new(ListenerState::new()));
|
||||||
|
@ -15,16 +15,16 @@ impl Network {
|
|||||||
task_count = 1;
|
task_count = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trace!("task_count: {}", task_count);
|
log_net!("task_count: {}", task_count);
|
||||||
for _ in 0..task_count {
|
for _ in 0..task_count {
|
||||||
trace!("Spawning UDP listener task");
|
log_net!("Spawning UDP listener task");
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// Run thread task to process stream of messages
|
// Run thread task to process stream of messages
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
|
|
||||||
let jh = spawn(async move {
|
let jh = spawn(async move {
|
||||||
trace!("UDP listener task spawned");
|
log_net!("UDP listener task spawned");
|
||||||
|
|
||||||
// Collect all our protocol handlers into a vector
|
// Collect all our protocol handlers into a vector
|
||||||
let mut protocol_handlers: Vec<RawUdpProtocolHandler> = this
|
let mut protocol_handlers: Vec<RawUdpProtocolHandler> = this
|
||||||
@ -103,7 +103,7 @@ impl Network {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!("UDP listener task stopped");
|
log_net!("UDP listener task stopped");
|
||||||
});
|
});
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use std::io;
|
|||||||
|
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(feature="rt-async-std")] {
|
if #[cfg(feature="rt-async-std")] {
|
||||||
pub use async_std::net::{TcpStream, TcpListener, Shutdown, UdpSocket};
|
pub use async_std::net::{TcpStream, TcpListener, UdpSocket};
|
||||||
} else if #[cfg(feature="rt-tokio")] {
|
} else if #[cfg(feature="rt-tokio")] {
|
||||||
pub use tokio::net::{TcpStream, TcpListener, UdpSocket};
|
pub use tokio::net::{TcpStream, TcpListener, UdpSocket};
|
||||||
pub use tokio_util::compat::*;
|
pub use tokio_util::compat::*;
|
||||||
@ -81,14 +81,21 @@ pub fn new_bound_first_udp_socket(local_address: SocketAddr) -> io::Result<Socke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind the socket -first- without turning on SO_REUSEPORT this way it will
|
||||||
|
// fail if the port is already taken
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(unix)] {
|
||||||
|
socket
|
||||||
|
.set_reuse_address(true)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
socket.bind(&socket2_addr)?;
|
socket.bind(&socket2_addr)?;
|
||||||
|
|
||||||
// Set 'reuse address' so future binds to this port will succeed
|
// Set 'reuse address' so future binds to this port will succeed
|
||||||
// This does not work on Windows, where reuse options can not be set after the bind
|
// This does not work on Windows, where reuse options can not be set after the bind
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(unix)] {
|
if #[cfg(unix)] {
|
||||||
socket
|
|
||||||
.set_reuse_address(true)?;
|
|
||||||
socket.set_reuse_port(true)?;
|
socket.set_reuse_port(true)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,17 +172,23 @@ pub fn new_bound_first_tcp_socket(local_address: SocketAddr) -> io::Result<Socke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind the socket -first- before turning on 'reuse address' this way it will
|
// Bind the socket -first- without turning on SO_REUSEPORT this way it will
|
||||||
// fail if the port is already taken
|
// fail if the port is already taken
|
||||||
let socket2_addr = SockAddr::from(local_address);
|
let socket2_addr = SockAddr::from(local_address);
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(unix)] {
|
||||||
|
socket
|
||||||
|
.set_reuse_address(true)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
socket.bind(&socket2_addr)?;
|
socket.bind(&socket2_addr)?;
|
||||||
|
|
||||||
// Set 'reuse address' so future binds to this port will succeed
|
// Set 'reuse address' so future binds to this port will succeed
|
||||||
// This does not work on Windows, where reuse options can not be set after the bind
|
// This does not work on Windows, where reuse options can not be set after the bind
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(unix)] {
|
if #[cfg(unix)] {
|
||||||
socket
|
|
||||||
.set_reuse_address(true)?;
|
|
||||||
socket.set_reuse_port(true)?;
|
socket.set_reuse_port(true)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,8 @@ impl RawUdpProtocolHandler {
|
|||||||
NetworkResult::Value(None) => {
|
NetworkResult::Value(None) => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#[cfg(feature = "network-result-extra")]
|
|
||||||
nres => {
|
nres => {
|
||||||
log_network_result!(
|
log_network_result!(debug
|
||||||
"UDP::recv_message insert_frame failed: {:?} <= size={} remote_addr={}",
|
"UDP::recv_message insert_frame failed: {:?} <= size={} remote_addr={}",
|
||||||
nres,
|
nres,
|
||||||
size,
|
size,
|
||||||
@ -49,10 +48,6 @@ impl RawUdpProtocolHandler {
|
|||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "network-result-extra"))]
|
|
||||||
_ => {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check length of reassembled message (same for all protocols)
|
// Check length of reassembled message (same for all protocols)
|
||||||
|
@ -280,7 +280,7 @@ impl Network {
|
|||||||
editor_public_internet: &mut RoutingDomainEditor,
|
editor_public_internet: &mut RoutingDomainEditor,
|
||||||
editor_local_network: &mut RoutingDomainEditor,
|
editor_local_network: &mut RoutingDomainEditor,
|
||||||
) -> EyreResult<()> {
|
) -> EyreResult<()> {
|
||||||
trace!("starting udp listeners");
|
log_net!("starting udp listeners");
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let (listen_address, public_address, detect_address_changes) = {
|
let (listen_address, public_address, detect_address_changes) = {
|
||||||
let c = self.config.get();
|
let c = self.config.get();
|
||||||
@ -312,7 +312,7 @@ impl Network {
|
|||||||
let local_dial_info_list = self.create_udp_inbound_sockets(ip_addrs, udp_port).await?;
|
let local_dial_info_list = self.create_udp_inbound_sockets(ip_addrs, udp_port).await?;
|
||||||
let mut static_public = false;
|
let mut static_public = false;
|
||||||
|
|
||||||
trace!("UDP: listener started on {:#?}", local_dial_info_list);
|
log_net!("UDP: listener started on {:#?}", local_dial_info_list);
|
||||||
|
|
||||||
// Register local dial info
|
// Register local dial info
|
||||||
for di in &local_dial_info_list {
|
for di in &local_dial_info_list {
|
||||||
@ -383,7 +383,7 @@ impl Network {
|
|||||||
editor_public_internet: &mut RoutingDomainEditor,
|
editor_public_internet: &mut RoutingDomainEditor,
|
||||||
editor_local_network: &mut RoutingDomainEditor,
|
editor_local_network: &mut RoutingDomainEditor,
|
||||||
) -> EyreResult<()> {
|
) -> EyreResult<()> {
|
||||||
trace!("starting ws listeners");
|
log_net!("starting ws listeners");
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let (listen_address, url, path, detect_address_changes) = {
|
let (listen_address, url, path, detect_address_changes) = {
|
||||||
let c = self.config.get();
|
let c = self.config.get();
|
||||||
@ -415,7 +415,7 @@ impl Network {
|
|||||||
Box::new(|c, t| Box::new(WebsocketProtocolHandler::new(c, t))),
|
Box::new(|c, t| Box::new(WebsocketProtocolHandler::new(c, t))),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
trace!("WS: listener started on {:#?}", socket_addresses);
|
log_net!("WS: listener started on {:#?}", socket_addresses);
|
||||||
|
|
||||||
let mut static_public = false;
|
let mut static_public = false;
|
||||||
let mut registered_addresses: HashSet<IpAddr> = HashSet::new();
|
let mut registered_addresses: HashSet<IpAddr> = HashSet::new();
|
||||||
@ -493,7 +493,7 @@ impl Network {
|
|||||||
editor_public_internet: &mut RoutingDomainEditor,
|
editor_public_internet: &mut RoutingDomainEditor,
|
||||||
editor_local_network: &mut RoutingDomainEditor,
|
editor_local_network: &mut RoutingDomainEditor,
|
||||||
) -> EyreResult<()> {
|
) -> EyreResult<()> {
|
||||||
trace!("starting wss listeners");
|
log_net!("starting wss listeners");
|
||||||
|
|
||||||
let (listen_address, url, detect_address_changes) = {
|
let (listen_address, url, detect_address_changes) = {
|
||||||
let c = self.config.get();
|
let c = self.config.get();
|
||||||
@ -524,7 +524,7 @@ impl Network {
|
|||||||
Box::new(|c, t| Box::new(WebsocketProtocolHandler::new(c, t))),
|
Box::new(|c, t| Box::new(WebsocketProtocolHandler::new(c, t))),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
trace!("WSS: listener started on {:#?}", socket_addresses);
|
log_net!("WSS: listener started on {:#?}", socket_addresses);
|
||||||
|
|
||||||
// NOTE: No interface dial info for WSS, as there is no way to connect to a local dialinfo via TLS
|
// NOTE: No interface dial info for WSS, as there is no way to connect to a local dialinfo via TLS
|
||||||
// If the hostname is specified, it is the public dialinfo via the URL. If no hostname
|
// If the hostname is specified, it is the public dialinfo via the URL. If no hostname
|
||||||
@ -586,7 +586,7 @@ impl Network {
|
|||||||
editor_public_internet: &mut RoutingDomainEditor,
|
editor_public_internet: &mut RoutingDomainEditor,
|
||||||
editor_local_network: &mut RoutingDomainEditor,
|
editor_local_network: &mut RoutingDomainEditor,
|
||||||
) -> EyreResult<()> {
|
) -> EyreResult<()> {
|
||||||
trace!("starting tcp listeners");
|
log_net!("starting tcp listeners");
|
||||||
|
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let (listen_address, public_address, detect_address_changes) = {
|
let (listen_address, public_address, detect_address_changes) = {
|
||||||
@ -618,7 +618,7 @@ impl Network {
|
|||||||
Box::new(|c, _| Box::new(RawTcpProtocolHandler::new(c))),
|
Box::new(|c, _| Box::new(RawTcpProtocolHandler::new(c))),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
trace!("TCP: listener started on {:#?}", socket_addresses);
|
log_net!("TCP: listener started on {:#?}", socket_addresses);
|
||||||
|
|
||||||
let mut static_public = false;
|
let mut static_public = false;
|
||||||
let mut registered_addresses: HashSet<IpAddr> = HashSet::new();
|
let mut registered_addresses: HashSet<IpAddr> = HashSet::new();
|
||||||
|
@ -185,9 +185,9 @@ impl ReceiptManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn startup(&self) -> EyreResult<()> {
|
pub async fn startup(&self) -> EyreResult<()> {
|
||||||
trace!("startup receipt manager");
|
log_net!(debug "startup receipt manager");
|
||||||
// Retrieve config
|
|
||||||
|
|
||||||
|
// Retrieve config
|
||||||
{
|
{
|
||||||
// let config = self.core().config();
|
// let config = self.core().config();
|
||||||
// let c = config.get();
|
// let c = config.get();
|
||||||
@ -296,7 +296,7 @@ impl ReceiptManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn shutdown(&self) {
|
pub async fn shutdown(&self) {
|
||||||
debug!("starting receipt manager shutdown");
|
log_net!(debug "starting receipt manager shutdown");
|
||||||
let network_manager = self.network_manager();
|
let network_manager = self.network_manager();
|
||||||
|
|
||||||
// Stop all tasks
|
// Stop all tasks
|
||||||
@ -308,13 +308,13 @@ impl ReceiptManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Wait for everything to stop
|
// Wait for everything to stop
|
||||||
debug!("waiting for timeout task to stop");
|
log_net!(debug "waiting for timeout task to stop");
|
||||||
if timeout_task.join().await.is_err() {
|
if timeout_task.join().await.is_err() {
|
||||||
panic!("joining timeout task failed");
|
panic!("joining timeout task failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
*self.inner.lock() = Self::new_inner(network_manager);
|
*self.inner.lock() = Self::new_inner(network_manager);
|
||||||
debug!("finished receipt manager shutdown");
|
log_net!(debug "finished receipt manager shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -11,17 +11,14 @@ impl NetworkManager {
|
|||||||
///
|
///
|
||||||
/// Sending to a node requires determining a NetworkClass compatible contact method
|
/// Sending to a node requires determining a NetworkClass compatible contact method
|
||||||
/// between the source and destination node
|
/// between the source and destination node
|
||||||
pub(crate) fn send_data(
|
pub(crate) async fn send_data(
|
||||||
&self,
|
&self,
|
||||||
destination_node_ref: NodeRef,
|
destination_node_ref: NodeRef,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
) -> SendPinBoxFuture<EyreResult<NetworkResult<SendDataMethod>>> {
|
) -> EyreResult<NetworkResult<SendDataMethod>> {
|
||||||
let this = self.clone();
|
|
||||||
Box::pin(
|
|
||||||
async move {
|
|
||||||
// First try to send data to the last flow we've seen this peer on
|
// First try to send data to the last flow we've seen this peer on
|
||||||
let data = if let Some(flow) = destination_node_ref.last_flow() {
|
let data = if let Some(flow) = destination_node_ref.last_flow() {
|
||||||
match this
|
match self
|
||||||
.net()
|
.net()
|
||||||
.send_data_to_existing_flow(flow, data)
|
.send_data_to_existing_flow(flow, data)
|
||||||
.await?
|
.await?
|
||||||
@ -51,7 +48,19 @@ impl NetworkManager {
|
|||||||
// No existing connection was found or usable, so we proceed to see how to make a new one
|
// No existing connection was found or usable, so we proceed to see how to make a new one
|
||||||
|
|
||||||
// Get the best way to contact this node
|
// Get the best way to contact this node
|
||||||
let possibly_relayed_contact_method = this.get_node_contact_method(destination_node_ref.clone())?;
|
let possibly_relayed_contact_method = self.get_node_contact_method(destination_node_ref.clone())?;
|
||||||
|
|
||||||
|
self.try_possibly_relayed_contact_method(possibly_relayed_contact_method, destination_node_ref, data).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn try_possibly_relayed_contact_method(&self,
|
||||||
|
possibly_relayed_contact_method: NodeContactMethod,
|
||||||
|
destination_node_ref: NodeRef,
|
||||||
|
data: Vec<u8>,
|
||||||
|
) -> SendPinBoxFuture<EyreResult<NetworkResult<SendDataMethod>>> {
|
||||||
|
let this = self.clone();
|
||||||
|
Box::pin(
|
||||||
|
async move {
|
||||||
|
|
||||||
// If we need to relay, do it
|
// If we need to relay, do it
|
||||||
let (contact_method, target_node_ref, opt_relayed_contact_method) = match possibly_relayed_contact_method.clone() {
|
let (contact_method, target_node_ref, opt_relayed_contact_method) = match possibly_relayed_contact_method.clone() {
|
||||||
@ -64,7 +73,7 @@ impl NetworkManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "verbose-tracing")]
|
#[cfg(feature = "verbose-tracing")]
|
||||||
debug!(
|
log_net!(debug
|
||||||
"ContactMethod: {:?} for {:?}",
|
"ContactMethod: {:?} for {:?}",
|
||||||
contact_method, destination_node_ref
|
contact_method, destination_node_ref
|
||||||
);
|
);
|
||||||
@ -96,16 +105,28 @@ impl NetworkManager {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
NodeContactMethod::SignalReverse(relay_nr, target_node_ref) => {
|
NodeContactMethod::SignalReverse(relay_nr, target_node_ref) => {
|
||||||
network_result_try!(
|
let nres =
|
||||||
this.send_data_ncm_signal_reverse(relay_nr, target_node_ref, data)
|
this.send_data_ncm_signal_reverse(relay_nr.clone(), target_node_ref.clone(), data.clone())
|
||||||
.await?
|
.await?;
|
||||||
)
|
if matches!(nres, NetworkResult::Timeout) {
|
||||||
|
// Failed to holepunch, fallback to inbound relay
|
||||||
|
log_network_result!(debug "Reverse connection failed to {}, falling back to inbound relay via {}", target_node_ref, relay_nr);
|
||||||
|
network_result_try!(this.try_possibly_relayed_contact_method(NodeContactMethod::InboundRelay(relay_nr), destination_node_ref, data).await?)
|
||||||
|
} else {
|
||||||
|
network_result_try!(nres)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
NodeContactMethod::SignalHolePunch(relay_nr, target_node_ref) => {
|
NodeContactMethod::SignalHolePunch(relay_nr, target_node_ref) => {
|
||||||
network_result_try!(
|
let nres =
|
||||||
this.send_data_ncm_signal_hole_punch(relay_nr, target_node_ref, data)
|
this.send_data_ncm_signal_hole_punch(relay_nr.clone(), target_node_ref.clone(), data.clone())
|
||||||
.await?
|
.await?;
|
||||||
)
|
if matches!(nres, NetworkResult::Timeout) {
|
||||||
|
// Failed to holepunch, fallback to inbound relay
|
||||||
|
log_network_result!(debug "Hole punch failed to {}, falling back to inbound relay via {}", target_node_ref, relay_nr);
|
||||||
|
network_result_try!(this.try_possibly_relayed_contact_method(NodeContactMethod::InboundRelay(relay_nr), destination_node_ref, data).await?)
|
||||||
|
} else {
|
||||||
|
network_result_try!(nres)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
NodeContactMethod::Existing => {
|
NodeContactMethod::Existing => {
|
||||||
network_result_try!(
|
network_result_try!(
|
||||||
@ -304,7 +325,7 @@ impl NetworkManager {
|
|||||||
// First try to send data to the last socket we've seen this peer on
|
// First try to send data to the last socket we've seen this peer on
|
||||||
let data = if let Some(flow) = node_ref.last_flow() {
|
let data = if let Some(flow) = node_ref.last_flow() {
|
||||||
#[cfg(feature = "verbose-tracing")]
|
#[cfg(feature = "verbose-tracing")]
|
||||||
debug!(
|
log_net!(debug
|
||||||
"ExistingConnection: {:?} for {:?}",
|
"ExistingConnection: {:?} for {:?}",
|
||||||
flow, node_ref
|
flow, node_ref
|
||||||
);
|
);
|
||||||
|
@ -85,12 +85,12 @@ impl NetworkManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn cancel_tasks(&self) {
|
pub(crate) async fn cancel_tasks(&self) {
|
||||||
debug!("stopping rolling transfers task");
|
log_net!(debug "stopping rolling transfers task");
|
||||||
if let Err(e) = self.unlocked_inner.rolling_transfers_task.stop().await {
|
if let Err(e) = self.unlocked_inner.rolling_transfers_task.stop().await {
|
||||||
warn!("rolling_transfers_task not stopped: {}", e);
|
warn!("rolling_transfers_task not stopped: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("stopping routing table tasks");
|
log_net!(debug "stopping routing table tasks");
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
routing_table.cancel_tasks().await;
|
routing_table.cancel_tasks().await;
|
||||||
|
|
||||||
|
@ -46,8 +46,7 @@ impl NetworkManager {
|
|||||||
flow: Flow, // the flow used
|
flow: Flow, // the flow used
|
||||||
reporting_peer: NodeRef, // the peer's noderef reporting the socket address
|
reporting_peer: NodeRef, // the peer's noderef reporting the socket address
|
||||||
) {
|
) {
|
||||||
#[cfg(feature = "network-result-extra")]
|
log_network_result!("report_global_socket_address\nsocket_address: {:#?}\nflow: {:#?}\nreporting_peer: {:#?}", socket_address, flow, reporting_peer);
|
||||||
debug!("report_global_socket_address\nsocket_address: {:#?}\nflow: {:#?}\nreporting_peer: {:#?}", socket_address, flow, reporting_peer);
|
|
||||||
|
|
||||||
// Ignore these reports if we are currently detecting public dial info
|
// Ignore these reports if we are currently detecting public dial info
|
||||||
let net = self.net();
|
let net = self.net();
|
||||||
@ -172,8 +171,7 @@ impl NetworkManager {
|
|||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
// Record the origin of the inconsistency
|
// Record the origin of the inconsistency
|
||||||
#[cfg(feature = "network-result-extra")]
|
log_network_result!(debug "inconsistency added from {:?}: reported {:?} with current_addresses = {:?}", reporting_ip_block, a, current_addresses);
|
||||||
debug!("inconsistency added from {:?}: reported {:?} with current_addresses = {:?}", reporting_ip_block, a, current_addresses);
|
|
||||||
|
|
||||||
inconsistencies.push(*reporting_ip_block);
|
inconsistencies.push(*reporting_ip_block);
|
||||||
}
|
}
|
||||||
@ -214,7 +212,7 @@ impl NetworkManager {
|
|||||||
|
|
||||||
// // debug code
|
// // debug code
|
||||||
// if inconsistent {
|
// if inconsistent {
|
||||||
// trace!("public_address_check_cache: {:#?}\ncurrent_addresses: {:#?}\ninconsistencies: {}", inner
|
// log_net!("public_address_check_cache: {:#?}\ncurrent_addresses: {:#?}\ninconsistencies: {}", inner
|
||||||
// .public_address_check_cache, current_addresses, inconsistencies);
|
// .public_address_check_cache, current_addresses, inconsistencies);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ impl Address {
|
|||||||
Address::IPV4(v4) => {
|
Address::IPV4(v4) => {
|
||||||
ipv4addr_is_private(v4)
|
ipv4addr_is_private(v4)
|
||||||
|| ipv4addr_is_link_local(v4)
|
|| ipv4addr_is_link_local(v4)
|
||||||
|
|| ipv4addr_is_shared(v4)
|
||||||
|| ipv4addr_is_ietf_protocol_assignment(v4)
|
|| ipv4addr_is_ietf_protocol_assignment(v4)
|
||||||
}
|
}
|
||||||
Address::IPV6(v6) => {
|
Address::IPV6(v6) => {
|
||||||
|
@ -334,6 +334,7 @@ impl Network {
|
|||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
pub async fn startup(&self) -> EyreResult<()> {
|
pub async fn startup(&self) -> EyreResult<()> {
|
||||||
|
log_net!(debug "starting network");
|
||||||
// get protocol config
|
// get protocol config
|
||||||
let protocol_config = {
|
let protocol_config = {
|
||||||
let c = self.config.get();
|
let c = self.config.get();
|
||||||
@ -396,6 +397,7 @@ impl Network {
|
|||||||
editor_public_internet.commit(true).await;
|
editor_public_internet.commit(true).await;
|
||||||
|
|
||||||
self.inner.lock().network_started = true;
|
self.inner.lock().network_started = true;
|
||||||
|
log_net!(debug "network started");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,7 +414,7 @@ impl Network {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn shutdown(&self) {
|
pub async fn shutdown(&self) {
|
||||||
trace!("stopping network");
|
log_net!(debug "stopping network");
|
||||||
|
|
||||||
// Reset state
|
// Reset state
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
@ -429,7 +431,7 @@ impl Network {
|
|||||||
// Cancels all async background tasks by dropping join handles
|
// Cancels all async background tasks by dropping join handles
|
||||||
*self.inner.lock() = Self::new_inner();
|
*self.inner.lock() = Self::new_inner();
|
||||||
|
|
||||||
trace!("network stopped");
|
log_net!(debug "network stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_preferred_local_address(&self, _dial_info: &DialInfo) -> Option<SocketAddr> {
|
pub fn get_preferred_local_address(&self, _dial_info: &DialInfo) -> Option<SocketAddr> {
|
||||||
|
@ -932,10 +932,10 @@ impl Drop for BucketEntry {
|
|||||||
if self.ref_count.load(Ordering::Acquire) != 0 {
|
if self.ref_count.load(Ordering::Acquire) != 0 {
|
||||||
#[cfg(feature = "tracking")]
|
#[cfg(feature = "tracking")]
|
||||||
{
|
{
|
||||||
println!("NodeRef Tracking");
|
info!("NodeRef Tracking");
|
||||||
for (id, bt) in &mut self.node_ref_tracks {
|
for (id, bt) in &mut self.node_ref_tracks {
|
||||||
bt.resolve();
|
bt.resolve();
|
||||||
println!("Id: {}\n----------------\n{:#?}", id, bt);
|
info!("Id: {}\n----------------\n{:#?}", id, bt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +247,7 @@ impl RoutingTable {
|
|||||||
|
|
||||||
/// Called to initialize the routing table after it is created
|
/// Called to initialize the routing table after it is created
|
||||||
pub async fn init(&self) -> EyreResult<()> {
|
pub async fn init(&self) -> EyreResult<()> {
|
||||||
debug!("starting routing table init");
|
log_rtab!(debug "starting routing table init");
|
||||||
|
|
||||||
// Set up routing buckets
|
// Set up routing buckets
|
||||||
{
|
{
|
||||||
@ -256,7 +256,7 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load bucket entries from table db if possible
|
// Load bucket entries from table db if possible
|
||||||
debug!("loading routing table entries");
|
log_rtab!(debug "loading routing table entries");
|
||||||
if let Err(e) = self.load_buckets().await {
|
if let Err(e) = self.load_buckets().await {
|
||||||
log_rtab!(debug "Error loading buckets from storage: {:#?}. Resetting.", e);
|
log_rtab!(debug "Error loading buckets from storage: {:#?}. Resetting.", e);
|
||||||
let mut inner = self.inner.write();
|
let mut inner = self.inner.write();
|
||||||
@ -264,7 +264,7 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up routespecstore
|
// Set up routespecstore
|
||||||
debug!("starting route spec store init");
|
log_rtab!(debug "starting route spec store init");
|
||||||
let route_spec_store = match RouteSpecStore::load(self.clone()).await {
|
let route_spec_store = match RouteSpecStore::load(self.clone()).await {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -272,7 +272,7 @@ impl RoutingTable {
|
|||||||
RouteSpecStore::new(self.clone())
|
RouteSpecStore::new(self.clone())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
debug!("finished route spec store init");
|
log_rtab!(debug "finished route spec store init");
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut inner = self.inner.write();
|
let mut inner = self.inner.write();
|
||||||
@ -285,13 +285,13 @@ impl RoutingTable {
|
|||||||
.set_routing_table(Some(self.clone()))
|
.set_routing_table(Some(self.clone()))
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
debug!("finished routing table init");
|
log_rtab!(debug "finished routing table init");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called to shut down the routing table
|
/// Called to shut down the routing table
|
||||||
pub async fn terminate(&self) {
|
pub async fn terminate(&self) {
|
||||||
debug!("starting routing table terminate");
|
log_rtab!(debug "starting routing table terminate");
|
||||||
|
|
||||||
// Stop storage manager from using us
|
// Stop storage manager from using us
|
||||||
self.network_manager
|
self.network_manager
|
||||||
@ -303,12 +303,12 @@ impl RoutingTable {
|
|||||||
self.cancel_tasks().await;
|
self.cancel_tasks().await;
|
||||||
|
|
||||||
// Load bucket entries from table db if possible
|
// Load bucket entries from table db if possible
|
||||||
debug!("saving routing table entries");
|
log_rtab!(debug "saving routing table entries");
|
||||||
if let Err(e) = self.save_buckets().await {
|
if let Err(e) = self.save_buckets().await {
|
||||||
error!("failed to save routing table entries: {}", e);
|
error!("failed to save routing table entries: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("saving route spec store");
|
log_rtab!(debug "saving route spec store");
|
||||||
let rss = {
|
let rss = {
|
||||||
let mut inner = self.inner.write();
|
let mut inner = self.inner.write();
|
||||||
inner.route_spec_store.take()
|
inner.route_spec_store.take()
|
||||||
@ -318,12 +318,12 @@ impl RoutingTable {
|
|||||||
error!("couldn't save route spec store: {}", e);
|
error!("couldn't save route spec store: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!("shutting down routing table");
|
log_rtab!(debug "shutting down routing table");
|
||||||
|
|
||||||
let mut inner = self.inner.write();
|
let mut inner = self.inner.write();
|
||||||
*inner = RoutingTableInner::new(self.unlocked_inner.clone());
|
*inner = RoutingTableInner::new(self.unlocked_inner.clone());
|
||||||
|
|
||||||
debug!("finished routing table terminate");
|
log_rtab!(debug "finished routing table terminate");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize the routing table.
|
/// Serialize the routing table.
|
||||||
@ -390,6 +390,15 @@ impl RoutingTable {
|
|||||||
for b in &c.network.routing_table.bootstrap {
|
for b in &c.network.routing_table.bootstrap {
|
||||||
cache_validity_key.append(&mut b.as_bytes().to_vec());
|
cache_validity_key.append(&mut b.as_bytes().to_vec());
|
||||||
}
|
}
|
||||||
|
cache_validity_key.append(
|
||||||
|
&mut c
|
||||||
|
.network
|
||||||
|
.network_key_password
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_bytes()
|
||||||
|
.to_vec(),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Deserialize bucket map and all entries from the table store
|
// Deserialize bucket map and all entries from the table store
|
||||||
@ -870,7 +879,15 @@ impl RoutingTable {
|
|||||||
// does it have some dial info we need?
|
// does it have some dial info we need?
|
||||||
let filter = |n: &NodeInfo| {
|
let filter = |n: &NodeInfo| {
|
||||||
let mut keep = false;
|
let mut keep = false;
|
||||||
|
// Bootstraps must have -only- inbound capable network class
|
||||||
|
if !matches!(n.network_class(), NetworkClass::InboundCapable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
for did in n.dial_info_detail_list() {
|
for did in n.dial_info_detail_list() {
|
||||||
|
// Bootstraps must have -only- direct dial info
|
||||||
|
if !matches!(did.class, DialInfoClass::Direct) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if matches!(did.dial_info.address_type(), AddressType::IPV4) {
|
if matches!(did.dial_info.address_type(), AddressType::IPV4) {
|
||||||
for (n, protocol_type) in protocol_types.iter().enumerate() {
|
for (n, protocol_type) in protocol_types.iter().enumerate() {
|
||||||
if nodes_proto_v4[n] < max_per_type
|
if nodes_proto_v4[n] < max_per_type
|
||||||
|
@ -849,8 +849,7 @@ impl RouteSpecStore {
|
|||||||
// Get all valid routes, allow routes that need testing
|
// Get all valid routes, allow routes that need testing
|
||||||
// but definitely prefer routes that have been recently tested
|
// but definitely prefer routes that have been recently tested
|
||||||
for (id, rssd) in inner.content.iter_details() {
|
for (id, rssd) in inner.content.iter_details() {
|
||||||
if rssd.get_stability() >= stability
|
if rssd.is_sequencing_match(sequencing)
|
||||||
&& rssd.is_sequencing_match(sequencing)
|
|
||||||
&& rssd.hop_count() >= min_hop_count
|
&& rssd.hop_count() >= min_hop_count
|
||||||
&& rssd.hop_count() <= max_hop_count
|
&& rssd.hop_count() <= max_hop_count
|
||||||
&& rssd.get_directions().is_superset(directions)
|
&& rssd.get_directions().is_superset(directions)
|
||||||
@ -864,6 +863,7 @@ impl RouteSpecStore {
|
|||||||
|
|
||||||
// Sort the routes by preference
|
// Sort the routes by preference
|
||||||
routes.sort_by(|a, b| {
|
routes.sort_by(|a, b| {
|
||||||
|
// Prefer routes that don't need testing
|
||||||
let a_needs_testing = a.1.get_stats().needs_testing(cur_ts);
|
let a_needs_testing = a.1.get_stats().needs_testing(cur_ts);
|
||||||
let b_needs_testing = b.1.get_stats().needs_testing(cur_ts);
|
let b_needs_testing = b.1.get_stats().needs_testing(cur_ts);
|
||||||
if !a_needs_testing && b_needs_testing {
|
if !a_needs_testing && b_needs_testing {
|
||||||
@ -872,6 +872,18 @@ impl RouteSpecStore {
|
|||||||
if !b_needs_testing && a_needs_testing {
|
if !b_needs_testing && a_needs_testing {
|
||||||
return cmp::Ordering::Greater;
|
return cmp::Ordering::Greater;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prefer routes that meet the stability selection
|
||||||
|
let a_meets_stability = a.1.get_stability() >= stability;
|
||||||
|
let b_meets_stability = b.1.get_stability() >= stability;
|
||||||
|
if a_meets_stability && !b_meets_stability {
|
||||||
|
return cmp::Ordering::Less;
|
||||||
|
}
|
||||||
|
if b_meets_stability && !a_meets_stability {
|
||||||
|
return cmp::Ordering::Greater;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer faster routes
|
||||||
let a_latency = a.1.get_stats().latency_stats().average;
|
let a_latency = a.1.get_stats().latency_stats().average;
|
||||||
let b_latency = b.1.get_stats().latency_stats().average;
|
let b_latency = b.1.get_stats().latency_stats().average;
|
||||||
|
|
||||||
@ -903,12 +915,16 @@ impl RouteSpecStore {
|
|||||||
F: FnMut(&RouteId, &RemotePrivateRouteInfo) -> Option<R>,
|
F: FnMut(&RouteId, &RemotePrivateRouteInfo) -> Option<R>,
|
||||||
{
|
{
|
||||||
let inner = self.inner.lock();
|
let inner = self.inner.lock();
|
||||||
let mut out = Vec::with_capacity(inner.cache.get_remote_private_route_count());
|
let cur_ts = get_aligned_timestamp();
|
||||||
for info in inner.cache.iter_remote_private_routes() {
|
let remote_route_ids = inner.cache.get_remote_private_route_ids(cur_ts);
|
||||||
if let Some(x) = filter(info.0, info.1) {
|
let mut out = Vec::with_capacity(remote_route_ids.len());
|
||||||
|
for id in remote_route_ids {
|
||||||
|
if let Some(rpri) = inner.cache.peek_remote_private_route(cur_ts, &id) {
|
||||||
|
if let Some(x) = filter(&id, rpri) {
|
||||||
out.push(x);
|
out.push(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -916,7 +932,7 @@ impl RouteSpecStore {
|
|||||||
pub fn debug_route(&self, id: &RouteId) -> Option<String> {
|
pub fn debug_route(&self, id: &RouteId) -> Option<String> {
|
||||||
let inner = &mut *self.inner.lock();
|
let inner = &mut *self.inner.lock();
|
||||||
let cur_ts = get_aligned_timestamp();
|
let cur_ts = get_aligned_timestamp();
|
||||||
if let Some(rpri) = inner.cache.peek_remote_private_route_mut(cur_ts, id) {
|
if let Some(rpri) = inner.cache.peek_remote_private_route(cur_ts, id) {
|
||||||
return Some(format!("{:#?}", rpri));
|
return Some(format!("{:#?}", rpri));
|
||||||
}
|
}
|
||||||
if let Some(rssd) = inner.content.get_detail(id) {
|
if let Some(rssd) = inner.content.get_detail(id) {
|
||||||
@ -1001,7 +1017,7 @@ impl RouteSpecStore {
|
|||||||
first_hop.set_sequencing(sequencing);
|
first_hop.set_sequencing(sequencing);
|
||||||
|
|
||||||
// Return the compiled safety route
|
// Return the compiled safety route
|
||||||
//println!("compile_safety_route profile (stub): {} us", (get_timestamp() - profile_start_ts));
|
//info!("compile_safety_route profile (stub): {} us", (get_timestamp() - profile_start_ts));
|
||||||
return Ok(CompiledRoute {
|
return Ok(CompiledRoute {
|
||||||
safety_route: SafetyRoute::new_stub(
|
safety_route: SafetyRoute::new_stub(
|
||||||
routing_table.node_id(crypto_kind),
|
routing_table.node_id(crypto_kind),
|
||||||
@ -1073,7 +1089,7 @@ impl RouteSpecStore {
|
|||||||
first_hop,
|
first_hop,
|
||||||
};
|
};
|
||||||
// Return compiled route
|
// Return compiled route
|
||||||
//println!("compile_safety_route profile (cached): {} us", (get_timestamp() - profile_start_ts));
|
//info!("compile_safety_route profile (cached): {} us", (get_timestamp() - profile_start_ts));
|
||||||
return Ok(compiled_route);
|
return Ok(compiled_route);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1192,7 +1208,7 @@ impl RouteSpecStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Return compiled route
|
// Return compiled route
|
||||||
//println!("compile_safety_route profile (uncached): {} us", (get_timestamp() - profile_start_ts));
|
//info!("compile_safety_route profile (uncached): {} us", (get_timestamp() - profile_start_ts));
|
||||||
Ok(compiled_route)
|
Ok(compiled_route)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1575,7 +1591,7 @@ impl RouteSpecStore {
|
|||||||
/// Check to see if this remote (not ours) private route has seen our current node info yet
|
/// Check to see if this remote (not ours) private route has seen our current node info yet
|
||||||
/// This happens when you communicate with a private route without a safety route
|
/// This happens when you communicate with a private route without a safety route
|
||||||
pub fn has_remote_private_route_seen_our_node_info(&self, key: &PublicKey) -> bool {
|
pub fn has_remote_private_route_seen_our_node_info(&self, key: &PublicKey) -> bool {
|
||||||
let inner = &mut *self.inner.lock();
|
let inner = &*self.inner.lock();
|
||||||
|
|
||||||
// Check for local route. If this is not a remote private route,
|
// Check for local route. If this is not a remote private route,
|
||||||
// we may be running a test and using our own local route as the destination private route.
|
// we may be running a test and using our own local route as the destination private route.
|
||||||
@ -1586,7 +1602,7 @@ impl RouteSpecStore {
|
|||||||
|
|
||||||
if let Some(rrid) = inner.cache.get_remote_private_route_id_by_key(key) {
|
if let Some(rrid) = inner.cache.get_remote_private_route_id_by_key(key) {
|
||||||
let cur_ts = get_aligned_timestamp();
|
let cur_ts = get_aligned_timestamp();
|
||||||
if let Some(rpri) = inner.cache.peek_remote_private_route_mut(cur_ts, &rrid) {
|
if let Some(rpri) = inner.cache.peek_remote_private_route(cur_ts, &rrid) {
|
||||||
let our_node_info_ts = self
|
let our_node_info_ts = self
|
||||||
.unlocked_inner
|
.unlocked_inner
|
||||||
.routing_table
|
.routing_table
|
||||||
@ -1634,7 +1650,7 @@ impl RouteSpecStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the route statistics for any route we know about, local or remote
|
/// Get the route statistics for any route we know about, local or remote
|
||||||
pub fn with_route_stats<F, R>(&self, cur_ts: Timestamp, key: &PublicKey, f: F) -> Option<R>
|
pub fn with_route_stats_mut<F, R>(&self, cur_ts: Timestamp, key: &PublicKey, f: F) -> Option<R>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut RouteStats) -> R,
|
F: FnOnce(&mut RouteStats) -> R,
|
||||||
{
|
{
|
||||||
|
@ -45,7 +45,7 @@ impl RemotePrivateRouteInfo {
|
|||||||
&mut self.stats
|
&mut self.stats
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_seen_our_node_info_ts(&mut self, our_node_info_ts: Timestamp) -> bool {
|
pub fn has_seen_our_node_info_ts(&self, our_node_info_ts: Timestamp) -> bool {
|
||||||
self.last_seen_our_node_info_ts == our_node_info_ts
|
self.last_seen_our_node_info_ts == our_node_info_ts
|
||||||
}
|
}
|
||||||
pub fn set_last_seen_our_node_info_ts(&mut self, last_seen_our_node_info_ts: Timestamp) {
|
pub fn set_last_seen_our_node_info_ts(&mut self, last_seen_our_node_info_ts: Timestamp) {
|
||||||
|
@ -173,16 +173,18 @@ impl RouteSpecStoreCache {
|
|||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get count of remote private routes in cache
|
|
||||||
pub fn get_remote_private_route_count(&self) -> usize {
|
|
||||||
self.remote_private_route_set_cache.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// iterate all of the remote private routes we have in the cache
|
/// iterate all of the remote private routes we have in the cache
|
||||||
pub fn iter_remote_private_routes(
|
pub fn get_remote_private_route_ids(&self, cur_ts: Timestamp) -> Vec<RouteId> {
|
||||||
&self,
|
self.remote_private_route_set_cache
|
||||||
) -> hashlink::linked_hash_map::Iter<RouteId, RemotePrivateRouteInfo> {
|
.iter()
|
||||||
self.remote_private_route_set_cache.iter()
|
.filter_map(|(id, rpri)| {
|
||||||
|
if !rpri.did_expire(cur_ts) {
|
||||||
|
Some(*id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// remote private route cache accessor
|
/// remote private route cache accessor
|
||||||
@ -217,6 +219,21 @@ impl RouteSpecStoreCache {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// remote private route cache accessor without lru action
|
||||||
|
/// will not LRU entries but may expire entries and not return them if they are stale
|
||||||
|
pub fn peek_remote_private_route(
|
||||||
|
&self,
|
||||||
|
cur_ts: Timestamp,
|
||||||
|
id: &RouteId,
|
||||||
|
) -> Option<&RemotePrivateRouteInfo> {
|
||||||
|
if let Some(rpri) = self.remote_private_route_set_cache.peek(id) {
|
||||||
|
if !rpri.did_expire(cur_ts) {
|
||||||
|
return Some(rpri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// mutable remote private route cache accessor without lru action
|
/// mutable remote private route cache accessor without lru action
|
||||||
/// will not LRU entries but may expire entries and not return them if they are stale
|
/// will not LRU entries but may expire entries and not return them if they are stale
|
||||||
pub fn peek_remote_private_route_mut(
|
pub fn peek_remote_private_route_mut(
|
||||||
|
@ -888,11 +888,16 @@ impl RoutingTableInner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Public internet routing domain is ready for app use,
|
||||||
|
// when we have proper dialinfo/networkclass
|
||||||
let public_internet_ready = !matches!(
|
let public_internet_ready = !matches!(
|
||||||
self.get_network_class(RoutingDomain::PublicInternet)
|
self.get_network_class(RoutingDomain::PublicInternet)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
NetworkClass::Invalid
|
NetworkClass::Invalid
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Local internet routing domain is ready for app use
|
||||||
|
// when we have proper dialinfo/networkclass
|
||||||
let local_network_ready = !matches!(
|
let local_network_ready = !matches!(
|
||||||
self.get_network_class(RoutingDomain::LocalNetwork)
|
self.get_network_class(RoutingDomain::LocalNetwork)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
|
@ -4,6 +4,7 @@ use futures_util::stream::{FuturesUnordered, StreamExt};
|
|||||||
use stop_token::future::FutureExt as StopFutureExt;
|
use stop_token::future::FutureExt as StopFutureExt;
|
||||||
|
|
||||||
pub const BOOTSTRAP_TXT_VERSION_0: u8 = 0;
|
pub const BOOTSTRAP_TXT_VERSION_0: u8 = 0;
|
||||||
|
pub const MIN_BOOTSTRAP_PEERS: usize = 4;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct BootstrapRecord {
|
pub struct BootstrapRecord {
|
||||||
@ -285,8 +286,7 @@ impl RoutingTable {
|
|||||||
{
|
{
|
||||||
Ok(NodeContactMethod::Direct(v)) => v,
|
Ok(NodeContactMethod::Direct(v)) => v,
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
log_rtab!(warn "invalid contact method for bootstrap, restarting network: {:?}", v);
|
log_rtab!(warn "invalid contact method for bootstrap, ignoring peer: {:?}", v);
|
||||||
routing_table.network_manager().restart_network();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -345,7 +345,7 @@ impl RoutingTable {
|
|||||||
// Do we need to bootstrap this crypto kind?
|
// Do we need to bootstrap this crypto kind?
|
||||||
let eckey = (RoutingDomain::PublicInternet, crypto_kind);
|
let eckey = (RoutingDomain::PublicInternet, crypto_kind);
|
||||||
let cnt = entry_count.get(&eckey).copied().unwrap_or_default();
|
let cnt = entry_count.get(&eckey).copied().unwrap_or_default();
|
||||||
if cnt == 0 {
|
if cnt < MIN_BOOTSTRAP_PEERS {
|
||||||
crypto_kinds.push(crypto_kind);
|
crypto_kinds.push(crypto_kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,31 +194,31 @@ impl RoutingTable {
|
|||||||
|
|
||||||
pub(crate) async fn cancel_tasks(&self) {
|
pub(crate) async fn cancel_tasks(&self) {
|
||||||
// Cancel all tasks being ticked
|
// Cancel all tasks being ticked
|
||||||
debug!("stopping rolling transfers task");
|
log_rtab!(debug "stopping rolling transfers task");
|
||||||
if let Err(e) = self.unlocked_inner.rolling_transfers_task.stop().await {
|
if let Err(e) = self.unlocked_inner.rolling_transfers_task.stop().await {
|
||||||
error!("rolling_transfers_task not stopped: {}", e);
|
error!("rolling_transfers_task not stopped: {}", e);
|
||||||
}
|
}
|
||||||
debug!("stopping kick buckets task");
|
log_rtab!(debug "stopping kick buckets task");
|
||||||
if let Err(e) = self.unlocked_inner.kick_buckets_task.stop().await {
|
if let Err(e) = self.unlocked_inner.kick_buckets_task.stop().await {
|
||||||
error!("kick_buckets_task not stopped: {}", e);
|
error!("kick_buckets_task not stopped: {}", e);
|
||||||
}
|
}
|
||||||
debug!("stopping bootstrap task");
|
log_rtab!(debug "stopping bootstrap task");
|
||||||
if let Err(e) = self.unlocked_inner.bootstrap_task.stop().await {
|
if let Err(e) = self.unlocked_inner.bootstrap_task.stop().await {
|
||||||
error!("bootstrap_task not stopped: {}", e);
|
error!("bootstrap_task not stopped: {}", e);
|
||||||
}
|
}
|
||||||
debug!("stopping peer minimum refresh task");
|
log_rtab!(debug "stopping peer minimum refresh task");
|
||||||
if let Err(e) = self.unlocked_inner.peer_minimum_refresh_task.stop().await {
|
if let Err(e) = self.unlocked_inner.peer_minimum_refresh_task.stop().await {
|
||||||
error!("peer_minimum_refresh_task not stopped: {}", e);
|
error!("peer_minimum_refresh_task not stopped: {}", e);
|
||||||
}
|
}
|
||||||
debug!("stopping ping_validator task");
|
log_rtab!(debug "stopping ping_validator task");
|
||||||
if let Err(e) = self.unlocked_inner.ping_validator_task.stop().await {
|
if let Err(e) = self.unlocked_inner.ping_validator_task.stop().await {
|
||||||
error!("ping_validator_task not stopped: {}", e);
|
error!("ping_validator_task not stopped: {}", e);
|
||||||
}
|
}
|
||||||
debug!("stopping relay management task");
|
log_rtab!(debug "stopping relay management task");
|
||||||
if let Err(e) = self.unlocked_inner.relay_management_task.stop().await {
|
if let Err(e) = self.unlocked_inner.relay_management_task.stop().await {
|
||||||
warn!("relay_management_task not stopped: {}", e);
|
warn!("relay_management_task not stopped: {}", e);
|
||||||
}
|
}
|
||||||
debug!("stopping private route management task");
|
log_rtab!(debug "stopping private route management task");
|
||||||
if let Err(e) = self
|
if let Err(e) = self
|
||||||
.unlocked_inner
|
.unlocked_inner
|
||||||
.private_route_management_task
|
.private_route_management_task
|
||||||
|
@ -105,9 +105,6 @@ impl RoutingTable {
|
|||||||
for relay_nr_filtered in relay_noderefs {
|
for relay_nr_filtered in relay_noderefs {
|
||||||
let rpc = rpc.clone();
|
let rpc = rpc.clone();
|
||||||
|
|
||||||
#[cfg(feature = "network-result-extra")]
|
|
||||||
log_rtab!(debug "--> Keepalive ping to {:?}", relay_nr_filtered);
|
|
||||||
#[cfg(not(feature = "network-result-extra"))]
|
|
||||||
log_rtab!("--> Keepalive ping to {:?}", relay_nr_filtered);
|
log_rtab!("--> Keepalive ping to {:?}", relay_nr_filtered);
|
||||||
|
|
||||||
futurequeue.push_back(
|
futurequeue.push_back(
|
||||||
|
@ -23,13 +23,13 @@ impl RoutingTable {
|
|||||||
let state = relay_node.state(cur_ts);
|
let state = relay_node.state(cur_ts);
|
||||||
// Relay node is dead or no longer needed
|
// Relay node is dead or no longer needed
|
||||||
if matches!(state, BucketEntryState::Dead) {
|
if matches!(state, BucketEntryState::Dead) {
|
||||||
debug!("Relay node died, dropping relay {}", relay_node);
|
log_rtab!(debug "Relay node died, dropping relay {}", relay_node);
|
||||||
editor.clear_relay_node();
|
editor.clear_relay_node();
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
// Relay node no longer can relay
|
// Relay node no longer can relay
|
||||||
else if relay_node.operate(|_rti, e| !relay_node_filter(e)) {
|
else if relay_node.operate(|_rti, e| !relay_node_filter(e)) {
|
||||||
debug!(
|
log_rtab!(debug
|
||||||
"Relay node can no longer relay, dropping relay {}",
|
"Relay node can no longer relay, dropping relay {}",
|
||||||
relay_node
|
relay_node
|
||||||
);
|
);
|
||||||
@ -38,7 +38,7 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
// Relay node is no longer required
|
// Relay node is no longer required
|
||||||
else if !own_node_info.requires_relay() {
|
else if !own_node_info.requires_relay() {
|
||||||
debug!(
|
log_rtab!(debug
|
||||||
"Relay node no longer required, dropping relay {}",
|
"Relay node no longer required, dropping relay {}",
|
||||||
relay_node
|
relay_node
|
||||||
);
|
);
|
||||||
@ -47,7 +47,7 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
// Should not have relay for invalid network class
|
// Should not have relay for invalid network class
|
||||||
else if !self.has_valid_network_class(RoutingDomain::PublicInternet) {
|
else if !self.has_valid_network_class(RoutingDomain::PublicInternet) {
|
||||||
debug!(
|
log_rtab!(debug
|
||||||
"Invalid network class does not get a relay, dropping relay {}",
|
"Invalid network class does not get a relay, dropping relay {}",
|
||||||
relay_node
|
relay_node
|
||||||
);
|
);
|
||||||
@ -75,7 +75,7 @@ impl RoutingTable {
|
|||||||
false,
|
false,
|
||||||
) {
|
) {
|
||||||
Ok(nr) => {
|
Ok(nr) => {
|
||||||
debug!("Outbound relay node selected: {}", nr);
|
log_rtab!(debug "Outbound relay node selected: {}", nr);
|
||||||
editor.set_relay_node(nr);
|
editor.set_relay_node(nr);
|
||||||
got_outbound_relay = true;
|
got_outbound_relay = true;
|
||||||
}
|
}
|
||||||
@ -84,13 +84,13 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!("Outbound relay desired but not available");
|
log_rtab!(debug "Outbound relay desired but not available");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !got_outbound_relay {
|
if !got_outbound_relay {
|
||||||
// Find a node in our routing table that is an acceptable inbound relay
|
// Find a node in our routing table that is an acceptable inbound relay
|
||||||
if let Some(nr) = self.find_inbound_relay(RoutingDomain::PublicInternet, cur_ts) {
|
if let Some(nr) = self.find_inbound_relay(RoutingDomain::PublicInternet, cur_ts) {
|
||||||
debug!("Inbound relay node selected: {}", nr);
|
log_rtab!(debug "Inbound relay node selected: {}", nr);
|
||||||
editor.set_relay_node(nr);
|
editor.set_relay_node(nr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ mod tunnel;
|
|||||||
mod typed_key;
|
mod typed_key;
|
||||||
mod typed_signature;
|
mod typed_signature;
|
||||||
|
|
||||||
|
pub(crate) use operations::MAX_INSPECT_VALUE_A_SEQS_LEN;
|
||||||
pub(in crate::rpc_processor) use operations::*;
|
pub(in crate::rpc_processor) use operations::*;
|
||||||
|
|
||||||
pub(crate) use address::*;
|
pub(crate) use address::*;
|
||||||
@ -60,9 +61,11 @@ pub use typed_signature::*;
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
pub(in crate::rpc_processor) enum QuestionContext {
|
pub(in crate::rpc_processor) enum QuestionContext {
|
||||||
GetValue(ValidateGetValueContext),
|
GetValue(ValidateGetValueContext),
|
||||||
SetValue(ValidateSetValueContext),
|
SetValue(ValidateSetValueContext),
|
||||||
|
InspectValue(ValidateInspectValueContext),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -12,6 +12,7 @@ impl RPCAnswer {
|
|||||||
pub fn validate(&mut self, validate_context: &RPCValidateContext) -> Result<(), RPCError> {
|
pub fn validate(&mut self, validate_context: &RPCValidateContext) -> Result<(), RPCError> {
|
||||||
self.detail.validate(validate_context)
|
self.detail.validate(validate_context)
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
pub fn desc(&self) -> &'static str {
|
pub fn desc(&self) -> &'static str {
|
||||||
self.detail.desc()
|
self.detail.desc()
|
||||||
}
|
}
|
||||||
@ -37,6 +38,7 @@ pub(in crate::rpc_processor) enum RPCAnswerDetail {
|
|||||||
GetValueA(Box<RPCOperationGetValueA>),
|
GetValueA(Box<RPCOperationGetValueA>),
|
||||||
SetValueA(Box<RPCOperationSetValueA>),
|
SetValueA(Box<RPCOperationSetValueA>),
|
||||||
WatchValueA(Box<RPCOperationWatchValueA>),
|
WatchValueA(Box<RPCOperationWatchValueA>),
|
||||||
|
InspectValueA(Box<RPCOperationInspectValueA>),
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
SupplyBlockA(Box<RPCOperationSupplyBlockA>),
|
SupplyBlockA(Box<RPCOperationSupplyBlockA>),
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
@ -50,6 +52,7 @@ pub(in crate::rpc_processor) enum RPCAnswerDetail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RPCAnswerDetail {
|
impl RPCAnswerDetail {
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
pub fn desc(&self) -> &'static str {
|
pub fn desc(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
RPCAnswerDetail::StatusA(_) => "StatusA",
|
RPCAnswerDetail::StatusA(_) => "StatusA",
|
||||||
@ -58,6 +61,7 @@ impl RPCAnswerDetail {
|
|||||||
RPCAnswerDetail::GetValueA(_) => "GetValueA",
|
RPCAnswerDetail::GetValueA(_) => "GetValueA",
|
||||||
RPCAnswerDetail::SetValueA(_) => "SetValueA",
|
RPCAnswerDetail::SetValueA(_) => "SetValueA",
|
||||||
RPCAnswerDetail::WatchValueA(_) => "WatchValueA",
|
RPCAnswerDetail::WatchValueA(_) => "WatchValueA",
|
||||||
|
RPCAnswerDetail::InspectValueA(_) => "InspectValueA",
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
RPCAnswerDetail::SupplyBlockA(_) => "SupplyBlockA",
|
RPCAnswerDetail::SupplyBlockA(_) => "SupplyBlockA",
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
@ -78,6 +82,7 @@ impl RPCAnswerDetail {
|
|||||||
RPCAnswerDetail::GetValueA(r) => r.validate(validate_context),
|
RPCAnswerDetail::GetValueA(r) => r.validate(validate_context),
|
||||||
RPCAnswerDetail::SetValueA(r) => r.validate(validate_context),
|
RPCAnswerDetail::SetValueA(r) => r.validate(validate_context),
|
||||||
RPCAnswerDetail::WatchValueA(r) => r.validate(validate_context),
|
RPCAnswerDetail::WatchValueA(r) => r.validate(validate_context),
|
||||||
|
RPCAnswerDetail::InspectValueA(r) => r.validate(validate_context),
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
RPCAnswerDetail::SupplyBlockA(r) => r.validate(validate_context),
|
RPCAnswerDetail::SupplyBlockA(r) => r.validate(validate_context),
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
@ -125,6 +130,11 @@ impl RPCAnswerDetail {
|
|||||||
let out = RPCOperationWatchValueA::decode(&op_reader)?;
|
let out = RPCOperationWatchValueA::decode(&op_reader)?;
|
||||||
RPCAnswerDetail::WatchValueA(Box::new(out))
|
RPCAnswerDetail::WatchValueA(Box::new(out))
|
||||||
}
|
}
|
||||||
|
veilid_capnp::answer::detail::InspectValueA(r) => {
|
||||||
|
let op_reader = r.map_err(RPCError::protocol)?;
|
||||||
|
let out = RPCOperationInspectValueA::decode(&op_reader)?;
|
||||||
|
RPCAnswerDetail::InspectValueA(Box::new(out))
|
||||||
|
}
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
veilid_capnp::answer::detail::SupplyBlockA(r) => {
|
veilid_capnp::answer::detail::SupplyBlockA(r) => {
|
||||||
let op_reader = r.map_err(RPCError::protocol)?;
|
let op_reader = r.map_err(RPCError::protocol)?;
|
||||||
@ -171,6 +181,9 @@ impl RPCAnswerDetail {
|
|||||||
RPCAnswerDetail::WatchValueA(d) => {
|
RPCAnswerDetail::WatchValueA(d) => {
|
||||||
d.encode(&mut builder.reborrow().init_watch_value_a())
|
d.encode(&mut builder.reborrow().init_watch_value_a())
|
||||||
}
|
}
|
||||||
|
RPCAnswerDetail::InspectValueA(d) => {
|
||||||
|
d.encode(&mut builder.reborrow().init_inspect_value_a())
|
||||||
|
}
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
RPCAnswerDetail::SupplyBlockA(d) => {
|
RPCAnswerDetail::SupplyBlockA(d) => {
|
||||||
d.encode(&mut builder.reborrow().init_supply_block_a())
|
d.encode(&mut builder.reborrow().init_supply_block_a())
|
||||||
|
@ -4,6 +4,7 @@ mod operation_app_call;
|
|||||||
mod operation_app_message;
|
mod operation_app_message;
|
||||||
mod operation_find_node;
|
mod operation_find_node;
|
||||||
mod operation_get_value;
|
mod operation_get_value;
|
||||||
|
mod operation_inspect_value;
|
||||||
mod operation_return_receipt;
|
mod operation_return_receipt;
|
||||||
mod operation_route;
|
mod operation_route;
|
||||||
mod operation_set_value;
|
mod operation_set_value;
|
||||||
@ -35,6 +36,7 @@ pub(in crate::rpc_processor) use operation_app_call::*;
|
|||||||
pub(in crate::rpc_processor) use operation_app_message::*;
|
pub(in crate::rpc_processor) use operation_app_message::*;
|
||||||
pub(in crate::rpc_processor) use operation_find_node::*;
|
pub(in crate::rpc_processor) use operation_find_node::*;
|
||||||
pub(in crate::rpc_processor) use operation_get_value::*;
|
pub(in crate::rpc_processor) use operation_get_value::*;
|
||||||
|
pub(in crate::rpc_processor) use operation_inspect_value::*;
|
||||||
pub(in crate::rpc_processor) use operation_return_receipt::*;
|
pub(in crate::rpc_processor) use operation_return_receipt::*;
|
||||||
pub(in crate::rpc_processor) use operation_route::*;
|
pub(in crate::rpc_processor) use operation_route::*;
|
||||||
pub(in crate::rpc_processor) use operation_set_value::*;
|
pub(in crate::rpc_processor) use operation_set_value::*;
|
||||||
@ -60,3 +62,5 @@ pub(in crate::rpc_processor) use operation_complete_tunnel::*;
|
|||||||
pub(in crate::rpc_processor) use operation_start_tunnel::*;
|
pub(in crate::rpc_processor) use operation_start_tunnel::*;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
pub(crate) use operation_inspect_value::MAX_INSPECT_VALUE_A_SEQS_LEN;
|
||||||
|
@ -8,6 +8,7 @@ pub(in crate::rpc_processor) enum RPCOperationKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RPCOperationKind {
|
impl RPCOperationKind {
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
pub fn desc(&self) -> &'static str {
|
pub fn desc(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
RPCOperationKind::Question(q) => q.desc(),
|
RPCOperationKind::Question(q) => q.desc(),
|
||||||
@ -105,6 +106,7 @@ impl RPCOperation {
|
|||||||
.validate(validate_context.crypto.clone())
|
.validate(validate_context.crypto.clone())
|
||||||
.map_err(RPCError::protocol)?;
|
.map_err(RPCError::protocol)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate operation kind
|
// Validate operation kind
|
||||||
self.kind.validate(validate_context)
|
self.kind.validate(validate_context)
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,8 @@ impl RPCOperationAppCallQ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(in crate::rpc_processor) struct RPCOperationAppCallA {
|
pub(in crate::rpc_processor) struct RPCOperationAppCallA {
|
||||||
message: Vec<u8>,
|
message: Vec<u8>,
|
||||||
|
@ -38,6 +38,8 @@ impl RPCOperationCancelTunnelQ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg(feature = "unstable-tunnels")]
|
#[cfg(feature = "unstable-tunnels")]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(in crate::rpc_processor) enum RPCOperationCancelTunnelA {
|
pub(in crate::rpc_processor) enum RPCOperationCancelTunnelA {
|
||||||
|
@ -75,6 +75,8 @@ impl RPCOperationCompleteTunnelQ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg(feature = "unstable-tunnels")]
|
#[cfg(feature = "unstable-tunnels")]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(in crate::rpc_processor) enum RPCOperationCompleteTunnelA {
|
pub(in crate::rpc_processor) enum RPCOperationCompleteTunnelA {
|
||||||
|
@ -44,6 +44,8 @@ impl RPCOperationFindBlockQ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(in crate::rpc_processor) struct RPCOperationFindBlockA {
|
pub(in crate::rpc_processor) struct RPCOperationFindBlockA {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
|
@ -73,6 +73,8 @@ impl RPCOperationFindNodeQ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(in crate::rpc_processor) struct RPCOperationFindNodeA {
|
pub(in crate::rpc_processor) struct RPCOperationFindNodeA {
|
||||||
peers: Vec<PeerInfo>,
|
peers: Vec<PeerInfo>,
|
||||||
|
@ -75,6 +75,8 @@ impl RPCOperationGetValueQ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(in crate::rpc_processor) struct RPCOperationGetValueA {
|
pub(in crate::rpc_processor) struct RPCOperationGetValueA {
|
||||||
value: Option<SignedValueData>,
|
value: Option<SignedValueData>,
|
||||||
@ -109,30 +111,36 @@ impl RPCOperationGetValueA {
|
|||||||
panic!("Wrong context type for GetValueA");
|
panic!("Wrong context type for GetValueA");
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(value) = &self.value {
|
// Validate descriptor
|
||||||
// Get descriptor to validate with
|
if let Some(descriptor) = &self.descriptor {
|
||||||
let descriptor = if let Some(descriptor) = &self.descriptor {
|
|
||||||
if let Some(last_descriptor) = &get_value_context.last_descriptor {
|
|
||||||
if descriptor.cmp_no_sig(last_descriptor) != cmp::Ordering::Equal {
|
|
||||||
return Err(RPCError::protocol(
|
|
||||||
"getvalue descriptor does not match last descriptor",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
descriptor
|
|
||||||
} else {
|
|
||||||
let Some(descriptor) = &get_value_context.last_descriptor else {
|
|
||||||
return Err(RPCError::protocol(
|
|
||||||
"no last descriptor, requires a descriptor",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
descriptor
|
|
||||||
};
|
|
||||||
// Ensure the descriptor itself validates
|
// Ensure the descriptor itself validates
|
||||||
descriptor
|
descriptor
|
||||||
.validate(get_value_context.vcrypto.clone())
|
.validate(get_value_context.vcrypto.clone())
|
||||||
.map_err(RPCError::protocol)?;
|
.map_err(RPCError::protocol)?;
|
||||||
|
|
||||||
|
// Ensure descriptor matches last one
|
||||||
|
if let Some(last_descriptor) = &get_value_context.last_descriptor {
|
||||||
|
if descriptor.cmp_no_sig(last_descriptor) != cmp::Ordering::Equal {
|
||||||
|
return Err(RPCError::protocol(
|
||||||
|
"GetValue descriptor does not match last descriptor",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the value validates
|
||||||
|
if let Some(value) = &self.value {
|
||||||
|
// Get descriptor to validate with
|
||||||
|
let Some(descriptor) = self
|
||||||
|
.descriptor
|
||||||
|
.as_ref()
|
||||||
|
.or(get_value_context.last_descriptor.as_ref())
|
||||||
|
else {
|
||||||
|
return Err(RPCError::protocol(
|
||||||
|
"no last descriptor, requires a descriptor",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
// And the signed value data
|
// And the signed value data
|
||||||
value
|
value
|
||||||
.validate(
|
.validate(
|
||||||
|
@ -0,0 +1,288 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::storage_manager::SignedValueDescriptor;
|
||||||
|
|
||||||
|
const MAX_INSPECT_VALUE_Q_SUBKEY_RANGES_LEN: usize = 512;
|
||||||
|
pub(crate) const MAX_INSPECT_VALUE_A_SEQS_LEN: usize = 512;
|
||||||
|
const MAX_INSPECT_VALUE_A_PEERS_LEN: usize = 20;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(in crate::rpc_processor) struct ValidateInspectValueContext {
|
||||||
|
pub last_descriptor: Option<SignedValueDescriptor>,
|
||||||
|
pub subkeys: ValueSubkeyRangeSet,
|
||||||
|
pub vcrypto: CryptoSystemVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ValidateInspectValueContext {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("ValidateInspectValueContext")
|
||||||
|
.field("last_descriptor", &self.last_descriptor)
|
||||||
|
.field("vcrypto", &self.vcrypto.kind().to_string())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(in crate::rpc_processor) struct RPCOperationInspectValueQ {
|
||||||
|
key: TypedKey,
|
||||||
|
subkeys: ValueSubkeyRangeSet,
|
||||||
|
want_descriptor: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RPCOperationInspectValueQ {
|
||||||
|
pub fn new(
|
||||||
|
key: TypedKey,
|
||||||
|
subkeys: ValueSubkeyRangeSet,
|
||||||
|
want_descriptor: bool,
|
||||||
|
) -> Result<Self, RPCError> {
|
||||||
|
Ok(Self {
|
||||||
|
key,
|
||||||
|
subkeys,
|
||||||
|
want_descriptor,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn validate(&mut self, _validate_context: &RPCValidateContext) -> Result<(), RPCError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn key(&self) -> &TypedKey {
|
||||||
|
// &self.key
|
||||||
|
// }
|
||||||
|
// pub fn subkeys(&self) -> &ValueSubkeyRangeSet {
|
||||||
|
// &self.subkeys
|
||||||
|
// }
|
||||||
|
// pub fn want_descriptor(&self) -> bool {
|
||||||
|
// self.want_descriptor
|
||||||
|
// }
|
||||||
|
pub fn destructure(self) -> (TypedKey, ValueSubkeyRangeSet, bool) {
|
||||||
|
(self.key, self.subkeys, self.want_descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode(
|
||||||
|
reader: &veilid_capnp::operation_inspect_value_q::Reader,
|
||||||
|
) -> Result<Self, RPCError> {
|
||||||
|
let k_reader = reader.reborrow().get_key().map_err(RPCError::protocol)?;
|
||||||
|
let key = decode_typed_key(&k_reader)?;
|
||||||
|
let sk_reader = reader.get_subkeys().map_err(RPCError::protocol)?;
|
||||||
|
// Maximum number of ranges that can hold the maximum number of subkeys is one subkey per range
|
||||||
|
if sk_reader.len() as usize > MAX_INSPECT_VALUE_Q_SUBKEY_RANGES_LEN {
|
||||||
|
return Err(RPCError::protocol("InspectValueQ too many subkey ranges"));
|
||||||
|
}
|
||||||
|
let mut subkeys = ValueSubkeyRangeSet::new();
|
||||||
|
for skr in sk_reader.iter() {
|
||||||
|
let vskr = (skr.get_start(), skr.get_end());
|
||||||
|
if vskr.0 > vskr.1 {
|
||||||
|
return Err(RPCError::protocol("invalid subkey range"));
|
||||||
|
}
|
||||||
|
if let Some(lvskr) = subkeys.last() {
|
||||||
|
if lvskr >= vskr.0 {
|
||||||
|
return Err(RPCError::protocol(
|
||||||
|
"subkey range out of order or not merged",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subkeys.ranges_insert(vskr.0..=vskr.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let want_descriptor = reader.reborrow().get_want_descriptor();
|
||||||
|
Ok(Self {
|
||||||
|
key,
|
||||||
|
subkeys,
|
||||||
|
want_descriptor,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn encode(
|
||||||
|
&self,
|
||||||
|
builder: &mut veilid_capnp::operation_inspect_value_q::Builder,
|
||||||
|
) -> Result<(), RPCError> {
|
||||||
|
let mut k_builder = builder.reborrow().init_key();
|
||||||
|
encode_typed_key(&self.key, &mut k_builder);
|
||||||
|
|
||||||
|
let mut sk_builder = builder.reborrow().init_subkeys(
|
||||||
|
self.subkeys
|
||||||
|
.ranges_len()
|
||||||
|
.try_into()
|
||||||
|
.map_err(RPCError::map_internal("invalid subkey range list length"))?,
|
||||||
|
);
|
||||||
|
for (i, skr) in self.subkeys.ranges().enumerate() {
|
||||||
|
let mut skr_builder = sk_builder.reborrow().get(i as u32);
|
||||||
|
skr_builder.set_start(*skr.start());
|
||||||
|
skr_builder.set_end(*skr.end());
|
||||||
|
}
|
||||||
|
builder.set_want_descriptor(self.want_descriptor);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(in crate::rpc_processor) struct RPCOperationInspectValueA {
|
||||||
|
seqs: Vec<ValueSeqNum>,
|
||||||
|
peers: Vec<PeerInfo>,
|
||||||
|
descriptor: Option<SignedValueDescriptor>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RPCOperationInspectValueA {
|
||||||
|
pub fn new(
|
||||||
|
seqs: Vec<ValueSeqNum>,
|
||||||
|
peers: Vec<PeerInfo>,
|
||||||
|
descriptor: Option<SignedValueDescriptor>,
|
||||||
|
) -> Result<Self, RPCError> {
|
||||||
|
if seqs.len() > MAX_INSPECT_VALUE_A_SEQS_LEN {
|
||||||
|
return Err(RPCError::protocol(
|
||||||
|
"encoded InspectValueA seqs length too long",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if peers.len() > MAX_INSPECT_VALUE_A_PEERS_LEN {
|
||||||
|
return Err(RPCError::protocol(
|
||||||
|
"encoded InspectValueA peers length too long",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
seqs,
|
||||||
|
peers,
|
||||||
|
descriptor,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate(&mut self, validate_context: &RPCValidateContext) -> Result<(), RPCError> {
|
||||||
|
let question_context = validate_context
|
||||||
|
.question_context
|
||||||
|
.as_ref()
|
||||||
|
.expect("InspectValueA requires question context");
|
||||||
|
let QuestionContext::InspectValue(inspect_value_context) = question_context else {
|
||||||
|
panic!("Wrong context type for InspectValueA");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensure seqs returned does not exceeed subkeys requested
|
||||||
|
#[allow(clippy::unnecessary_cast)]
|
||||||
|
if self.seqs.len() as u64 > inspect_value_context.subkeys.len() as u64 {
|
||||||
|
return Err(RPCError::protocol(format!(
|
||||||
|
"InspectValue seqs length is greater than subkeys requested: {} > {}",
|
||||||
|
self.seqs.len(),
|
||||||
|
inspect_value_context.subkeys.len()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate descriptor
|
||||||
|
if let Some(descriptor) = &self.descriptor {
|
||||||
|
// Ensure the descriptor itself validates
|
||||||
|
descriptor
|
||||||
|
.validate(inspect_value_context.vcrypto.clone())
|
||||||
|
.map_err(RPCError::protocol)?;
|
||||||
|
|
||||||
|
// Ensure descriptor matches last one
|
||||||
|
if let Some(last_descriptor) = &inspect_value_context.last_descriptor {
|
||||||
|
if descriptor.cmp_no_sig(last_descriptor) != cmp::Ordering::Equal {
|
||||||
|
return Err(RPCError::protocol(
|
||||||
|
"InspectValue descriptor does not match last descriptor",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PeerInfo::validate_vec(&mut self.peers, validate_context.crypto.clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn seqs(&self) -> &[ValueSeqNum] {
|
||||||
|
// &self.seqs
|
||||||
|
// }
|
||||||
|
// pub fn peers(&self) -> &[PeerInfo] {
|
||||||
|
// &self.peers
|
||||||
|
// }
|
||||||
|
// pub fn descriptor(&self) -> Option<&SignedValueDescriptor> {
|
||||||
|
// self.descriptor.as_ref()
|
||||||
|
// }
|
||||||
|
pub fn destructure(
|
||||||
|
self,
|
||||||
|
) -> (
|
||||||
|
Vec<ValueSeqNum>,
|
||||||
|
Vec<PeerInfo>,
|
||||||
|
Option<SignedValueDescriptor>,
|
||||||
|
) {
|
||||||
|
(self.seqs, self.peers, self.descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode(
|
||||||
|
reader: &veilid_capnp::operation_inspect_value_a::Reader,
|
||||||
|
) -> Result<Self, RPCError> {
|
||||||
|
let seqs = if reader.has_seqs() {
|
||||||
|
let seqs_reader = reader.get_seqs().map_err(RPCError::protocol)?;
|
||||||
|
if seqs_reader.len() as usize > MAX_INSPECT_VALUE_A_SEQS_LEN {
|
||||||
|
return Err(RPCError::protocol(
|
||||||
|
"decoded InspectValueA seqs length too long",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let Some(seqs) = seqs_reader.as_slice().map(|s| s.to_vec()) else {
|
||||||
|
return Err(RPCError::protocol("invalid decoded InspectValueA seqs"));
|
||||||
|
};
|
||||||
|
seqs
|
||||||
|
} else {
|
||||||
|
return Err(RPCError::protocol("missing decoded InspectValueA seqs"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let peers_reader = reader.get_peers().map_err(RPCError::protocol)?;
|
||||||
|
if peers_reader.len() as usize > MAX_INSPECT_VALUE_A_PEERS_LEN {
|
||||||
|
return Err(RPCError::protocol(
|
||||||
|
"decoded InspectValueA peers length too long",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut peers = Vec::<PeerInfo>::with_capacity(
|
||||||
|
peers_reader
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.map_err(RPCError::map_internal("too many peers"))?,
|
||||||
|
);
|
||||||
|
for p in peers_reader.iter() {
|
||||||
|
let peer_info = decode_peer_info(&p)?;
|
||||||
|
peers.push(peer_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
let descriptor = if reader.has_descriptor() {
|
||||||
|
let d_reader = reader.get_descriptor().map_err(RPCError::protocol)?;
|
||||||
|
let descriptor = decode_signed_value_descriptor(&d_reader)?;
|
||||||
|
Some(descriptor)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
seqs,
|
||||||
|
peers,
|
||||||
|
descriptor,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn encode(
|
||||||
|
&self,
|
||||||
|
builder: &mut veilid_capnp::operation_inspect_value_a::Builder,
|
||||||
|
) -> Result<(), RPCError> {
|
||||||
|
let mut seqs_builder = builder.reborrow().init_seqs(
|
||||||
|
self.seqs
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.map_err(RPCError::map_internal("invalid seqs list length"))?,
|
||||||
|
);
|
||||||
|
for (i, seq) in self.seqs.iter().enumerate() {
|
||||||
|
seqs_builder.set(i as u32, *seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut peers_builder = builder.reborrow().init_peers(
|
||||||
|
self.peers
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.map_err(RPCError::map_internal("invalid peers list length"))?,
|
||||||
|
);
|
||||||
|
for (i, peer) in self.peers.iter().enumerate() {
|
||||||
|
let mut pi_builder = peers_builder.reborrow().get(i as u32);
|
||||||
|
encode_peer_info(peer, &mut pi_builder)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(descriptor) = &self.descriptor {
|
||||||
|
let mut d_builder = builder.reborrow().init_descriptor();
|
||||||
|
encode_signed_value_descriptor(descriptor, &mut d_builder)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -109,6 +109,8 @@ impl RPCOperationSetValueQ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(in crate::rpc_processor) struct RPCOperationSetValueA {
|
pub(in crate::rpc_processor) struct RPCOperationSetValueA {
|
||||||
set: bool,
|
set: bool,
|
||||||
@ -139,13 +141,13 @@ impl RPCOperationSetValueA {
|
|||||||
panic!("Wrong context type for SetValueA");
|
panic!("Wrong context type for SetValueA");
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(value) = &self.value {
|
|
||||||
// Ensure the descriptor itself validates
|
// Ensure the descriptor itself validates
|
||||||
set_value_context
|
set_value_context
|
||||||
.descriptor
|
.descriptor
|
||||||
.validate(set_value_context.vcrypto.clone())
|
.validate(set_value_context.vcrypto.clone())
|
||||||
.map_err(RPCError::protocol)?;
|
.map_err(RPCError::protocol)?;
|
||||||
|
|
||||||
|
if let Some(value) = &self.value {
|
||||||
// And the signed value data
|
// And the signed value data
|
||||||
value
|
value
|
||||||
.validate(
|
.validate(
|
||||||
|
@ -65,6 +65,8 @@ impl RPCOperationStartTunnelQ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg(feature = "unstable-tunnels")]
|
#[cfg(feature = "unstable-tunnels")]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(in crate::rpc_processor) enum RPCOperationStartTunnelA {
|
pub(in crate::rpc_processor) enum RPCOperationStartTunnelA {
|
||||||
|
@ -42,6 +42,8 @@ impl RPCOperationStatusQ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(in crate::rpc_processor) struct RPCOperationStatusA {
|
pub(in crate::rpc_processor) struct RPCOperationStatusA {
|
||||||
node_status: Option<NodeStatus>,
|
node_status: Option<NodeStatus>,
|
||||||
|
@ -42,6 +42,8 @@ impl RPCOperationSupplyBlockQ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(in crate::rpc_processor) struct RPCOperationSupplyBlockA {
|
pub(in crate::rpc_processor) struct RPCOperationSupplyBlockA {
|
||||||
expiration: u64,
|
expiration: u64,
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::storage_manager::SignedValueData;
|
use crate::storage_manager::SignedValueData;
|
||||||
|
|
||||||
const MAX_VALUE_CHANGED_SUBKEYS_LEN: usize = 512;
|
const MAX_VALUE_CHANGED_SUBKEY_RANGES_LEN: usize = 512;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(in crate::rpc_processor) struct RPCOperationValueChanged {
|
pub(in crate::rpc_processor) struct RPCOperationValueChanged {
|
||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkeys: ValueSubkeyRangeSet,
|
subkeys: ValueSubkeyRangeSet,
|
||||||
count: u32,
|
count: u32,
|
||||||
|
watch_id: u64,
|
||||||
value: SignedValueData,
|
value: SignedValueData,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,26 +18,33 @@ impl RPCOperationValueChanged {
|
|||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkeys: ValueSubkeyRangeSet,
|
subkeys: ValueSubkeyRangeSet,
|
||||||
count: u32,
|
count: u32,
|
||||||
|
watch_id: u64,
|
||||||
value: SignedValueData,
|
value: SignedValueData,
|
||||||
) -> Result<Self, RPCError> {
|
) -> Result<Self, RPCError> {
|
||||||
// Needed because RangeSetBlaze uses different types here all the time
|
if subkeys.ranges_len() > MAX_VALUE_CHANGED_SUBKEY_RANGES_LEN {
|
||||||
#[allow(clippy::unnecessary_cast)]
|
return Err(RPCError::protocol(
|
||||||
let subkeys_len = subkeys.ranges_len() as usize;
|
"ValueChanged subkey ranges length too long",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if subkeys_len > MAX_VALUE_CHANGED_SUBKEYS_LEN {
|
if watch_id == 0 {
|
||||||
return Err(RPCError::protocol("ValueChanged subkeys length too long"));
|
return Err(RPCError::protocol("ValueChanged needs a nonzero watch id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
key,
|
key,
|
||||||
subkeys,
|
subkeys,
|
||||||
count,
|
count,
|
||||||
|
watch_id,
|
||||||
value,
|
value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate(&mut self, _validate_context: &RPCValidateContext) -> Result<(), RPCError> {
|
pub fn validate(&mut self, _validate_context: &RPCValidateContext) -> Result<(), RPCError> {
|
||||||
// validation must be done by storage manager as this is more complicated
|
if self.watch_id == 0 {
|
||||||
|
return Err(RPCError::protocol("ValueChanged does not have a valid id"));
|
||||||
|
}
|
||||||
|
// further validation must be done by storage manager as this is more complicated
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,14 +63,25 @@ impl RPCOperationValueChanged {
|
|||||||
self.count
|
self.count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn watch_id(&self) -> u64 {
|
||||||
|
self.watch_id
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn value(&self) -> &SignedValueData {
|
pub fn value(&self) -> &SignedValueData {
|
||||||
&self.value
|
&self.value
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn destructure(self) -> (TypedKey, ValueSubkeyRangeSet, u32, SignedValueData) {
|
pub fn destructure(self) -> (TypedKey, ValueSubkeyRangeSet, u32, u64, SignedValueData) {
|
||||||
(self.key, self.subkeys, self.count, self.value)
|
(
|
||||||
|
self.key,
|
||||||
|
self.subkeys,
|
||||||
|
self.count,
|
||||||
|
self.watch_id,
|
||||||
|
self.value,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode(
|
pub fn decode(
|
||||||
@ -72,8 +91,10 @@ impl RPCOperationValueChanged {
|
|||||||
let key = decode_typed_key(&k_reader)?;
|
let key = decode_typed_key(&k_reader)?;
|
||||||
|
|
||||||
let sk_reader = reader.get_subkeys().map_err(RPCError::protocol)?;
|
let sk_reader = reader.get_subkeys().map_err(RPCError::protocol)?;
|
||||||
if sk_reader.len() as usize > MAX_VALUE_CHANGED_SUBKEYS_LEN {
|
if sk_reader.len() as usize > MAX_VALUE_CHANGED_SUBKEY_RANGES_LEN {
|
||||||
return Err(RPCError::protocol("ValueChanged subkeys length too long"));
|
return Err(RPCError::protocol(
|
||||||
|
"ValueChanged subkey ranges length too long",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut subkeys = ValueSubkeyRangeSet::new();
|
let mut subkeys = ValueSubkeyRangeSet::new();
|
||||||
@ -93,11 +114,14 @@ impl RPCOperationValueChanged {
|
|||||||
}
|
}
|
||||||
let count = reader.get_count();
|
let count = reader.get_count();
|
||||||
let v_reader = reader.get_value().map_err(RPCError::protocol)?;
|
let v_reader = reader.get_value().map_err(RPCError::protocol)?;
|
||||||
|
let watch_id = reader.get_watch_id();
|
||||||
let value = decode_signed_value_data(&v_reader)?;
|
let value = decode_signed_value_data(&v_reader)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
key,
|
key,
|
||||||
subkeys,
|
subkeys,
|
||||||
count,
|
count,
|
||||||
|
watch_id,
|
||||||
value,
|
value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -121,6 +145,7 @@ impl RPCOperationValueChanged {
|
|||||||
}
|
}
|
||||||
|
|
||||||
builder.set_count(self.count);
|
builder.set_count(self.count);
|
||||||
|
builder.set_watch_id(self.watch_id);
|
||||||
|
|
||||||
let mut v_builder = builder.reborrow().init_value();
|
let mut v_builder = builder.reborrow().init_value();
|
||||||
encode_signed_value_data(&self.value, &mut v_builder)?;
|
encode_signed_value_data(&self.value, &mut v_builder)?;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const MAX_WATCH_VALUE_Q_SUBKEYS_LEN: usize = 512;
|
const MAX_WATCH_VALUE_Q_SUBKEY_RANGES_LEN: usize = 512;
|
||||||
const MAX_WATCH_VALUE_A_PEERS_LEN: usize = 20;
|
const MAX_WATCH_VALUE_A_PEERS_LEN: usize = 20;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -9,6 +9,7 @@ pub(in crate::rpc_processor) struct RPCOperationWatchValueQ {
|
|||||||
subkeys: ValueSubkeyRangeSet,
|
subkeys: ValueSubkeyRangeSet,
|
||||||
expiration: u64,
|
expiration: u64,
|
||||||
count: u32,
|
count: u32,
|
||||||
|
watch_id: Option<u64>,
|
||||||
watcher: PublicKey,
|
watcher: PublicKey,
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
}
|
}
|
||||||
@ -20,18 +21,20 @@ impl RPCOperationWatchValueQ {
|
|||||||
subkeys: ValueSubkeyRangeSet,
|
subkeys: ValueSubkeyRangeSet,
|
||||||
expiration: u64,
|
expiration: u64,
|
||||||
count: u32,
|
count: u32,
|
||||||
|
watch_id: Option<u64>,
|
||||||
watcher: KeyPair,
|
watcher: KeyPair,
|
||||||
vcrypto: CryptoSystemVersion,
|
vcrypto: CryptoSystemVersion,
|
||||||
) -> Result<Self, RPCError> {
|
) -> Result<Self, RPCError> {
|
||||||
// Needed because RangeSetBlaze uses different types here all the time
|
if subkeys.ranges_len() > MAX_WATCH_VALUE_Q_SUBKEY_RANGES_LEN {
|
||||||
#[allow(clippy::unnecessary_cast)]
|
|
||||||
let subkeys_len = subkeys.ranges_len() as usize;
|
|
||||||
|
|
||||||
if subkeys_len > MAX_WATCH_VALUE_Q_SUBKEYS_LEN {
|
|
||||||
return Err(RPCError::protocol("WatchValueQ subkeys length too long"));
|
return Err(RPCError::protocol("WatchValueQ subkeys length too long"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let signature_data = Self::make_signature_data(&key, &subkeys, expiration, count);
|
// Count is zero means cancelling, so there should always be a watch id in this case
|
||||||
|
if count == 0 && watch_id.is_none() {
|
||||||
|
return Err(RPCError::protocol("can't cancel zero watch id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let signature_data = Self::make_signature_data(&key, &subkeys, expiration, count, watch_id);
|
||||||
let signature = vcrypto
|
let signature = vcrypto
|
||||||
.sign(&watcher.key, &watcher.secret, &signature_data)
|
.sign(&watcher.key, &watcher.secret, &signature_data)
|
||||||
.map_err(RPCError::protocol)?;
|
.map_err(RPCError::protocol)?;
|
||||||
@ -41,6 +44,7 @@ impl RPCOperationWatchValueQ {
|
|||||||
subkeys,
|
subkeys,
|
||||||
expiration,
|
expiration,
|
||||||
count,
|
count,
|
||||||
|
watch_id,
|
||||||
watcher: watcher.key,
|
watcher: watcher.key,
|
||||||
signature,
|
signature,
|
||||||
})
|
})
|
||||||
@ -52,12 +56,12 @@ impl RPCOperationWatchValueQ {
|
|||||||
subkeys: &ValueSubkeyRangeSet,
|
subkeys: &ValueSubkeyRangeSet,
|
||||||
expiration: u64,
|
expiration: u64,
|
||||||
count: u32,
|
count: u32,
|
||||||
|
watch_id: Option<u64>,
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
// Needed because RangeSetBlaze uses different types here all the time
|
let subkeys_ranges_len = subkeys.ranges_len();
|
||||||
#[allow(clippy::unnecessary_cast)]
|
|
||||||
let subkeys_len = subkeys.ranges_len() as usize;
|
|
||||||
|
|
||||||
let mut sig_data = Vec::with_capacity(PUBLIC_KEY_LENGTH + 4 + (subkeys_len * 8) + 8 + 4);
|
let mut sig_data =
|
||||||
|
Vec::with_capacity(PUBLIC_KEY_LENGTH + 4 + (subkeys_ranges_len * 8) + 8 + 8);
|
||||||
sig_data.extend_from_slice(&key.kind.0);
|
sig_data.extend_from_slice(&key.kind.0);
|
||||||
sig_data.extend_from_slice(&key.value.bytes);
|
sig_data.extend_from_slice(&key.value.bytes);
|
||||||
for sk in subkeys.ranges() {
|
for sk in subkeys.ranges() {
|
||||||
@ -66,6 +70,9 @@ impl RPCOperationWatchValueQ {
|
|||||||
}
|
}
|
||||||
sig_data.extend_from_slice(&expiration.to_le_bytes());
|
sig_data.extend_from_slice(&expiration.to_le_bytes());
|
||||||
sig_data.extend_from_slice(&count.to_le_bytes());
|
sig_data.extend_from_slice(&count.to_le_bytes());
|
||||||
|
if let Some(watch_id) = watch_id {
|
||||||
|
sig_data.extend_from_slice(&watch_id.to_le_bytes());
|
||||||
|
}
|
||||||
sig_data
|
sig_data
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,11 +81,22 @@ impl RPCOperationWatchValueQ {
|
|||||||
return Err(RPCError::protocol("unsupported cryptosystem"));
|
return Err(RPCError::protocol("unsupported cryptosystem"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let sig_data =
|
let sig_data = Self::make_signature_data(
|
||||||
Self::make_signature_data(&self.key, &self.subkeys, self.expiration, self.count);
|
&self.key,
|
||||||
|
&self.subkeys,
|
||||||
|
self.expiration,
|
||||||
|
self.count,
|
||||||
|
self.watch_id,
|
||||||
|
);
|
||||||
vcrypto
|
vcrypto
|
||||||
.verify(&self.watcher, &sig_data, &self.signature)
|
.verify(&self.watcher, &sig_data, &self.signature)
|
||||||
.map_err(RPCError::protocol)?;
|
.map_err(RPCError::protocol)?;
|
||||||
|
|
||||||
|
// Count is zero means cancelling, so there should always be a watch id in this case
|
||||||
|
if self.count == 0 && self.watch_id.is_none() {
|
||||||
|
return Err(RPCError::protocol("can't cancel zero watch id"));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +120,11 @@ impl RPCOperationWatchValueQ {
|
|||||||
self.count
|
self.count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn watch_id(&self) -> Option<u64> {
|
||||||
|
self.watch_id
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn watcher(&self) -> &PublicKey {
|
pub fn watcher(&self) -> &PublicKey {
|
||||||
&self.watcher
|
&self.watcher
|
||||||
@ -118,6 +141,7 @@ impl RPCOperationWatchValueQ {
|
|||||||
ValueSubkeyRangeSet,
|
ValueSubkeyRangeSet,
|
||||||
u64,
|
u64,
|
||||||
u32,
|
u32,
|
||||||
|
Option<u64>,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
Signature,
|
Signature,
|
||||||
) {
|
) {
|
||||||
@ -126,6 +150,7 @@ impl RPCOperationWatchValueQ {
|
|||||||
self.subkeys,
|
self.subkeys,
|
||||||
self.expiration,
|
self.expiration,
|
||||||
self.count,
|
self.count,
|
||||||
|
self.watch_id,
|
||||||
self.watcher,
|
self.watcher,
|
||||||
self.signature,
|
self.signature,
|
||||||
)
|
)
|
||||||
@ -138,8 +163,8 @@ impl RPCOperationWatchValueQ {
|
|||||||
let key = decode_typed_key(&k_reader)?;
|
let key = decode_typed_key(&k_reader)?;
|
||||||
|
|
||||||
let sk_reader = reader.get_subkeys().map_err(RPCError::protocol)?;
|
let sk_reader = reader.get_subkeys().map_err(RPCError::protocol)?;
|
||||||
if sk_reader.len() as usize > MAX_WATCH_VALUE_Q_SUBKEYS_LEN {
|
if sk_reader.len() as usize > MAX_WATCH_VALUE_Q_SUBKEY_RANGES_LEN {
|
||||||
return Err(RPCError::protocol("WatchValueQ subkeys length too long"));
|
return Err(RPCError::protocol("WatchValueQ too many subkey ranges"));
|
||||||
}
|
}
|
||||||
let mut subkeys = ValueSubkeyRangeSet::new();
|
let mut subkeys = ValueSubkeyRangeSet::new();
|
||||||
for skr in sk_reader.iter() {
|
for skr in sk_reader.iter() {
|
||||||
@ -159,6 +184,11 @@ impl RPCOperationWatchValueQ {
|
|||||||
|
|
||||||
let expiration = reader.get_expiration();
|
let expiration = reader.get_expiration();
|
||||||
let count = reader.get_count();
|
let count = reader.get_count();
|
||||||
|
let watch_id = if reader.get_watch_id() != 0 {
|
||||||
|
Some(reader.get_watch_id())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let w_reader = reader.get_watcher().map_err(RPCError::protocol)?;
|
let w_reader = reader.get_watcher().map_err(RPCError::protocol)?;
|
||||||
let watcher = decode_key256(&w_reader);
|
let watcher = decode_key256(&w_reader);
|
||||||
@ -171,6 +201,7 @@ impl RPCOperationWatchValueQ {
|
|||||||
subkeys,
|
subkeys,
|
||||||
expiration,
|
expiration,
|
||||||
count,
|
count,
|
||||||
|
watch_id,
|
||||||
watcher,
|
watcher,
|
||||||
signature,
|
signature,
|
||||||
})
|
})
|
||||||
@ -196,6 +227,7 @@ impl RPCOperationWatchValueQ {
|
|||||||
}
|
}
|
||||||
builder.set_expiration(self.expiration);
|
builder.set_expiration(self.expiration);
|
||||||
builder.set_count(self.count);
|
builder.set_count(self.count);
|
||||||
|
builder.set_watch_id(self.watch_id.unwrap_or(0u64));
|
||||||
|
|
||||||
let mut w_builder = builder.reborrow().init_watcher();
|
let mut w_builder = builder.reborrow().init_watcher();
|
||||||
encode_key256(&self.watcher, &mut w_builder);
|
encode_key256(&self.watcher, &mut w_builder);
|
||||||
@ -207,19 +239,33 @@ impl RPCOperationWatchValueQ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(in crate::rpc_processor) struct RPCOperationWatchValueA {
|
pub(in crate::rpc_processor) struct RPCOperationWatchValueA {
|
||||||
|
accepted: bool,
|
||||||
expiration: u64,
|
expiration: u64,
|
||||||
peers: Vec<PeerInfo>,
|
peers: Vec<PeerInfo>,
|
||||||
|
watch_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RPCOperationWatchValueA {
|
impl RPCOperationWatchValueA {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn new(expiration: u64, peers: Vec<PeerInfo>) -> Result<Self, RPCError> {
|
pub fn new(
|
||||||
|
accepted: bool,
|
||||||
|
expiration: u64,
|
||||||
|
peers: Vec<PeerInfo>,
|
||||||
|
watch_id: u64,
|
||||||
|
) -> Result<Self, RPCError> {
|
||||||
if peers.len() > MAX_WATCH_VALUE_A_PEERS_LEN {
|
if peers.len() > MAX_WATCH_VALUE_A_PEERS_LEN {
|
||||||
return Err(RPCError::protocol("WatchValueA peers length too long"));
|
return Err(RPCError::protocol("WatchValueA peers length too long"));
|
||||||
}
|
}
|
||||||
Ok(Self { expiration, peers })
|
Ok(Self {
|
||||||
|
accepted,
|
||||||
|
expiration,
|
||||||
|
peers,
|
||||||
|
watch_id,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate(&mut self, validate_context: &RPCValidateContext) -> Result<(), RPCError> {
|
pub fn validate(&mut self, validate_context: &RPCValidateContext) -> Result<(), RPCError> {
|
||||||
@ -227,6 +273,10 @@ impl RPCOperationWatchValueA {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn accepted(&self) -> bool {
|
||||||
|
self.accepted
|
||||||
|
}
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn expiration(&self) -> u64 {
|
pub fn expiration(&self) -> u64 {
|
||||||
self.expiration
|
self.expiration
|
||||||
@ -236,13 +286,18 @@ impl RPCOperationWatchValueA {
|
|||||||
&self.peers
|
&self.peers
|
||||||
}
|
}
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn destructure(self) -> (u64, Vec<PeerInfo>) {
|
pub fn watch_id(&self) -> u64 {
|
||||||
(self.expiration, self.peers)
|
self.watch_id
|
||||||
|
}
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn destructure(self) -> (bool, u64, Vec<PeerInfo>, u64) {
|
||||||
|
(self.accepted, self.expiration, self.peers, self.watch_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode(
|
pub fn decode(
|
||||||
reader: &veilid_capnp::operation_watch_value_a::Reader,
|
reader: &veilid_capnp::operation_watch_value_a::Reader,
|
||||||
) -> Result<Self, RPCError> {
|
) -> Result<Self, RPCError> {
|
||||||
|
let accepted = reader.get_accepted();
|
||||||
let expiration = reader.get_expiration();
|
let expiration = reader.get_expiration();
|
||||||
let peers_reader = reader.get_peers().map_err(RPCError::protocol)?;
|
let peers_reader = reader.get_peers().map_err(RPCError::protocol)?;
|
||||||
if peers_reader.len() as usize > MAX_WATCH_VALUE_A_PEERS_LEN {
|
if peers_reader.len() as usize > MAX_WATCH_VALUE_A_PEERS_LEN {
|
||||||
@ -258,13 +313,20 @@ impl RPCOperationWatchValueA {
|
|||||||
let peer_info = decode_peer_info(&p)?;
|
let peer_info = decode_peer_info(&p)?;
|
||||||
peers.push(peer_info);
|
peers.push(peer_info);
|
||||||
}
|
}
|
||||||
|
let watch_id = reader.get_watch_id();
|
||||||
|
|
||||||
Ok(Self { expiration, peers })
|
Ok(Self {
|
||||||
|
accepted,
|
||||||
|
expiration,
|
||||||
|
peers,
|
||||||
|
watch_id,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
pub fn encode(
|
pub fn encode(
|
||||||
&self,
|
&self,
|
||||||
builder: &mut veilid_capnp::operation_watch_value_a::Builder,
|
builder: &mut veilid_capnp::operation_watch_value_a::Builder,
|
||||||
) -> Result<(), RPCError> {
|
) -> Result<(), RPCError> {
|
||||||
|
builder.set_accepted(self.accepted);
|
||||||
builder.set_expiration(self.expiration);
|
builder.set_expiration(self.expiration);
|
||||||
|
|
||||||
let mut peers_builder = builder.reborrow().init_peers(
|
let mut peers_builder = builder.reborrow().init_peers(
|
||||||
@ -277,6 +339,7 @@ impl RPCOperationWatchValueA {
|
|||||||
let mut pi_builder = peers_builder.reborrow().get(i as u32);
|
let mut pi_builder = peers_builder.reborrow().get(i as u32);
|
||||||
encode_peer_info(peer, &mut pi_builder)?;
|
encode_peer_info(peer, &mut pi_builder)?;
|
||||||
}
|
}
|
||||||
|
builder.set_watch_id(self.watch_id);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ impl RPCQuestion {
|
|||||||
pub fn detail(&self) -> &RPCQuestionDetail {
|
pub fn detail(&self) -> &RPCQuestionDetail {
|
||||||
&self.detail
|
&self.detail
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
pub fn desc(&self) -> &'static str {
|
pub fn desc(&self) -> &'static str {
|
||||||
self.detail.desc()
|
self.detail.desc()
|
||||||
}
|
}
|
||||||
@ -49,6 +50,7 @@ pub(in crate::rpc_processor) enum RPCQuestionDetail {
|
|||||||
GetValueQ(Box<RPCOperationGetValueQ>),
|
GetValueQ(Box<RPCOperationGetValueQ>),
|
||||||
SetValueQ(Box<RPCOperationSetValueQ>),
|
SetValueQ(Box<RPCOperationSetValueQ>),
|
||||||
WatchValueQ(Box<RPCOperationWatchValueQ>),
|
WatchValueQ(Box<RPCOperationWatchValueQ>),
|
||||||
|
InspectValueQ(Box<RPCOperationInspectValueQ>),
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
SupplyBlockQ(Box<RPCOperationSupplyBlockQ>),
|
SupplyBlockQ(Box<RPCOperationSupplyBlockQ>),
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
@ -62,6 +64,7 @@ pub(in crate::rpc_processor) enum RPCQuestionDetail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RPCQuestionDetail {
|
impl RPCQuestionDetail {
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
pub fn desc(&self) -> &'static str {
|
pub fn desc(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
RPCQuestionDetail::StatusQ(_) => "StatusQ",
|
RPCQuestionDetail::StatusQ(_) => "StatusQ",
|
||||||
@ -70,6 +73,7 @@ impl RPCQuestionDetail {
|
|||||||
RPCQuestionDetail::GetValueQ(_) => "GetValueQ",
|
RPCQuestionDetail::GetValueQ(_) => "GetValueQ",
|
||||||
RPCQuestionDetail::SetValueQ(_) => "SetValueQ",
|
RPCQuestionDetail::SetValueQ(_) => "SetValueQ",
|
||||||
RPCQuestionDetail::WatchValueQ(_) => "WatchValueQ",
|
RPCQuestionDetail::WatchValueQ(_) => "WatchValueQ",
|
||||||
|
RPCQuestionDetail::InspectValueQ(_) => "InspectValueQ",
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
RPCQuestionDetail::SupplyBlockQ(_) => "SupplyBlockQ",
|
RPCQuestionDetail::SupplyBlockQ(_) => "SupplyBlockQ",
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
@ -90,6 +94,7 @@ impl RPCQuestionDetail {
|
|||||||
RPCQuestionDetail::GetValueQ(r) => r.validate(validate_context),
|
RPCQuestionDetail::GetValueQ(r) => r.validate(validate_context),
|
||||||
RPCQuestionDetail::SetValueQ(r) => r.validate(validate_context),
|
RPCQuestionDetail::SetValueQ(r) => r.validate(validate_context),
|
||||||
RPCQuestionDetail::WatchValueQ(r) => r.validate(validate_context),
|
RPCQuestionDetail::WatchValueQ(r) => r.validate(validate_context),
|
||||||
|
RPCQuestionDetail::InspectValueQ(r) => r.validate(validate_context),
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
RPCQuestionDetail::SupplyBlockQ(r) => r.validate(validate_context),
|
RPCQuestionDetail::SupplyBlockQ(r) => r.validate(validate_context),
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
@ -138,6 +143,11 @@ impl RPCQuestionDetail {
|
|||||||
let out = RPCOperationWatchValueQ::decode(&op_reader)?;
|
let out = RPCOperationWatchValueQ::decode(&op_reader)?;
|
||||||
RPCQuestionDetail::WatchValueQ(Box::new(out))
|
RPCQuestionDetail::WatchValueQ(Box::new(out))
|
||||||
}
|
}
|
||||||
|
veilid_capnp::question::detail::InspectValueQ(r) => {
|
||||||
|
let op_reader = r.map_err(RPCError::protocol)?;
|
||||||
|
let out = RPCOperationInspectValueQ::decode(&op_reader)?;
|
||||||
|
RPCQuestionDetail::InspectValueQ(Box::new(out))
|
||||||
|
}
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
veilid_capnp::question::detail::SupplyBlockQ(r) => {
|
veilid_capnp::question::detail::SupplyBlockQ(r) => {
|
||||||
let op_reader = r.map_err(RPCError::protocol)?;
|
let op_reader = r.map_err(RPCError::protocol)?;
|
||||||
@ -184,6 +194,9 @@ impl RPCQuestionDetail {
|
|||||||
RPCQuestionDetail::WatchValueQ(d) => {
|
RPCQuestionDetail::WatchValueQ(d) => {
|
||||||
d.encode(&mut builder.reborrow().init_watch_value_q())
|
d.encode(&mut builder.reborrow().init_watch_value_q())
|
||||||
}
|
}
|
||||||
|
RPCQuestionDetail::InspectValueQ(d) => {
|
||||||
|
d.encode(&mut builder.reborrow().init_inspect_value_q())
|
||||||
|
}
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
RPCQuestionDetail::SupplyBlockQ(d) => {
|
RPCQuestionDetail::SupplyBlockQ(d) => {
|
||||||
d.encode(&mut builder.reborrow().init_supply_block_q())
|
d.encode(&mut builder.reborrow().init_supply_block_q())
|
||||||
|
@ -15,6 +15,7 @@ impl RPCStatement {
|
|||||||
pub fn detail(&self) -> &RPCStatementDetail {
|
pub fn detail(&self) -> &RPCStatementDetail {
|
||||||
&self.detail
|
&self.detail
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
pub fn desc(&self) -> &'static str {
|
pub fn desc(&self) -> &'static str {
|
||||||
self.detail.desc()
|
self.detail.desc()
|
||||||
}
|
}
|
||||||
@ -43,6 +44,7 @@ pub(in crate::rpc_processor) enum RPCStatementDetail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RPCStatementDetail {
|
impl RPCStatementDetail {
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
pub fn desc(&self) -> &'static str {
|
pub fn desc(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
RPCStatementDetail::ValidateDialInfo(_) => "ValidateDialInfo",
|
RPCStatementDetail::ValidateDialInfo(_) => "ValidateDialInfo",
|
||||||
|
@ -8,6 +8,49 @@ where
|
|||||||
result: Option<Result<R, RPCError>>,
|
result: Option<Result<R, RPCError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub(crate) enum FanoutResultKind {
|
||||||
|
Timeout,
|
||||||
|
Finished,
|
||||||
|
Exhausted,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct FanoutResult {
|
||||||
|
pub kind: FanoutResultKind,
|
||||||
|
pub value_nodes: Vec<NodeRef>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn debug_fanout_result(result: &FanoutResult) -> String {
|
||||||
|
let kc = match result.kind {
|
||||||
|
FanoutResultKind::Timeout => "T",
|
||||||
|
FanoutResultKind::Finished => "F",
|
||||||
|
FanoutResultKind::Exhausted => "E",
|
||||||
|
};
|
||||||
|
format!("{}:{}", kc, result.value_nodes.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn debug_fanout_results(results: &[FanoutResult]) -> String {
|
||||||
|
let mut col = 0;
|
||||||
|
let mut out = String::new();
|
||||||
|
let mut left = results.len();
|
||||||
|
for r in results {
|
||||||
|
if col == 0 {
|
||||||
|
out += " ";
|
||||||
|
}
|
||||||
|
let sr = debug_fanout_result(r);
|
||||||
|
out += &sr;
|
||||||
|
out += ",";
|
||||||
|
col += 1;
|
||||||
|
left -= 1;
|
||||||
|
if col == 32 && left != 0 {
|
||||||
|
col = 0;
|
||||||
|
out += "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) type FanoutCallReturnType = RPCNetworkResult<Vec<PeerInfo>>;
|
pub(crate) type FanoutCallReturnType = RPCNetworkResult<Vec<PeerInfo>>;
|
||||||
pub(crate) type FanoutNodeInfoFilter = Arc<dyn Fn(&[TypedKey], &NodeInfo) -> bool + Send + Sync>;
|
pub(crate) type FanoutNodeInfoFilter = Arc<dyn Fn(&[TypedKey], &NodeInfo) -> bool + Send + Sync>;
|
||||||
|
|
||||||
@ -158,8 +201,7 @@ where
|
|||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
Ok(x) => {
|
Ok(x) => {
|
||||||
// Call failed, node will not be considered again
|
// Call failed, node will not be considered again
|
||||||
#[cfg(feature = "network-result-extra")]
|
log_network_result!(debug "Fanout result {}: {:?}", &next_node, x);
|
||||||
log_rpc!(debug "Fanout result {}: {:?}", &next_node, x);
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Error happened, abort everything and return the error
|
// Error happened, abort everything and return the error
|
||||||
|
@ -8,6 +8,7 @@ mod rpc_app_message;
|
|||||||
mod rpc_error;
|
mod rpc_error;
|
||||||
mod rpc_find_node;
|
mod rpc_find_node;
|
||||||
mod rpc_get_value;
|
mod rpc_get_value;
|
||||||
|
mod rpc_inspect_value;
|
||||||
mod rpc_return_receipt;
|
mod rpc_return_receipt;
|
||||||
mod rpc_route;
|
mod rpc_route;
|
||||||
mod rpc_set_value;
|
mod rpc_set_value;
|
||||||
@ -168,14 +169,14 @@ pub(crate) struct RPCMessage {
|
|||||||
opt_sender_nr: Option<NodeRef>,
|
opt_sender_nr: Option<NodeRef>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all, err)]
|
||||||
pub fn builder_to_vec<'a, T>(builder: capnp::message::Builder<T>) -> Result<Vec<u8>, RPCError>
|
pub fn builder_to_vec<'a, T>(builder: capnp::message::Builder<T>) -> Result<Vec<u8>, RPCError>
|
||||||
where
|
where
|
||||||
T: capnp::message::Allocator + 'a,
|
T: capnp::message::Allocator + 'a,
|
||||||
{
|
{
|
||||||
let mut buffer = vec![];
|
let mut buffer = vec![];
|
||||||
capnp::serialize_packed::write_message(&mut buffer, &builder)
|
capnp::serialize_packed::write_message(&mut buffer, &builder)
|
||||||
.map_err(RPCError::protocol)
|
.map_err(RPCError::protocol)?;
|
||||||
.map_err(logthru_rpc!())?;
|
|
||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,7 +374,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
#[instrument(level = "debug", skip_all, err)]
|
#[instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn startup(&self) -> EyreResult<()> {
|
pub async fn startup(&self) -> EyreResult<()> {
|
||||||
debug!("startup rpc processor");
|
log_rpc!(debug "startup rpc processor");
|
||||||
{
|
{
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
|
|
||||||
@ -382,7 +383,7 @@ impl RPCProcessor {
|
|||||||
inner.stop_source = Some(StopSource::new());
|
inner.stop_source = Some(StopSource::new());
|
||||||
|
|
||||||
// spin up N workers
|
// spin up N workers
|
||||||
trace!(
|
log_rpc!(
|
||||||
"Spinning up {} RPC workers",
|
"Spinning up {} RPC workers",
|
||||||
self.unlocked_inner.concurrency
|
self.unlocked_inner.concurrency
|
||||||
);
|
);
|
||||||
@ -408,7 +409,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
#[instrument(level = "debug", skip_all)]
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub async fn shutdown(&self) {
|
pub async fn shutdown(&self) {
|
||||||
debug!("starting rpc processor shutdown");
|
log_rpc!(debug "starting rpc processor shutdown");
|
||||||
|
|
||||||
// Stop storage manager from using us
|
// Stop storage manager from using us
|
||||||
self.storage_manager.set_rpc_processor(None).await;
|
self.storage_manager.set_rpc_processor(None).await;
|
||||||
@ -424,17 +425,17 @@ impl RPCProcessor {
|
|||||||
// drop the stop
|
// drop the stop
|
||||||
drop(inner.stop_source.take());
|
drop(inner.stop_source.take());
|
||||||
}
|
}
|
||||||
debug!("stopping {} rpc worker tasks", unord.len());
|
log_rpc!(debug "stopping {} rpc worker tasks", unord.len());
|
||||||
|
|
||||||
// Wait for them to complete
|
// Wait for them to complete
|
||||||
while unord.next().await.is_some() {}
|
while unord.next().await.is_some() {}
|
||||||
|
|
||||||
debug!("resetting rpc processor state");
|
log_rpc!(debug "resetting rpc processor state");
|
||||||
|
|
||||||
// Release the rpc processor
|
// Release the rpc processor
|
||||||
*self.inner.lock() = Self::new_inner();
|
*self.inner.lock() = Self::new_inner();
|
||||||
|
|
||||||
debug!("finished rpc processor shutdown");
|
log_rpc!(debug "finished rpc processor shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
@ -470,6 +471,11 @@ impl RPCProcessor {
|
|||||||
) -> TimeoutOr<Result<Option<NodeRef>, RPCError>> {
|
) -> TimeoutOr<Result<Option<NodeRef>, RPCError>> {
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
|
|
||||||
|
// Ignore own node
|
||||||
|
if routing_table.matches_own_node_id(&[node_id]) {
|
||||||
|
return TimeoutOr::Value(Err(RPCError::network("can't search for own node id")));
|
||||||
|
}
|
||||||
|
|
||||||
// Routine to call to generate fanout
|
// Routine to call to generate fanout
|
||||||
let call_routine = |next_node: NodeRef| {
|
let call_routine = |next_node: NodeRef| {
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
@ -610,6 +616,20 @@ impl RPCProcessor {
|
|||||||
// Reply received
|
// Reply received
|
||||||
let recv_ts = get_aligned_timestamp();
|
let recv_ts = get_aligned_timestamp();
|
||||||
|
|
||||||
|
// Ensure the reply comes over the private route that was requested
|
||||||
|
if let Some(reply_private_route) = waitable_reply.reply_private_route {
|
||||||
|
match &rpcreader.header.detail {
|
||||||
|
RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => {
|
||||||
|
return Err(RPCError::protocol("should have received reply over private route"));
|
||||||
|
}
|
||||||
|
RPCMessageHeaderDetail::PrivateRouted(pr) => {
|
||||||
|
if pr.private_route != reply_private_route {
|
||||||
|
return Err(RPCError::protocol("received reply over the wrong private route"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Record answer received
|
// Record answer received
|
||||||
self.record_answer_received(
|
self.record_answer_received(
|
||||||
waitable_reply.send_ts,
|
waitable_reply.send_ts,
|
||||||
@ -918,12 +938,12 @@ impl RPCProcessor {
|
|||||||
// If safety route was in use, record failure to send there
|
// If safety route was in use, record failure to send there
|
||||||
if let Some(sr_pubkey) = &safety_route {
|
if let Some(sr_pubkey) = &safety_route {
|
||||||
let rss = self.routing_table.route_spec_store();
|
let rss = self.routing_table.route_spec_store();
|
||||||
rss.with_route_stats(send_ts, sr_pubkey, |s| s.record_send_failed());
|
rss.with_route_stats_mut(send_ts, sr_pubkey, |s| s.record_send_failed());
|
||||||
} else {
|
} else {
|
||||||
// If no safety route was in use, then it's the private route's fault if we have one
|
// If no safety route was in use, then it's the private route's fault if we have one
|
||||||
if let Some(pr_pubkey) = &remote_private_route {
|
if let Some(pr_pubkey) = &remote_private_route {
|
||||||
let rss = self.routing_table.route_spec_store();
|
let rss = self.routing_table.route_spec_store();
|
||||||
rss.with_route_stats(send_ts, pr_pubkey, |s| s.record_send_failed());
|
rss.with_route_stats_mut(send_ts, pr_pubkey, |s| s.record_send_failed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -952,19 +972,19 @@ impl RPCProcessor {
|
|||||||
// If safety route was used, record question lost there
|
// If safety route was used, record question lost there
|
||||||
if let Some(sr_pubkey) = &safety_route {
|
if let Some(sr_pubkey) = &safety_route {
|
||||||
let rss = self.routing_table.route_spec_store();
|
let rss = self.routing_table.route_spec_store();
|
||||||
rss.with_route_stats(send_ts, sr_pubkey, |s| {
|
rss.with_route_stats_mut(send_ts, sr_pubkey, |s| {
|
||||||
s.record_question_lost();
|
s.record_question_lost();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// If remote private route was used, record question lost there
|
// If remote private route was used, record question lost there
|
||||||
if let Some(rpr_pubkey) = &remote_private_route {
|
if let Some(rpr_pubkey) = &remote_private_route {
|
||||||
rss.with_route_stats(send_ts, rpr_pubkey, |s| {
|
rss.with_route_stats_mut(send_ts, rpr_pubkey, |s| {
|
||||||
s.record_question_lost();
|
s.record_question_lost();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// If private route was used, record question lost there
|
// If private route was used, record question lost there
|
||||||
if let Some(pr_pubkey) = &private_route {
|
if let Some(pr_pubkey) = &private_route {
|
||||||
rss.with_route_stats(send_ts, pr_pubkey, |s| {
|
rss.with_route_stats_mut(send_ts, pr_pubkey, |s| {
|
||||||
s.record_question_lost();
|
s.record_question_lost();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -998,7 +1018,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// If safety route was used, record send there
|
// If safety route was used, record send there
|
||||||
if let Some(sr_pubkey) = &safety_route {
|
if let Some(sr_pubkey) = &safety_route {
|
||||||
rss.with_route_stats(send_ts, sr_pubkey, |s| {
|
rss.with_route_stats_mut(send_ts, sr_pubkey, |s| {
|
||||||
s.record_sent(send_ts, bytes);
|
s.record_sent(send_ts, bytes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1006,7 +1026,7 @@ impl RPCProcessor {
|
|||||||
// If remote private route was used, record send there
|
// If remote private route was used, record send there
|
||||||
if let Some(pr_pubkey) = &remote_private_route {
|
if let Some(pr_pubkey) = &remote_private_route {
|
||||||
let rss = self.routing_table.route_spec_store();
|
let rss = self.routing_table.route_spec_store();
|
||||||
rss.with_route_stats(send_ts, pr_pubkey, |s| {
|
rss.with_route_stats_mut(send_ts, pr_pubkey, |s| {
|
||||||
s.record_sent(send_ts, bytes);
|
s.record_sent(send_ts, bytes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1039,7 +1059,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// If safety route was used, record route there
|
// If safety route was used, record route there
|
||||||
if let Some(sr_pubkey) = &safety_route {
|
if let Some(sr_pubkey) = &safety_route {
|
||||||
rss.with_route_stats(send_ts, sr_pubkey, |s| {
|
rss.with_route_stats_mut(send_ts, sr_pubkey, |s| {
|
||||||
// If we received an answer, the safety route we sent over can be considered tested
|
// If we received an answer, the safety route we sent over can be considered tested
|
||||||
s.record_tested(recv_ts);
|
s.record_tested(recv_ts);
|
||||||
|
|
||||||
@ -1050,7 +1070,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// If local private route was used, record route there
|
// If local private route was used, record route there
|
||||||
if let Some(pr_pubkey) = &reply_private_route {
|
if let Some(pr_pubkey) = &reply_private_route {
|
||||||
rss.with_route_stats(send_ts, pr_pubkey, |s| {
|
rss.with_route_stats_mut(send_ts, pr_pubkey, |s| {
|
||||||
// Record received bytes
|
// Record received bytes
|
||||||
s.record_received(recv_ts, bytes);
|
s.record_received(recv_ts, bytes);
|
||||||
|
|
||||||
@ -1061,7 +1081,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// If remote private route was used, record there
|
// If remote private route was used, record there
|
||||||
if let Some(rpr_pubkey) = &remote_private_route {
|
if let Some(rpr_pubkey) = &remote_private_route {
|
||||||
rss.with_route_stats(send_ts, rpr_pubkey, |s| {
|
rss.with_route_stats_mut(send_ts, rpr_pubkey, |s| {
|
||||||
// Record received bytes
|
// Record received bytes
|
||||||
s.record_received(recv_ts, bytes);
|
s.record_received(recv_ts, bytes);
|
||||||
|
|
||||||
@ -1086,12 +1106,12 @@ impl RPCProcessor {
|
|||||||
// then we must have received with a local private route too, per the design rules
|
// then we must have received with a local private route too, per the design rules
|
||||||
if let Some(sr_pubkey) = &safety_route {
|
if let Some(sr_pubkey) = &safety_route {
|
||||||
let rss = self.routing_table.route_spec_store();
|
let rss = self.routing_table.route_spec_store();
|
||||||
rss.with_route_stats(send_ts, sr_pubkey, |s| {
|
rss.with_route_stats_mut(send_ts, sr_pubkey, |s| {
|
||||||
s.record_latency(total_latency / 2u64);
|
s.record_latency(total_latency / 2u64);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if let Some(pr_pubkey) = &reply_private_route {
|
if let Some(pr_pubkey) = &reply_private_route {
|
||||||
rss.with_route_stats(send_ts, pr_pubkey, |s| {
|
rss.with_route_stats_mut(send_ts, pr_pubkey, |s| {
|
||||||
s.record_latency(total_latency / 2u64);
|
s.record_latency(total_latency / 2u64);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1117,7 +1137,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// This may record nothing if the remote safety route is not also
|
// This may record nothing if the remote safety route is not also
|
||||||
// a remote private route that been imported, but that's okay
|
// a remote private route that been imported, but that's okay
|
||||||
rss.with_route_stats(recv_ts, &d.remote_safety_route, |s| {
|
rss.with_route_stats_mut(recv_ts, &d.remote_safety_route, |s| {
|
||||||
s.record_received(recv_ts, bytes);
|
s.record_received(recv_ts, bytes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1129,12 +1149,12 @@ impl RPCProcessor {
|
|||||||
// a remote private route that been imported, but that's okay
|
// a remote private route that been imported, but that's okay
|
||||||
// it could also be a node id if no remote safety route was used
|
// it could also be a node id if no remote safety route was used
|
||||||
// in which case this also will do nothing
|
// in which case this also will do nothing
|
||||||
rss.with_route_stats(recv_ts, &d.remote_safety_route, |s| {
|
rss.with_route_stats_mut(recv_ts, &d.remote_safety_route, |s| {
|
||||||
s.record_received(recv_ts, bytes);
|
s.record_received(recv_ts, bytes);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Record for our local private route we received over
|
// Record for our local private route we received over
|
||||||
rss.with_route_stats(recv_ts, &d.private_route, |s| {
|
rss.with_route_stats_mut(recv_ts, &d.private_route, |s| {
|
||||||
s.record_received(recv_ts, bytes);
|
s.record_received(recv_ts, bytes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1403,6 +1423,7 @@ impl RPCProcessor {
|
|||||||
/// Decoding RPC from the wire
|
/// Decoding RPC from the wire
|
||||||
/// This performs a capnp decode on the data, and if it passes the capnp schema
|
/// This performs a capnp decode on the data, and if it passes the capnp schema
|
||||||
/// it performs the cryptographic validation required to pass the operation up for processing
|
/// it performs the cryptographic validation required to pass the operation up for processing
|
||||||
|
#[instrument(skip_all)]
|
||||||
fn decode_rpc_operation(
|
fn decode_rpc_operation(
|
||||||
&self,
|
&self,
|
||||||
encoded_msg: &RPCMessageEncoded,
|
encoded_msg: &RPCMessageEncoded,
|
||||||
@ -1410,8 +1431,7 @@ impl RPCProcessor {
|
|||||||
let reader = encoded_msg.data.get_reader()?;
|
let reader = encoded_msg.data.get_reader()?;
|
||||||
let op_reader = reader
|
let op_reader = reader
|
||||||
.get_root::<veilid_capnp::operation::Reader>()
|
.get_root::<veilid_capnp::operation::Reader>()
|
||||||
.map_err(RPCError::protocol)
|
.map_err(RPCError::protocol)?;
|
||||||
.map_err(logthru_rpc!())?;
|
|
||||||
let mut operation = RPCOperation::decode(&op_reader)?;
|
let mut operation = RPCOperation::decode(&op_reader)?;
|
||||||
|
|
||||||
// Validate the RPC message
|
// Validate the RPC message
|
||||||
@ -1420,7 +1440,12 @@ impl RPCProcessor {
|
|||||||
Ok(operation)
|
Ok(operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cryptographic RPC validation
|
/// Cryptographic RPC validation and sanitization
|
||||||
|
///
|
||||||
|
/// This code may modify the RPC operation to remove elements that are inappropriate for this node
|
||||||
|
/// or reject the RPC operation entirely. For example, PeerInfo in fanout peer lists may be
|
||||||
|
/// removed if they are deemed inappropriate for this node, without rejecting the entire operation.
|
||||||
|
///
|
||||||
/// We do this as part of the RPC network layer to ensure that any RPC operations that are
|
/// We do this as part of the RPC network layer to ensure that any RPC operations that are
|
||||||
/// processed have already been validated cryptographically and it is not the job of the
|
/// processed have already been validated cryptographically and it is not the job of the
|
||||||
/// caller or receiver. This does not mean the operation is 'semantically correct'. For
|
/// caller or receiver. This does not mean the operation is 'semantically correct'. For
|
||||||
@ -1467,15 +1492,29 @@ impl RPCProcessor {
|
|||||||
let sender_node_id = detail.envelope.get_sender_typed_id();
|
let sender_node_id = detail.envelope.get_sender_typed_id();
|
||||||
|
|
||||||
// Decode and validate the RPC operation
|
// Decode and validate the RPC operation
|
||||||
let operation = match self.decode_rpc_operation(&encoded_msg) {
|
let decode_res = self.decode_rpc_operation(&encoded_msg);
|
||||||
|
let operation = match decode_res {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
match e {
|
||||||
|
// Invalid messages that should be punished
|
||||||
|
RPCError::Protocol(_) | RPCError::InvalidFormat(_) => {
|
||||||
|
log_rpc!(debug "Invalid RPC Operation: {}", e);
|
||||||
|
|
||||||
// Punish nodes that send direct undecodable crap
|
// Punish nodes that send direct undecodable crap
|
||||||
if matches!(e, RPCError::Protocol(_) | RPCError::InvalidFormat(_)) {
|
|
||||||
address_filter.punish_node_id(sender_node_id);
|
address_filter.punish_node_id(sender_node_id);
|
||||||
|
},
|
||||||
|
// Ignored messages that should be dropped
|
||||||
|
RPCError::Ignore(_) | RPCError::Network(_) | RPCError::TryAgain(_) => {
|
||||||
|
log_rpc!(debug "Dropping RPC Operation: {}", e);
|
||||||
|
},
|
||||||
|
// Internal errors that deserve louder logging
|
||||||
|
RPCError::Unimplemented(_) | RPCError::Internal(_) => {
|
||||||
|
log_rpc!(error "Error decoding RPC operation: {}", e);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
return Ok(NetworkResult::invalid_message(e));
|
return Ok(NetworkResult::invalid_message(e));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the routing domain this message came over
|
// Get the routing domain this message came over
|
||||||
@ -1547,7 +1586,10 @@ impl RPCProcessor {
|
|||||||
let operation = match self.decode_rpc_operation(&encoded_msg) {
|
let operation = match self.decode_rpc_operation(&encoded_msg) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Punish routes that send routed undecodable crap
|
// Debug on error
|
||||||
|
log_rpc!(debug "Dropping RPC operation: {}", e);
|
||||||
|
|
||||||
|
// XXX: Punish routes that send routed undecodable crap
|
||||||
// address_filter.punish_route_id(xxx);
|
// address_filter.punish_route_id(xxx);
|
||||||
return Ok(NetworkResult::invalid_message(e));
|
return Ok(NetworkResult::invalid_message(e));
|
||||||
}
|
}
|
||||||
@ -1563,29 +1605,35 @@ impl RPCProcessor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Process stats for questions/statements received
|
// Process stats for questions/statements received
|
||||||
let kind = match msg.operation.kind() {
|
match msg.operation.kind() {
|
||||||
RPCOperationKind::Question(_) => {
|
RPCOperationKind::Question(_) => {
|
||||||
self.record_question_received(&msg);
|
self.record_question_received(&msg);
|
||||||
|
|
||||||
if let Some(sender_nr) = msg.opt_sender_nr.clone() {
|
if let Some(sender_nr) = msg.opt_sender_nr.clone() {
|
||||||
sender_nr.stats_question_rcvd(msg.header.timestamp, msg.header.body_len);
|
sender_nr.stats_question_rcvd(msg.header.timestamp, msg.header.body_len);
|
||||||
}
|
}
|
||||||
"question"
|
|
||||||
|
// Log rpc receive
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
|
debug!(target: "rpc_message", dir = "recv", kind = "question", op_id = msg.operation.op_id().as_u64(), desc = msg.operation.kind().desc(), header = ?msg.header);
|
||||||
}
|
}
|
||||||
RPCOperationKind::Statement(_) => {
|
RPCOperationKind::Statement(_) => {
|
||||||
if let Some(sender_nr) = msg.opt_sender_nr.clone() {
|
if let Some(sender_nr) = msg.opt_sender_nr.clone() {
|
||||||
sender_nr.stats_question_rcvd(msg.header.timestamp, msg.header.body_len);
|
sender_nr.stats_question_rcvd(msg.header.timestamp, msg.header.body_len);
|
||||||
}
|
}
|
||||||
"statement"
|
|
||||||
|
// Log rpc receive
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
|
debug!(target: "rpc_message", dir = "recv", kind = "statement", op_id = msg.operation.op_id().as_u64(), desc = msg.operation.kind().desc(), header = ?msg.header);
|
||||||
}
|
}
|
||||||
RPCOperationKind::Answer(_) => {
|
RPCOperationKind::Answer(_) => {
|
||||||
// Answer stats are processed in wait_for_reply
|
// Answer stats are processed in wait_for_reply
|
||||||
"answer"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Log rpc receive
|
// Log rpc receive
|
||||||
trace!(target: "rpc_message", dir = "recv", kind, op_id = msg.operation.op_id().as_u64(), desc = msg.operation.kind().desc(), header = ?msg.header);
|
#[cfg(feature = "verbose-tracing")]
|
||||||
|
debug!(target: "rpc_message", dir = "recv", kind = "answer", op_id = msg.operation.op_id().as_u64(), desc = msg.operation.kind().desc(), header = ?msg.header);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Process specific message kind
|
// Process specific message kind
|
||||||
match msg.operation.kind() {
|
match msg.operation.kind() {
|
||||||
@ -1596,6 +1644,7 @@ impl RPCProcessor {
|
|||||||
RPCQuestionDetail::GetValueQ(_) => self.process_get_value_q(msg).await,
|
RPCQuestionDetail::GetValueQ(_) => self.process_get_value_q(msg).await,
|
||||||
RPCQuestionDetail::SetValueQ(_) => self.process_set_value_q(msg).await,
|
RPCQuestionDetail::SetValueQ(_) => self.process_set_value_q(msg).await,
|
||||||
RPCQuestionDetail::WatchValueQ(_) => self.process_watch_value_q(msg).await,
|
RPCQuestionDetail::WatchValueQ(_) => self.process_watch_value_q(msg).await,
|
||||||
|
RPCQuestionDetail::InspectValueQ(_) => self.process_inspect_value_q(msg).await,
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
RPCQuestionDetail::SupplyBlockQ(_) => self.process_supply_block_q(msg).await,
|
RPCQuestionDetail::SupplyBlockQ(_) => self.process_supply_block_q(msg).await,
|
||||||
#[cfg(feature = "unstable-blockstore")]
|
#[cfg(feature = "unstable-blockstore")]
|
||||||
|
@ -116,7 +116,7 @@ where
|
|||||||
pub fn get_op_context(&self, op_id: OperationId) -> Result<C, RPCError> {
|
pub fn get_op_context(&self, op_id: OperationId) -> Result<C, RPCError> {
|
||||||
let inner = self.inner.lock();
|
let inner = self.inner.lock();
|
||||||
let Some(waiting_op) = inner.waiting_op_table.get(&op_id) else {
|
let Some(waiting_op) = inner.waiting_op_table.get(&op_id) else {
|
||||||
return Err(RPCError::internal("Missing operation id getting op context"));
|
return Err(RPCError::ignore("Missing operation id getting op context"));
|
||||||
};
|
};
|
||||||
Ok(waiting_op.context.clone())
|
Ok(waiting_op.context.clone())
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,8 @@ impl RPCProcessor {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
||||||
pub(crate) async fn process_app_call_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
pub(crate) async fn process_app_call_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
||||||
// Ignore if disabled
|
// Ignore if disabled
|
||||||
@ -71,6 +73,24 @@ impl RPCProcessor {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the private route this came over
|
||||||
|
let opt_pr_pubkey = match &msg.header.detail {
|
||||||
|
RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => None,
|
||||||
|
RPCMessageHeaderDetail::PrivateRouted(pr) => Some(pr.private_route),
|
||||||
|
};
|
||||||
|
let route_id = if let Some(pr_pubkey) = opt_pr_pubkey {
|
||||||
|
let rss = routing_table.route_spec_store();
|
||||||
|
let Some(route_id) = rss.get_route_id_for_key(&pr_pubkey) else {
|
||||||
|
return Ok(NetworkResult::invalid_message(format!(
|
||||||
|
"private route does not exist for key: {}",
|
||||||
|
pr_pubkey
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
Some(route_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// Get the question
|
// Get the question
|
||||||
let (op_id, _, _, kind) = msg.operation.clone().destructure();
|
let (op_id, _, _, kind) = msg.operation.clone().destructure();
|
||||||
let app_call_q = match kind {
|
let app_call_q = match kind {
|
||||||
@ -99,7 +119,7 @@ impl RPCProcessor {
|
|||||||
// Pass the call up through the update callback
|
// Pass the call up through the update callback
|
||||||
let message_q = app_call_q.destructure();
|
let message_q = app_call_q.destructure();
|
||||||
(self.unlocked_inner.update_callback)(VeilidUpdate::AppCall(Box::new(VeilidAppCall::new(
|
(self.unlocked_inner.update_callback)(VeilidUpdate::AppCall(Box::new(VeilidAppCall::new(
|
||||||
sender, message_q, op_id,
|
sender, route_id, message_q, op_id,
|
||||||
))));
|
))));
|
||||||
|
|
||||||
// Wait for an app call answer to come back from the app
|
// Wait for an app call answer to come back from the app
|
||||||
|
@ -19,6 +19,8 @@ impl RPCProcessor {
|
|||||||
self.statement(dest, statement).await
|
self.statement(dest, statement).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
||||||
pub(crate) async fn process_app_message(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
pub(crate) async fn process_app_message(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
||||||
// Ignore if disabled
|
// Ignore if disabled
|
||||||
@ -34,6 +36,24 @@ impl RPCProcessor {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the private route this came over
|
||||||
|
let opt_pr_pubkey = match &msg.header.detail {
|
||||||
|
RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => None,
|
||||||
|
RPCMessageHeaderDetail::PrivateRouted(pr) => Some(pr.private_route),
|
||||||
|
};
|
||||||
|
let route_id = if let Some(pr_pubkey) = opt_pr_pubkey {
|
||||||
|
let rss = routing_table.route_spec_store();
|
||||||
|
let Some(route_id) = rss.get_route_id_for_key(&pr_pubkey) else {
|
||||||
|
return Ok(NetworkResult::invalid_message(format!(
|
||||||
|
"private route does not exist for key: {}",
|
||||||
|
pr_pubkey
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
Some(route_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// Get the statement
|
// Get the statement
|
||||||
let (_, _, _, kind) = msg.operation.destructure();
|
let (_, _, _, kind) = msg.operation.destructure();
|
||||||
let app_message = match kind {
|
let app_message = match kind {
|
||||||
@ -56,7 +76,7 @@ impl RPCProcessor {
|
|||||||
// Pass the message up through the update callback
|
// Pass the message up through the update callback
|
||||||
let message = app_message.destructure();
|
let message = app_message.destructure();
|
||||||
(self.unlocked_inner.update_callback)(VeilidUpdate::AppMessage(Box::new(
|
(self.unlocked_inner.update_callback)(VeilidUpdate::AppMessage(Box::new(
|
||||||
VeilidAppMessage::new(sender, message),
|
VeilidAppMessage::new(sender, route_id, message),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
Ok(NetworkResult::value(()))
|
Ok(NetworkResult::value(()))
|
||||||
|
@ -15,6 +15,8 @@ pub enum RPCError {
|
|||||||
Network(String),
|
Network(String),
|
||||||
#[error("[RPCError: TryAgain({0})]")]
|
#[error("[RPCError: TryAgain({0})]")]
|
||||||
TryAgain(String),
|
TryAgain(String),
|
||||||
|
#[error("[RPCError: Ignore({0})]")]
|
||||||
|
Ignore(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RPCError {
|
impl RPCError {
|
||||||
@ -48,6 +50,18 @@ impl RPCError {
|
|||||||
pub fn map_network<M: ToString, X: ToString>(message: M) -> impl FnOnce(X) -> Self {
|
pub fn map_network<M: ToString, X: ToString>(message: M) -> impl FnOnce(X) -> Self {
|
||||||
move |x| Self::Network(format!("{}: {}", message.to_string(), x.to_string()))
|
move |x| Self::Network(format!("{}: {}", message.to_string(), x.to_string()))
|
||||||
}
|
}
|
||||||
|
pub fn try_again<X: ToString>(x: X) -> Self {
|
||||||
|
Self::TryAgain(x.to_string())
|
||||||
|
}
|
||||||
|
pub fn map_try_again<M: ToString, X: ToString>(message: M) -> impl FnOnce(X) -> Self {
|
||||||
|
move |x| Self::TryAgain(format!("{}: {}", message.to_string(), x.to_string()))
|
||||||
|
}
|
||||||
|
pub fn ignore<X: ToString>(x: X) -> Self {
|
||||||
|
Self::Ignore(x.to_string())
|
||||||
|
}
|
||||||
|
pub fn map_ignore<M: ToString, X: ToString>(message: M) -> impl FnOnce(X) -> Self {
|
||||||
|
move |x| Self::Ignore(format!("{}: {}", message.to_string(), x.to_string()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RPCError> for VeilidAPIError {
|
impl From<RPCError> for VeilidAPIError {
|
||||||
@ -59,6 +73,7 @@ impl From<RPCError> for VeilidAPIError {
|
|||||||
RPCError::Internal(message) => VeilidAPIError::Internal { message },
|
RPCError::Internal(message) => VeilidAPIError::Internal { message },
|
||||||
RPCError::Network(message) => VeilidAPIError::Generic { message },
|
RPCError::Network(message) => VeilidAPIError::Generic { message },
|
||||||
RPCError::TryAgain(message) => VeilidAPIError::TryAgain { message },
|
RPCError::TryAgain(message) => VeilidAPIError::TryAgain { message },
|
||||||
|
RPCError::Ignore(message) => VeilidAPIError::Generic { message },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,8 @@ impl RPCProcessor {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
||||||
pub(crate) async fn process_find_node_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
pub(crate) async fn process_find_node_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
||||||
// Ensure this never came over a private route, safety route is okay though
|
// Ensure this never came over a private route, safety route is okay though
|
||||||
|
@ -74,8 +74,7 @@ impl RPCProcessor {
|
|||||||
vcrypto: vcrypto.clone(),
|
vcrypto: vcrypto.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature="debug-dht")]
|
log_dht!(debug "{}", debug_string);
|
||||||
log_rpc!(debug "{}", debug_string);
|
|
||||||
|
|
||||||
let waitable_reply = network_result_try!(
|
let waitable_reply = network_result_try!(
|
||||||
self.question(dest.clone(), question, Some(question_context))
|
self.question(dest.clone(), question, Some(question_context))
|
||||||
@ -102,8 +101,7 @@ impl RPCProcessor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (value, peers, descriptor) = get_value_a.destructure();
|
let (value, peers, descriptor) = get_value_a.destructure();
|
||||||
#[cfg(feature="debug-dht")]
|
if debug_target_enabled!("dht") {
|
||||||
{
|
|
||||||
let debug_string_value = value.as_ref().map(|v| {
|
let debug_string_value = value.as_ref().map(|v| {
|
||||||
format!(" len={} seq={} writer={}",
|
format!(" len={} seq={} writer={}",
|
||||||
v.value_data().data().len(),
|
v.value_data().data().len(),
|
||||||
@ -126,10 +124,10 @@ impl RPCProcessor {
|
|||||||
dest
|
dest
|
||||||
);
|
);
|
||||||
|
|
||||||
log_rpc!(debug "{}", debug_string_answer);
|
log_dht!(debug "{}", debug_string_answer);
|
||||||
|
|
||||||
let peer_ids:Vec<String> = peers.iter().filter_map(|p| p.node_ids().get(key.kind).map(|k| k.to_string())).collect();
|
let peer_ids:Vec<String> = peers.iter().filter_map(|p| p.node_ids().get(key.kind).map(|k| k.to_string())).collect();
|
||||||
log_rpc!(debug "Peers: {:#?}", peer_ids);
|
log_dht!(debug "Peers: {:#?}", peer_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate peers returned are, in fact, closer to the key than the node we sent this to
|
// Validate peers returned are, in fact, closer to the key than the node we sent this to
|
||||||
@ -168,6 +166,8 @@ impl RPCProcessor {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
||||||
pub(crate) async fn process_get_value_q(
|
pub(crate) async fn process_get_value_q(
|
||||||
&self,
|
&self,
|
||||||
@ -213,8 +213,7 @@ impl RPCProcessor {
|
|||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let closer_to_key_peers = network_result_try!(routing_table.find_preferred_peers_closer_to_key(key, vec![CAP_DHT, CAP_DHT_WATCH]));
|
let closer_to_key_peers = network_result_try!(routing_table.find_preferred_peers_closer_to_key(key, vec![CAP_DHT, CAP_DHT_WATCH]));
|
||||||
|
|
||||||
#[cfg(feature="debug-dht")]
|
if debug_target_enabled!("dht") {
|
||||||
{
|
|
||||||
let debug_string = format!(
|
let debug_string = format!(
|
||||||
"IN <=== GetValueQ({} #{}{}) <== {}",
|
"IN <=== GetValueQ({} #{}{}) <== {}",
|
||||||
key,
|
key,
|
||||||
@ -227,7 +226,7 @@ impl RPCProcessor {
|
|||||||
msg.header.direct_sender_node_id()
|
msg.header.direct_sender_node_id()
|
||||||
);
|
);
|
||||||
|
|
||||||
log_rpc!(debug "{}", debug_string);
|
log_dht!(debug "{}", debug_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if we would have accepted this as a set
|
// See if we would have accepted this as a set
|
||||||
@ -235,7 +234,7 @@ impl RPCProcessor {
|
|||||||
let c = self.config.get();
|
let c = self.config.get();
|
||||||
c.network.dht.set_value_count as usize
|
c.network.dht.set_value_count as usize
|
||||||
};
|
};
|
||||||
let (subkey_result_value, subkey_result_descriptor) = if closer_to_key_peers.len() >= set_value_count {
|
let (get_result_value, get_result_descriptor) = if closer_to_key_peers.len() >= set_value_count {
|
||||||
// Not close enough
|
// Not close enough
|
||||||
(None, None)
|
(None, None)
|
||||||
} else {
|
} else {
|
||||||
@ -243,16 +242,15 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// See if we have this record ourselves
|
// See if we have this record ourselves
|
||||||
let storage_manager = self.storage_manager();
|
let storage_manager = self.storage_manager();
|
||||||
let subkey_result = network_result_try!(storage_manager
|
let get_result = network_result_try!(storage_manager
|
||||||
.inbound_get_value(key, subkey, want_descriptor)
|
.inbound_get_value(key, subkey, want_descriptor)
|
||||||
.await
|
.await
|
||||||
.map_err(RPCError::internal)?);
|
.map_err(RPCError::internal)?);
|
||||||
(subkey_result.value, subkey_result.descriptor)
|
(get_result.opt_value, get_result.opt_descriptor)
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature="debug-dht")]
|
if debug_target_enabled!("dht") {
|
||||||
{
|
let debug_string_value = get_result_value.as_ref().map(|v| {
|
||||||
let debug_string_value = subkey_result_value.as_ref().map(|v| {
|
|
||||||
format!(" len={} seq={} writer={}",
|
format!(" len={} seq={} writer={}",
|
||||||
v.value_data().data().len(),
|
v.value_data().data().len(),
|
||||||
v.value_data().seq(),
|
v.value_data().seq(),
|
||||||
@ -265,7 +263,7 @@ impl RPCProcessor {
|
|||||||
key,
|
key,
|
||||||
subkey,
|
subkey,
|
||||||
debug_string_value,
|
debug_string_value,
|
||||||
if subkey_result_descriptor.is_some() {
|
if get_result_descriptor.is_some() {
|
||||||
" +desc"
|
" +desc"
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
@ -274,14 +272,14 @@ impl RPCProcessor {
|
|||||||
msg.header.direct_sender_node_id()
|
msg.header.direct_sender_node_id()
|
||||||
);
|
);
|
||||||
|
|
||||||
log_rpc!(debug "{}", debug_string_answer);
|
log_dht!(debug "{}", debug_string_answer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make GetValue answer
|
// Make GetValue answer
|
||||||
let get_value_a = RPCOperationGetValueA::new(
|
let get_value_a = RPCOperationGetValueA::new(
|
||||||
subkey_result_value.map(|x| (*x).clone()),
|
get_result_value.map(|x| (*x).clone()),
|
||||||
closer_to_key_peers,
|
closer_to_key_peers,
|
||||||
subkey_result_descriptor.map(|x| (*x).clone()),
|
get_result_descriptor.map(|x| (*x).clone()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Send GetValue answer
|
// Send GetValue answer
|
||||||
|
269
veilid-core/src/rpc_processor/rpc_inspect_value.rs
Normal file
269
veilid-core/src/rpc_processor/rpc_inspect_value.rs
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::storage_manager::SignedValueDescriptor;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct InspectValueAnswer {
|
||||||
|
pub seqs: Vec<ValueSeqNum>,
|
||||||
|
pub peers: Vec<PeerInfo>,
|
||||||
|
pub descriptor: Option<SignedValueDescriptor>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RPCProcessor {
|
||||||
|
/// Sends an inspect value request and wait for response
|
||||||
|
/// Can be sent via all methods including relays
|
||||||
|
/// Safety routes may be used, but never private routes.
|
||||||
|
/// Because this leaks information about the identity of the node itself,
|
||||||
|
/// replying to this request received over a private route will leak
|
||||||
|
/// the identity of the node and defeat the private route.
|
||||||
|
/// The number of subkey sequence numbers returned may either be:
|
||||||
|
/// * the amount requested
|
||||||
|
/// * an amount truncated to MAX_INSPECT_VALUE_A_SEQS_LEN subkeys
|
||||||
|
/// * zero if nothing was found
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "verbose-tracing",
|
||||||
|
instrument(level = "trace", skip(self, last_descriptor),
|
||||||
|
fields(ret.value.data.len,
|
||||||
|
ret.seqs,
|
||||||
|
ret.peers.len,
|
||||||
|
ret.latency
|
||||||
|
),err)
|
||||||
|
)]
|
||||||
|
pub async fn rpc_call_inspect_value(
|
||||||
|
self,
|
||||||
|
dest: Destination,
|
||||||
|
key: TypedKey,
|
||||||
|
subkeys: ValueSubkeyRangeSet,
|
||||||
|
last_descriptor: Option<SignedValueDescriptor>,
|
||||||
|
) -> RPCNetworkResult<Answer<InspectValueAnswer>> {
|
||||||
|
// Ensure destination never has a private route
|
||||||
|
// and get the target noderef so we can validate the response
|
||||||
|
let Some(target) = dest.node() else {
|
||||||
|
return Err(RPCError::internal(
|
||||||
|
"Never send get 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"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let debug_string = format!(
|
||||||
|
"OUT ==> InspectValueQ({} #{}{}) => {}",
|
||||||
|
key,
|
||||||
|
&subkeys,
|
||||||
|
if last_descriptor.is_some() {
|
||||||
|
" +lastdesc"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
dest
|
||||||
|
);
|
||||||
|
|
||||||
|
// Send the inspectvalue question
|
||||||
|
let inspect_value_q = RPCOperationInspectValueQ::new(key, subkeys.clone(), last_descriptor.is_none())?;
|
||||||
|
let question = RPCQuestion::new(
|
||||||
|
network_result_try!(self.get_destination_respond_to(&dest)?),
|
||||||
|
RPCQuestionDetail::InspectValueQ(Box::new(inspect_value_q)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let question_context = QuestionContext::InspectValue(ValidateInspectValueContext {
|
||||||
|
last_descriptor,
|
||||||
|
subkeys,
|
||||||
|
vcrypto: vcrypto.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
log_dht!(debug "{}", debug_string);
|
||||||
|
|
||||||
|
let waitable_reply = network_result_try!(
|
||||||
|
self.question(dest.clone(), question, Some(question_context))
|
||||||
|
.await?
|
||||||
|
);
|
||||||
|
|
||||||
|
// Keep the reply private route that was used to return with the answer
|
||||||
|
let reply_private_route = waitable_reply.reply_private_route;
|
||||||
|
|
||||||
|
// Wait for reply
|
||||||
|
let (msg, latency) = match self.wait_for_reply(waitable_reply, debug_string).await? {
|
||||||
|
TimeoutOr::Timeout => return Ok(NetworkResult::Timeout),
|
||||||
|
TimeoutOr::Value(v) => v,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the right answer type
|
||||||
|
let (_, _, _, kind) = msg.operation.destructure();
|
||||||
|
let inspect_value_a = match kind {
|
||||||
|
RPCOperationKind::Answer(a) => match a.destructure() {
|
||||||
|
RPCAnswerDetail::InspectValueA(a) => a,
|
||||||
|
_ => return Ok(NetworkResult::invalid_message("not an inspectvalue answer")),
|
||||||
|
},
|
||||||
|
_ => return Ok(NetworkResult::invalid_message("not an answer")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (seqs, peers, descriptor) = inspect_value_a.destructure();
|
||||||
|
if debug_target_enabled!("dht") {
|
||||||
|
let debug_string_answer = format!(
|
||||||
|
"OUT <== InspectValueA({} {} peers={}) <= {} seqs:\n{}",
|
||||||
|
key,
|
||||||
|
if descriptor.is_some() {
|
||||||
|
" +desc"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
peers.len(),
|
||||||
|
dest,
|
||||||
|
debug_seqs(&seqs)
|
||||||
|
);
|
||||||
|
|
||||||
|
log_dht!(debug "{}", debug_string_answer);
|
||||||
|
|
||||||
|
let peer_ids:Vec<String> = peers.iter().filter_map(|p| p.node_ids().get(key.kind).map(|k| k.to_string())).collect();
|
||||||
|
log_dht!(debug "Peers: {:#?}", peer_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
|
tracing::Span::current().record("ret.latency", latency.as_u64());
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
|
tracing::Span::current().record("ret.seqs", seqs);
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
|
tracing::Span::current().record("ret.peers.len", peers.len());
|
||||||
|
|
||||||
|
Ok(NetworkResult::value(Answer::new(
|
||||||
|
latency,
|
||||||
|
reply_private_route,
|
||||||
|
InspectValueAnswer {
|
||||||
|
seqs,
|
||||||
|
peers,
|
||||||
|
descriptor,
|
||||||
|
},
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
||||||
|
pub(crate) async fn process_inspect_value_q(
|
||||||
|
&self,
|
||||||
|
msg: RPCMessage,
|
||||||
|
) -> RPCNetworkResult<()> {
|
||||||
|
|
||||||
|
// Ensure this never came over a private route, safety route is okay though
|
||||||
|
match &msg.header.detail {
|
||||||
|
RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => {}
|
||||||
|
RPCMessageHeaderDetail::PrivateRouted(_) => {
|
||||||
|
return Ok(NetworkResult::invalid_message(
|
||||||
|
"not processing inspect value request over private route",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Ignore if disabled
|
||||||
|
let routing_table = self.routing_table();
|
||||||
|
let opi = routing_table.get_own_peer_info(msg.header.routing_domain());
|
||||||
|
if !opi
|
||||||
|
.signed_node_info()
|
||||||
|
.node_info()
|
||||||
|
.has_capability(CAP_DHT)
|
||||||
|
{
|
||||||
|
return Ok(NetworkResult::service_unavailable(
|
||||||
|
"dht is not available",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the question
|
||||||
|
let kind = msg.operation.kind().clone();
|
||||||
|
let inspect_value_q = match kind {
|
||||||
|
RPCOperationKind::Question(q) => match q.destructure() {
|
||||||
|
(_, RPCQuestionDetail::InspectValueQ(q)) => q,
|
||||||
|
_ => panic!("not a inspectvalue question"),
|
||||||
|
},
|
||||||
|
_ => panic!("not a question"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Destructure
|
||||||
|
let (key, subkeys, want_descriptor) = inspect_value_q.destructure();
|
||||||
|
|
||||||
|
// Get the nodes that we know about that are closer to the the key than our own node
|
||||||
|
let routing_table = self.routing_table();
|
||||||
|
let closer_to_key_peers = network_result_try!(routing_table.find_preferred_peers_closer_to_key(key, vec![CAP_DHT, CAP_DHT_WATCH]));
|
||||||
|
|
||||||
|
if debug_target_enabled!("dht") {
|
||||||
|
let debug_string = format!(
|
||||||
|
"IN <=== InspectValueQ({} {}{}) <== {}",
|
||||||
|
key,
|
||||||
|
subkeys,
|
||||||
|
if want_descriptor {
|
||||||
|
" +wantdesc"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
msg.header.direct_sender_node_id()
|
||||||
|
);
|
||||||
|
|
||||||
|
log_dht!(debug "{}", debug_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if we would have accepted this as a set
|
||||||
|
let set_value_count = {
|
||||||
|
let c = self.config.get();
|
||||||
|
c.network.dht.set_value_count as usize
|
||||||
|
};
|
||||||
|
let (inspect_result_seqs, inspect_result_descriptor) = if closer_to_key_peers.len() >= set_value_count {
|
||||||
|
// Not close enough
|
||||||
|
(Vec::new(), None)
|
||||||
|
} else {
|
||||||
|
// Close enough, lets get it
|
||||||
|
|
||||||
|
// See if we have this record ourselves
|
||||||
|
let storage_manager = self.storage_manager();
|
||||||
|
let inspect_result = network_result_try!(storage_manager
|
||||||
|
.inbound_inspect_value(key, subkeys, want_descriptor)
|
||||||
|
.await
|
||||||
|
.map_err(RPCError::internal)?);
|
||||||
|
(inspect_result.seqs, inspect_result.opt_descriptor)
|
||||||
|
};
|
||||||
|
|
||||||
|
if debug_target_enabled!("dht") {
|
||||||
|
let debug_string_answer = format!(
|
||||||
|
"IN ===> InspectValueA({} {:?}{} peers={}) ==> {}",
|
||||||
|
key,
|
||||||
|
inspect_result_seqs,
|
||||||
|
if inspect_result_descriptor.is_some() {
|
||||||
|
" +desc"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
closer_to_key_peers.len(),
|
||||||
|
msg.header.direct_sender_node_id()
|
||||||
|
);
|
||||||
|
|
||||||
|
log_dht!(debug "{}", debug_string_answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make InspectValue answer
|
||||||
|
let inspect_value_a = RPCOperationInspectValueA::new(
|
||||||
|
inspect_result_seqs,
|
||||||
|
closer_to_key_peers,
|
||||||
|
inspect_result_descriptor.map(|x| (*x).clone()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Send InspectValue answer
|
||||||
|
self.answer(msg, RPCAnswer::new(RPCAnswerDetail::InspectValueA(Box::new(inspect_value_a))))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
@ -88,8 +88,9 @@ impl RPCProcessor {
|
|||||||
vcrypto: vcrypto.clone(),
|
vcrypto: vcrypto.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature="debug-dht")]
|
if debug_target_enabled!("dht") {
|
||||||
log_rpc!(debug "{}", debug_string);
|
log_dht!(debug "{}", debug_string);
|
||||||
|
}
|
||||||
|
|
||||||
let waitable_reply = network_result_try!(
|
let waitable_reply = network_result_try!(
|
||||||
self.question(dest.clone(), question, Some(question_context))
|
self.question(dest.clone(), question, Some(question_context))
|
||||||
@ -117,8 +118,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
let (set, value, peers) = set_value_a.destructure();
|
let (set, value, peers) = set_value_a.destructure();
|
||||||
|
|
||||||
#[cfg(feature="debug-dht")]
|
if debug_target_enabled!("dht") {
|
||||||
{
|
|
||||||
let debug_string_value = value.as_ref().map(|v| {
|
let debug_string_value = value.as_ref().map(|v| {
|
||||||
format!(" len={} writer={}",
|
format!(" len={} writer={}",
|
||||||
v.value_data().data().len(),
|
v.value_data().data().len(),
|
||||||
@ -141,10 +141,10 @@ impl RPCProcessor {
|
|||||||
dest,
|
dest,
|
||||||
);
|
);
|
||||||
|
|
||||||
log_rpc!(debug "{}", debug_string_answer);
|
log_dht!(debug "{}", debug_string_answer);
|
||||||
|
|
||||||
let peer_ids:Vec<String> = peers.iter().filter_map(|p| p.node_ids().get(key.kind).map(|k| k.to_string())).collect();
|
let peer_ids:Vec<String> = peers.iter().filter_map(|p| p.node_ids().get(key.kind).map(|k| k.to_string())).collect();
|
||||||
log_rpc!(debug "Peers: {:#?}", peer_ids);
|
log_dht!(debug "Peers: {:#?}", peer_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate peers returned are, in fact, closer to the key than the node we sent this to
|
// Validate peers returned are, in fact, closer to the key than the node we sent this to
|
||||||
@ -181,6 +181,8 @@ impl RPCProcessor {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
||||||
pub(crate) async fn process_set_value_q(
|
pub(crate) async fn process_set_value_q(
|
||||||
&self,
|
&self,
|
||||||
@ -270,8 +272,7 @@ impl RPCProcessor {
|
|||||||
(true, new_value)
|
(true, new_value)
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature="debug-dht")]
|
if debug_target_enabled!("dht") {
|
||||||
{
|
|
||||||
let debug_string_value = new_value.as_ref().map(|v| {
|
let debug_string_value = new_value.as_ref().map(|v| {
|
||||||
format!(" len={} seq={} writer={}",
|
format!(" len={} seq={} writer={}",
|
||||||
v.value_data().data().len(),
|
v.value_data().data().len(),
|
||||||
@ -294,7 +295,7 @@ impl RPCProcessor {
|
|||||||
msg.header.direct_sender_node_id()
|
msg.header.direct_sender_node_id()
|
||||||
);
|
);
|
||||||
|
|
||||||
log_rpc!(debug "{}", debug_string_answer);
|
log_dht!(debug "{}", debug_string_answer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make SetValue answer
|
// Make SetValue answer
|
||||||
|
@ -32,6 +32,8 @@ impl RPCProcessor {
|
|||||||
self.statement(dest, statement).await
|
self.statement(dest, statement).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
||||||
pub(crate) async fn process_signal(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
pub(crate) async fn process_signal(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
||||||
// Ignore if disabled
|
// Ignore if disabled
|
||||||
|
@ -200,6 +200,8 @@ impl RPCProcessor {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
||||||
pub(crate) async fn process_status_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
pub(crate) async fn process_status_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
||||||
// Get the question
|
// Get the question
|
||||||
|
@ -56,6 +56,8 @@ impl RPCProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
||||||
pub(crate) async fn process_validate_dial_info(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
pub(crate) async fn process_validate_dial_info(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
|
@ -14,9 +14,16 @@ impl RPCProcessor {
|
|||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkeys: ValueSubkeyRangeSet,
|
subkeys: ValueSubkeyRangeSet,
|
||||||
count: u32,
|
count: u32,
|
||||||
|
watch_id: u64,
|
||||||
value: SignedValueData,
|
value: SignedValueData,
|
||||||
) -> RPCNetworkResult<()> {
|
) -> RPCNetworkResult<()> {
|
||||||
let value_changed = RPCOperationValueChanged::new(key, subkeys, count, value)?;
|
// Ensure destination is never using a safety route
|
||||||
|
if matches!(dest.get_safety_selection(), SafetySelection::Safe(_)) {
|
||||||
|
return Err(RPCError::internal(
|
||||||
|
"Never send value changes over safety routes",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let value_changed = RPCOperationValueChanged::new(key, subkeys, count, watch_id, value)?;
|
||||||
let statement =
|
let statement =
|
||||||
RPCStatement::new(RPCStatementDetail::ValueChanged(Box::new(value_changed)));
|
RPCStatement::new(RPCStatementDetail::ValueChanged(Box::new(value_changed)));
|
||||||
|
|
||||||
@ -24,10 +31,12 @@ impl RPCProcessor {
|
|||||||
self.statement(dest, statement).await
|
self.statement(dest, statement).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
pub(crate) async fn process_value_changed(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
pub(crate) async fn process_value_changed(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
||||||
// Get the statement
|
// Get the statement
|
||||||
let (_, _, _, kind) = msg.operation.destructure();
|
let (_, _, _, kind) = msg.operation.destructure();
|
||||||
let (key, subkeys, count, value) = match kind {
|
let (key, subkeys, count, watch_id, value) = match kind {
|
||||||
RPCOperationKind::Statement(s) => match s.destructure() {
|
RPCOperationKind::Statement(s) => match s.destructure() {
|
||||||
RPCStatementDetail::ValueChanged(s) => s.destructure(),
|
RPCStatementDetail::ValueChanged(s) => s.destructure(),
|
||||||
_ => panic!("not a value changed statement"),
|
_ => panic!("not a value changed statement"),
|
||||||
@ -35,8 +44,25 @@ impl RPCProcessor {
|
|||||||
_ => panic!("not a statement"),
|
_ => panic!("not a statement"),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "debug-dht")]
|
// Get the inbound node if if this came in directly
|
||||||
{
|
// If this was received over just a safety route, ignore it
|
||||||
|
// It this was received over a private route, the inbound node id could be either the actual
|
||||||
|
// node id, or a safety route (can't tell if a stub was used).
|
||||||
|
// Try it as the node if, and the storage manager will reject the
|
||||||
|
// value change if it doesn't match the active watch's node id
|
||||||
|
let inbound_node_id = match &msg.header.detail {
|
||||||
|
RPCMessageHeaderDetail::Direct(d) => d.envelope.get_sender_typed_id(),
|
||||||
|
RPCMessageHeaderDetail::SafetyRouted(_) => {
|
||||||
|
return Ok(NetworkResult::invalid_message(
|
||||||
|
"not processing value change over safety route",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
RPCMessageHeaderDetail::PrivateRouted(p) => {
|
||||||
|
TypedKey::new(p.direct.envelope.get_crypto_kind(), p.remote_safety_route)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if debug_target_enabled!("dht") {
|
||||||
let debug_string_value = format!(
|
let debug_string_value = format!(
|
||||||
" len={} seq={} writer={}",
|
" len={} seq={} writer={}",
|
||||||
value.value_data().data().len(),
|
value.value_data().data().len(),
|
||||||
@ -45,21 +71,30 @@ impl RPCProcessor {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let debug_string_stmt = format!(
|
let debug_string_stmt = format!(
|
||||||
"IN <== ValueChanged({} #{:?}+{}{}) <= {}",
|
"IN <== ValueChanged(id={} {} #{:?}+{}{}) from {} <= {}",
|
||||||
|
watch_id,
|
||||||
key,
|
key,
|
||||||
subkeys,
|
subkeys,
|
||||||
count,
|
count,
|
||||||
debug_string_value,
|
debug_string_value,
|
||||||
msg.header.direct_sender_node_id()
|
inbound_node_id,
|
||||||
|
msg.header.direct_sender_node_id(),
|
||||||
);
|
);
|
||||||
|
|
||||||
log_rpc!(debug "{}", debug_string_stmt);
|
log_dht!(debug "{}", debug_string_stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the subkey, creating a new record if necessary
|
// Save the subkey, creating a new record if necessary
|
||||||
let storage_manager = self.storage_manager();
|
let storage_manager = self.storage_manager();
|
||||||
storage_manager
|
storage_manager
|
||||||
.inbound_value_changed(key, subkeys, count, Arc::new(value))
|
.inbound_value_changed(
|
||||||
|
key,
|
||||||
|
subkeys,
|
||||||
|
count,
|
||||||
|
Arc::new(value),
|
||||||
|
inbound_node_id,
|
||||||
|
watch_id,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(RPCError::internal)?;
|
.map_err(RPCError::internal)?;
|
||||||
|
|
||||||
|
@ -2,8 +2,10 @@ use super::*;
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct WatchValueAnswer {
|
pub struct WatchValueAnswer {
|
||||||
|
pub accepted: bool,
|
||||||
pub expiration_ts: Timestamp,
|
pub expiration_ts: Timestamp,
|
||||||
pub peers: Vec<PeerInfo>,
|
pub peers: Vec<PeerInfo>,
|
||||||
|
pub watch_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RPCProcessor {
|
impl RPCProcessor {
|
||||||
@ -22,6 +24,7 @@ impl RPCProcessor {
|
|||||||
ret.peers.len
|
ret.peers.len
|
||||||
),err)
|
),err)
|
||||||
)]
|
)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn rpc_call_watch_value(
|
pub async fn rpc_call_watch_value(
|
||||||
self,
|
self,
|
||||||
dest: Destination,
|
dest: Destination,
|
||||||
@ -30,6 +33,7 @@ impl RPCProcessor {
|
|||||||
expiration: Timestamp,
|
expiration: Timestamp,
|
||||||
count: u32,
|
count: u32,
|
||||||
watcher: KeyPair,
|
watcher: KeyPair,
|
||||||
|
watch_id: Option<u64>,
|
||||||
) -> RPCNetworkResult<Answer<WatchValueAnswer>> {
|
) -> RPCNetworkResult<Answer<WatchValueAnswer>> {
|
||||||
// Ensure destination never has a private route
|
// Ensure destination never has a private route
|
||||||
// and get the target noderef so we can validate the response
|
// and get the target noderef so we can validate the response
|
||||||
@ -48,8 +52,18 @@ impl RPCProcessor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let debug_string = format!(
|
let debug_string = format!(
|
||||||
"OUT ==> WatchValueQ({} {}@{}+{}) => {} (watcher={})",
|
"OUT ==> WatchValueQ({} {} {}@{}+{}) => {} (watcher={}) ",
|
||||||
key, subkeys, expiration, count, dest, watcher.key
|
if let Some(watch_id) = watch_id {
|
||||||
|
format!("id={} ", watch_id)
|
||||||
|
} else {
|
||||||
|
"".to_owned()
|
||||||
|
},
|
||||||
|
key,
|
||||||
|
subkeys,
|
||||||
|
expiration,
|
||||||
|
count,
|
||||||
|
dest,
|
||||||
|
watcher.key
|
||||||
);
|
);
|
||||||
|
|
||||||
// Send the watchvalue question
|
// Send the watchvalue question
|
||||||
@ -58,6 +72,7 @@ impl RPCProcessor {
|
|||||||
subkeys.clone(),
|
subkeys.clone(),
|
||||||
expiration.as_u64(),
|
expiration.as_u64(),
|
||||||
count,
|
count,
|
||||||
|
watch_id,
|
||||||
watcher,
|
watcher,
|
||||||
vcrypto.clone(),
|
vcrypto.clone(),
|
||||||
)?;
|
)?;
|
||||||
@ -66,8 +81,7 @@ impl RPCProcessor {
|
|||||||
RPCQuestionDetail::WatchValueQ(Box::new(watch_value_q)),
|
RPCQuestionDetail::WatchValueQ(Box::new(watch_value_q)),
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "debug-dht")]
|
log_dht!(debug "{}", debug_string);
|
||||||
log_rpc!(debug "{}", debug_string);
|
|
||||||
|
|
||||||
let waitable_reply =
|
let waitable_reply =
|
||||||
network_result_try!(self.question(dest.clone(), question, None).await?);
|
network_result_try!(self.question(dest.clone(), question, None).await?);
|
||||||
@ -90,12 +104,13 @@ impl RPCProcessor {
|
|||||||
},
|
},
|
||||||
_ => return Ok(NetworkResult::invalid_message("not an answer")),
|
_ => return Ok(NetworkResult::invalid_message("not an answer")),
|
||||||
};
|
};
|
||||||
|
let question_watch_id = watch_id;
|
||||||
let (expiration, peers) = watch_value_a.destructure();
|
let (accepted, expiration, peers, watch_id) = watch_value_a.destructure();
|
||||||
#[cfg(feature = "debug-dht")]
|
if debug_target_enabled!("dht") {
|
||||||
{
|
|
||||||
let debug_string_answer = format!(
|
let debug_string_answer = format!(
|
||||||
"OUT <== WatchValueA({} #{:?}@{} peers={}) <= {}",
|
"OUT <== WatchValueA({}id={} {} #{:?}@{} peers={}) <= {}",
|
||||||
|
if accepted { "+accept " } else { "" },
|
||||||
|
watch_id,
|
||||||
key,
|
key,
|
||||||
subkeys,
|
subkeys,
|
||||||
expiration,
|
expiration,
|
||||||
@ -103,13 +118,32 @@ impl RPCProcessor {
|
|||||||
dest
|
dest
|
||||||
);
|
);
|
||||||
|
|
||||||
log_rpc!(debug "{}", debug_string_answer);
|
log_dht!(debug "{}", debug_string_answer);
|
||||||
|
|
||||||
let peer_ids: Vec<String> = peers
|
let peer_ids: Vec<String> = peers
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|p| p.node_ids().get(key.kind).map(|k| k.to_string()))
|
.filter_map(|p| p.node_ids().get(key.kind).map(|k| k.to_string()))
|
||||||
.collect();
|
.collect();
|
||||||
log_rpc!(debug "Peers: {:#?}", peer_ids);
|
log_dht!(debug "Peers: {:#?}", peer_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate accepted requests
|
||||||
|
if accepted {
|
||||||
|
// Verify returned answer watch id is the same as the question watch id if it exists
|
||||||
|
if let Some(question_watch_id) = question_watch_id {
|
||||||
|
if question_watch_id != watch_id {
|
||||||
|
return Ok(NetworkResult::invalid_message(format!(
|
||||||
|
"answer watch id={} doesn't match question watch id={}",
|
||||||
|
watch_id, question_watch_id,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Validate if a watch is created/updated, that it has a nonzero id
|
||||||
|
if expiration != 0 && watch_id == 0 {
|
||||||
|
return Ok(NetworkResult::invalid_message(
|
||||||
|
"zero watch id returned on accepted or cancelled watch",
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate peers returned are, in fact, closer to the key than the node we sent this to
|
// Validate peers returned are, in fact, closer to the key than the node we sent this to
|
||||||
@ -137,12 +171,16 @@ impl RPCProcessor {
|
|||||||
latency,
|
latency,
|
||||||
reply_private_route,
|
reply_private_route,
|
||||||
WatchValueAnswer {
|
WatchValueAnswer {
|
||||||
|
accepted,
|
||||||
expiration_ts: Timestamp::new(expiration),
|
expiration_ts: Timestamp::new(expiration),
|
||||||
peers,
|
peers,
|
||||||
|
watch_id,
|
||||||
},
|
},
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))]
|
||||||
pub(crate) async fn process_watch_value_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
pub(crate) async fn process_watch_value_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> {
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
@ -185,16 +223,21 @@ impl RPCProcessor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Destructure
|
// Destructure
|
||||||
let (key, subkeys, expiration, count, watcher, _signature) = watch_value_q.destructure();
|
let (key, subkeys, expiration, count, watch_id, watcher, _signature) =
|
||||||
|
watch_value_q.destructure();
|
||||||
|
|
||||||
// Get target for ValueChanged notifications
|
// Get target for ValueChanged notifications
|
||||||
let dest = network_result_try!(self.get_respond_to_destination(&msg));
|
let dest = network_result_try!(self.get_respond_to_destination(&msg));
|
||||||
let target = dest.get_target(rss)?;
|
let target = dest.get_target(rss)?;
|
||||||
|
|
||||||
#[cfg(feature = "debug-dht")]
|
if debug_target_enabled!("dht") {
|
||||||
{
|
|
||||||
let debug_string = format!(
|
let debug_string = format!(
|
||||||
"IN <=== WatchValueQ({} {}@{}+{}) <== {} (watcher={})",
|
"IN <=== WatchValueQ({}{} {}@{}+{}) <== {} (watcher={})",
|
||||||
|
if let Some(watch_id) = watch_id {
|
||||||
|
format!("id={} ", watch_id)
|
||||||
|
} else {
|
||||||
|
"".to_owned()
|
||||||
|
},
|
||||||
key,
|
key,
|
||||||
subkeys,
|
subkeys,
|
||||||
expiration,
|
expiration,
|
||||||
@ -203,7 +246,7 @@ impl RPCProcessor {
|
|||||||
watcher
|
watcher
|
||||||
);
|
);
|
||||||
|
|
||||||
log_rpc!(debug "{}", debug_string);
|
log_dht!(debug "{}", debug_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the nodes that we know about that are closer to the the key than our own node
|
// Get the nodes that we know about that are closer to the the key than our own node
|
||||||
@ -211,40 +254,53 @@ impl RPCProcessor {
|
|||||||
routing_table.find_preferred_peers_closer_to_key(key, vec![CAP_DHT, CAP_DHT_WATCH])
|
routing_table.find_preferred_peers_closer_to_key(key, vec![CAP_DHT, CAP_DHT_WATCH])
|
||||||
);
|
);
|
||||||
|
|
||||||
// See if we would have accepted this as a set
|
// See if we would have accepted this as a set, same set_value_count for watches
|
||||||
let set_value_count = {
|
let set_value_count = {
|
||||||
let c = self.config.get();
|
let c = self.config.get();
|
||||||
c.network.dht.set_value_count as usize
|
c.network.dht.set_value_count as usize
|
||||||
};
|
};
|
||||||
let ret_expiration = if closer_to_key_peers.len() >= set_value_count {
|
let (ret_accepted, ret_expiration, ret_watch_id) =
|
||||||
// Not close enough
|
if closer_to_key_peers.len() >= set_value_count {
|
||||||
|
// Not close enough, not accepted
|
||||||
|
log_dht!(debug "Not close enough for watch value");
|
||||||
|
|
||||||
#[cfg(feature = "debug-dht")]
|
(false, 0, watch_id.unwrap_or_default())
|
||||||
log_rpc!(debug "Not close enough for watch value");
|
|
||||||
|
|
||||||
Timestamp::default()
|
|
||||||
} else {
|
} else {
|
||||||
// Close enough, lets watch it
|
// Accepted, lets try to watch or cancel it
|
||||||
|
|
||||||
|
let params = WatchParameters {
|
||||||
|
subkeys: subkeys.clone(),
|
||||||
|
expiration: Timestamp::new(expiration),
|
||||||
|
count,
|
||||||
|
watcher,
|
||||||
|
target,
|
||||||
|
};
|
||||||
|
|
||||||
// See if we have this record ourselves, if so, accept the watch
|
// See if we have this record ourselves, if so, accept the watch
|
||||||
let storage_manager = self.storage_manager();
|
let storage_manager = self.storage_manager();
|
||||||
network_result_try!(storage_manager
|
let watch_result = network_result_try!(storage_manager
|
||||||
.inbound_watch_value(
|
.inbound_watch_value(key, params, watch_id,)
|
||||||
key,
|
|
||||||
subkeys.clone(),
|
|
||||||
Timestamp::new(expiration),
|
|
||||||
count,
|
|
||||||
target,
|
|
||||||
watcher
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.map_err(RPCError::internal)?)
|
.map_err(RPCError::internal)?);
|
||||||
|
|
||||||
|
// Encode the watch result
|
||||||
|
// Rejections and cancellations are treated the same way by clients
|
||||||
|
let (ret_expiration, ret_watch_id) = match watch_result {
|
||||||
|
WatchResult::Created { id, expiration } => (expiration.as_u64(), id),
|
||||||
|
WatchResult::Changed { expiration } => {
|
||||||
|
(expiration.as_u64(), watch_id.unwrap_or_default())
|
||||||
|
}
|
||||||
|
WatchResult::Cancelled => (0, watch_id.unwrap_or_default()),
|
||||||
|
WatchResult::Rejected => (0, watch_id.unwrap_or_default()),
|
||||||
|
};
|
||||||
|
(true, ret_expiration, ret_watch_id)
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "debug-dht")]
|
if debug_target_enabled!("dht") {
|
||||||
{
|
|
||||||
let debug_string_answer = format!(
|
let debug_string_answer = format!(
|
||||||
"IN ===> WatchValueA({} #{} expiration={} peers={}) ==> {}",
|
"IN ===> WatchValueA({}id={} {} #{} expiration={} peers={}) ==> {}",
|
||||||
|
if ret_accepted { "+accept " } else { "" },
|
||||||
|
ret_watch_id,
|
||||||
key,
|
key,
|
||||||
subkeys,
|
subkeys,
|
||||||
ret_expiration,
|
ret_expiration,
|
||||||
@ -252,17 +308,15 @@ impl RPCProcessor {
|
|||||||
msg.header.direct_sender_node_id()
|
msg.header.direct_sender_node_id()
|
||||||
);
|
);
|
||||||
|
|
||||||
log_rpc!(debug "{}", debug_string_answer);
|
log_dht!(debug "{}", debug_string_answer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make WatchValue answer
|
// Make WatchValue answer
|
||||||
let watch_value_a = RPCOperationWatchValueA::new(
|
let watch_value_a = RPCOperationWatchValueA::new(
|
||||||
ret_expiration.as_u64(),
|
ret_accepted,
|
||||||
if ret_expiration.as_u64() == 0 {
|
ret_expiration,
|
||||||
closer_to_key_peers
|
closer_to_key_peers,
|
||||||
} else {
|
ret_watch_id,
|
||||||
vec![]
|
|
||||||
},
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Send GetValue answer
|
// Send GetValue answer
|
||||||
|
@ -15,6 +15,20 @@ impl StorageManager {
|
|||||||
};
|
};
|
||||||
remote_record_store.debug_records()
|
remote_record_store.debug_records()
|
||||||
}
|
}
|
||||||
|
pub(crate) async fn debug_opened_records(&self) -> String {
|
||||||
|
let inner = self.inner.lock().await;
|
||||||
|
let mut out = "[\n".to_owned();
|
||||||
|
for (k, v) in &inner.opened_records {
|
||||||
|
let writer = if let Some(w) = v.writer() {
|
||||||
|
w.to_string()
|
||||||
|
} else {
|
||||||
|
"".to_owned()
|
||||||
|
};
|
||||||
|
out += &format!(" {} {},\n", k, writer);
|
||||||
|
}
|
||||||
|
format!("{}]\n", out)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn purge_local_records(&self, reclaim: Option<usize>) -> String {
|
pub(crate) async fn purge_local_records(&self, reclaim: Option<usize>) -> String {
|
||||||
let mut inner = self.inner.lock().await;
|
let mut inner = self.inner.lock().await;
|
||||||
let Some(local_record_store) = &mut inner.local_record_store else {
|
let Some(local_record_store) = &mut inner.local_record_store else {
|
||||||
@ -66,8 +80,17 @@ impl StorageManager {
|
|||||||
let Some(local_record_store) = &inner.local_record_store else {
|
let Some(local_record_store) = &inner.local_record_store else {
|
||||||
return "not initialized".to_owned();
|
return "not initialized".to_owned();
|
||||||
};
|
};
|
||||||
local_record_store.debug_record_info(key)
|
let local_debug = local_record_store.debug_record_info(key);
|
||||||
|
|
||||||
|
let opened_debug = if let Some(o) = inner.opened_records.get(&key) {
|
||||||
|
format!("Opened Record: {:#?}\n", o)
|
||||||
|
} else {
|
||||||
|
"".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
format!("{}\n{}", local_debug, opened_debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn debug_remote_record_info(&self, key: TypedKey) -> String {
|
pub(crate) async fn debug_remote_record_info(&self, key: TypedKey) -> String {
|
||||||
let inner = self.inner.lock().await;
|
let inner = self.inner.lock().await;
|
||||||
let Some(remote_record_store) = &inner.remote_record_store else {
|
let Some(remote_record_store) = &inner.remote_record_store else {
|
||||||
|
@ -14,10 +14,10 @@ struct OutboundGetValueContext {
|
|||||||
|
|
||||||
/// The result of the outbound_get_value operation
|
/// The result of the outbound_get_value operation
|
||||||
pub(super) struct OutboundGetValueResult {
|
pub(super) struct OutboundGetValueResult {
|
||||||
|
/// Fanout result
|
||||||
|
pub fanout_result: FanoutResult,
|
||||||
/// The subkey that was retrieved
|
/// The subkey that was retrieved
|
||||||
pub subkey_result: SubkeyResult,
|
pub get_result: GetResult,
|
||||||
/// And where it was retrieved from
|
|
||||||
pub value_nodes: Vec<NodeRef>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StorageManager {
|
impl StorageManager {
|
||||||
@ -28,7 +28,7 @@ impl StorageManager {
|
|||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkey: ValueSubkey,
|
subkey: ValueSubkey,
|
||||||
safety_selection: SafetySelection,
|
safety_selection: SafetySelection,
|
||||||
last_subkey_result: SubkeyResult,
|
last_get_result: GetResult,
|
||||||
) -> VeilidAPIResult<OutboundGetValueResult> {
|
) -> VeilidAPIResult<OutboundGetValueResult> {
|
||||||
let routing_table = rpc_processor.routing_table();
|
let routing_table = rpc_processor.routing_table();
|
||||||
|
|
||||||
@ -44,15 +44,15 @@ impl StorageManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Make do-get-value answer context
|
// Make do-get-value answer context
|
||||||
let schema = if let Some(d) = &last_subkey_result.descriptor {
|
let schema = if let Some(d) = &last_get_result.opt_descriptor {
|
||||||
Some(d.schema()?)
|
Some(d.schema()?)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let context = Arc::new(Mutex::new(OutboundGetValueContext {
|
let context = Arc::new(Mutex::new(OutboundGetValueContext {
|
||||||
value: last_subkey_result.value,
|
value: last_get_result.opt_value,
|
||||||
value_nodes: vec![],
|
value_nodes: vec![],
|
||||||
descriptor: last_subkey_result.descriptor.clone(),
|
descriptor: last_get_result.opt_descriptor.clone(),
|
||||||
schema,
|
schema,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ impl StorageManager {
|
|||||||
let call_routine = |next_node: NodeRef| {
|
let call_routine = |next_node: NodeRef| {
|
||||||
let rpc_processor = rpc_processor.clone();
|
let rpc_processor = rpc_processor.clone();
|
||||||
let context = context.clone();
|
let context = context.clone();
|
||||||
let last_descriptor = last_subkey_result.descriptor.clone();
|
let last_descriptor = last_get_result.opt_descriptor.clone();
|
||||||
async move {
|
async move {
|
||||||
let gva = network_result_try!(
|
let gva = network_result_try!(
|
||||||
rpc_processor
|
rpc_processor
|
||||||
@ -79,14 +79,20 @@ impl StorageManager {
|
|||||||
if let Some(descriptor) = gva.answer.descriptor {
|
if let Some(descriptor) = gva.answer.descriptor {
|
||||||
let mut ctx = context.lock();
|
let mut ctx = context.lock();
|
||||||
if ctx.descriptor.is_none() && ctx.schema.is_none() {
|
if ctx.descriptor.is_none() && ctx.schema.is_none() {
|
||||||
ctx.schema = Some(descriptor.schema().map_err(RPCError::invalid_format)?);
|
let schema = match descriptor.schema() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
return Ok(NetworkResult::invalid_message(e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctx.schema = Some(schema);
|
||||||
ctx.descriptor = Some(Arc::new(descriptor));
|
ctx.descriptor = Some(Arc::new(descriptor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep the value if we got one and it is newer and it passes schema validation
|
// Keep the value if we got one and it is newer and it passes schema validation
|
||||||
if let Some(value) = gva.answer.value {
|
if let Some(value) = gva.answer.value {
|
||||||
log_stor!(debug "Got value back: len={} seq={}", value.value_data().data().len(), value.value_data().seq());
|
log_dht!(debug "Got value back: len={} seq={}", value.value_data().data().len(), value.value_data().seq());
|
||||||
let mut ctx = context.lock();
|
let mut ctx = context.lock();
|
||||||
|
|
||||||
// Ensure we have a schema and descriptor
|
// Ensure we have a schema and descriptor
|
||||||
@ -142,8 +148,7 @@ impl StorageManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return peers if we have some
|
// Return peers if we have some
|
||||||
#[cfg(feature = "network-result-extra")]
|
log_network_result!(debug "GetValue fanout call returned peers {}", gva.answer.peers.len());
|
||||||
log_stor!(debug "GetValue fanout call returned peers {}", gva.answer.peers.len());
|
|
||||||
|
|
||||||
Ok(NetworkResult::value(gva.answer.peers))
|
Ok(NetworkResult::value(gva.answer.peers))
|
||||||
}
|
}
|
||||||
@ -174,65 +179,35 @@ impl StorageManager {
|
|||||||
check_done,
|
check_done,
|
||||||
);
|
);
|
||||||
|
|
||||||
match fanout_call.run(vec![]).await {
|
let kind = match fanout_call.run(vec![]).await {
|
||||||
// If we don't finish in the timeout (too much time passed checking for consensus)
|
// If we don't finish in the timeout (too much time passed checking for consensus)
|
||||||
TimeoutOr::Timeout => {
|
TimeoutOr::Timeout => FanoutResultKind::Timeout,
|
||||||
// Return the best answer we've got
|
// If we finished with or without consensus (enough nodes returning the same value)
|
||||||
let ctx = context.lock();
|
TimeoutOr::Value(Ok(Some(()))) => FanoutResultKind::Finished,
|
||||||
if ctx.value_nodes.len() >= consensus_count {
|
// If we ran out of nodes before getting consensus)
|
||||||
log_stor!(debug "GetValue Fanout Timeout Consensus");
|
TimeoutOr::Value(Ok(None)) => FanoutResultKind::Exhausted,
|
||||||
} else {
|
|
||||||
log_stor!(debug "GetValue Fanout Timeout Non-Consensus: {}", ctx.value_nodes.len());
|
|
||||||
}
|
|
||||||
Ok(OutboundGetValueResult {
|
|
||||||
subkey_result: SubkeyResult {
|
|
||||||
value: ctx.value.clone(),
|
|
||||||
descriptor: ctx.descriptor.clone(),
|
|
||||||
},
|
|
||||||
value_nodes: ctx.value_nodes.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// If we finished with consensus (enough nodes returning the same value)
|
|
||||||
TimeoutOr::Value(Ok(Some(()))) => {
|
|
||||||
// Return the best answer we've got
|
|
||||||
let ctx = context.lock();
|
|
||||||
if ctx.value_nodes.len() >= consensus_count {
|
|
||||||
log_stor!(debug "GetValue Fanout Consensus");
|
|
||||||
} else {
|
|
||||||
log_stor!(debug "GetValue Fanout Non-Consensus: {}", ctx.value_nodes.len());
|
|
||||||
}
|
|
||||||
Ok(OutboundGetValueResult {
|
|
||||||
subkey_result: SubkeyResult {
|
|
||||||
value: ctx.value.clone(),
|
|
||||||
descriptor: ctx.descriptor.clone(),
|
|
||||||
},
|
|
||||||
value_nodes: ctx.value_nodes.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// If we finished without consensus (ran out of nodes before getting consensus)
|
|
||||||
TimeoutOr::Value(Ok(None)) => {
|
|
||||||
// Return the best answer we've got
|
|
||||||
let ctx = context.lock();
|
|
||||||
if ctx.value_nodes.len() >= consensus_count {
|
|
||||||
log_stor!(debug "GetValue Fanout Exhausted Consensus");
|
|
||||||
} else {
|
|
||||||
log_stor!(debug "GetValue Fanout Exhausted Non-Consensus: {}", ctx.value_nodes.len());
|
|
||||||
}
|
|
||||||
Ok(OutboundGetValueResult {
|
|
||||||
subkey_result: SubkeyResult {
|
|
||||||
value: ctx.value.clone(),
|
|
||||||
descriptor: ctx.descriptor.clone(),
|
|
||||||
},
|
|
||||||
value_nodes: ctx.value_nodes.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// Failed
|
// Failed
|
||||||
TimeoutOr::Value(Err(e)) => {
|
TimeoutOr::Value(Err(e)) => {
|
||||||
// If we finished with an error, return that
|
// If we finished with an error, return that
|
||||||
log_stor!(debug "GetValue Fanout Error: {}", e);
|
log_dht!(debug "GetValue Fanout Error: {}", e);
|
||||||
Err(e.into())
|
return Err(e.into());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ctx = context.lock();
|
||||||
|
let fanout_result = FanoutResult {
|
||||||
|
kind,
|
||||||
|
value_nodes: ctx.value_nodes.clone(),
|
||||||
|
};
|
||||||
|
log_network_result!(debug "GetValue Fanout: {:?}", fanout_result);
|
||||||
|
|
||||||
|
Ok(OutboundGetValueResult {
|
||||||
|
fanout_result,
|
||||||
|
get_result: GetResult {
|
||||||
|
opt_value: ctx.value.clone(),
|
||||||
|
opt_descriptor: ctx.descriptor.clone(),
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a received 'Get Value' query
|
/// Handle a received 'Get Value' query
|
||||||
@ -241,28 +216,28 @@ impl StorageManager {
|
|||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkey: ValueSubkey,
|
subkey: ValueSubkey,
|
||||||
want_descriptor: bool,
|
want_descriptor: bool,
|
||||||
) -> VeilidAPIResult<NetworkResult<SubkeyResult>> {
|
) -> VeilidAPIResult<NetworkResult<GetResult>> {
|
||||||
let mut inner = self.lock().await?;
|
let mut inner = self.lock().await?;
|
||||||
|
|
||||||
// See if this is a remote or local value
|
// See if this is a remote or local value
|
||||||
let (_is_local, last_subkey_result) = {
|
let (_is_local, last_get_result) = {
|
||||||
// See if the subkey we are getting has a last known local value
|
// See if the subkey we are getting has a last known local value
|
||||||
let mut last_subkey_result = inner.handle_get_local_value(key, subkey, true).await?;
|
let mut last_get_result = inner.handle_get_local_value(key, subkey, true).await?;
|
||||||
// If this is local, it must have a descriptor already
|
// If this is local, it must have a descriptor already
|
||||||
if last_subkey_result.descriptor.is_some() {
|
if last_get_result.opt_descriptor.is_some() {
|
||||||
if !want_descriptor {
|
if !want_descriptor {
|
||||||
last_subkey_result.descriptor = None;
|
last_get_result.opt_descriptor = None;
|
||||||
}
|
}
|
||||||
(true, last_subkey_result)
|
(true, last_get_result)
|
||||||
} else {
|
} else {
|
||||||
// See if the subkey we are getting has a last known remote value
|
// See if the subkey we are getting has a last known remote value
|
||||||
let last_subkey_result = inner
|
let last_get_result = inner
|
||||||
.handle_get_remote_value(key, subkey, want_descriptor)
|
.handle_get_remote_value(key, subkey, want_descriptor)
|
||||||
.await?;
|
.await?;
|
||||||
(false, last_subkey_result)
|
(false, last_get_result)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(NetworkResult::value(last_subkey_result))
|
Ok(NetworkResult::value(last_get_result))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
337
veilid-core/src/storage_manager/inspect_value.rs
Normal file
337
veilid-core/src/storage_manager/inspect_value.rs
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// The fully parsed descriptor
|
||||||
|
struct DescriptorInfo {
|
||||||
|
/// The descriptor itself
|
||||||
|
descriptor: Arc<SignedValueDescriptor>,
|
||||||
|
|
||||||
|
/// The in-schema subkeys that overlap the inspected range
|
||||||
|
subkeys: ValueSubkeyRangeSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DescriptorInfo {
|
||||||
|
pub fn new(
|
||||||
|
descriptor: Arc<SignedValueDescriptor>,
|
||||||
|
subkeys: &ValueSubkeyRangeSet,
|
||||||
|
) -> VeilidAPIResult<Self> {
|
||||||
|
let schema = descriptor.schema().map_err(RPCError::invalid_format)?;
|
||||||
|
let subkeys = schema.truncate_subkeys(subkeys, Some(MAX_INSPECT_VALUE_A_SEQS_LEN));
|
||||||
|
Ok(Self {
|
||||||
|
descriptor,
|
||||||
|
subkeys,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Info tracked per subkey
|
||||||
|
struct SubkeySeqCount {
|
||||||
|
/// The newest sequence number found for a subkey
|
||||||
|
pub seq: ValueSeqNum,
|
||||||
|
/// The nodes that have returned the value so far (up to the consensus count)
|
||||||
|
pub value_nodes: Vec<NodeRef>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The context of the outbound_get_value operation
|
||||||
|
struct OutboundInspectValueContext {
|
||||||
|
/// The combined sequence numbers and result counts so far
|
||||||
|
pub seqcounts: Vec<SubkeySeqCount>,
|
||||||
|
/// The descriptor if we got a fresh one or empty if no descriptor was needed
|
||||||
|
pub opt_descriptor_info: Option<DescriptorInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The result of the outbound_get_value operation
|
||||||
|
pub(super) struct OutboundInspectValueResult {
|
||||||
|
/// Fanout results for each subkey
|
||||||
|
pub fanout_results: Vec<FanoutResult>,
|
||||||
|
/// The inspection that was retrieved
|
||||||
|
pub inspect_result: InspectResult,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StorageManager {
|
||||||
|
/// Perform a 'inspect value' query on the network
|
||||||
|
pub(super) async fn outbound_inspect_value(
|
||||||
|
&self,
|
||||||
|
rpc_processor: RPCProcessor,
|
||||||
|
key: TypedKey,
|
||||||
|
subkeys: ValueSubkeyRangeSet,
|
||||||
|
safety_selection: SafetySelection,
|
||||||
|
local_inspect_result: InspectResult,
|
||||||
|
use_set_scope: bool,
|
||||||
|
) -> VeilidAPIResult<OutboundInspectValueResult> {
|
||||||
|
let routing_table = rpc_processor.routing_table();
|
||||||
|
|
||||||
|
// Get the DHT parameters for 'InspectValue'
|
||||||
|
// Can use either 'get scope' or 'set scope' depending on the purpose of the inspection
|
||||||
|
let (key_count, consensus_count, fanout, timeout_us) = {
|
||||||
|
let c = self.unlocked_inner.config.get();
|
||||||
|
|
||||||
|
if use_set_scope {
|
||||||
|
(
|
||||||
|
c.network.dht.max_find_node_count as usize,
|
||||||
|
c.network.dht.set_value_count as usize,
|
||||||
|
c.network.dht.set_value_fanout as usize,
|
||||||
|
TimestampDuration::from(ms_to_us(c.network.dht.set_value_timeout_ms)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
c.network.dht.max_find_node_count as usize,
|
||||||
|
c.network.dht.get_value_count as usize,
|
||||||
|
c.network.dht.get_value_fanout as usize,
|
||||||
|
TimestampDuration::from(ms_to_us(c.network.dht.get_value_timeout_ms)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make do-inspect-value answer context
|
||||||
|
let opt_descriptor_info = if let Some(descriptor) = &local_inspect_result.opt_descriptor {
|
||||||
|
// Get the descriptor info. This also truncates the subkeys list to what can be returned from the network.
|
||||||
|
Some(DescriptorInfo::new(descriptor.clone(), &subkeys)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let context = Arc::new(Mutex::new(OutboundInspectValueContext {
|
||||||
|
seqcounts: local_inspect_result
|
||||||
|
.seqs
|
||||||
|
.iter()
|
||||||
|
.map(|s| SubkeySeqCount {
|
||||||
|
seq: *s,
|
||||||
|
value_nodes: vec![],
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
opt_descriptor_info,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Routine to call to generate fanout
|
||||||
|
let call_routine = |next_node: NodeRef| {
|
||||||
|
let rpc_processor = rpc_processor.clone();
|
||||||
|
let context = context.clone();
|
||||||
|
let opt_descriptor = local_inspect_result.opt_descriptor.clone();
|
||||||
|
let subkeys = subkeys.clone();
|
||||||
|
async move {
|
||||||
|
let iva = network_result_try!(
|
||||||
|
rpc_processor
|
||||||
|
.clone()
|
||||||
|
.rpc_call_inspect_value(
|
||||||
|
Destination::direct(next_node.clone()).with_safety(safety_selection),
|
||||||
|
key,
|
||||||
|
subkeys.clone(),
|
||||||
|
opt_descriptor.map(|x| (*x).clone()),
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
);
|
||||||
|
let answer = iva.answer;
|
||||||
|
|
||||||
|
// Keep the descriptor if we got one. If we had a last_descriptor it will
|
||||||
|
// already be validated by rpc_call_inspect_value
|
||||||
|
if let Some(descriptor) = answer.descriptor {
|
||||||
|
let mut ctx = context.lock();
|
||||||
|
if ctx.opt_descriptor_info.is_none() {
|
||||||
|
// Get the descriptor info. This also truncates the subkeys list to what can be returned from the network.
|
||||||
|
let descriptor_info =
|
||||||
|
match DescriptorInfo::new(Arc::new(descriptor.clone()), &subkeys) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
return Ok(NetworkResult::invalid_message(e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctx.opt_descriptor_info = Some(descriptor_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the value if we got one and it is newer and it passes schema validation
|
||||||
|
if !answer.seqs.is_empty() {
|
||||||
|
log_dht!(debug "Got seqs back: len={}", answer.seqs.len());
|
||||||
|
let mut ctx = context.lock();
|
||||||
|
|
||||||
|
// Ensure we have a schema and descriptor etc
|
||||||
|
let Some(descriptor_info) = &ctx.opt_descriptor_info else {
|
||||||
|
// Got a value but no descriptor for it
|
||||||
|
// Move to the next node
|
||||||
|
return Ok(NetworkResult::invalid_message(
|
||||||
|
"Got inspection with no descriptor",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get number of subkeys from schema and ensure we are getting the
|
||||||
|
// right number of sequence numbers betwen that and what we asked for
|
||||||
|
#[allow(clippy::unnecessary_cast)]
|
||||||
|
if answer.seqs.len() as u64 != descriptor_info.subkeys.len() as u64 {
|
||||||
|
// Not the right number of sequence numbers
|
||||||
|
// Move to the next node
|
||||||
|
return Ok(NetworkResult::invalid_message(format!(
|
||||||
|
"wrong number of seqs returned {} (wanted {})",
|
||||||
|
answer.seqs.len(),
|
||||||
|
descriptor_info.subkeys.len()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a prior seqs list, merge in the new seqs
|
||||||
|
if ctx.seqcounts.is_empty() {
|
||||||
|
ctx.seqcounts = answer
|
||||||
|
.seqs
|
||||||
|
.iter()
|
||||||
|
.map(|s| SubkeySeqCount {
|
||||||
|
seq: *s,
|
||||||
|
// One node has shown us the newest sequence numbers so far
|
||||||
|
value_nodes: if *s == ValueSeqNum::MAX {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec![next_node.clone()]
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
} else {
|
||||||
|
if ctx.seqcounts.len() != answer.seqs.len() {
|
||||||
|
return Err(RPCError::internal(
|
||||||
|
"seqs list length should always be equal by now",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
for pair in ctx.seqcounts.iter_mut().zip(answer.seqs.iter()) {
|
||||||
|
let ctx_seqcnt = pair.0;
|
||||||
|
let answer_seq = *pair.1;
|
||||||
|
|
||||||
|
// If we already have consensus for this subkey, don't bother updating it any more
|
||||||
|
// While we may find a better sequence number if we keep looking, this does not mimic the behavior
|
||||||
|
// of get and set unless we stop here
|
||||||
|
if ctx_seqcnt.value_nodes.len() >= consensus_count {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the new seq isn't undefined and is better than the old seq (either greater or old is undefined)
|
||||||
|
// Then take that sequence number and note that we have gotten newer sequence numbers so we keep
|
||||||
|
// looking for consensus
|
||||||
|
// If the sequence number matches the old sequence number, then we keep the value node for reference later
|
||||||
|
if answer_seq != ValueSeqNum::MAX {
|
||||||
|
if ctx_seqcnt.seq == ValueSeqNum::MAX || answer_seq > ctx_seqcnt.seq
|
||||||
|
{
|
||||||
|
// One node has shown us the latest sequence numbers so far
|
||||||
|
ctx_seqcnt.seq = answer_seq;
|
||||||
|
ctx_seqcnt.value_nodes = vec![next_node.clone()];
|
||||||
|
} else if answer_seq == ctx_seqcnt.seq {
|
||||||
|
// Keep the nodes that showed us the latest values
|
||||||
|
ctx_seqcnt.value_nodes.push(next_node.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return peers if we have some
|
||||||
|
log_network_result!(debug "InspectValue fanout call returned peers {}", answer.peers.len());
|
||||||
|
|
||||||
|
Ok(NetworkResult::value(answer.peers))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Routine to call to check if we're done at each step
|
||||||
|
let check_done = |_closest_nodes: &[NodeRef]| {
|
||||||
|
// If we have reached sufficient consensus on all subkeys, return done
|
||||||
|
let ctx = context.lock();
|
||||||
|
let mut has_consensus = true;
|
||||||
|
for cs in ctx.seqcounts.iter() {
|
||||||
|
if cs.value_nodes.len() < consensus_count {
|
||||||
|
has_consensus = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ctx.seqcounts.is_empty() && ctx.opt_descriptor_info.is_some() && has_consensus {
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call the fanout
|
||||||
|
let fanout_call = FanoutCall::new(
|
||||||
|
routing_table.clone(),
|
||||||
|
key,
|
||||||
|
key_count,
|
||||||
|
fanout,
|
||||||
|
timeout_us,
|
||||||
|
capability_fanout_node_info_filter(vec![CAP_DHT, CAP_DHT_WATCH]),
|
||||||
|
call_routine,
|
||||||
|
check_done,
|
||||||
|
);
|
||||||
|
|
||||||
|
let kind = match fanout_call.run(vec![]).await {
|
||||||
|
// If we don't finish in the timeout (too much time passed checking for consensus)
|
||||||
|
TimeoutOr::Timeout => FanoutResultKind::Timeout,
|
||||||
|
// If we finished with or without consensus (enough nodes returning the same value)
|
||||||
|
TimeoutOr::Value(Ok(Some(()))) => FanoutResultKind::Finished,
|
||||||
|
// If we ran out of nodes before getting consensus)
|
||||||
|
TimeoutOr::Value(Ok(None)) => FanoutResultKind::Exhausted,
|
||||||
|
// Failed
|
||||||
|
TimeoutOr::Value(Err(e)) => {
|
||||||
|
// If we finished with an error, return that
|
||||||
|
log_dht!(debug "InspectValue Fanout Error: {}", e);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ctx = context.lock();
|
||||||
|
let mut fanout_results = vec![];
|
||||||
|
for cs in &ctx.seqcounts {
|
||||||
|
let has_consensus = cs.value_nodes.len() >= consensus_count;
|
||||||
|
let fanout_result = FanoutResult {
|
||||||
|
kind: if has_consensus {
|
||||||
|
FanoutResultKind::Finished
|
||||||
|
} else {
|
||||||
|
kind
|
||||||
|
},
|
||||||
|
value_nodes: cs.value_nodes.clone(),
|
||||||
|
};
|
||||||
|
fanout_results.push(fanout_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_network_result!(debug "InspectValue Fanout ({:?}):\n{}", kind, debug_fanout_results(&fanout_results));
|
||||||
|
|
||||||
|
Ok(OutboundInspectValueResult {
|
||||||
|
fanout_results,
|
||||||
|
inspect_result: InspectResult {
|
||||||
|
subkeys: ctx
|
||||||
|
.opt_descriptor_info
|
||||||
|
.as_ref()
|
||||||
|
.map(|d| d.subkeys.clone())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
seqs: ctx.seqcounts.iter().map(|cs| cs.seq).collect(),
|
||||||
|
opt_descriptor: ctx
|
||||||
|
.opt_descriptor_info
|
||||||
|
.as_ref()
|
||||||
|
.map(|d| d.descriptor.clone()),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle a received 'Inspect Value' query
|
||||||
|
pub async fn inbound_inspect_value(
|
||||||
|
&self,
|
||||||
|
key: TypedKey,
|
||||||
|
subkeys: ValueSubkeyRangeSet,
|
||||||
|
want_descriptor: bool,
|
||||||
|
) -> VeilidAPIResult<NetworkResult<InspectResult>> {
|
||||||
|
let mut inner = self.lock().await?;
|
||||||
|
|
||||||
|
// See if this is a remote or local value
|
||||||
|
let (_is_local, inspect_result) = {
|
||||||
|
// See if the subkey we are getting has a last known local value
|
||||||
|
let mut local_inspect_result = inner
|
||||||
|
.handle_inspect_local_value(key, subkeys.clone(), true)
|
||||||
|
.await?;
|
||||||
|
// If this is local, it must have a descriptor already
|
||||||
|
if local_inspect_result.opt_descriptor.is_some() {
|
||||||
|
if !want_descriptor {
|
||||||
|
local_inspect_result.opt_descriptor = None;
|
||||||
|
}
|
||||||
|
(true, local_inspect_result)
|
||||||
|
} else {
|
||||||
|
// See if the subkey we are getting has a last known remote value
|
||||||
|
let remote_inspect_result = inner
|
||||||
|
.handle_inspect_remote_value(key, subkeys, want_descriptor)
|
||||||
|
.await?;
|
||||||
|
(false, remote_inspect_result)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(NetworkResult::value(inspect_result))
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,22 @@
|
|||||||
mod debug;
|
mod debug;
|
||||||
mod get_value;
|
mod get_value;
|
||||||
mod keys;
|
mod inspect_value;
|
||||||
mod limited_size;
|
|
||||||
mod record_store;
|
mod record_store;
|
||||||
mod record_store_limits;
|
|
||||||
mod set_value;
|
mod set_value;
|
||||||
mod storage_manager_inner;
|
mod storage_manager_inner;
|
||||||
mod tasks;
|
mod tasks;
|
||||||
mod types;
|
mod types;
|
||||||
mod watch_value;
|
mod watch_value;
|
||||||
|
|
||||||
use keys::*;
|
|
||||||
use limited_size::*;
|
|
||||||
use record_store::*;
|
|
||||||
use record_store_limits::*;
|
|
||||||
use storage_manager_inner::*;
|
|
||||||
|
|
||||||
pub use types::*;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use network_manager::*;
|
use network_manager::*;
|
||||||
|
use record_store::*;
|
||||||
use routing_table::*;
|
use routing_table::*;
|
||||||
use rpc_processor::*;
|
use rpc_processor::*;
|
||||||
|
use storage_manager_inner::*;
|
||||||
|
|
||||||
|
pub use record_store::{WatchParameters, WatchResult};
|
||||||
|
pub use types::*;
|
||||||
|
|
||||||
/// The maximum size of a single subkey
|
/// The maximum size of a single subkey
|
||||||
const MAX_SUBKEY_SIZE: usize = ValueData::MAX_LEN;
|
const MAX_SUBKEY_SIZE: usize = ValueData::MAX_LEN;
|
||||||
@ -33,8 +28,10 @@ const FLUSH_RECORD_STORES_INTERVAL_SECS: u32 = 1;
|
|||||||
const OFFLINE_SUBKEY_WRITES_INTERVAL_SECS: u32 = 1;
|
const OFFLINE_SUBKEY_WRITES_INTERVAL_SECS: u32 = 1;
|
||||||
/// Frequency to send ValueChanged notifications to the network
|
/// Frequency to send ValueChanged notifications to the network
|
||||||
const SEND_VALUE_CHANGES_INTERVAL_SECS: u32 = 1;
|
const SEND_VALUE_CHANGES_INTERVAL_SECS: u32 = 1;
|
||||||
/// Frequence to check for dead nodes and routes for active watches
|
/// Frequency to check for dead nodes and routes for client-side active watches
|
||||||
const CHECK_ACTIVE_WATCHES_INTERVAL_SECS: u32 = 1;
|
const CHECK_ACTIVE_WATCHES_INTERVAL_SECS: u32 = 1;
|
||||||
|
/// Frequency to check for expired server-side watched records
|
||||||
|
const CHECK_WATCHED_RECORDS_INTERVAL_SECS: u32 = 1;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
/// A single 'value changed' message to send
|
/// A single 'value changed' message to send
|
||||||
@ -43,6 +40,7 @@ struct ValueChangedInfo {
|
|||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkeys: ValueSubkeyRangeSet,
|
subkeys: ValueSubkeyRangeSet,
|
||||||
count: u32,
|
count: u32,
|
||||||
|
watch_id: u64,
|
||||||
value: Arc<SignedValueData>,
|
value: Arc<SignedValueData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +56,7 @@ struct StorageManagerUnlockedInner {
|
|||||||
offline_subkey_writes_task: TickTask<EyreReport>,
|
offline_subkey_writes_task: TickTask<EyreReport>,
|
||||||
send_value_changes_task: TickTask<EyreReport>,
|
send_value_changes_task: TickTask<EyreReport>,
|
||||||
check_active_watches_task: TickTask<EyreReport>,
|
check_active_watches_task: TickTask<EyreReport>,
|
||||||
|
check_watched_records_task: TickTask<EyreReport>,
|
||||||
|
|
||||||
// Anonymous watch keys
|
// Anonymous watch keys
|
||||||
anonymous_watch_keys: TypedKeyPairGroup,
|
anonymous_watch_keys: TypedKeyPairGroup,
|
||||||
@ -94,6 +93,7 @@ impl StorageManager {
|
|||||||
offline_subkey_writes_task: TickTask::new(OFFLINE_SUBKEY_WRITES_INTERVAL_SECS),
|
offline_subkey_writes_task: TickTask::new(OFFLINE_SUBKEY_WRITES_INTERVAL_SECS),
|
||||||
send_value_changes_task: TickTask::new(SEND_VALUE_CHANGES_INTERVAL_SECS),
|
send_value_changes_task: TickTask::new(SEND_VALUE_CHANGES_INTERVAL_SECS),
|
||||||
check_active_watches_task: TickTask::new(CHECK_ACTIVE_WATCHES_INTERVAL_SECS),
|
check_active_watches_task: TickTask::new(CHECK_ACTIVE_WATCHES_INTERVAL_SECS),
|
||||||
|
check_watched_records_task: TickTask::new(CHECK_WATCHED_RECORDS_INTERVAL_SECS),
|
||||||
|
|
||||||
anonymous_watch_keys,
|
anonymous_watch_keys,
|
||||||
}
|
}
|
||||||
@ -127,7 +127,7 @@ impl StorageManager {
|
|||||||
|
|
||||||
#[instrument(level = "debug", skip_all, err)]
|
#[instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn init(&self, update_callback: UpdateCallback) -> EyreResult<()> {
|
pub async fn init(&self, update_callback: UpdateCallback) -> EyreResult<()> {
|
||||||
debug!("startup storage manager");
|
log_stor!(debug "startup storage manager");
|
||||||
|
|
||||||
let mut inner = self.inner.lock().await;
|
let mut inner = self.inner.lock().await;
|
||||||
inner.init(self.clone(), update_callback).await?;
|
inner.init(self.clone(), update_callback).await?;
|
||||||
@ -137,7 +137,7 @@ impl StorageManager {
|
|||||||
|
|
||||||
#[instrument(level = "debug", skip_all)]
|
#[instrument(level = "debug", skip_all)]
|
||||||
pub async fn terminate(&self) {
|
pub async fn terminate(&self) {
|
||||||
debug!("starting storage manager shutdown");
|
log_stor!(debug "starting storage manager shutdown");
|
||||||
|
|
||||||
let mut inner = self.inner.lock().await;
|
let mut inner = self.inner.lock().await;
|
||||||
inner.terminate().await;
|
inner.terminate().await;
|
||||||
@ -148,7 +148,7 @@ impl StorageManager {
|
|||||||
// Release the storage manager
|
// Release the storage manager
|
||||||
*inner = Self::new_inner(self.unlocked_inner.clone());
|
*inner = Self::new_inner(self.unlocked_inner.clone());
|
||||||
|
|
||||||
debug!("finished storage manager shutdown");
|
log_stor!(debug "finished storage manager shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_rpc_processor(&self, opt_rpc_processor: Option<RPCProcessor>) {
|
pub async fn set_rpc_processor(&self, opt_rpc_processor: Option<RPCProcessor>) {
|
||||||
@ -169,7 +169,7 @@ impl StorageManager {
|
|||||||
Ok(inner)
|
Ok(inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn online_writes_ready_inner(inner: &StorageManagerInner) -> Option<RPCProcessor> {
|
fn online_ready_inner(inner: &StorageManagerInner) -> Option<RPCProcessor> {
|
||||||
if let Some(rpc_processor) = { inner.opt_rpc_processor.clone() } {
|
if let Some(rpc_processor) = { inner.opt_rpc_processor.clone() } {
|
||||||
if let Some(network_class) = rpc_processor
|
if let Some(network_class) = rpc_processor
|
||||||
.routing_table()
|
.routing_table()
|
||||||
@ -193,7 +193,7 @@ impl StorageManager {
|
|||||||
|
|
||||||
async fn online_writes_ready(&self) -> EyreResult<Option<RPCProcessor>> {
|
async fn online_writes_ready(&self) -> EyreResult<Option<RPCProcessor>> {
|
||||||
let inner = self.lock().await?;
|
let inner = self.lock().await?;
|
||||||
Ok(Self::online_writes_ready_inner(&inner))
|
Ok(Self::online_ready_inner(&inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn has_offline_subkey_writes(&self) -> EyreResult<bool> {
|
async fn has_offline_subkey_writes(&self) -> EyreResult<bool> {
|
||||||
@ -209,6 +209,7 @@ impl StorageManager {
|
|||||||
safety_selection: SafetySelection,
|
safety_selection: SafetySelection,
|
||||||
) -> VeilidAPIResult<DHTRecordDescriptor> {
|
) -> VeilidAPIResult<DHTRecordDescriptor> {
|
||||||
let mut inner = self.lock().await?;
|
let mut inner = self.lock().await?;
|
||||||
|
schema.validate()?;
|
||||||
|
|
||||||
// Create a new owned local record from scratch
|
// Create a new owned local record from scratch
|
||||||
let (key, owner) = inner
|
let (key, owner) = inner
|
||||||
@ -243,7 +244,7 @@ impl StorageManager {
|
|||||||
// No record yet, try to get it from the network
|
// No record yet, try to get it from the network
|
||||||
|
|
||||||
// Get rpc processor and drop mutex so we don't block while getting the value from the network
|
// Get rpc processor and drop mutex so we don't block while getting the value from the network
|
||||||
let Some(rpc_processor) = inner.opt_rpc_processor.clone() else {
|
let Some(rpc_processor) = Self::online_ready_inner(&inner) else {
|
||||||
apibail_try_again!("offline, try again later");
|
apibail_try_again!("offline, try again later");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -259,12 +260,12 @@ impl StorageManager {
|
|||||||
key,
|
key,
|
||||||
subkey,
|
subkey,
|
||||||
safety_selection,
|
safety_selection,
|
||||||
SubkeyResult::default(),
|
GetResult::default(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// If we got nothing back, the key wasn't found
|
// If we got nothing back, the key wasn't found
|
||||||
if result.subkey_result.value.is_none() && result.subkey_result.descriptor.is_none() {
|
if result.get_result.opt_value.is_none() && result.get_result.opt_descriptor.is_none() {
|
||||||
// No result
|
// No result
|
||||||
apibail_key_not_found!(key);
|
apibail_key_not_found!(key);
|
||||||
};
|
};
|
||||||
@ -285,7 +286,7 @@ impl StorageManager {
|
|||||||
|
|
||||||
// Open the new record
|
// Open the new record
|
||||||
inner
|
inner
|
||||||
.open_new_record(key, writer, subkey, result.subkey_result, safety_selection)
|
.open_new_record(key, writer, subkey, result.get_result, safety_selection)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +294,7 @@ impl StorageManager {
|
|||||||
pub async fn close_record(&self, key: TypedKey) -> VeilidAPIResult<()> {
|
pub async fn close_record(&self, key: TypedKey) -> VeilidAPIResult<()> {
|
||||||
let (opt_opened_record, opt_rpc_processor) = {
|
let (opt_opened_record, opt_rpc_processor) = {
|
||||||
let mut inner = self.lock().await?;
|
let mut inner = self.lock().await?;
|
||||||
(inner.close_record(key)?, inner.opt_rpc_processor.clone())
|
(inner.close_record(key)?, Self::online_ready_inner(&inner))
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send a one-time cancel request for the watch if we have one and we're online
|
// Send a one-time cancel request for the watch if we have one and we're online
|
||||||
@ -311,14 +312,14 @@ impl StorageManager {
|
|||||||
0,
|
0,
|
||||||
opened_record.safety_selection(),
|
opened_record.safety_selection(),
|
||||||
opened_record.writer().cloned(),
|
opened_record.writer().cloned(),
|
||||||
|
Some(active_watch.id),
|
||||||
Some(active_watch.watch_node),
|
Some(active_watch.watch_node),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
if let Some(owvresult) = opt_owvresult {
|
if let Some(owvresult) = opt_owvresult {
|
||||||
if owvresult.expiration_ts.as_u64() != 0 {
|
if owvresult.expiration_ts.as_u64() != 0 {
|
||||||
log_stor!(debug
|
log_stor!(debug
|
||||||
"close record watch cancel got unexpected expiration: {}",
|
"close record watch cancel should have zero expiration"
|
||||||
owvresult.expiration_ts
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -364,22 +365,22 @@ impl StorageManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// See if the requested subkey is our local record store
|
// See if the requested subkey is our local record store
|
||||||
let last_subkey_result = inner.handle_get_local_value(key, subkey, true).await?;
|
let last_get_result = inner.handle_get_local_value(key, subkey, true).await?;
|
||||||
|
|
||||||
// Return the existing value if we have one unless we are forcing a refresh
|
// Return the existing value if we have one unless we are forcing a refresh
|
||||||
if !force_refresh {
|
if !force_refresh {
|
||||||
if let Some(last_subkey_result_value) = last_subkey_result.value {
|
if let Some(last_get_result_value) = last_get_result.opt_value {
|
||||||
return Ok(Some(last_subkey_result_value.value_data().clone()));
|
return Ok(Some(last_get_result_value.value_data().clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh if we can
|
// Refresh if we can
|
||||||
|
|
||||||
// Get rpc processor and drop mutex so we don't block while getting the value from the network
|
// Get rpc processor and drop mutex so we don't block while getting the value from the network
|
||||||
let Some(rpc_processor) = inner.opt_rpc_processor.clone() else {
|
let Some(rpc_processor) = Self::online_ready_inner(&inner) else {
|
||||||
// Return the existing value if we have one if we aren't online
|
// Return the existing value if we have one if we aren't online
|
||||||
if let Some(last_subkey_result_value) = last_subkey_result.value {
|
if let Some(last_get_result_value) = last_get_result.opt_value {
|
||||||
return Ok(Some(last_subkey_result_value.value_data().clone()));
|
return Ok(Some(last_get_result_value.value_data().clone()));
|
||||||
}
|
}
|
||||||
apibail_try_again!("offline, try again later");
|
apibail_try_again!("offline, try again later");
|
||||||
};
|
};
|
||||||
@ -389,8 +390,8 @@ impl StorageManager {
|
|||||||
|
|
||||||
// May have last descriptor / value
|
// May have last descriptor / value
|
||||||
// Use the safety selection we opened the record with
|
// Use the safety selection we opened the record with
|
||||||
let opt_last_seq = last_subkey_result
|
let opt_last_seq = last_get_result
|
||||||
.value
|
.opt_value
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|v| v.value_data().seq());
|
.map(|v| v.value_data().seq());
|
||||||
let result = self
|
let result = self
|
||||||
@ -399,32 +400,36 @@ impl StorageManager {
|
|||||||
key,
|
key,
|
||||||
subkey,
|
subkey,
|
||||||
safety_selection,
|
safety_selection,
|
||||||
last_subkey_result,
|
last_get_result,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// See if we got a value back
|
// See if we got a value back
|
||||||
let Some(subkey_result_value) = result.subkey_result.value else {
|
let Some(get_result_value) = result.get_result.opt_value else {
|
||||||
// If we got nothing back then we also had nothing beforehand, return nothing
|
// If we got nothing back then we also had nothing beforehand, return nothing
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Keep the list of nodes that returned a value for later reference
|
// Keep the list of nodes that returned a value for later reference
|
||||||
let mut inner = self.lock().await?;
|
let mut inner = self.lock().await?;
|
||||||
inner.set_value_nodes(key, result.value_nodes)?;
|
inner.process_fanout_results(
|
||||||
|
key,
|
||||||
|
core::iter::once((subkey, &result.fanout_result)),
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
// If we got a new value back then write it to the opened record
|
// If we got a new value back then write it to the opened record
|
||||||
if Some(subkey_result_value.value_data().seq()) != opt_last_seq {
|
if Some(get_result_value.value_data().seq()) != opt_last_seq {
|
||||||
inner
|
inner
|
||||||
.handle_set_local_value(
|
.handle_set_local_value(
|
||||||
key,
|
key,
|
||||||
subkey,
|
subkey,
|
||||||
subkey_result_value.clone(),
|
get_result_value.clone(),
|
||||||
WatchUpdateMode::UpdateAll,
|
WatchUpdateMode::UpdateAll,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
Ok(Some(subkey_result_value.value_data().clone()))
|
Ok(Some(get_result_value.value_data().clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the value of a subkey on an opened local record
|
/// Set the value of a subkey on an opened local record
|
||||||
@ -433,6 +438,7 @@ impl StorageManager {
|
|||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkey: ValueSubkey,
|
subkey: ValueSubkey,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
|
writer: Option<KeyPair>,
|
||||||
) -> VeilidAPIResult<Option<ValueData>> {
|
) -> VeilidAPIResult<Option<ValueData>> {
|
||||||
let mut inner = self.lock().await?;
|
let mut inner = self.lock().await?;
|
||||||
|
|
||||||
@ -451,22 +457,25 @@ impl StorageManager {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Use the specified writer, or if not specified, the default writer when the record was opened
|
||||||
|
let opt_writer = writer.or(opt_writer);
|
||||||
|
|
||||||
// If we don't have a writer then we can't write
|
// If we don't have a writer then we can't write
|
||||||
let Some(writer) = opt_writer else {
|
let Some(writer) = opt_writer else {
|
||||||
apibail_generic!("value is not writable");
|
apibail_generic!("value is not writable");
|
||||||
};
|
};
|
||||||
|
|
||||||
// See if the subkey we are modifying has a last known local value
|
// See if the subkey we are modifying has a last known local value
|
||||||
let last_subkey_result = inner.handle_get_local_value(key, subkey, true).await?;
|
let last_get_result = inner.handle_get_local_value(key, subkey, true).await?;
|
||||||
|
|
||||||
// Get the descriptor and schema for the key
|
// Get the descriptor and schema for the key
|
||||||
let Some(descriptor) = last_subkey_result.descriptor else {
|
let Some(descriptor) = last_get_result.opt_descriptor else {
|
||||||
apibail_generic!("must have a descriptor");
|
apibail_generic!("must have a descriptor");
|
||||||
};
|
};
|
||||||
let schema = descriptor.schema()?;
|
let schema = descriptor.schema()?;
|
||||||
|
|
||||||
// Make new subkey data
|
// Make new subkey data
|
||||||
let value_data = if let Some(last_signed_value_data) = last_subkey_result.value {
|
let value_data = if let Some(last_signed_value_data) = last_get_result.opt_value {
|
||||||
if last_signed_value_data.value_data().data() == data
|
if last_signed_value_data.value_data().data() == data
|
||||||
&& last_signed_value_data.value_data().writer() == &writer.key
|
&& last_signed_value_data.value_data().writer() == &writer.key
|
||||||
{
|
{
|
||||||
@ -495,20 +504,19 @@ impl StorageManager {
|
|||||||
writer.secret,
|
writer.secret,
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
// Get rpc processor and drop mutex so we don't block while getting the value from the network
|
// Write the value locally first
|
||||||
let Some(rpc_processor) = Self::online_writes_ready_inner(&inner) else {
|
|
||||||
log_stor!(debug "Writing subkey locally: {}:{} len={}", key, subkey, signed_value_data.value_data().data().len() );
|
log_stor!(debug "Writing subkey locally: {}:{} len={}", key, subkey, signed_value_data.value_data().data().len() );
|
||||||
|
|
||||||
// Offline, just write it locally and return immediately
|
|
||||||
inner
|
inner
|
||||||
.handle_set_local_value(
|
.handle_set_local_value(
|
||||||
key,
|
key,
|
||||||
subkey,
|
subkey,
|
||||||
signed_value_data.clone(),
|
signed_value_data.clone(),
|
||||||
WatchUpdateMode::UpdateAll,
|
WatchUpdateMode::NoUpdate,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// Get rpc processor and drop mutex so we don't block while getting the value from the network
|
||||||
|
let Some(rpc_processor) = Self::online_ready_inner(&inner) else {
|
||||||
log_stor!(debug "Writing subkey offline: {}:{} len={}", key, subkey, signed_value_data.value_data().data().len() );
|
log_stor!(debug "Writing subkey offline: {}:{} len={}", key, subkey, signed_value_data.value_data().data().len() );
|
||||||
// Add to offline writes to flush
|
// Add to offline writes to flush
|
||||||
inner
|
inner
|
||||||
@ -527,6 +535,8 @@ impl StorageManager {
|
|||||||
// Drop the lock for network access
|
// Drop the lock for network access
|
||||||
drop(inner);
|
drop(inner);
|
||||||
|
|
||||||
|
log_stor!(debug "Writing subkey to the network: {}:{} len={}", key, subkey, signed_value_data.value_data().data().len() );
|
||||||
|
|
||||||
// Use the safety selection we opened the record with
|
// Use the safety selection we opened the record with
|
||||||
let result = self
|
let result = self
|
||||||
.outbound_set_value(
|
.outbound_set_value(
|
||||||
@ -541,9 +551,15 @@ impl StorageManager {
|
|||||||
|
|
||||||
// Keep the list of nodes that returned a value for later reference
|
// Keep the list of nodes that returned a value for later reference
|
||||||
let mut inner = self.lock().await?;
|
let mut inner = self.lock().await?;
|
||||||
inner.set_value_nodes(key, result.value_nodes)?;
|
inner.process_fanout_results(
|
||||||
|
key,
|
||||||
|
core::iter::once((subkey, &result.fanout_result)),
|
||||||
|
true,
|
||||||
|
)?;
|
||||||
|
|
||||||
// Whatever record we got back, store it locally, might be newer than the one we asked to save
|
// Return the new value if it differs from what was asked to set
|
||||||
|
if result.signed_value_data.value_data() != signed_value_data.value_data() {
|
||||||
|
// Record the newer value and send and update since it is different than what we just set
|
||||||
inner
|
inner
|
||||||
.handle_set_local_value(
|
.handle_set_local_value(
|
||||||
key,
|
key,
|
||||||
@ -553,8 +569,6 @@ impl StorageManager {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Return the new value if it differs from what was asked to set
|
|
||||||
if result.signed_value_data.value_data() != signed_value_data.value_data() {
|
|
||||||
return Ok(Some(result.signed_value_data.value_data().clone()));
|
return Ok(Some(result.signed_value_data.value_data().clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -562,7 +576,7 @@ impl StorageManager {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a watch to a DHT value
|
/// Create,update or cancel an outbound watch to a DHT value
|
||||||
pub async fn watch_values(
|
pub async fn watch_values(
|
||||||
&self,
|
&self,
|
||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
@ -572,6 +586,20 @@ impl StorageManager {
|
|||||||
) -> VeilidAPIResult<Timestamp> {
|
) -> VeilidAPIResult<Timestamp> {
|
||||||
let inner = self.lock().await?;
|
let inner = self.lock().await?;
|
||||||
|
|
||||||
|
// Get the safety selection and the writer we opened this record
|
||||||
|
// and whatever active watch id and watch node we may have in case this is a watch update
|
||||||
|
let (safety_selection, opt_writer, opt_watch_id, opt_watch_node) = {
|
||||||
|
let Some(opened_record) = inner.opened_records.get(&key) else {
|
||||||
|
apibail_generic!("record not open");
|
||||||
|
};
|
||||||
|
(
|
||||||
|
opened_record.safety_selection(),
|
||||||
|
opened_record.writer().cloned(),
|
||||||
|
opened_record.active_watch().map(|aw| aw.id),
|
||||||
|
opened_record.active_watch().map(|aw| aw.watch_node.clone()),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
// Rewrite subkey range if empty to full
|
// Rewrite subkey range if empty to full
|
||||||
let subkeys = if subkeys.is_empty() {
|
let subkeys = if subkeys.is_empty() {
|
||||||
ValueSubkeyRangeSet::full()
|
ValueSubkeyRangeSet::full()
|
||||||
@ -579,20 +607,19 @@ impl StorageManager {
|
|||||||
subkeys
|
subkeys
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the safety selection and the writer we opened this record with
|
// Get the schema so we can truncate the watch to the number of subkeys
|
||||||
let (safety_selection, opt_writer, opt_watch_node) = {
|
let schema = if let Some(lrs) = inner.local_record_store.as_ref() {
|
||||||
let Some(opened_record) = inner.opened_records.get(&key) else {
|
let Some(schema) = lrs.peek_record(key, |r| r.schema()) else {
|
||||||
apibail_generic!("record not open");
|
apibail_generic!("no local record found");
|
||||||
};
|
};
|
||||||
(
|
schema
|
||||||
opened_record.safety_selection(),
|
} else {
|
||||||
opened_record.writer().cloned(),
|
apibail_not_initialized!();
|
||||||
opened_record.active_watch().map(|aw| aw.watch_node.clone()),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
let subkeys = schema.truncate_subkeys(&subkeys, None);
|
||||||
|
|
||||||
// Get rpc processor and drop mutex so we don't block while requesting the watch from the network
|
// Get rpc processor and drop mutex so we don't block while requesting the watch from the network
|
||||||
let Some(rpc_processor) = inner.opt_rpc_processor.clone() else {
|
let Some(rpc_processor) = Self::online_ready_inner(&inner) else {
|
||||||
apibail_try_again!("offline, try again later");
|
apibail_try_again!("offline, try again later");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -610,13 +637,14 @@ impl StorageManager {
|
|||||||
count,
|
count,
|
||||||
safety_selection,
|
safety_selection,
|
||||||
opt_writer,
|
opt_writer,
|
||||||
|
opt_watch_id,
|
||||||
opt_watch_node,
|
opt_watch_node,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// If we did not get a valid response return a zero timestamp
|
// If we did not get a valid response assume nothing changed
|
||||||
let Some(owvresult) = opt_owvresult else {
|
let Some(owvresult) = opt_owvresult else {
|
||||||
return Ok(Timestamp::new(0));
|
apibail_try_again!("did not get a valid response");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Clear any existing watch if the watch succeeded or got cancelled
|
// Clear any existing watch if the watch succeeded or got cancelled
|
||||||
@ -642,24 +670,29 @@ impl StorageManager {
|
|||||||
expiration.as_u64()
|
expiration.as_u64()
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the expiration time is less than our minimum expiration time consider this watch cancelled
|
// If the expiration time is less than our minimum expiration time (or zero) consider this watch inactive
|
||||||
let mut expiration_ts = owvresult.expiration_ts;
|
let mut expiration_ts = owvresult.expiration_ts;
|
||||||
if expiration_ts.as_u64() < min_expiration_ts {
|
if expiration_ts.as_u64() < min_expiration_ts {
|
||||||
return Ok(Timestamp::new(0));
|
return Ok(Timestamp::new(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the expiration time is greated than our maximum expiration time, clamp our local watch so we ignore extra valuechanged messages
|
// If the expiration time is greater than our maximum expiration time, clamp our local watch so we ignore extra valuechanged messages
|
||||||
if expiration_ts.as_u64() > max_expiration_ts {
|
if expiration_ts.as_u64() > max_expiration_ts {
|
||||||
expiration_ts = Timestamp::new(max_expiration_ts);
|
expiration_ts = Timestamp::new(max_expiration_ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we requested a cancellation, then consider this watch cancelled
|
// If we requested a cancellation, then consider this watch cancelled
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
|
// Expiration returned should be zero if we requested a cancellation
|
||||||
|
if expiration_ts.as_u64() != 0 {
|
||||||
|
log_stor!(debug "got active watch despite asking for a cancellation");
|
||||||
|
}
|
||||||
return Ok(Timestamp::new(0));
|
return Ok(Timestamp::new(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep a record of the watch
|
// Keep a record of the watch
|
||||||
opened_record.set_active_watch(ActiveWatch {
|
opened_record.set_active_watch(ActiveWatch {
|
||||||
|
id: owvresult.watch_id,
|
||||||
expiration_ts,
|
expiration_ts,
|
||||||
watch_node: owvresult.watch_node,
|
watch_node: owvresult.watch_node,
|
||||||
opt_value_changed_route: owvresult.opt_value_changed_route,
|
opt_value_changed_route: owvresult.opt_value_changed_route,
|
||||||
@ -707,25 +740,141 @@ impl StorageManager {
|
|||||||
active_watch.count
|
active_watch.count
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update the watch
|
// Update the watch. This just calls through to the above watch_values() function
|
||||||
|
// This will update the active_watch so we don't need to do that in this routine.
|
||||||
let expiration_ts = self
|
let expiration_ts = self
|
||||||
.watch_values(key, subkeys, active_watch.expiration_ts, count)
|
.watch_values(key, subkeys, active_watch.expiration_ts, count)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// A zero expiration time means the watch is done or nothing is left, and the watch is no longer active
|
// A zero expiration time returned from watch_value() means the watch is done
|
||||||
|
// or no subkeys are left, and the watch is no longer active
|
||||||
if expiration_ts.as_u64() == 0 {
|
if expiration_ts.as_u64() == 0 {
|
||||||
|
// Return false indicating the watch is completely gone
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return true because the the watch was changed
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inspect an opened DHT record for its subkey sequence numbers
|
||||||
|
pub async fn inspect_record(
|
||||||
|
&self,
|
||||||
|
key: TypedKey,
|
||||||
|
subkeys: ValueSubkeyRangeSet,
|
||||||
|
scope: DHTReportScope,
|
||||||
|
) -> VeilidAPIResult<DHTRecordReport> {
|
||||||
|
let subkeys = if subkeys.is_empty() {
|
||||||
|
ValueSubkeyRangeSet::full()
|
||||||
|
} else {
|
||||||
|
subkeys
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut inner = self.lock().await?;
|
||||||
|
let safety_selection = {
|
||||||
|
let Some(opened_record) = inner.opened_records.get(&key) else {
|
||||||
|
apibail_generic!("record not open");
|
||||||
|
};
|
||||||
|
opened_record.safety_selection()
|
||||||
|
};
|
||||||
|
|
||||||
|
// See if the requested record is our local record store
|
||||||
|
let mut local_inspect_result = inner
|
||||||
|
.handle_inspect_local_value(key, subkeys.clone(), true)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
#[allow(clippy::unnecessary_cast)]
|
||||||
|
{
|
||||||
|
assert!(
|
||||||
|
local_inspect_result.subkeys.len() as u64 == local_inspect_result.seqs.len() as u64,
|
||||||
|
"mismatch between local subkeys returned and sequence number list returned"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
local_inspect_result.subkeys.is_subset(&subkeys),
|
||||||
|
"more subkeys returned locally than requested"
|
||||||
|
);
|
||||||
|
|
||||||
|
// If this is the maximum scope we're interested in, return the report
|
||||||
|
if matches!(scope, DHTReportScope::Local) {
|
||||||
|
return Ok(DHTRecordReport::new(
|
||||||
|
local_inspect_result.subkeys,
|
||||||
|
local_inspect_result.seqs,
|
||||||
|
vec![],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get rpc processor and drop mutex so we don't block while getting the value from the network
|
||||||
|
let Some(rpc_processor) = Self::online_ready_inner(&inner) else {
|
||||||
|
apibail_try_again!("offline, try again later");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Drop the lock for network access
|
||||||
|
drop(inner);
|
||||||
|
|
||||||
|
// If we're simulating a set, increase the previous sequence number we have by 1
|
||||||
|
if matches!(scope, DHTReportScope::UpdateSet) {
|
||||||
|
for seq in &mut local_inspect_result.seqs {
|
||||||
|
*seq = seq.overflowing_add(1).0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the inspect record report from the network
|
||||||
|
let result = self
|
||||||
|
.outbound_inspect_value(
|
||||||
|
rpc_processor,
|
||||||
|
key,
|
||||||
|
subkeys,
|
||||||
|
safety_selection,
|
||||||
|
if matches!(scope, DHTReportScope::SyncGet | DHTReportScope::SyncSet) {
|
||||||
|
InspectResult::default()
|
||||||
|
} else {
|
||||||
|
local_inspect_result.clone()
|
||||||
|
},
|
||||||
|
matches!(scope, DHTReportScope::UpdateSet | DHTReportScope::SyncSet),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Sanity check before zip
|
||||||
|
#[allow(clippy::unnecessary_cast)]
|
||||||
|
{
|
||||||
|
assert_eq!(
|
||||||
|
result.inspect_result.subkeys.len() as u64,
|
||||||
|
result.fanout_results.len() as u64,
|
||||||
|
"mismatch between subkeys returned and fanout results returned"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if !local_inspect_result.subkeys.is_empty() && !result.inspect_result.subkeys.is_empty() {
|
||||||
|
assert_eq!(
|
||||||
|
result.inspect_result.subkeys.len(),
|
||||||
|
local_inspect_result.subkeys.len(),
|
||||||
|
"mismatch between local subkeys returned and network results returned"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the list of nodes that returned a value for later reference
|
||||||
|
let mut inner = self.lock().await?;
|
||||||
|
let results_iter = result
|
||||||
|
.inspect_result
|
||||||
|
.subkeys
|
||||||
|
.iter()
|
||||||
|
.zip(result.fanout_results.iter());
|
||||||
|
|
||||||
|
inner.process_fanout_results(key, results_iter, false)?;
|
||||||
|
|
||||||
|
Ok(DHTRecordReport::new(
|
||||||
|
result.inspect_result.subkeys,
|
||||||
|
local_inspect_result.seqs,
|
||||||
|
result.inspect_result.seqs,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
// Send single value change out to the network
|
// Send single value change out to the network
|
||||||
#[instrument(level = "trace", skip(self), err)]
|
#[instrument(level = "trace", skip(self), err)]
|
||||||
async fn send_value_change(&self, vc: ValueChangedInfo) -> VeilidAPIResult<()> {
|
async fn send_value_change(&self, vc: ValueChangedInfo) -> VeilidAPIResult<()> {
|
||||||
let rpc_processor = {
|
let rpc_processor = {
|
||||||
let inner = self.inner.lock().await;
|
let inner = self.inner.lock().await;
|
||||||
if let Some(rpc_processor) = &inner.opt_rpc_processor {
|
if let Some(rpc_processor) = Self::online_ready_inner(&inner) {
|
||||||
rpc_processor.clone()
|
rpc_processor.clone()
|
||||||
} else {
|
} else {
|
||||||
apibail_try_again!("network is not available");
|
apibail_try_again!("network is not available");
|
||||||
@ -741,10 +890,9 @@ impl StorageManager {
|
|||||||
.map_err(VeilidAPIError::from)?;
|
.map_err(VeilidAPIError::from)?;
|
||||||
|
|
||||||
network_result_value_or_log!(rpc_processor
|
network_result_value_or_log!(rpc_processor
|
||||||
.rpc_call_value_changed(dest, vc.key, vc.subkeys.clone(), vc.count, (*vc.value).clone())
|
.rpc_call_value_changed(dest, vc.key, vc.subkeys.clone(), vc.count, vc.watch_id, (*vc.value).clone() )
|
||||||
.await
|
.await
|
||||||
.map_err(VeilidAPIError::from)? => [format!(": dest={:?} vc={:?}", dest, vc)] {
|
.map_err(VeilidAPIError::from)? => [format!(": dest={:?} vc={:?}", dest, vc)] {});
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
const L2_CACHE_DEPTH: usize = 4; // XXX: i just picked this. we could probably do better than this someday
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub struct InspectCacheL2Value {
|
||||||
|
pub seqs: Vec<ValueSeqNum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
struct InspectCacheL2 {
|
||||||
|
pub cache: LruCache<ValueSubkeyRangeSet, InspectCacheL2Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InspectCacheL2 {
|
||||||
|
pub fn new(l2_cache_limit: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
cache: LruCache::new(l2_cache_limit),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InspectCache {
|
||||||
|
cache: LruCache<TypedKey, InspectCacheL2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InspectCache {
|
||||||
|
pub fn new(l1_cache_limit: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
cache: LruCache::new(l1_cache_limit),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(
|
||||||
|
&mut self,
|
||||||
|
key: &TypedKey,
|
||||||
|
subkeys: &ValueSubkeyRangeSet,
|
||||||
|
) -> Option<InspectCacheL2Value> {
|
||||||
|
if let Some(l2c) = self.cache.get_mut(key) {
|
||||||
|
if let Some(l2v) = l2c.cache.get(subkeys) {
|
||||||
|
return Some(l2v.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put(&mut self, key: TypedKey, subkeys: ValueSubkeyRangeSet, value: InspectCacheL2Value) {
|
||||||
|
self.cache
|
||||||
|
.entry(key)
|
||||||
|
.or_insert_with(|| InspectCacheL2::new(L2_CACHE_DEPTH))
|
||||||
|
.cache
|
||||||
|
.insert(subkeys, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalidate(&mut self, key: &TypedKey) {
|
||||||
|
self.cache.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn replace_subkey_seq(&mut self, key: &TypedKey, subkey: ValueSubkey, seq: ValueSeqNum) {
|
||||||
|
let Some(l2) = self.cache.get_mut(key) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for entry in &mut l2.cache {
|
||||||
|
let Some(idx) = entry.0.idx_of_subkey(subkey) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if idx < entry.1.seqs.len() {
|
||||||
|
entry.1.seqs[idx] = seq;
|
||||||
|
} else if idx > entry.1.seqs.len() {
|
||||||
|
panic!(
|
||||||
|
"representational error in l2 inspect cache: {} > {}",
|
||||||
|
idx,
|
||||||
|
entry.1.seqs.len()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Information about nodes that cache a local record remotely
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub(in crate::storage_manager) struct PerNodeRecordDetail {
|
||||||
|
pub last_set: Timestamp,
|
||||||
|
pub last_seen: Timestamp,
|
||||||
|
pub subkeys: ValueSubkeyRangeSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information required to handle locally opened records
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub(in crate::storage_manager) struct LocalRecordDetail {
|
||||||
|
/// The last 'safety selection' used when creating/opening this record.
|
||||||
|
/// Even when closed, this safety selection applies to re-publication attempts by the system.
|
||||||
|
pub safety_selection: SafetySelection,
|
||||||
|
/// The nodes that we have seen this record cached on recently
|
||||||
|
#[serde(default)]
|
||||||
|
pub nodes: HashMap<PublicKey, PerNodeRecordDetail>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalRecordDetail {
|
||||||
|
pub fn new(safety_selection: SafetySelection) -> Self {
|
||||||
|
Self {
|
||||||
|
safety_selection,
|
||||||
|
nodes: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,29 @@
|
|||||||
/// This store does not perform any validation on the schema, and all ValueRecordData passed in must have been previously validated.
|
/// This store does not perform any validation on the schema, and all ValueRecordData passed in must have been previously validated.
|
||||||
/// Uses an in-memory store for the records, backed by the TableStore. Subkey data is LRU cached and rotated out by a limits policy,
|
/// Uses an in-memory store for the records, backed by the TableStore. Subkey data is LRU cached and rotated out by a limits policy,
|
||||||
/// and backed to the TableStore for persistence.
|
/// and backed to the TableStore for persistence.
|
||||||
|
mod inspect_cache;
|
||||||
|
mod keys;
|
||||||
|
mod limited_size;
|
||||||
|
mod local_record_detail;
|
||||||
|
mod opened_record;
|
||||||
|
mod record;
|
||||||
|
mod record_data;
|
||||||
|
mod record_store_limits;
|
||||||
|
mod remote_record_detail;
|
||||||
|
mod watch;
|
||||||
|
|
||||||
|
pub(super) use inspect_cache::*;
|
||||||
|
pub(super) use keys::*;
|
||||||
|
pub(super) use limited_size::*;
|
||||||
|
pub(super) use local_record_detail::*;
|
||||||
|
pub(super) use opened_record::*;
|
||||||
|
pub(super) use record::*;
|
||||||
|
pub(super) use record_data::*;
|
||||||
|
pub(super) use record_store_limits::*;
|
||||||
|
pub(super) use remote_record_detail::*;
|
||||||
|
pub(super) use watch::*;
|
||||||
|
pub use watch::{WatchParameters, WatchResult};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use hashlink::LruCache;
|
use hashlink::LruCache;
|
||||||
|
|
||||||
@ -22,30 +45,6 @@ where
|
|||||||
in_total_storage: bool,
|
in_total_storage: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An individual watch
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct WatchedRecordWatch {
|
|
||||||
subkeys: ValueSubkeyRangeSet,
|
|
||||||
expiration: Timestamp,
|
|
||||||
count: u32,
|
|
||||||
target: Target,
|
|
||||||
watcher: CryptoKey,
|
|
||||||
changed: ValueSubkeyRangeSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
|
||||||
/// A record being watched for changes
|
|
||||||
struct WatchedRecord {
|
|
||||||
/// The list of active watchers
|
|
||||||
watchers: Vec<WatchedRecordWatch>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) enum WatchUpdateMode {
|
|
||||||
NoUpdate,
|
|
||||||
UpdateAll,
|
|
||||||
ExcludeTarget(Target),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) struct RecordStore<D>
|
pub(super) struct RecordStore<D>
|
||||||
where
|
where
|
||||||
D: fmt::Debug + Clone + Serialize + for<'d> Deserialize<'d>,
|
D: fmt::Debug + Clone + Serialize + for<'d> Deserialize<'d>,
|
||||||
@ -62,6 +61,8 @@ where
|
|||||||
record_index: LruCache<RecordTableKey, Record<D>>,
|
record_index: LruCache<RecordTableKey, Record<D>>,
|
||||||
/// The in-memory cache of commonly accessed subkey data so we don't have to keep hitting the db
|
/// The in-memory cache of commonly accessed subkey data so we don't have to keep hitting the db
|
||||||
subkey_cache: LruCache<SubkeyTableKey, RecordData>,
|
subkey_cache: LruCache<SubkeyTableKey, RecordData>,
|
||||||
|
/// The in-memory cache of commonly accessed sequence number data so we don't have to keep hitting the db
|
||||||
|
inspect_cache: InspectCache,
|
||||||
/// Total storage space or subkey data inclusive of structures in memory
|
/// Total storage space or subkey data inclusive of structures in memory
|
||||||
subkey_cache_total_size: LimitedSize<usize>,
|
subkey_cache_total_size: LimitedSize<usize>,
|
||||||
/// Total storage space of records in the tabledb inclusive of subkey data and structures
|
/// Total storage space of records in the tabledb inclusive of subkey data and structures
|
||||||
@ -71,21 +72,31 @@ where
|
|||||||
/// The list of records that have changed since last flush to disk (optimization for batched writes)
|
/// The list of records that have changed since last flush to disk (optimization for batched writes)
|
||||||
changed_records: HashSet<RecordTableKey>,
|
changed_records: HashSet<RecordTableKey>,
|
||||||
/// The list of records being watched for changes
|
/// The list of records being watched for changes
|
||||||
watched_records: HashMap<RecordTableKey, WatchedRecord>,
|
watched_records: HashMap<RecordTableKey, WatchList>,
|
||||||
/// The list of watched records that have changed values since last notification
|
/// The list of watched records that have changed values since last notification
|
||||||
changed_watched_values: HashSet<RecordTableKey>,
|
changed_watched_values: HashSet<RecordTableKey>,
|
||||||
|
|
||||||
/// A mutex to ensure we handle this concurrently
|
/// A mutex to ensure we handle this concurrently
|
||||||
purge_dead_records_mutex: Arc<AsyncMutex<()>>,
|
purge_dead_records_mutex: Arc<AsyncMutex<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of the do_get_value_operation
|
/// The result of the do_get_value_operation
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub struct SubkeyResult {
|
pub struct GetResult {
|
||||||
/// The subkey value if we got one
|
/// The subkey value if we got one
|
||||||
pub value: Option<Arc<SignedValueData>>,
|
pub opt_value: Option<Arc<SignedValueData>>,
|
||||||
/// The descriptor if we got a fresh one or empty if no descriptor was needed
|
/// The descriptor if we got a fresh one or empty if no descriptor was needed
|
||||||
pub descriptor: Option<Arc<SignedValueDescriptor>>,
|
pub opt_descriptor: Option<Arc<SignedValueDescriptor>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The result of the do_inspect_value_operation
|
||||||
|
#[derive(Default, Clone, Debug)]
|
||||||
|
pub struct InspectResult {
|
||||||
|
/// The actual in-schema subkey range being reported on
|
||||||
|
pub subkeys: ValueSubkeyRangeSet,
|
||||||
|
/// The sequence map
|
||||||
|
pub seqs: Vec<ValueSeqNum>,
|
||||||
|
/// The descriptor if we got a fresh one or empty if no descriptor was needed
|
||||||
|
pub opt_descriptor: Option<Arc<SignedValueDescriptor>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> RecordStore<D>
|
impl<D> RecordStore<D>
|
||||||
@ -109,6 +120,7 @@ where
|
|||||||
subkey_table: None,
|
subkey_table: None,
|
||||||
record_index: LruCache::new(limits.max_records.unwrap_or(usize::MAX)),
|
record_index: LruCache::new(limits.max_records.unwrap_or(usize::MAX)),
|
||||||
subkey_cache: LruCache::new(subkey_cache_size),
|
subkey_cache: LruCache::new(subkey_cache_size),
|
||||||
|
inspect_cache: InspectCache::new(subkey_cache_size),
|
||||||
subkey_cache_total_size: LimitedSize::new(
|
subkey_cache_total_size: LimitedSize::new(
|
||||||
"subkey_cache_total_size",
|
"subkey_cache_total_size",
|
||||||
0,
|
0,
|
||||||
@ -279,6 +291,16 @@ where
|
|||||||
log_stor!(error "dead record found in index: {:?}", dr.key);
|
log_stor!(error "dead record found in index: {:?}", dr.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record should have no watches now
|
||||||
|
if self.watched_records.contains_key(&dr.key) {
|
||||||
|
log_stor!(error "dead record found in watches: {:?}", dr.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record should have no watch changes now
|
||||||
|
if self.changed_watched_values.contains(&dr.key) {
|
||||||
|
log_stor!(error "dead record found in watch changes: {:?}", dr.key);
|
||||||
|
}
|
||||||
|
|
||||||
// Delete record
|
// Delete record
|
||||||
if let Err(e) = rt_xact.delete(0, &dr.key.bytes()) {
|
if let Err(e) = rt_xact.delete(0, &dr.key.bytes()) {
|
||||||
log_stor!(error "record could not be deleted: {}", e);
|
log_stor!(error "record could not be deleted: {}", e);
|
||||||
@ -399,13 +421,27 @@ where
|
|||||||
apibail_key_not_found!(key);
|
apibail_key_not_found!(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.add_dead_record(rtk, record);
|
// Remove watches
|
||||||
|
self.watched_records.remove(&rtk);
|
||||||
|
|
||||||
|
// Remove watch changes
|
||||||
|
self.changed_watched_values.remove(&rtk);
|
||||||
|
|
||||||
|
// Invalidate inspect cache for this key
|
||||||
|
self.inspect_cache.invalidate(&rtk.key);
|
||||||
|
|
||||||
|
// Remove from table store immediately
|
||||||
|
self.add_dead_record(rtk, record);
|
||||||
self.purge_dead_records(false).await;
|
self.purge_dead_records(false).await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn contains_record(&mut self, key: TypedKey) -> bool {
|
||||||
|
let rtk = RecordTableKey { key };
|
||||||
|
self.record_index.contains_key(&rtk)
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn with_record<R, F>(&mut self, key: TypedKey, f: F) -> Option<R>
|
pub(super) fn with_record<R, F>(&mut self, key: TypedKey, f: F) -> Option<R>
|
||||||
where
|
where
|
||||||
F: FnOnce(&Record<D>) -> R,
|
F: FnOnce(&Record<D>) -> R,
|
||||||
@ -471,7 +507,7 @@ where
|
|||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkey: ValueSubkey,
|
subkey: ValueSubkey,
|
||||||
want_descriptor: bool,
|
want_descriptor: bool,
|
||||||
) -> VeilidAPIResult<Option<SubkeyResult>> {
|
) -> VeilidAPIResult<Option<GetResult>> {
|
||||||
// Get record from index
|
// Get record from index
|
||||||
let Some((subkey_count, has_subkey, opt_descriptor)) = self.with_record(key, |record| {
|
let Some((subkey_count, has_subkey, opt_descriptor)) = self.with_record(key, |record| {
|
||||||
(
|
(
|
||||||
@ -496,9 +532,9 @@ where
|
|||||||
// See if we have this subkey stored
|
// See if we have this subkey stored
|
||||||
if !has_subkey {
|
if !has_subkey {
|
||||||
// If not, return no value but maybe with descriptor
|
// If not, return no value but maybe with descriptor
|
||||||
return Ok(Some(SubkeyResult {
|
return Ok(Some(GetResult {
|
||||||
value: None,
|
opt_value: None,
|
||||||
descriptor: opt_descriptor,
|
opt_descriptor,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,12 +545,12 @@ where
|
|||||||
|
|
||||||
// If subkey exists in subkey cache, use that
|
// If subkey exists in subkey cache, use that
|
||||||
let stk = SubkeyTableKey { key, subkey };
|
let stk = SubkeyTableKey { key, subkey };
|
||||||
if let Some(record_data) = self.subkey_cache.get_mut(&stk) {
|
if let Some(record_data) = self.subkey_cache.get(&stk) {
|
||||||
let out = record_data.signed_value_data().clone();
|
let out = record_data.signed_value_data().clone();
|
||||||
|
|
||||||
return Ok(Some(SubkeyResult {
|
return Ok(Some(GetResult {
|
||||||
value: Some(out),
|
opt_value: Some(out),
|
||||||
descriptor: opt_descriptor,
|
opt_descriptor,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// If not in cache, try to pull from table store if it is in our stored subkey set
|
// If not in cache, try to pull from table store if it is in our stored subkey set
|
||||||
@ -531,9 +567,9 @@ where
|
|||||||
// Add to cache, do nothing with lru out
|
// Add to cache, do nothing with lru out
|
||||||
self.add_to_subkey_cache(stk, record_data);
|
self.add_to_subkey_cache(stk, record_data);
|
||||||
|
|
||||||
Ok(Some(SubkeyResult {
|
Ok(Some(GetResult {
|
||||||
value: Some(out),
|
opt_value: Some(out),
|
||||||
descriptor: opt_descriptor,
|
opt_descriptor,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,7 +578,7 @@ where
|
|||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkey: ValueSubkey,
|
subkey: ValueSubkey,
|
||||||
want_descriptor: bool,
|
want_descriptor: bool,
|
||||||
) -> VeilidAPIResult<Option<SubkeyResult>> {
|
) -> VeilidAPIResult<Option<GetResult>> {
|
||||||
// record from index
|
// record from index
|
||||||
let Some((subkey_count, has_subkey, opt_descriptor)) = self.peek_record(key, |record| {
|
let Some((subkey_count, has_subkey, opt_descriptor)) = self.peek_record(key, |record| {
|
||||||
(
|
(
|
||||||
@ -567,9 +603,9 @@ where
|
|||||||
// See if we have this subkey stored
|
// See if we have this subkey stored
|
||||||
if !has_subkey {
|
if !has_subkey {
|
||||||
// If not, return no value but maybe with descriptor
|
// If not, return no value but maybe with descriptor
|
||||||
return Ok(Some(SubkeyResult {
|
return Ok(Some(GetResult {
|
||||||
value: None,
|
opt_value: None,
|
||||||
descriptor: opt_descriptor,
|
opt_descriptor,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,9 +619,9 @@ where
|
|||||||
if let Some(record_data) = self.subkey_cache.peek(&stk) {
|
if let Some(record_data) = self.subkey_cache.peek(&stk) {
|
||||||
let out = record_data.signed_value_data().clone();
|
let out = record_data.signed_value_data().clone();
|
||||||
|
|
||||||
return Ok(Some(SubkeyResult {
|
return Ok(Some(GetResult {
|
||||||
value: Some(out),
|
opt_value: Some(out),
|
||||||
descriptor: opt_descriptor,
|
opt_descriptor,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// If not in cache, try to pull from table store if it is in our stored subkey set
|
// If not in cache, try to pull from table store if it is in our stored subkey set
|
||||||
@ -599,9 +635,9 @@ where
|
|||||||
|
|
||||||
let out = record_data.signed_value_data().clone();
|
let out = record_data.signed_value_data().clone();
|
||||||
|
|
||||||
Ok(Some(SubkeyResult {
|
Ok(Some(GetResult {
|
||||||
value: Some(out),
|
opt_value: Some(out),
|
||||||
descriptor: opt_descriptor,
|
opt_descriptor,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,20 +645,30 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkey: ValueSubkey,
|
subkey: ValueSubkey,
|
||||||
opt_ignore_target: Option<Target>,
|
watch_update_mode: WatchUpdateMode,
|
||||||
) {
|
) {
|
||||||
|
let (do_update, opt_ignore_target) = match watch_update_mode {
|
||||||
|
WatchUpdateMode::NoUpdate => (false, None),
|
||||||
|
WatchUpdateMode::UpdateAll => (true, None),
|
||||||
|
WatchUpdateMode::ExcludeTarget(target) => (true, Some(target)),
|
||||||
|
};
|
||||||
|
if !do_update {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let rtk = RecordTableKey { key };
|
let rtk = RecordTableKey { key };
|
||||||
let Some(wr) = self.watched_records.get_mut(&rtk) else {
|
let Some(wr) = self.watched_records.get_mut(&rtk) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update all watchers
|
// Update all watchers
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
for w in &mut wr.watchers {
|
for w in &mut wr.watches {
|
||||||
// If this watcher is watching the changed subkey then add to the watcher's changed list
|
// If this watcher is watching the changed subkey then add to the watcher's changed list
|
||||||
// Don't bother marking changes for value sets coming from the same watching node/target because they
|
// Don't bother marking changes for value sets coming from the same watching node/target because they
|
||||||
// are already going to be aware of the changes in that case
|
// are already going to be aware of the changes in that case
|
||||||
if Some(&w.target) != opt_ignore_target.as_ref()
|
if Some(&w.params.target) != opt_ignore_target.as_ref()
|
||||||
&& w.subkeys.contains(subkey)
|
&& w.params.subkeys.contains(subkey)
|
||||||
&& w.changed.insert(subkey)
|
&& w.changed.insert(subkey)
|
||||||
{
|
{
|
||||||
changed = true;
|
changed = true;
|
||||||
@ -713,6 +759,13 @@ where
|
|||||||
.await
|
.await
|
||||||
.map_err(VeilidAPIError::internal)?;
|
.map_err(VeilidAPIError::internal)?;
|
||||||
|
|
||||||
|
// Write to inspect cache
|
||||||
|
self.inspect_cache.replace_subkey_seq(
|
||||||
|
&stk.key,
|
||||||
|
subkey,
|
||||||
|
subkey_record_data.signed_value_data().value_data().seq(),
|
||||||
|
);
|
||||||
|
|
||||||
// Write to subkey cache
|
// Write to subkey cache
|
||||||
self.add_to_subkey_cache(stk, subkey_record_data);
|
self.add_to_subkey_cache(stk, subkey_record_data);
|
||||||
|
|
||||||
@ -726,34 +779,239 @@ where
|
|||||||
// Update storage space
|
// Update storage space
|
||||||
self.total_storage_space.commit().unwrap();
|
self.total_storage_space.commit().unwrap();
|
||||||
|
|
||||||
// Update watched value
|
// Send updates to
|
||||||
|
self.update_watched_value(key, subkey, watch_update_mode)
|
||||||
let (do_update, opt_ignore_target) = match watch_update_mode {
|
|
||||||
WatchUpdateMode::NoUpdate => (false, None),
|
|
||||||
WatchUpdateMode::UpdateAll => (true, None),
|
|
||||||
WatchUpdateMode::ExcludeTarget(target) => (true, Some(target)),
|
|
||||||
};
|
|
||||||
if do_update {
|
|
||||||
self.update_watched_value(key, subkey, opt_ignore_target)
|
|
||||||
.await;
|
.await;
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a record watch for changes
|
pub async fn inspect_record(
|
||||||
pub async fn watch_record(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkeys: ValueSubkeyRangeSet,
|
subkeys: ValueSubkeyRangeSet,
|
||||||
mut expiration: Timestamp,
|
want_descriptor: bool,
|
||||||
count: u32,
|
) -> VeilidAPIResult<Option<InspectResult>> {
|
||||||
target: Target,
|
// Get subkey table
|
||||||
watcher: CryptoKey,
|
let Some(subkey_table) = self.subkey_table.clone() else {
|
||||||
) -> VeilidAPIResult<Option<Timestamp>> {
|
apibail_internal!("record store not initialized");
|
||||||
// If subkeys is empty or count is zero then we're cancelling a watch completely
|
};
|
||||||
if subkeys.is_empty() || count == 0 {
|
|
||||||
return self.cancel_watch(key, target, watcher).await;
|
// Get record from index
|
||||||
|
let Some((subkeys, opt_descriptor)) = self.with_record(key, |record| {
|
||||||
|
// Get number of subkeys from schema and ensure we are getting the
|
||||||
|
// right number of sequence numbers betwen that and what we asked for
|
||||||
|
let truncated_subkeys = record
|
||||||
|
.schema()
|
||||||
|
.truncate_subkeys(&subkeys, Some(MAX_INSPECT_VALUE_A_SEQS_LEN));
|
||||||
|
(
|
||||||
|
truncated_subkeys,
|
||||||
|
if want_descriptor {
|
||||||
|
Some(record.descriptor().clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}) else {
|
||||||
|
// Record not available
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if we can return some subkeys
|
||||||
|
if subkeys.is_empty() {
|
||||||
|
apibail_invalid_argument!("subkeys set does not overlap schema", "subkeys", subkeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if we have this inspection cached
|
||||||
|
if let Some(icv) = self.inspect_cache.get(&key, &subkeys) {
|
||||||
|
return Ok(Some(InspectResult {
|
||||||
|
subkeys,
|
||||||
|
seqs: icv.seqs,
|
||||||
|
opt_descriptor,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build sequence number list to return
|
||||||
|
#[allow(clippy::unnecessary_cast)]
|
||||||
|
let mut seqs = Vec::with_capacity(subkeys.len() as usize);
|
||||||
|
for subkey in subkeys.iter() {
|
||||||
|
let stk = SubkeyTableKey { key, subkey };
|
||||||
|
let seq = if let Some(record_data) = self.subkey_cache.peek(&stk) {
|
||||||
|
record_data.signed_value_data().value_data().seq()
|
||||||
|
} else {
|
||||||
|
// If not in cache, try to pull from table store if it is in our stored subkey set
|
||||||
|
// XXX: This would be better if it didn't have to pull the whole record data to get the seq.
|
||||||
|
if let Some(record_data) = subkey_table
|
||||||
|
.load_json::<RecordData>(0, &stk.bytes())
|
||||||
|
.await
|
||||||
|
.map_err(VeilidAPIError::internal)?
|
||||||
|
{
|
||||||
|
record_data.signed_value_data().value_data().seq()
|
||||||
|
} else {
|
||||||
|
// Subkey not written to
|
||||||
|
ValueSubkey::MAX
|
||||||
|
}
|
||||||
|
};
|
||||||
|
seqs.push(seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save seqs cache
|
||||||
|
self.inspect_cache.put(
|
||||||
|
key,
|
||||||
|
subkeys.clone(),
|
||||||
|
InspectCacheL2Value { seqs: seqs.clone() },
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Some(InspectResult {
|
||||||
|
subkeys,
|
||||||
|
seqs,
|
||||||
|
opt_descriptor,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn _change_existing_watch(
|
||||||
|
&mut self,
|
||||||
|
key: TypedKey,
|
||||||
|
params: WatchParameters,
|
||||||
|
watch_id: u64,
|
||||||
|
) -> VeilidAPIResult<WatchResult> {
|
||||||
|
if params.count == 0 {
|
||||||
|
apibail_internal!("cancel watch should not have gotten here");
|
||||||
|
}
|
||||||
|
if params.expiration.as_u64() == 0 {
|
||||||
|
apibail_internal!("zero expiration should have been resolved to max by now");
|
||||||
|
}
|
||||||
|
// Get the watch list for this record
|
||||||
|
let rtk = RecordTableKey { key };
|
||||||
|
let Some(watch_list) = self.watched_records.get_mut(&rtk) else {
|
||||||
|
// No watches, nothing to change
|
||||||
|
return Ok(WatchResult::Rejected);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check each watch to see if we have an exact match for the id to change
|
||||||
|
for w in &mut watch_list.watches {
|
||||||
|
// If the watch id doesn't match, then we're not updating
|
||||||
|
// Also do not allow the watcher key to change
|
||||||
|
if w.id == watch_id && w.params.watcher == params.watcher {
|
||||||
|
// Updating an existing watch
|
||||||
|
w.params = params;
|
||||||
|
return Ok(WatchResult::Changed {
|
||||||
|
expiration: w.params.expiration,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No existing watch found
|
||||||
|
Ok(WatchResult::Rejected)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn _create_new_watch(
|
||||||
|
&mut self,
|
||||||
|
key: TypedKey,
|
||||||
|
params: WatchParameters,
|
||||||
|
member_check: Box<dyn Fn(PublicKey) -> bool + Send>,
|
||||||
|
) -> VeilidAPIResult<WatchResult> {
|
||||||
|
// Generate a record-unique watch id > 0
|
||||||
|
let rtk = RecordTableKey { key };
|
||||||
|
let mut id = 0;
|
||||||
|
while id == 0 {
|
||||||
|
id = get_random_u64();
|
||||||
|
}
|
||||||
|
if let Some(watched_record) = self.watched_records.get_mut(&rtk) {
|
||||||
|
// Make sure it doesn't match any other id (unlikely, but lets be certain)
|
||||||
|
'x: loop {
|
||||||
|
for w in &mut watched_record.watches {
|
||||||
|
if w.id == id {
|
||||||
|
loop {
|
||||||
|
id = id.overflowing_add(1).0;
|
||||||
|
if id != 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue 'x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate watch limits
|
||||||
|
let mut watch_count = 0;
|
||||||
|
let mut target_watch_count = 0;
|
||||||
|
|
||||||
|
let is_member = member_check(params.watcher);
|
||||||
|
|
||||||
|
let rtk = RecordTableKey { key };
|
||||||
|
if let Some(watched_record) = self.watched_records.get_mut(&rtk) {
|
||||||
|
// Total up the number of watches for this key
|
||||||
|
for w in &mut watched_record.watches {
|
||||||
|
// See if this watch should be counted toward any limits
|
||||||
|
let count_watch = if is_member {
|
||||||
|
// If the watcher is a member of the schema, then consider the total per-watcher key
|
||||||
|
w.params.watcher == params.watcher
|
||||||
|
} else {
|
||||||
|
// If the watcher is not a member of the schema, the check if this watch is an anonymous watch and contributes to per-record key total
|
||||||
|
!member_check(w.params.watcher)
|
||||||
|
};
|
||||||
|
|
||||||
|
// For any watch, if the target matches our also tally that separately
|
||||||
|
// If the watcher is a member of the schema, then consider the total per-target-per-watcher key
|
||||||
|
// If the watcher is not a member of the schema, then it is an anonymous watch and the total is per-target-per-record key
|
||||||
|
if count_watch {
|
||||||
|
watch_count += 1;
|
||||||
|
if w.params.target == params.target {
|
||||||
|
target_watch_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For members, no more than one watch per target per watcher per record
|
||||||
|
// For anonymous, no more than one watch per target per record
|
||||||
|
if target_watch_count > 0 {
|
||||||
|
// Too many watches
|
||||||
|
return Ok(WatchResult::Rejected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check watch table for limits
|
||||||
|
let watch_limit = if is_member {
|
||||||
|
self.limits.member_watch_limit
|
||||||
|
} else {
|
||||||
|
self.limits.public_watch_limit
|
||||||
|
};
|
||||||
|
if watch_count >= watch_limit {
|
||||||
|
return Ok(WatchResult::Rejected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok this is an acceptable new watch, add it
|
||||||
|
let watch_list = self.watched_records.entry(rtk).or_default();
|
||||||
|
let expiration = params.expiration;
|
||||||
|
watch_list.watches.push(Watch {
|
||||||
|
params,
|
||||||
|
id,
|
||||||
|
changed: ValueSubkeyRangeSet::new(),
|
||||||
|
});
|
||||||
|
Ok(WatchResult::Created { id, expiration })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add or update an inbound record watch for changes
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub async fn watch_record(
|
||||||
|
&mut self,
|
||||||
|
key: TypedKey,
|
||||||
|
mut params: WatchParameters,
|
||||||
|
opt_watch_id: Option<u64>,
|
||||||
|
) -> VeilidAPIResult<WatchResult> {
|
||||||
|
// If count is zero then we're cancelling a watch completely
|
||||||
|
if params.count == 0 {
|
||||||
|
if let Some(watch_id) = opt_watch_id {
|
||||||
|
let cancelled = self.cancel_watch(key, watch_id, params.watcher).await?;
|
||||||
|
if cancelled {
|
||||||
|
return Ok(WatchResult::Cancelled);
|
||||||
|
}
|
||||||
|
return Ok(WatchResult::Rejected);
|
||||||
|
}
|
||||||
|
apibail_internal!("shouldn't have let a None watch id get here");
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if expiration timestamp is too far in the future or not enough in the future
|
// See if expiration timestamp is too far in the future or not enough in the future
|
||||||
@ -761,114 +1019,67 @@ where
|
|||||||
let max_ts = cur_ts + self.limits.max_watch_expiration.as_u64();
|
let max_ts = cur_ts + self.limits.max_watch_expiration.as_u64();
|
||||||
let min_ts = cur_ts + self.limits.min_watch_expiration.as_u64();
|
let min_ts = cur_ts + self.limits.min_watch_expiration.as_u64();
|
||||||
|
|
||||||
if expiration.as_u64() == 0 || expiration.as_u64() > max_ts {
|
if params.expiration.as_u64() == 0 || params.expiration.as_u64() > max_ts {
|
||||||
// Clamp expiration max time (or set zero expiration to max)
|
// Clamp expiration max time (or set zero expiration to max)
|
||||||
expiration = Timestamp::new(max_ts);
|
params.expiration = Timestamp::new(max_ts);
|
||||||
} else if expiration.as_u64() < min_ts {
|
} else if params.expiration.as_u64() < min_ts {
|
||||||
// Don't add watches with too low of an expiration time
|
// Don't add watches with too low of an expiration time
|
||||||
return Ok(None);
|
if let Some(watch_id) = opt_watch_id {
|
||||||
|
let cancelled = self.cancel_watch(key, watch_id, params.watcher).await?;
|
||||||
|
if cancelled {
|
||||||
|
return Ok(WatchResult::Cancelled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(WatchResult::Rejected);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the record being watched
|
// Make a closure to check for member vs anonymous
|
||||||
let Some(is_member) = self.with_record(key, |record| {
|
let Some(member_check) = self.with_record(key, |record| {
|
||||||
// Check if the watcher specified is a schema member
|
|
||||||
let schema = record.schema();
|
let schema = record.schema();
|
||||||
(*record.owner()) == watcher || schema.is_member(&watcher)
|
let owner = *record.owner();
|
||||||
|
Box::new(move |watcher| owner == params.watcher || schema.is_member(&watcher))
|
||||||
}) else {
|
}) else {
|
||||||
// Record not found
|
// Record not found
|
||||||
return Ok(None);
|
return Ok(WatchResult::Rejected);
|
||||||
};
|
};
|
||||||
|
|
||||||
// See if we are updating an existing watch
|
// Create or update depending on if a watch id is specified or not
|
||||||
// with the watcher matched on target
|
if let Some(watch_id) = opt_watch_id {
|
||||||
let mut watch_count = 0;
|
self._change_existing_watch(key, params, watch_id).await
|
||||||
let rtk = RecordTableKey { key };
|
|
||||||
if let Some(watch) = self.watched_records.get_mut(&rtk) {
|
|
||||||
for w in &mut watch.watchers {
|
|
||||||
if w.watcher == watcher {
|
|
||||||
watch_count += 1;
|
|
||||||
|
|
||||||
// Only one watch for an anonymous watcher
|
|
||||||
// Allow members to have one watch per target
|
|
||||||
if !is_member || w.target == target {
|
|
||||||
// Updating an existing watch
|
|
||||||
w.subkeys = subkeys;
|
|
||||||
w.expiration = expiration;
|
|
||||||
w.count = count;
|
|
||||||
return Ok(Some(expiration));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adding a new watcher to a watch
|
|
||||||
// Check watch table for limits
|
|
||||||
if is_member {
|
|
||||||
// Member watch
|
|
||||||
if watch_count >= self.limits.member_watch_limit {
|
|
||||||
// Too many watches
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Public watch
|
self._create_new_watch(key, params, member_check).await
|
||||||
if watch_count >= self.limits.public_watch_limit {
|
|
||||||
// Too many watches
|
|
||||||
return Ok(None);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok this is an acceptable new watch, add it
|
/// Clear a specific watch for a record
|
||||||
let watch = self.watched_records.entry(rtk).or_default();
|
/// returns true if the watch was found and cancelled
|
||||||
watch.watchers.push(WatchedRecordWatch {
|
|
||||||
subkeys,
|
|
||||||
expiration,
|
|
||||||
count,
|
|
||||||
target,
|
|
||||||
watcher,
|
|
||||||
changed: ValueSubkeyRangeSet::new(),
|
|
||||||
});
|
|
||||||
Ok(Some(expiration))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a record watch for changes
|
|
||||||
async fn cancel_watch(
|
async fn cancel_watch(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
target: Target,
|
watch_id: u64,
|
||||||
watcher: CryptoKey,
|
watcher: PublicKey,
|
||||||
) -> VeilidAPIResult<Option<Timestamp>> {
|
) -> VeilidAPIResult<bool> {
|
||||||
// Get the record being watched
|
if watch_id == 0 {
|
||||||
let Some(is_member) = self.with_record(key, |record| {
|
apibail_internal!("should not have let a zero watch id get here");
|
||||||
// Check if the watcher specified is a schema member
|
}
|
||||||
let schema = record.schema();
|
|
||||||
(*record.owner()) == watcher || schema.is_member(&watcher)
|
|
||||||
}) else {
|
|
||||||
// Record not found
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
|
|
||||||
// See if we are cancelling an existing watch
|
// See if we are cancelling an existing watch
|
||||||
// with the watcher matched on target
|
|
||||||
let rtk = RecordTableKey { key };
|
let rtk = RecordTableKey { key };
|
||||||
let mut is_empty = false;
|
let mut is_empty = false;
|
||||||
let mut ret_timestamp = None;
|
let mut ret = false;
|
||||||
if let Some(watch) = self.watched_records.get_mut(&rtk) {
|
if let Some(watch_list) = self.watched_records.get_mut(&rtk) {
|
||||||
let mut dead_watcher = None;
|
let mut dead_watcher = None;
|
||||||
for (wn, w) in watch.watchers.iter_mut().enumerate() {
|
for (wn, w) in watch_list.watches.iter_mut().enumerate() {
|
||||||
if w.watcher == watcher {
|
// Must match the watch id and the watcher key to cancel
|
||||||
// Only one watch for an anonymous watcher
|
if w.id == watch_id && w.params.watcher == watcher {
|
||||||
// Allow members to have one watch per target
|
|
||||||
if !is_member || w.target == target {
|
|
||||||
// Canceling an existing watch
|
// Canceling an existing watch
|
||||||
dead_watcher = Some(wn);
|
dead_watcher = Some(wn);
|
||||||
ret_timestamp = Some(w.expiration);
|
ret = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if let Some(dw) = dead_watcher {
|
if let Some(dw) = dead_watcher {
|
||||||
watch.watchers.remove(dw);
|
watch_list.watches.remove(dw);
|
||||||
if watch.watchers.is_empty() {
|
if watch_list.watches.is_empty() {
|
||||||
is_empty = true;
|
is_empty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -877,7 +1088,42 @@ where
|
|||||||
self.watched_records.remove(&rtk);
|
self.watched_records.remove(&rtk);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ret_timestamp)
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move watches from one store to another
|
||||||
|
pub fn move_watches(
|
||||||
|
&mut self,
|
||||||
|
key: TypedKey,
|
||||||
|
in_watch: Option<(WatchList, bool)>,
|
||||||
|
) -> Option<(WatchList, bool)> {
|
||||||
|
let rtk = RecordTableKey { key };
|
||||||
|
let out = self.watched_records.remove(&rtk);
|
||||||
|
if let Some(in_watch) = in_watch {
|
||||||
|
self.watched_records.insert(rtk, in_watch.0);
|
||||||
|
if in_watch.1 {
|
||||||
|
self.changed_watched_values.insert(rtk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let is_watched = self.changed_watched_values.remove(&rtk);
|
||||||
|
out.map(|r| (r, is_watched))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See if any watched records have expired and clear them out
|
||||||
|
pub fn check_watched_records(&mut self) {
|
||||||
|
let now = get_aligned_timestamp();
|
||||||
|
self.watched_records.retain(|key, watch_list| {
|
||||||
|
watch_list.watches.retain(|w| {
|
||||||
|
w.params.count != 0 && w.params.expiration > now && !w.params.subkeys.is_empty()
|
||||||
|
});
|
||||||
|
if watch_list.watches.is_empty() {
|
||||||
|
// If we're removing the watched record, drop any changed watch values too
|
||||||
|
self.changed_watched_values.remove(key);
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn take_value_changes(&mut self, changes: &mut Vec<ValueChangedInfo>) {
|
pub async fn take_value_changes(&mut self, changes: &mut Vec<ValueChangedInfo>) {
|
||||||
@ -887,6 +1133,7 @@ where
|
|||||||
key: TypedKey,
|
key: TypedKey,
|
||||||
subkeys: ValueSubkeyRangeSet,
|
subkeys: ValueSubkeyRangeSet,
|
||||||
count: u32,
|
count: u32,
|
||||||
|
watch_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut evcis = vec![];
|
let mut evcis = vec![];
|
||||||
@ -895,31 +1142,38 @@ where
|
|||||||
if let Some(watch) = self.watched_records.get_mut(&rtk) {
|
if let Some(watch) = self.watched_records.get_mut(&rtk) {
|
||||||
// Process watch notifications
|
// Process watch notifications
|
||||||
let mut dead_watchers = vec![];
|
let mut dead_watchers = vec![];
|
||||||
for (wn, w) in watch.watchers.iter_mut().enumerate() {
|
for (wn, w) in watch.watches.iter_mut().enumerate() {
|
||||||
// Get the subkeys that have changed
|
// Get the subkeys that have changed
|
||||||
let subkeys = w.changed.clone();
|
let subkeys = w.changed.clone();
|
||||||
|
|
||||||
|
// If no subkeys on this watcher have changed then skip it
|
||||||
|
if subkeys.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
w.changed.clear();
|
w.changed.clear();
|
||||||
|
|
||||||
// Reduce the count of changes sent
|
// Reduce the count of changes sent
|
||||||
// if count goes to zero mark this watcher dead
|
// if count goes to zero mark this watcher dead
|
||||||
w.count -= 1;
|
w.params.count -= 1;
|
||||||
let count = w.count;
|
let count = w.params.count;
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
dead_watchers.push(wn);
|
dead_watchers.push(wn);
|
||||||
}
|
}
|
||||||
|
|
||||||
evcis.push(EarlyValueChangedInfo {
|
evcis.push(EarlyValueChangedInfo {
|
||||||
target: w.target,
|
target: w.params.target,
|
||||||
key: rtk.key,
|
key: rtk.key,
|
||||||
subkeys,
|
subkeys,
|
||||||
count,
|
count,
|
||||||
|
watch_id: w.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove in reverse so we don't have to offset the index to remove the right key
|
// Remove in reverse so we don't have to offset the index to remove the right key
|
||||||
for dw in dead_watchers.iter().rev().copied() {
|
for dw in dead_watchers.iter().rev().copied() {
|
||||||
watch.watchers.remove(dw);
|
watch.watches.remove(dw);
|
||||||
if watch.watchers.is_empty() {
|
if watch.watches.is_empty() {
|
||||||
empty_watched_records.push(rtk);
|
empty_watched_records.push(rtk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -935,7 +1189,7 @@ where
|
|||||||
log_stor!(error "first subkey should exist for value change notification");
|
log_stor!(error "first subkey should exist for value change notification");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let subkey_result = match self.get_subkey(evci.key, first_subkey, false).await {
|
let get_result = match self.get_subkey(evci.key, first_subkey, false).await {
|
||||||
Ok(Some(skr)) => skr,
|
Ok(Some(skr)) => skr,
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
log_stor!(error "subkey should have data for value change notification");
|
log_stor!(error "subkey should have data for value change notification");
|
||||||
@ -946,7 +1200,7 @@ where
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let Some(value) = subkey_result.value else {
|
let Some(value) = get_result.opt_value else {
|
||||||
log_stor!(error "first subkey should have had value for value change notification");
|
log_stor!(error "first subkey should have had value for value change notification");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@ -956,6 +1210,7 @@ where
|
|||||||
key: evci.key,
|
key: evci.key,
|
||||||
subkeys: evci.subkeys,
|
subkeys: evci.subkeys,
|
||||||
count: evci.count,
|
count: evci.count,
|
||||||
|
watch_id: evci.watch_id,
|
||||||
value,
|
value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1013,8 +1268,16 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_record_info(&self, key: TypedKey) -> String {
|
pub fn debug_record_info(&self, key: TypedKey) -> String {
|
||||||
self.peek_record(key, |r| format!("{:#?}", r))
|
let record_info = self
|
||||||
.unwrap_or("Not found".to_owned())
|
.peek_record(key, |r| format!("{:#?}", r))
|
||||||
|
.unwrap_or("Not found".to_owned());
|
||||||
|
let watched_record = match self.watched_records.get(&RecordTableKey { key }) {
|
||||||
|
Some(w) => {
|
||||||
|
format!("Remote Watches: {:#?}", w)
|
||||||
|
}
|
||||||
|
None => "No remote watches".to_owned(),
|
||||||
|
};
|
||||||
|
format!("{}\n{}\n", record_info, watched_record)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn debug_record_subkey_info(&self, key: TypedKey, subkey: ValueSubkey) -> String {
|
pub async fn debug_record_subkey_info(&self, key: TypedKey, subkey: ValueSubkey) -> String {
|
@ -2,6 +2,8 @@ use super::*;
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(in crate::storage_manager) struct ActiveWatch {
|
pub(in crate::storage_manager) struct ActiveWatch {
|
||||||
|
/// The watch id returned from the watch node
|
||||||
|
pub id: u64,
|
||||||
/// The expiration of a successful watch
|
/// The expiration of a successful watch
|
||||||
pub expiration_ts: Timestamp,
|
pub expiration_ts: Timestamp,
|
||||||
/// Which node accepted the watch
|
/// Which node accepted the watch
|
||||||
@ -42,10 +44,16 @@ impl OpenedRecord {
|
|||||||
pub fn writer(&self) -> Option<&KeyPair> {
|
pub fn writer(&self) -> Option<&KeyPair> {
|
||||||
self.writer.as_ref()
|
self.writer.as_ref()
|
||||||
}
|
}
|
||||||
|
pub fn set_writer(&mut self, writer: Option<KeyPair>) {
|
||||||
|
self.writer = writer;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn safety_selection(&self) -> SafetySelection {
|
pub fn safety_selection(&self) -> SafetySelection {
|
||||||
self.safety_selection
|
self.safety_selection
|
||||||
}
|
}
|
||||||
|
pub fn set_safety_selection(&mut self, safety_selection: SafetySelection) {
|
||||||
|
self.safety_selection = safety_selection;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_active_watch(&mut self, active_watch: ActiveWatch) {
|
pub fn set_active_watch(&mut self, active_watch: ActiveWatch) {
|
||||||
self.active_watch = Some(active_watch);
|
self.active_watch = Some(active_watch);
|
@ -23,7 +23,7 @@ where
|
|||||||
detail: D,
|
detail: D,
|
||||||
) -> VeilidAPIResult<Self> {
|
) -> VeilidAPIResult<Self> {
|
||||||
let schema = descriptor.schema()?;
|
let schema = descriptor.schema()?;
|
||||||
let subkey_count = schema.subkey_count();
|
let subkey_count = schema.max_subkey() as usize + 1;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
descriptor,
|
descriptor,
|
||||||
subkey_count,
|
subkey_count,
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user