Merge branch 'v0.4.0' into jf/qns-v2

This commit is contained in:
dr-frmr 2023-12-19 16:23:27 -05:00
commit 0d56046a3a
No known key found for this signature in database
91 changed files with 5680 additions and 10760 deletions

2
.gitignore vendored
View File

@ -1,4 +1,6 @@
target/
wit/
uqbar
.vscode
.app-signing
.DS_Store

1124
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,18 +10,17 @@ repository = "https://github.com/uqbar-dao/uqbar"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
reqwest = { version = "0.11.22", features = ["blocking"] }
sha2 = "0.10"
walkdir = "2.4"
zip = "0.6"
[features]
llm = []
simulation-mode = []
[dependencies]
aes-gcm = "0.10.2"
anyhow = "1.0.71"
async-recursion = "1.0.4"
async-trait = "0.1.71"
base64 = "0.13"
bincode = "1.3.3"
@ -34,7 +33,6 @@ clap = { version = "4.4", features = ["derive"] }
crossterm = { version = "0.26.1", features = ["event-stream", "bracketed-paste"] }
dashmap = "5.5.3"
digest = "0.10"
dotenv = "0.15.0"
elliptic-curve = { version = "0.13.5", features = ["ecdh"] }
ethers = "2.0"
ethers-providers = "2.0.9"
@ -49,7 +47,6 @@ http = "0.2.9"
jwt = "0.16"
lazy_static = "1.4.0"
log = "*"
lru-mem = "0.3.0"
nohash-hasher = "0.2.0"
num-traits = "0.2"
open = "5.0.0"
@ -59,11 +56,7 @@ reqwest = "0.11.18"
ring = "0.16.20"
rmp-serde = "1.1.2"
route-recognizer = "0.3.1"
rsa = "0.9"
rusoto_core = "0.48.0"
rusoto_s3 = "0.48.0"
rusoto_credential = "0.48.0"
serde = {version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_urlencoded = "0.7"
sha2 = "0.10"
@ -72,9 +65,10 @@ thiserror = "1.0"
tokio = { version = "1.28", features = ["fs", "macros", "rt-multi-thread", "sync"] }
tokio-tungstenite = "0.20.1"
url = "2.4.1"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
uuid = { version = "1.1.2", features = ["serde", "v4"] }
warp = "0.3.5"
wasmtime = "14.0.4"
wasmtime-wasi = "14.0.4"
wasmtime = "15.0.1"
wasmtime-wasi = "15.0.1"
zip = "0.6"
rocksdb = { version = "0.21.0", features = ["multi-threaded-cf"] }
rusqlite = { version = "0.30.0", features = ["bundled"] }

View File

@ -31,7 +31,7 @@ Get an eth-sepolia-rpc API key and pass that as an argument. You can get one for
Make sure not to use the same home directory for two nodes at once! You can use any name for the home directory: here we just use `home`. The `--` here separates cargo arguments from binary arguments.
TODO: document feature flags here, `--llm` and `--simulation-mode`
TODO: document feature flags `--simulation-mode`
```bash
cargo +nightly run --release -- home --rpc wss://eth-sepolia.g.alchemy.com/v2/<your-api-key>
```
@ -73,6 +73,6 @@ On boot you will be prompted to navigate to `localhost:8080`. Make sure your ETH
Download and install an app:
```
!m our@main:app_store:uqbar {"Download": {"package": {"package_name": "<pkg>", "publisher_node": "<node>"}, "install_from": "<node>"}}
!m our@main:app_store:uqbar {"Install": {"package_name": "<pkg>", "publisher_node": "<node>"}}
/m our@main:app_store:uqbar {"Download": {"package": {"package_name": "<pkg>", "publisher_node": "<node>"}, "install_from": "<node>"}}
/m our@main:app_store:uqbar {"Install": {"package_name": "<pkg>", "publisher_node": "<node>"}}
```

42
build-release.py Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env python3
import os
import shutil
import subprocess
def build_and_move(feature, tmp_dir):
print("\n" + "=" * 50)
print(f"BUILDING {feature if feature else 'default'}")
print("=" * 50 + "\n")
if feature:
subprocess.run(["cargo", "+nightly", "build", "--release", "--features", feature], check=True)
binary_name = f"uqbar-{feature}"
else:
subprocess.run(["cargo", "+nightly", "build", "--release"], check=True)
binary_name = "uqbar"
# Move and rename the binary
source_path = "target/release/uqbar"
dest_path = os.path.join(tmp_dir, binary_name)
shutil.move(source_path, dest_path)
def main():
# Features to compile with
features = ["", "simulation-mode"] # Add more features as needed
# Ensure the tmp directory is clean
tmp_dir = "/tmp/uqbar-release"
if os.path.exists(tmp_dir):
shutil.rmtree(tmp_dir)
os.makedirs(tmp_dir)
# Loop through the features and build
for feature in features:
build_and_move(feature, tmp_dir)
print("Build and move process completed.\nFind release in {tmp_dir}.")
if __name__ == "__main__":
main()

View File

@ -135,6 +135,17 @@ fn main() {
let pwd = std::env::current_dir().unwrap();
// Pull wit from git repo
let wit_dir = pwd.join("wit");
fs::create_dir_all(&wit_dir).unwrap();
let wit_file = wit_dir.join("uqbar.wit");
if !wit_file.exists() {
let mut wit_file = std::fs::File::create(&wit_file).unwrap();
let uqbar_wit_url = "https://raw.githubusercontent.com/uqbar-dao/uqwit/master/uqbar.wit";
let mut response = reqwest::blocking::get(uqbar_wit_url).unwrap();
io::copy(&mut response, &mut wit_file).unwrap();
}
// Create target.wasm (compiled .wit) & world
run_command(Command::new("wasm-tools").args([
"component",

View File

@ -120,9 +120,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
@ -131,9 +131,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.14.2"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
@ -184,9 +184,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "leb128"
@ -196,9 +196,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "log"
@ -220,9 +220,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.69"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
@ -268,9 +268,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "semver"
@ -280,18 +280,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.191"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a834c4821019838224821468552240d4d95d14e751986442c816572d39a080c9"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.191"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46fa52d5646bce91b680189fe5b1c049d2ea38dabb4e2e7c8d00ca12cfbfbcfd"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@ -322,9 +322,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.11.1"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "spdx"
@ -337,9 +337,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.39"
version = "2.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
dependencies = [
"proc-macro2",
"quote",
@ -389,9 +389,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
[[package]]
name = "unicode-ident"
@ -422,8 +422,8 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uqbar_process_lib"
version = "0.3.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=5e1b94a#5e1b94ae2f85c66da33ec52117a72b90d53c4d22"
version = "0.4.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=8342b1a#8342b1a131401fb5d141dab8c90e79aa6d2bc909"
dependencies = [
"anyhow",
"bincode",
@ -461,18 +461,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.36.2"
version = "0.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.10.11"
version = "0.10.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f"
checksum = "d835d67708f6374937c550ad8dd1d17c616ae009e3f00d7a0ac9f7825e78c36a"
dependencies = [
"anyhow",
"indexmap",
@ -486,9 +486,9 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.116.1"
version = "0.118.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50"
checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9"
dependencies = [
"indexmap",
"semver",
@ -496,8 +496,8 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"bitflags",
"wit-bindgen-rust-macro",
@ -505,8 +505,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-core"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"wit-component",
@ -515,8 +515,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust"
version = "0.13.2"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"heck",
@ -527,8 +527,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"proc-macro2",
@ -541,9 +541,9 @@ dependencies = [
[[package]]
name = "wit-component"
version = "0.17.0"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e"
checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2"
dependencies = [
"anyhow",
"bitflags",
@ -560,9 +560,9 @@ dependencies = [
[[package]]
name = "wit-parser"
version = "0.12.2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76"
checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3"
dependencies = [
"anyhow",
"id-arena",

View File

@ -14,11 +14,11 @@ lto = true
anyhow = "1.0"
bincode = "1.3.3"
rand = "0.8"
serde = {version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sha2 = "0.10.8"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" }
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "8342b1a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
[lib]
crate-type = ["cdylib"]

View File

@ -2,10 +2,9 @@ use serde::{Deserialize, Serialize};
use sha2::Digest;
use std::collections::{HashMap, HashSet};
use uqbar_process_lib::kernel_types as kt;
use uqbar_process_lib::uqbar::process::standard as wit;
use uqbar_process_lib::{
get_capability, get_payload, get_typed_state, grant_messaging, println, receive, set_state,
share_capability, Address, Message, NodeId, PackageId, ProcessId, Request, Response,
await_message, get_capability, get_payload, get_typed_state, grant_messaging, println,
set_state, share_capability, Address, Message, NodeId, PackageId, ProcessId, Request, Response,
};
wit_bindgen::generate!({
@ -182,36 +181,34 @@ impl Guest for Component {
// active the main messaging loop: handle requests and responses
loop {
let (source, message) = match receive() {
Ok((source, message)) => (source, message),
Err((error, _context)) => {
// TODO handle net errors more usefully based on their context
println!("net error: {:?}", error.kind);
match await_message() {
Err(send_error) => {
println!("{our}: got network error: {send_error:?}");
continue;
}
};
match handle_message(&our, &source, &mut state, &message) {
Ok(()) => {}
Err(e) => println!("app-store: error handling message: {:?}", e),
Ok(message) => match handle_message(&our, &mut state, &message) {
Ok(()) => {}
Err(e) => println!("app-store: error handling message: {:?}", e),
},
}
}
}
}
fn handle_message(
our: &Address,
source: &Address,
mut state: &mut State,
message: &Message,
) -> anyhow::Result<()> {
fn handle_message(our: &Address, mut state: &mut State, message: &Message) -> anyhow::Result<()> {
match message {
Message::Request(req) => {
match &serde_json::from_slice::<Req>(&req.ipc) {
Message::Request {
source,
expects_response,
ipc,
..
} => {
match &serde_json::from_slice::<Req>(&ipc) {
Ok(Req::LocalRequest(local_request)) => {
match handle_local_request(&our, &source, local_request, &mut state) {
Ok(None) => return Ok(()),
Ok(Some(resp)) => {
if req.expects_response.is_some() {
if expects_response.is_some() {
Response::new().ipc(serde_json::to_vec(&resp)?).send()?;
}
}
@ -224,7 +221,7 @@ fn handle_message(
match handle_remote_request(&our, &source, remote_request, &mut state) {
Ok(None) => return Ok(()),
Ok(Some(resp)) => {
if req.expects_response.is_some() {
if expects_response.is_some() {
Response::new().ipc(serde_json::to_vec(&resp)?).send()?;
}
}
@ -259,50 +256,48 @@ fn handle_message(
}
}
Ok(Req::FTWorkerCommand(_)) => {
spawn_receive_transfer(&our, &req.ipc);
spawn_receive_transfer(&our, &ipc);
}
e => {
return Err(anyhow::anyhow!(
"app store bad request: {:?}, error {:?}",
req.ipc,
ipc,
e
))
}
}
}
Message::Response((response, context)) => {
match &serde_json::from_slice::<Resp>(&response.ipc) {
Ok(Resp::RemoteResponse(remote_response)) => match remote_response {
RemoteResponse::DownloadApproved => {
println!("app store: download approved, should be starting");
}
RemoteResponse::DownloadDenied => {
println!("app store: could not download package from that node!");
}
},
Ok(Resp::FTWorkerResult(ft_worker_result)) => {
let Ok(context) =
serde_json::from_slice::<FileTransferContext>(&context.as_ref().unwrap())
else {
return Err(anyhow::anyhow!("file_transfer: got weird local request"));
};
match ft_worker_result {
FTWorkerResult::SendSuccess => {
println!(
"file_transfer: successfully shared app {} in {:.4}s",
context.file_name,
std::time::SystemTime::now()
.duration_since(context.start_time)
.unwrap()
.as_secs_f64(),
);
}
e => return Err(anyhow::anyhow!("file_transfer: {:?}", e)),
}
Message::Response { ipc, context, .. } => match &serde_json::from_slice::<Resp>(&ipc) {
Ok(Resp::RemoteResponse(remote_response)) => match remote_response {
RemoteResponse::DownloadApproved => {
println!("app store: download approved, should be starting");
}
RemoteResponse::DownloadDenied => {
println!("app store: could not download package from that node!");
}
},
Ok(Resp::FTWorkerResult(ft_worker_result)) => {
let Ok(context) =
serde_json::from_slice::<FileTransferContext>(&context.as_ref().unwrap())
else {
return Err(anyhow::anyhow!("file_transfer: got weird local request"));
};
match ft_worker_result {
FTWorkerResult::SendSuccess => {
println!(
"file_transfer: successfully shared app {} in {:.4}s",
context.file_name,
std::time::SystemTime::now()
.duration_since(context.start_time)
.unwrap()
.as_secs_f64(),
);
}
e => return Err(anyhow::anyhow!("file_transfer: {:?}", e)),
}
_ => return Err(anyhow::anyhow!("bad response from file transfer worker")),
}
}
_ => return Err(anyhow::anyhow!("bad response from file transfer worker")),
},
}
Ok(())
}
@ -321,14 +316,16 @@ fn handle_local_request(
let Some(mut payload) = get_payload() else {
return Err(anyhow::anyhow!("no payload"));
};
let drive = format!("/{}/pkg", package);
Request::new()
.target(Address::from_str("our@vfs:sys:uqbar")?)
.ipc(serde_json::to_vec(&kt::VfsRequest {
drive: package.to_string(),
action: kt::VfsAction::New,
path: drive.clone(),
action: kt::VfsAction::CreateDrive,
})?)
.send_and_await_response(5)??;
.send_and_await_response(5)?
.unwrap();
// produce the version hash for this new package
let mut hasher = sha2::Sha256::new();
@ -337,42 +334,49 @@ fn handle_local_request(
// add zip bytes
payload.mime = Some("application/zip".to_string());
Request::new()
let response = Request::new()
.target(Address::from_str("our@vfs:sys:uqbar")?)
.ipc(serde_json::to_vec(&kt::VfsRequest {
drive: package.to_string(),
action: kt::VfsAction::Add {
full_path: package.to_string(),
entry_type: kt::AddEntryType::ZipArchive,
},
path: drive.clone(),
action: kt::VfsAction::AddZip,
})?)
.payload(payload.clone())
.send_and_await_response(5)??;
.send_and_await_response(5)?.unwrap();
let Message::Response { ipc: ref vfs_ipc, .. } = response else {
panic!("app_store: send_and_await_response must return Response");
};
let vfs_ipc = serde_json::from_slice::<serde_json::Value>(vfs_ipc)?;
if vfs_ipc == serde_json::json!({"Err": "NoCap"}) {
return Err(anyhow::anyhow!("cannot add NewPackage: do not have capability to access vfs"));
}
// save the zip file itself in VFS for sharing with other nodes
// call it <package>.zip
let zip_path = format!("{}/{}.zip", drive.clone(), package);
Request::new()
.target(Address::from_str("our@vfs:sys:uqbar")?)
.inherit(true)
.ipc(serde_json::to_vec(&kt::VfsRequest {
drive: package.to_string(),
action: kt::VfsAction::Add {
full_path: format!("/{}.zip", package.to_string()),
entry_type: kt::AddEntryType::NewFile,
},
path: zip_path,
action: kt::VfsAction::ReWrite,
})?)
.payload(payload)
.send_and_await_response(5)??;
.send_and_await_response(5)?
.unwrap();
let metadata_path = format!("{}/metadata.json", drive.clone());
Request::new()
.target(Address::from_str("our@vfs:sys:uqbar")?)
.ipc(serde_json::to_vec(&kt::VfsRequest {
drive: package.to_string(),
action: kt::VfsAction::GetEntry("/metadata.json".into()),
path: metadata_path,
action: kt::VfsAction::Read,
})?)
.send_and_await_response(5)??;
.send_and_await_response(5)?
.unwrap();
let Some(payload) = get_payload() else {
return Err(anyhow::anyhow!("no metadata found!"));
};
let metadata = String::from_utf8(payload.bytes)?;
let metadata = serde_json::from_str::<kt::PackageMetadata>(&metadata)?;
@ -406,8 +410,8 @@ fn handle_local_request(
))?)
.send_and_await_response(5)
{
Ok(Ok((_source, Message::Response((resp, _context))))) => {
let resp = serde_json::from_slice::<Resp>(&resp.ipc)?;
Ok(Ok(Message::Response { ipc, .. })) => {
let resp = serde_json::from_slice::<Resp>(&ipc)?;
match resp {
Resp::RemoteResponse(RemoteResponse::DownloadApproved) => {
state.requested_packages.insert(package.clone());
@ -421,13 +425,15 @@ fn handle_local_request(
},
))),
LocalRequest::Install(package) => {
let drive_path = format!("/{}/pkg", package);
Request::new()
.target(Address::from_str("our@vfs:sys:uqbar")?)
.ipc(serde_json::to_vec(&kt::VfsRequest {
drive: package.to_string(),
action: kt::VfsAction::GetEntry("/manifest.json".into()),
path: format!("{}/manifest.json", drive_path),
action: kt::VfsAction::Read,
})?)
.send_and_await_response(5)??;
.send_and_await_response(5)?
.unwrap();
let Some(payload) = get_payload() else {
return Err(anyhow::anyhow!("no payload"));
};
@ -438,7 +444,7 @@ fn handle_local_request(
&Address::new(&our.node, ("vfs", "sys", "uqbar")),
&serde_json::to_string(&serde_json::json!({
"kind": "read",
"drive": package.to_string(),
"drive": drive_path,
}))?,
) else {
return Err(anyhow::anyhow!("app-store: no read cap"));
@ -447,7 +453,7 @@ fn handle_local_request(
&Address::new(&our.node, ("vfs", "sys", "uqbar")),
&serde_json::to_string(&serde_json::json!({
"kind": "write",
"drive": package.to_string(),
"drive": drive_path,
}))?,
) else {
return Err(anyhow::anyhow!("app-store: no write cap"));
@ -462,33 +468,21 @@ fn handle_local_request(
// then, once all have been initialized, grant them requested caps
// and finally start them.
for entry in &manifest {
let path = if entry.process_wasm_path.starts_with("/") {
let wasm_path = if entry.process_wasm_path.starts_with("/") {
entry.process_wasm_path.clone()
} else {
format!("/{}", entry.process_wasm_path)
};
let (_, hash_response) = Request::new()
.target(Address::from_str("our@vfs:sys:uqbar")?)
.ipc(serde_json::to_vec(&kt::VfsRequest {
drive: package.to_string(),
action: kt::VfsAction::GetHash(path.clone()),
})?)
.send_and_await_response(5)??;
let Message::Response((wit::Response { ipc, .. }, _)) = hash_response else {
return Err(anyhow::anyhow!("bad vfs response"));
};
let kt::VfsResponse::GetHash(Some(hash)) = serde_json::from_slice(&ipc)? else {
return Err(anyhow::anyhow!("no hash in vfs"));
};
let wasm_path = format!("{}{}", drive_path, wasm_path);
// build initial caps
let mut initial_capabilities: HashSet<kt::SignedCapability> = HashSet::new();
if entry.request_networking {
initial_capabilities.insert(kt::de_wit_signed_capability(networking_cap.clone()));
initial_capabilities
.insert(kt::de_wit_signed_capability(networking_cap.clone()));
}
initial_capabilities.insert(kt::de_wit_signed_capability(read_cap.clone()));
initial_capabilities.insert(kt::de_wit_signed_capability(write_cap.clone()));
let process_id = format!("{}:{}", entry.process_name, package.to_string());
let process_id = format!("{}:{}", entry.process_name, package);
let Ok(parsed_new_process_id) = ProcessId::from_str(&process_id) else {
return Err(anyhow::anyhow!("app-store: invalid process id!"));
};
@ -500,24 +494,26 @@ fn handle_local_request(
))?)
.send()?;
let (_, _bytes_response) = Request::new()
let _bytes_response = Request::new()
.target(Address::from_str("our@vfs:sys:uqbar")?)
.ipc(serde_json::to_vec(&kt::VfsRequest {
drive: package.to_string(),
action: kt::VfsAction::GetEntry(path),
path: wasm_path.clone(),
action: kt::VfsAction::Read,
})?)
.send_and_await_response(5)??;
.send_and_await_response(5)?
.unwrap();
Request::new()
.target(Address::from_str("our@kernel:sys:uqbar")?)
.ipc(serde_json::to_vec(&kt::KernelCommand::InitializeProcess {
id: parsed_new_process_id,
wasm_bytes_handle: hash,
on_panic: entry.on_panic.clone(),
wasm_bytes_handle: wasm_path,
on_exit: entry.on_exit.clone(),
initial_capabilities,
public: entry.public,
})?)
.inherit(true)
.send_and_await_response(5)?;
.send_and_await_response(5)?
.unwrap();
}
for entry in &manifest {
let process_id = ProcessId::new(
@ -526,41 +522,93 @@ fn handle_local_request(
package.publisher(),
);
if let Some(to_request) = &entry.request_messaging {
for process_name in to_request {
let Ok(parsed_process_id) = ProcessId::from_str(&process_name) else {
// TODO handle arbitrary caps here
continue;
};
let Some(messaging_cap) = get_capability(
&Address {
node: our.node.clone(),
process: parsed_process_id.clone(),
},
&"\"messaging\"".into(),
) else {
println!("app-store: no cap for {} to give away!", process_name);
continue;
};
share_capability(&process_id, &messaging_cap);
for value in to_request {
let mut capability = None;
match value {
serde_json::Value::String(process_name) => {
if let Ok(parsed_process_id) = ProcessId::from_str(process_name) {
capability = get_capability(
&Address {
node: our.node.clone(),
process: parsed_process_id.clone(),
},
&"\"messaging\"".into(),
);
}
}
serde_json::Value::Object(map) => {
if let Some(process_name) = map.get("process") {
if let Ok(parsed_process_id) =
ProcessId::from_str(&process_name.to_string())
{
if let Some(params) = map.get("params") {
capability = get_capability(
&Address {
node: our.node.clone(),
process: parsed_process_id.clone(),
},
&params.to_string(),
);
}
}
}
}
_ => {
continue;
}
}
if let Some(cap) = capability {
share_capability(&process_id, &cap);
} else {
println!("app-store: no cap: {}, for {} to request!", value.to_string(), process_id);
}
}
}
if let Some(to_grant) = &entry.grant_messaging {
let Some(messaging_cap) = get_capability(
&Address {
node: our.node.clone(),
process: process_id.clone(),
},
&"\"messaging\"".into(),
) else {
println!("app-store: no cap for {} to give away!", process_id);
continue;
};
for process_name in to_grant {
let Ok(parsed_process_id) = ProcessId::from_str(&process_name) else {
// TODO handle arbitrary caps here
continue;
};
share_capability(&parsed_process_id, &messaging_cap);
for value in to_grant {
let mut capability = None;
let mut to_process = None;
match value {
serde_json::Value::String(process_name) => {
if let Ok(parsed_process_id) = ProcessId::from_str(process_name) {
capability = get_capability(
&Address {
node: our.node.clone(),
process: process_id.clone(),
},
&"\"messaging\"".into(),
);
to_process = Some(parsed_process_id);
}
}
serde_json::Value::Object(map) => {
if let Some(process_name) = map.get("process") {
if let Ok(parsed_process_id) =
ProcessId::from_str(&process_name.to_string())
{
if let Some(params) = map.get("params") {
capability = get_capability(
&Address {
node: our.node.clone(),
process: process_id.clone(),
},
&params.to_string(),
);
to_process = Some(parsed_process_id);
}
}
}
}
_ => {
continue;
}
}
if let Some(cap) = capability {
share_capability(&to_process.unwrap(), &cap);
} else {
println!("app-store: no cap: {}, for {} to grant!", value.to_string(), process_id);
}
}
}
Request::new()
@ -568,7 +616,8 @@ fn handle_local_request(
.ipc(serde_json::to_vec(&kt::KernelCommand::RunProcess(
process_id,
))?)
.send_and_await_response(5)?;
.send_and_await_response(5)?
.unwrap();
}
Ok(Some(Resp::InstallResponse(InstallResponse::Success)))
}
@ -598,15 +647,18 @@ fn handle_remote_request(
return Ok(Some(Resp::RemoteResponse(RemoteResponse::DownloadDenied)));
}
// get the .zip from VFS and attach as payload to response
let file_name = format!("/{}.zip", package.to_string());
let drive_name = format!("/{}/pkg", package);
let file_path = format!("/{}.zip", drive_name);
Request::new()
.target(Address::from_str("our@vfs:sys:uqbar")?)
.ipc(serde_json::to_vec(&kt::VfsRequest {
drive: package.to_string(),
action: kt::VfsAction::GetEntry(file_name.clone()),
path: file_path,
action: kt::VfsAction::Read,
})?)
.send_and_await_response(5)?;
.send_and_await_response(5)?
.unwrap();
// transfer will inherit the payload bytes we receive from VFS
let file_name = format!("/{}.zip", package);
spawn_transfer(&our, &file_name, None, &source);
Ok(Some(Resp::RemoteResponse(RemoteResponse::DownloadApproved)))
}

View File

@ -71,9 +71,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
@ -82,9 +82,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.14.2"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
@ -135,9 +135,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "leb128"
@ -147,9 +147,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "log"
@ -171,9 +171,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.69"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
@ -219,9 +219,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "semver"
@ -231,18 +231,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.191"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a834c4821019838224821468552240d4d95d14e751986442c816572d39a080c9"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.191"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46fa52d5646bce91b680189fe5b1c049d2ea38dabb4e2e7c8d00ca12cfbfbcfd"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@ -262,9 +262,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.11.1"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "spdx"
@ -277,9 +277,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.39"
version = "2.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
dependencies = [
"proc-macro2",
"quote",
@ -323,9 +323,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
[[package]]
name = "unicode-ident"
@ -356,8 +356,8 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uqbar_process_lib"
version = "0.3.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=5e1b94a#5e1b94ae2f85c66da33ec52117a72b90d53c4d22"
version = "0.4.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=b09d987#b09d9875edce1a230549cf56cf088f95e38d4abd"
dependencies = [
"anyhow",
"bincode",
@ -389,18 +389,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.36.2"
version = "0.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.10.11"
version = "0.10.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f"
checksum = "d835d67708f6374937c550ad8dd1d17c616ae009e3f00d7a0ac9f7825e78c36a"
dependencies = [
"anyhow",
"indexmap",
@ -414,9 +414,9 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.116.1"
version = "0.118.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50"
checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9"
dependencies = [
"indexmap",
"semver",
@ -424,8 +424,8 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"bitflags",
"wit-bindgen-rust-macro",
@ -433,8 +433,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-core"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"wit-component",
@ -443,8 +443,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust"
version = "0.13.2"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"heck",
@ -455,8 +455,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"proc-macro2",
@ -469,9 +469,9 @@ dependencies = [
[[package]]
name = "wit-component"
version = "0.17.0"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e"
checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2"
dependencies = [
"anyhow",
"bitflags",
@ -488,9 +488,9 @@ dependencies = [
[[package]]
name = "wit-parser"
version = "0.12.2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76"
checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3"
dependencies = [
"anyhow",
"id-arena",

View File

@ -14,10 +14,10 @@ lto = true
anyhow = "1.0"
bincode = "1.3.3"
rand = "0.8"
serde = {version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" }
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "b09d987" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
[lib]
crate-type = ["cdylib"]

View File

@ -55,7 +55,7 @@ pub fn spawn_transfer(
let Ok(worker_process_id) = spawn(
Some(&transfer_id.to_string()),
"/ft_worker.wasm".into(),
&OnPanic::None, // can set message-on-panic here
&OnExit::None, // can set message-on-panic here
&Capabilities::All,
false, // not public
) else {
@ -106,7 +106,7 @@ pub fn spawn_receive_transfer(our: &Address, ipc: &[u8]) {
let Ok(worker_process_id) = spawn(
Some(&transfer_id.to_string()),
"/ft_worker.wasm".into(),
&OnPanic::None, // can set message-on-panic here
&OnExit::None, // can set message-on-panic here
&Capabilities::All,
false, // not public
) else {

View File

@ -1,5 +1,8 @@
use serde::{Deserialize, Serialize};
use uqbar_process_lib::uqbar::process::standard::*;
//use uqbar_process_lib::uqbar::process::standard::*;
use uqbar_process_lib::uqbar::process::standard::{Message as StdMessage, Request as StdRequest, Response as StdResponse, SendErrorKind};
use uqbar_process_lib::{await_message, get_payload, print_to_terminal, send_and_await_response, send_request, send_response, Address, Message, Payload};
mod ft_worker_lib;
use ft_worker_lib::*;
@ -25,11 +28,11 @@ impl Guest for Component {
let our = Address::from_str(&our).unwrap();
print_to_terminal(1, &format!("{}: start", our.process));
let Ok((parent_process, Message::Request(req))) = receive() else {
let Ok(Message::Request { source: parent_process, ipc, .. }) = await_message() else {
panic!("ft_worker: got bad init message");
};
let command = serde_json::from_slice::<FTWorkerCommand>(&req.ipc)
let command = serde_json::from_slice::<FTWorkerCommand>(&ipc)
.expect("ft_worker: got unparseable init message");
match command {
@ -55,7 +58,7 @@ impl Guest for Component {
// acknowledgement.
match send_and_await_response(
&Address::from_str(&target).unwrap(),
&Request {
&StdRequest {
inherit: false,
expects_response: Some(timeout),
ipc: serde_json::to_vec(&FTWorkerCommand::Receive {
@ -76,7 +79,7 @@ impl Guest for Component {
SendErrorKind::Timeout => TransferError::TargetTimeout,
}))
}
Ok((opp_worker, Message::Response((response, _)))) => {
Ok((opp_worker, StdMessage::Response((response, _)))) => {
let Ok(FTWorkerProtocol::Ready) = serde_json::from_slice(&response.ipc) else {
respond_to_parent(FTWorkerResult::Err(TransferError::TargetRejected));
return;
@ -94,7 +97,7 @@ impl Guest for Component {
};
send_request(
&opp_worker,
&Request {
&StdRequest {
inherit: false,
expects_response: Some(timeout),
ipc: vec![],
@ -113,7 +116,7 @@ impl Guest for Component {
};
send_request(
&opp_worker,
&Request {
&StdRequest {
inherit: false,
expects_response: None,
ipc: vec![],
@ -126,11 +129,11 @@ impl Guest for Component {
offset += chunk_size;
}
// now wait for Finished response
let Ok((receiving_worker, Message::Response((resp, _)))) = receive() else {
let Ok(Message::Response { ipc, .. }) = await_message() else {
respond_to_parent(FTWorkerResult::Err(TransferError::TargetRejected));
return;
};
let Ok(FTWorkerProtocol::Finished) = serde_json::from_slice(&resp.ipc) else {
let Ok(FTWorkerProtocol::Finished) = serde_json::from_slice(&ipc) else {
respond_to_parent(FTWorkerResult::Err(TransferError::TargetRejected));
return;
};
@ -141,15 +144,14 @@ impl Guest for Component {
}
}
FTWorkerCommand::Receive {
transfer_id,
file_name,
file_size,
total_chunks,
timeout,
..
} => {
// send Ready response to counterparty
send_response(
&Response {
&StdResponse {
inherit: false,
ipc: serde_json::to_vec(&FTWorkerProtocol::Ready).unwrap(),
metadata: None,
@ -163,7 +165,7 @@ impl Guest for Component {
let mut chunks_received = 0;
let start_time = std::time::Instant::now();
loop {
let Ok((source, Message::Request(req))) = receive() else {
let Ok(Message::Request { .. }) = await_message() else {
respond_to_parent(FTWorkerResult::Err(TransferError::SourceFailed));
return;
};
@ -183,7 +185,7 @@ impl Guest for Component {
}
// send Finished message to sender
send_response(
&Response {
&StdResponse {
inherit: false,
ipc: serde_json::to_vec(&FTWorkerProtocol::Finished).unwrap(),
metadata: None,
@ -193,7 +195,7 @@ impl Guest for Component {
// send Success message to parent
send_request(
&parent_process,
&Request {
&StdRequest {
inherit: false,
expects_response: None,
ipc: serde_json::to_vec(&FTWorkerResult::ReceiveSuccess(file_name))
@ -213,7 +215,7 @@ impl Guest for Component {
fn respond_to_parent(result: FTWorkerResult) {
send_response(
&Response {
&StdResponse {
inherit: false,
ipc: serde_json::to_vec(&result).unwrap(),
metadata: None,

View File

@ -2,7 +2,7 @@
{
"process_name": "main",
"process_wasm_path": "/app_store.wasm",
"on_panic": "Restart",
"on_exit": "Restart",
"request_networking": true,
"request_messaging": [
"terminal:terminal:uqbar",
@ -13,7 +13,13 @@
"net:sys:uqbar",
"vfs:sys:uqbar",
"kernel:sys:uqbar",
"eth_rpc:sys:uqbar"
"eth_rpc:sys:uqbar",
{
"process": "vfs:sys:uqbar",
"params": {
"root": true
}
}
],
"public": false
}

103
modules/chess/Cargo.lock generated
View File

@ -64,7 +64,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chess"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"anyhow",
"base64",
@ -73,6 +73,7 @@ dependencies = [
"serde",
"serde_json",
"uqbar_process_lib",
"url",
"wit-bindgen",
]
@ -138,9 +139,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
@ -153,9 +154,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
@ -164,9 +165,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.14.2"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
@ -202,9 +203,9 @@ checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "idna"
version = "0.4.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
@ -223,9 +224,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "lazy_static"
@ -241,9 +242,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "log"
@ -278,9 +279,9 @@ dependencies = [
[[package]]
name = "percent-encoding"
version = "2.3.0"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pleco"
@ -304,9 +305,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.69"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
@ -487,9 +488,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "scopeguard"
@ -505,18 +506,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.191"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a834c4821019838224821468552240d4d95d14e751986442c816572d39a080c9"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.191"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46fa52d5646bce91b680189fe5b1c049d2ea38dabb4e2e7c8d00ca12cfbfbcfd"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@ -536,9 +537,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.11.1"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "spdx"
@ -551,9 +552,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.39"
version = "2.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
dependencies = [
"proc-macro2",
"quote",
@ -597,9 +598,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
[[package]]
name = "unicode-ident"
@ -630,8 +631,8 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uqbar_process_lib"
version = "0.3.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=5e1b94a#5e1b94ae2f85c66da33ec52117a72b90d53c4d22"
version = "0.4.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=65e07e4#65e07e49553a5fa3340ff7c3d9cf3340a53610c8"
dependencies = [
"anyhow",
"bincode",
@ -646,9 +647,9 @@ dependencies = [
[[package]]
name = "url"
version = "2.4.1"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
"idna",
@ -663,18 +664,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.36.2"
version = "0.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.10.11"
version = "0.10.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f"
checksum = "d835d67708f6374937c550ad8dd1d17c616ae009e3f00d7a0ac9f7825e78c36a"
dependencies = [
"anyhow",
"indexmap",
@ -688,9 +689,9 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.116.1"
version = "0.118.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50"
checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9"
dependencies = [
"indexmap",
"semver",
@ -720,8 +721,8 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "wit-bindgen"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"bitflags 2.4.1",
"wit-bindgen-rust-macro",
@ -729,8 +730,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-core"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"wit-component",
@ -739,8 +740,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust"
version = "0.13.2"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"heck",
@ -751,8 +752,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"proc-macro2",
@ -765,9 +766,9 @@ dependencies = [
[[package]]
name = "wit-component"
version = "0.17.0"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e"
checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2"
dependencies = [
"anyhow",
"bitflags 2.4.1",
@ -784,9 +785,9 @@ dependencies = [
[[package]]
name = "wit-parser"
version = "0.12.2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76"
checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3"
dependencies = [
"anyhow",
"id-arena",

View File

@ -1,10 +1,8 @@
[package]
name = "chess"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.release]
panic = "abort"
opt-level = "s"
@ -15,10 +13,11 @@ anyhow = "1.0"
base64 = "0.13"
bincode = "1.3.3"
pleco = "0.5"
serde = {version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" }
url = "*"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "65e07e4" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
[lib]
crate-type = ["cdylib"]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2,11 +2,14 @@
{
"process_name": "chess",
"process_wasm_path": "/chess.wasm",
"on_panic": "Restart",
"on_exit": "Restart",
"request_networking": true,
"request_messaging": [
"net:sys:uqbar"
],
"grant_messaging": [
"http_server:sys:uqbar"
],
"public": false
"public": true
}
]

View File

@ -1,39 +1,45 @@
#![feature(let_chains)]
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
extern crate base64;
extern crate pleco;
use pleco::Board;
use uqbar_process_lib::uqbar::process::standard as wit;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use uqbar_process_lib::{
get_payload, get_typed_state, grant_messaging, http, println, receive, set_state, Address,
Message, Payload, ProcessId, Request, Response,
await_message, call_init, get_payload, get_typed_state, http, println, set_state, Address,
Message, NodeId, Payload, Request, Response,
};
extern crate base64;
mod utils;
// Lazy way to include our static files in the binary. We'll use these to serve
// our chess app's frontend.
const CHESS_HTML: &str = include_str!("../pkg/chess.html");
const CHESS_JS: &str = include_str!("../pkg/index.js");
const CHESS_CSS: &str = include_str!("../pkg/index.css");
wit_bindgen::generate!({
path: "../../wit",
world: "process",
exports: {
world: Component,
},
});
//
// Our "chess protocol" request/response format. We'll always serialize these
// to a byte vector and send them over IPC.
//
struct Component;
#[derive(Clone, Debug)]
pub struct Game {
pub id: String, // the node with whom we are playing
pub turns: u64,
pub board: Board,
pub white: String,
pub black: String,
pub ended: bool,
#[derive(Debug, Serialize, Deserialize)]
enum ChessRequest {
NewGame { white: String, black: String },
Move { game_id: String, move_str: String },
Resign(String),
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
enum ChessResponse {
NewGameAccepted,
NewGameRejected,
MoveAccepted,
MoveRejected,
}
//
// Our serializable state format.
//
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct StoredGame {
struct Game {
pub id: String, // the node with whom we are playing
pub turns: u64,
pub board: String,
@ -42,280 +48,408 @@ pub struct StoredGame {
pub ended: bool,
}
#[derive(Clone, Debug)]
pub struct ChessState {
#[derive(Debug, Serialize, Deserialize)]
struct ChessState {
pub games: HashMap<String, Game>, // game is by opposing player id
pub records: HashMap<String, (u64, u64, u64)>, // wins, losses, draws
pub clients: HashSet<u32>, // doesn't get persisted
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct StoredChessState {
pub games: HashMap<String, StoredGame>, // game is by opposing player id
pub records: HashMap<String, (u64, u64, u64)>, // wins, losses, draws
#[derive(Debug, Serialize, Deserialize)]
struct StoredChessState {
pub games: HashMap<String, Game>, // game is by opposing player id
}
const CHESS_PAGE: &str = include_str!("../pkg/chess.html");
const CHESS_JS: &str = include_str!("../pkg/index.js");
const CHESS_CSS: &str = include_str!("../pkg/index.css");
fn save_chess_state(state: &ChessState) {
set_state(&bincode::serialize(&state.games).unwrap());
}
impl Guest for Component {
fn init(our: String) {
let our = Address::from_str(&our).unwrap();
println!("{our}: start");
fn load_chess_state() -> ChessState {
match get_typed_state(|bytes| Ok(bincode::deserialize::<HashMap<String, Game>>(bytes)?)) {
Some(games) => ChessState {
games,
clients: HashSet::new(),
},
None => ChessState {
games: HashMap::new(),
clients: HashSet::new(),
},
}
}
grant_messaging(
&our,
vec![ProcessId::new(Some("http_server"), "sys", "uqbar")],
);
fn send_ws_update(our: &Address, game: &Game, open_channels: &HashSet<u32>) -> anyhow::Result<()> {
for channel in open_channels {
Request::new()
.target((&our.node, "http_server", "sys", "uqbar"))
.ipc(serde_json::to_vec(
&http::HttpServerAction::WebSocketPush {
channel_id: *channel,
message_type: http::WsMessageType::Binary,
},
)?)
.payload(Payload {
mime: Some("application/json".to_string()),
bytes: serde_json::json!({
"kind": "game_update",
"data": game,
})
.to_string()
.into_bytes(),
})
.send()?;
}
Ok(())
}
// serve static page at /
// dynamically handle requests to /games
http::bind_http_static_path(
"/",
true,
false,
Some("text/html".to_string()),
CHESS_PAGE
.replace("${node}", &our.node)
.replace("${process}", &our.process.to_string())
// TODO serve these independently on paths..
// also build utils for just serving a vfs dir
.replace("${js}", CHESS_JS)
.replace("${css}", CHESS_CSS)
.as_bytes()
.to_vec(),
)
.unwrap();
http::bind_http_path("/games", true, false).unwrap();
// Boilerplate: generate the wasm bindings for an Uqbar app
wit_bindgen::generate!({
path: "../../wit",
world: "process",
exports: {
world: Component,
},
});
// After generating bindings, use this macro to define the Component struct
// and its init() function, which the kernel will look for on startup.
call_init!(initialize);
let mut state: ChessState = match get_typed_state(|bytes| {
Ok(bincode::deserialize::<StoredChessState>(bytes)?)
}) {
Some(mut state) => ChessState {
games: state
.games
.iter_mut()
.map(|(id, game)| {
(
id.clone(),
Game {
id: id.to_owned(),
turns: game.turns,
board: Board::from_fen(&game.board).unwrap_or(Board::start_pos()),
white: game.white.to_owned(),
black: game.black.to_owned(),
ended: game.ended,
},
)
})
.collect(),
records: state.records,
},
None => ChessState {
games: HashMap::new(),
records: HashMap::new(),
},
};
fn initialize(our: Address) {
// A little printout to show in terminal that the process has started.
println!("{} by {}: start", our.process(), our.publisher());
loop {
let Ok((source, message)) = receive() else {
println!("chess: got network error");
// serve static page at /index.html, /index.js, /index.css
// dynamically handle requests to /games
http::bind_http_static_path(
"/",
true, // only serve for ourselves
false, // can access remotely
Some("text/html".to_string()),
CHESS_HTML
.replace("${node}", &our.node)
.replace("${process}", &our.process.to_string())
.as_bytes()
.to_vec(),
)
.unwrap();
http::bind_http_static_path(
"/index.js",
true,
false,
Some("text/javascript".to_string()),
CHESS_JS.as_bytes().to_vec(),
)
.unwrap();
http::bind_http_static_path(
"/index.css",
true,
false,
Some("text/css".to_string()),
CHESS_CSS.as_bytes().to_vec(),
)
.unwrap();
http::bind_http_path("/games", true, false).unwrap();
// Allow websockets to be opened at / (our process ID will be prepended).
http::bind_ws_path("/", true, false).unwrap();
// Grab our state, then enter the main event loop.
let mut state: ChessState = load_chess_state();
main_loop(&our, &mut state);
}
fn main_loop(our: &Address, state: &mut ChessState) {
loop {
// Call await_message() to wait for any incoming messages.
// If we get a network error, make a print and throw it away.
// In a high-quality consumer-grade app, we'd want to explicitly handle
// this and surface it to the user.
match await_message() {
Err(send_error) => {
println!("{our}: got network error: {send_error:?}");
continue;
};
let Message::Request(request) = message else {
println!("chess: got unexpected Response");
continue;
};
match handle_request(&our, &source, &request, &mut state) {
Ok(()) => continue,
Err(e) => println!("chess: error handling request: {:?}", e),
}
Ok(message) => match handle_request(&our, &message, state) {
Ok(()) => continue,
Err(e) => println!("{our}: error handling request: {:?}", e),
},
}
}
}
fn handle_request(
our: &Address,
source: &Address,
request: &wit::Request,
state: &mut ChessState,
) -> anyhow::Result<()> {
if source.process == "chess:chess:uqbar" {
let message_json = serde_json::from_slice::<serde_json::Value>(&request.ipc)?;
handle_chess_request(our, source, message_json, state)
} else if source.process.to_string() == "http_server:sys:uqbar" {
let http_request = serde_json::from_slice::<http::IncomingHttpRequest>(&request.ipc)?;
handle_http_request(our, http_request, state)
/// Handle chess protocol messages from ourself *or* other nodes.
fn handle_request(our: &Address, message: &Message, state: &mut ChessState) -> anyhow::Result<()> {
// Throw away responses. We never expect any responses *here*, because for every
// chess protocol request, we *await* its response in-place. This is appropriate
// for direct node<>node comms, less appropriate for other circumstances...
if !message.is_request() {
return Ok(());
}
// If the request is from another node, handle it as an incoming request.
// Note that we can enforce the ProcessId as well, but it shouldn't be a trusted
// piece of information, since another node can easily spoof any ProcessId on a request.
// It can still be useful simply as a protocol-level switch to handle different kinds of
// requests from the same node, with the knowledge that the remote node can finagle with
// which ProcessId a given message can be from. It's their code, after all.
if message.source().node != our.node {
// Deserialize the request IPC to our format, and throw it away if it
// doesn't fit.
let Ok(chess_request) = serde_json::from_slice::<ChessRequest>(message.ipc()) else {
return Err(anyhow::anyhow!("invalid chess request"));
};
handle_chess_request(our, &message.source().node, state, &chess_request)
// ...and if the request is from ourselves, handle it as our own!
// Note that since this is a local request, we *can* trust the ProcessId.
// Here, we'll accept messages from the local terminal so as to make this a "CLI" app.
} else if message.source().node == our.node
&& message.source().process == "terminal:terminal:uqbar"
{
let Ok(chess_request) = serde_json::from_slice::<ChessRequest>(message.ipc()) else {
return Err(anyhow::anyhow!("invalid chess request"));
};
handle_local_request(our, state, &chess_request)
} else if message.source().node == our.node
&& message.source().process == "http_server:sys:uqbar"
{
// receive HTTP requests and websocket connection messages from our server
match serde_json::from_slice::<http::HttpServerRequest>(message.ipc())? {
http::HttpServerRequest::Http(ref incoming) => {
match handle_http_request(our, state, incoming) {
Ok(()) => Ok(()),
Err(e) => {
println!("chess: error handling http request: {:?}", e);
http::send_response(
http::StatusCode::SERVICE_UNAVAILABLE,
None,
"Service Unavailable".to_string().as_bytes().to_vec(),
)
}
}
}
http::HttpServerRequest::WebSocketOpen { path, channel_id } => {
// We know this is authenticated and unencrypted because we only
// bound one path, the root path. So we know that client
// frontend opened a websocket and can send updates
state.clients.insert(channel_id);
Ok(())
}
http::HttpServerRequest::WebSocketClose(channel_id) => {
// client frontend closed a websocket
state.clients.remove(&channel_id);
Ok(())
}
http::HttpServerRequest::WebSocketPush { .. } => {
// client frontend sent a websocket message
// we don't expect this! we only use websockets to push updates
Ok(())
}
}
} else {
return Err(anyhow::anyhow!("chess: got request from unexpected source"));
// If we get a request from ourselves that isn't from the terminal, we'll just
// throw it away. This is a good place to put a printout to show that we've
// received a request from ourselves that we don't know how to handle.
return Err(anyhow::anyhow!(
"got request from not-the-terminal, ignoring"
));
}
}
/// Handle chess protocol messages from other nodes.
fn handle_chess_request(
our: &Address,
source: &Address,
message_json: serde_json::Value,
source_node: &NodeId,
state: &mut ChessState,
action: &ChessRequest,
) -> anyhow::Result<()> {
let action = message_json["action"].as_str().unwrap_or("");
let game_id = &source.node;
println!("chess: handling action from {source_node}: {action:?}");
// For simplicity's sake, we'll just use the node we're playing with as the game id.
// This limits us to one active game per partner.
let game_id = source_node;
match action {
"new_game" => {
// make a new game with source.node if the current game has ended
if let Some(game) = state.games.get(game_id) {
if !game.ended {
return Response::new()
.ipc(vec![])
.payload(Payload {
mime: Some("application/octet-stream".to_string()),
bytes: "conflict".as_bytes().to_vec(),
})
.send();
}
ChessRequest::NewGame { white, black } => {
// Make a new game with source.node
// This will replace any existing game with source.node!
if state.games.contains_key(game_id) {
println!("chess: resetting game with {game_id} on their request!");
}
let game = Game {
id: game_id.to_string(),
turns: 0,
board: Board::start_pos(),
white: message_json["white"]
.as_str()
.unwrap_or(game_id)
.to_string(),
black: message_json["black"]
.as_str()
.unwrap_or(&our.node)
.to_string(),
board: Board::start_pos().fen(),
white: white.to_string(),
black: black.to_string(),
ended: false,
};
state.games.insert(game_id.to_string(), game.clone());
utils::send_ws_update(&our, &game)?;
utils::save_chess_state(&state);
// Use our helper function to persist state after every action.
// The simplest and most trivial way to keep state. You'll want to
// use a database or something in a real app, and consider performance
// when doing intensive data-based operations.
send_ws_update(&our, &game, &state.clients)?;
state.games.insert(game_id.to_string(), game);
save_chess_state(&state);
// Send a response to tell them we've accepted the game.
// Remember, the other player is waiting for this.
Response::new()
.ipc(vec![])
.payload(Payload {
mime: Some("application/octet-stream".to_string()),
bytes: "success".as_bytes().to_vec(),
})
.ipc(serde_json::to_vec(&ChessResponse::NewGameAccepted)?)
.send()
}
"make_move" => {
// check the move and then update if correct and send WS update
ChessRequest::Move { ref move_str, .. } => {
// Get the associated game, and respond with an error if
// we don't have it in our state.
let Some(game) = state.games.get_mut(game_id) else {
// If we don't have a game with them, reject the move.
return Response::new()
.ipc(vec![])
.payload(Payload {
mime: Some("application/octet-stream".to_string()),
bytes: "not found".as_bytes().to_vec(),
})
.ipc(serde_json::to_vec(&ChessResponse::MoveRejected)?)
.send();
};
let valid_move = game
.board
.apply_uci_move(message_json["move"].as_str().unwrap_or(""));
if valid_move {
game.turns += 1;
let checkmate = game.board.checkmate();
let draw = game.board.stalemate();
if checkmate || draw {
// Convert the saved board to one we can manipulate.
let mut board = Board::from_fen(&game.board).unwrap();
if !board.apply_uci_move(move_str) {
// Reject invalid moves!
return Response::new()
.ipc(serde_json::to_vec(&ChessResponse::MoveRejected)?)
.send();
}
game.turns += 1;
if board.checkmate() || board.stalemate() {
game.ended = true;
}
// Persist state.
game.board = board.fen();
send_ws_update(&our, &game, &state.clients)?;
save_chess_state(&state);
// Send a response to tell them we've accepted the move.
Response::new()
.ipc(serde_json::to_vec(&ChessResponse::MoveAccepted)?)
.send()
}
ChessRequest::Resign(_) => {
// They've resigned. The sender isn't waiting for a response to this,
// so we don't need to send one.
match state.games.get_mut(game_id) {
Some(game) => {
game.ended = true;
let winner = if checkmate {
if game.turns % 2 == 1 {
game.white.clone()
} else {
game.black.clone()
}
} else {
"".to_string()
};
// update the records
if draw {
if let Some(record) = state.records.get_mut(&game.id) {
record.2 += 1;
} else {
state.records.insert(game.id.clone(), (0, 0, 1));
}
} else {
if let Some(record) = state.records.get_mut(&game.id) {
if winner == our.node {
record.0 += 1;
} else {
record.1 += 1;
}
} else {
if winner == our.node {
state.records.insert(game.id.clone(), (1, 0, 0));
} else {
state.records.insert(game.id.clone(), (0, 1, 0));
}
}
}
send_ws_update(&our, &game, &state.clients)?;
save_chess_state(&state);
}
utils::send_ws_update(&our, &game)?;
utils::save_chess_state(&state);
Response::new()
.ipc(vec![])
.payload(Payload {
mime: Some("application/octet-stream".to_string()),
bytes: "success".as_bytes().to_vec(),
})
.send()
} else {
Response::new()
.ipc(vec![])
.payload(Payload {
mime: Some("application/octet-stream".to_string()),
bytes: "invalid move".as_bytes().to_vec(),
})
.send()
None => {}
}
Ok(())
}
"end_game" => {
// end the game and send WS update, update the standings
let Some(game) = state.games.get_mut(game_id) else {
return Response::new()
.ipc(vec![])
.payload(Payload {
mime: Some("application/octet-stream".to_string()),
bytes: "not found".as_bytes().to_vec(),
})
.send();
};
game.ended = true;
if let Some(record) = state.records.get_mut(&game.id) {
record.0 += 1;
} else {
state.records.insert(game.id.clone(), (1, 0, 0));
}
utils::send_ws_update(&our, &game)?;
utils::save_chess_state(&state);
Response::new()
.ipc(vec![])
.payload(Payload {
mime: Some("application/octet-stream".to_string()),
bytes: "success".as_bytes().to_vec(),
})
.send()
}
_ => return Err(anyhow::anyhow!("chess: got unexpected action")),
}
}
/// Handle actions we are performing. Here's where we'll send_and_await various requests.
fn handle_local_request(
our: &Address,
state: &mut ChessState,
action: &ChessRequest,
) -> anyhow::Result<()> {
match action {
ChessRequest::NewGame { white, black } => {
// Create a new game. We'll enforce that one of the two players is us.
if white != &our.node && black != &our.node {
return Err(anyhow::anyhow!("cannot start a game without us!"));
}
let game_id = if white == &our.node { black } else { white };
// If we already have a game with this player, throw an error.
if let Some(game) = state.games.get(game_id)
&& !game.ended
{
return Err(anyhow::anyhow!("already have a game with {game_id}"));
};
// Send the other player a NewGame request
// The request is exactly the same as what we got from terminal.
// We'll give them 5 seconds to respond...
let Ok(Message::Response { ref ipc, .. }) = Request::new()
.target((game_id.as_ref(), our.process.clone()))
.ipc(serde_json::to_vec(&action)?)
.send_and_await_response(5)?
else {
return Err(anyhow::anyhow!(
"other player did not respond properly to new game request"
));
};
// If they accept, create a new game -- otherwise, error out.
if serde_json::from_slice::<ChessResponse>(ipc)? != ChessResponse::NewGameAccepted {
return Err(anyhow::anyhow!("other player rejected new game request!"));
}
// New game with default board.
let game = Game {
id: game_id.to_string(),
turns: 0,
board: Board::start_pos().fen(),
white: white.to_string(),
black: black.to_string(),
ended: false,
};
state.games.insert(game_id.to_string(), game);
save_chess_state(&state);
Ok(())
}
ChessRequest::Move { game_id, move_str } => {
// Make a move. We'll enforce that it's our turn. The game_id is the
// person we're playing with.
let Some(game) = state.games.get_mut(game_id) else {
return Err(anyhow::anyhow!("no game with {game_id}"));
};
if (game.turns % 2 == 0 && game.white != our.node)
|| (game.turns % 2 == 1 && game.black != our.node)
{
return Err(anyhow::anyhow!("not our turn!"));
} else if game.ended {
return Err(anyhow::anyhow!("that game is over!"));
}
let mut board = Board::from_fen(&game.board).unwrap();
if !board.apply_uci_move(move_str) {
return Err(anyhow::anyhow!("illegal move!"));
}
// Send the move to the other player, then check if the game is over.
// The request is exactly the same as what we got from terminal.
// We'll give them 5 seconds to respond...
let Ok(Message::Response { ref ipc, .. }) = Request::new()
.target((game_id.as_ref(), our.process.clone()))
.ipc(serde_json::to_vec(&action)?)
.send_and_await_response(5)?
else {
return Err(anyhow::anyhow!(
"other player did not respond properly to our move"
));
};
if serde_json::from_slice::<ChessResponse>(ipc)? != ChessResponse::MoveAccepted {
return Err(anyhow::anyhow!("other player rejected our move"));
}
game.turns += 1;
if board.checkmate() || board.stalemate() {
game.ended = true;
}
game.board = board.fen();
save_chess_state(&state);
Ok(())
}
ChessRequest::Resign(ref with_who) => {
// Resign from a game with a given player.
let Some(game) = state.games.get_mut(with_who) else {
return Err(anyhow::anyhow!("no game with {with_who}"));
};
// send the other player an end game request -- no response expected
Request::new()
.target((with_who.as_ref(), our.process.clone()))
.ipc(serde_json::to_vec(&action)?)
.send()?;
game.ended = true;
save_chess_state(&state);
Ok(())
}
}
}
/// Handle HTTP requests from our own frontend.
fn handle_http_request(
our: &Address,
http_request: http::IncomingHttpRequest,
state: &mut ChessState,
http_request: &http::IncomingHttpRequest,
) -> anyhow::Result<()> {
if http_request.path()? != "/games" {
if http_request.path()? != "games" {
return http::send_response(
http::StatusCode::NOT_FOUND,
None,
@ -323,20 +457,17 @@ fn handle_http_request(
);
}
match http_request.method.as_str() {
// on GET: give the frontend all of our active games
"GET" => http::send_response(
http::StatusCode::OK,
Some(HashMap::from([(
String::from("Content-Type"),
String::from("application/json"),
)])),
serde_json::to_vec(&serde_json::json!(state
.games
.iter()
.map(|(id, game)| (id.to_string(), utils::json_game(game)))
.collect::<HashMap<String, serde_json::Value>>()))?,
serde_json::to_vec(&state.games)?,
),
// on POST: create a new game
"POST" => {
// create a new game
let Some(payload) = get_payload() else {
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
};
@ -360,38 +491,36 @@ fn handle_http_request(
.to_string();
// send the other player a new game request
let response = Request::new()
.target((game_id, "chess", "chess", "uqbar"))
.ipc(serde_json::to_vec(&serde_json::json!({
"action": "new_game",
"white": player_white.clone(),
"black": player_black.clone(),
}))?)
.send_and_await_response(30)?;
let Ok(msg) = Request::new()
.target((game_id, our.process.clone()))
.ipc(serde_json::to_vec(&ChessRequest::NewGame {
white: player_white.clone(),
black: player_black.clone(),
})?)
.send_and_await_response(5)?
else {
return Err(anyhow::anyhow!(
"other player did not respond properly to new game request"
));
};
// if they accept, create a new game
// otherwise, should surface error to FE...
let Ok((_source, Message::Response((resp, _context)))) = response else {
return http::send_response(
http::StatusCode::SERVICE_UNAVAILABLE,
None,
"Service Unavailable".to_string().as_bytes().to_vec(),
);
};
if resp.ipc != "success".as_bytes() {
return http::send_response(http::StatusCode::SERVICE_UNAVAILABLE, None, vec![]);
if serde_json::from_slice::<ChessResponse>(msg.ipc())? != ChessResponse::NewGameAccepted
{
return Err(anyhow::anyhow!("other player rejected new game request"));
}
// create a new game
let game = Game {
id: game_id.to_string(),
turns: 0,
board: Board::start_pos(),
board: Board::start_pos().fen(),
white: player_white,
black: player_black,
ended: false,
};
let body = serde_json::to_vec(&utils::json_game(&game))?;
let body = serde_json::to_vec(&game)?;
state.games.insert(game_id.to_string(), game);
utils::save_chess_state(&state);
save_chess_state(&state);
http::send_response(
http::StatusCode::OK,
Some(HashMap::from([(
@ -401,8 +530,8 @@ fn handle_http_request(
body,
)
}
// on PUT: make a move
"PUT" => {
// make a move
let Some(payload) = get_payload() else {
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
};
@ -420,71 +549,41 @@ fn handle_http_request(
} else if game.ended {
return http::send_response(http::StatusCode::CONFLICT, None, vec![]);
}
let move_str = payload_json["move"].as_str().unwrap_or("");
if !game.board.apply_uci_move(move_str) {
let Some(move_str) = payload_json["move"].as_str() else {
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
};
let mut board = Board::from_fen(&game.board).unwrap();
if !board.apply_uci_move(move_str) {
// TODO surface illegal move to player or something here
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
}
// send the move to the other player
// check if the game is over
// if so, update the records
let response = Request::new()
.target((game_id, "chess", "chess", "uqbar"))
.ipc(serde_json::to_vec(&serde_json::json!({
"action": "make_move",
"move": move_str,
}))?)
.send_and_await_response(30)?;
let Ok((_source, Message::Response((resp, _context)))) = response else {
// TODO surface error to player, let them know other player is
// offline or whatever they respond here was invalid
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
let Ok(msg) = Request::new()
.target((game_id, our.process.clone()))
.ipc(serde_json::to_vec(&ChessRequest::Move {
game_id: game_id.to_string(),
move_str: move_str.to_string(),
})?)
.send_and_await_response(5)?
else {
return Err(anyhow::anyhow!(
"other player did not respond properly to our move"
));
};
if resp.ipc != "success".as_bytes() {
return http::send_response(http::StatusCode::SERVICE_UNAVAILABLE, None, vec![]);
if serde_json::from_slice::<ChessResponse>(msg.ipc())? != ChessResponse::MoveAccepted {
return Err(anyhow::anyhow!("other player rejected our move"));
}
// update the game
game.turns += 1;
let checkmate = game.board.checkmate();
let draw = game.board.stalemate();
if checkmate || draw {
if board.checkmate() || board.stalemate() {
game.ended = true;
let winner = if checkmate {
if game.turns % 2 == 1 {
&game.white
} else {
&game.black
}
} else {
""
};
// update the records
if draw {
if let Some(record) = state.records.get_mut(&game.id) {
record.2 += 1;
} else {
state.records.insert(game.id.clone(), (0, 0, 1));
}
} else {
if let Some(record) = state.records.get_mut(&game.id) {
if winner == our.node {
record.0 += 1;
} else {
record.1 += 1;
}
} else {
if winner == our.node {
state.records.insert(game.id.clone(), (1, 0, 0));
} else {
state.records.insert(game.id.clone(), (0, 1, 0));
}
}
}
}
// game is not over, update state and return to FE
let body = serde_json::to_vec(&utils::json_game(&game))?;
utils::save_chess_state(&state);
game.board = board.fen();
// update state and return to FE
let body = serde_json::to_vec(&game)?;
save_chess_state(&state);
// return the game
http::send_response(
http::StatusCode::OK,
@ -495,41 +594,22 @@ fn handle_http_request(
body,
)
}
// on DELETE: end the game
"DELETE" => {
// "end the game"?
let query_params = http_request.query_params()?;
let Some(game_id) = query_params.get("id") else {
let Some(game_id) = http_request.query_params.get("id") else {
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
};
let Some(game) = state.games.get_mut(game_id) else {
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
};
// send the other player an end game request
let response = Request::new()
.target((game_id, "chess", "chess", "uqbar"))
.ipc(serde_json::to_vec(&serde_json::json!({
"action": "end_game",
}))?)
.send_and_await_response(30)?;
let Ok((_source, Message::Response((resp, _context)))) = response else {
// TODO surface error to player, let them know other player is
// offline or whatever they respond here was invalid
return http::send_response(http::StatusCode::SERVICE_UNAVAILABLE, None, vec![]);
};
if resp.ipc != "success".as_bytes() {
return http::send_response(http::StatusCode::SERVICE_UNAVAILABLE, None, vec![]);
}
Request::new()
.target((game_id.as_str(), our.process.clone()))
.ipc(serde_json::to_vec(&ChessRequest::Resign(our.node.clone()))?)
.send()?;
game.ended = true;
if let Some(record) = state.records.get_mut(&game.id) {
record.1 += 1;
} else {
state.records.insert(game.id.clone(), (0, 1, 0));
}
// return the game
let body = serde_json::to_vec(&utils::json_game(&game))?;
utils::save_chess_state(&state);
let body = serde_json::to_vec(&game)?;
save_chess_state(&state);
http::send_response(
http::StatusCode::OK,
Some(HashMap::from([(
@ -539,11 +619,7 @@ fn handle_http_request(
body,
)
}
_ => Response::new()
.ipc(serde_json::to_vec(&http::HttpResponse {
status: 405,
headers: HashMap::new(),
})?)
.send(),
// Any other method will be rejected.
_ => http::send_response(http::StatusCode::METHOD_NOT_ALLOWED, None, vec![]),
}
}

View File

@ -1,82 +0,0 @@
use crate::*;
pub fn save_chess_state(state: &ChessState) {
let stored_state = convert_state(&state);
set_state(&bincode::serialize(&stored_state).unwrap());
}
fn convert_game(game: Game) -> StoredGame {
StoredGame {
id: game.id,
turns: game.turns,
board: game.board.fen(),
white: game.white,
black: game.black,
ended: game.ended,
}
}
fn convert_state(state: &ChessState) -> StoredChessState {
StoredChessState {
games: state
.games
.iter()
.map(|(id, game)| (id.to_string(), convert_game(game.clone())))
.collect(),
records: state.records.clone(),
}
}
pub fn json_game(game: &Game) -> serde_json::Value {
serde_json::json!({
"id": game.id,
"turns": game.turns,
"board": game.board.fen(),
"white": game.white,
"black": game.black,
"ended": game.ended,
})
}
pub fn send_ws_update(our: &Address, game: &Game) -> anyhow::Result<()> {
Request::new()
.target((&our.node, "http_server", "sys", "uqbar"))
.ipc(
serde_json::json!({
"EncryptAndForward": {
"channel_id": our.process.to_string(),
"forward_to": {
"node": our.node.clone(),
"process": {
"process_name": "http_server",
"package_name": "sys",
"publisher_node": "uqbar"
}
}, // node, process
"json": Some(serde_json::json!({ // this is the JSON to forward
"WebSocketPush": {
"target": {
"node": our.node.clone(),
"id": "chess", // If the message passed in an ID then we could send to just that ID
}
}
})),
}
})
.to_string()
.as_bytes()
.to_vec(),
)
.payload(Payload {
mime: Some("application/json".to_string()),
bytes: serde_json::json!({
"kind": "game_update",
"data": json_game(game),
})
.to_string()
.as_bytes()
.to_vec(),
})
.send()
}

View File

@ -58,9 +58,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
@ -69,9 +69,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.14.2"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
@ -134,9 +134,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "leb128"
@ -146,9 +146,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "log"
@ -170,9 +170,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.69"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
@ -218,9 +218,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "semver"
@ -230,18 +230,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.191"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a834c4821019838224821468552240d4d95d14e751986442c816572d39a080c9"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.191"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46fa52d5646bce91b680189fe5b1c049d2ea38dabb4e2e7c8d00ca12cfbfbcfd"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@ -261,9 +261,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.11.1"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "spdx"
@ -276,9 +276,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.39"
version = "2.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
dependencies = [
"proc-macro2",
"quote",
@ -322,9 +322,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
[[package]]
name = "unicode-ident"
@ -355,8 +355,8 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uqbar_process_lib"
version = "0.3.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=5e1b94a#5e1b94ae2f85c66da33ec52117a72b90d53c4d22"
version = "0.4.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=1ce0d41#1ce0d412169e795c2a99464563b42ae2a2628d77"
dependencies = [
"anyhow",
"bincode",
@ -388,18 +388,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.36.2"
version = "0.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.10.11"
version = "0.10.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f"
checksum = "d835d67708f6374937c550ad8dd1d17c616ae009e3f00d7a0ac9f7825e78c36a"
dependencies = [
"anyhow",
"indexmap",
@ -413,9 +413,9 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.116.1"
version = "0.118.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50"
checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9"
dependencies = [
"indexmap",
"semver",
@ -423,8 +423,8 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"bitflags",
"wit-bindgen-rust-macro",
@ -432,8 +432,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-core"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"wit-component",
@ -442,8 +442,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust"
version = "0.13.2"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"heck",
@ -454,8 +454,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"proc-macro2",
@ -468,9 +468,9 @@ dependencies = [
[[package]]
name = "wit-component"
version = "0.17.0"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e"
checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2"
dependencies = [
"anyhow",
"bitflags",
@ -487,9 +487,9 @@ dependencies = [
[[package]]
name = "wit-parser"
version = "0.12.2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76"
checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3"
dependencies = [
"anyhow",
"id-arena",

View File

@ -13,10 +13,10 @@ lto = true
[dependencies]
anyhow = "1.0"
bincode = "1.3.3"
serde = {version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" }
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "1ce0d41" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
[lib]
crate-type = ["cdylib"]

View File

@ -2,7 +2,7 @@
{
"process_name": "homepage",
"process_wasm_path": "/homepage.wasm",
"on_panic": "Restart",
"on_exit": "Restart",
"request_networking": false,
"request_messaging": [
"http_bindings:http_bindings:uqbar",

View File

@ -196,7 +196,7 @@
<h4>Apps:</h4>
<!-- <a id="file-transfer" href="/file-transfer">File Transfer</a> -->
<a id="chess" href="/chess:chess:uqbar/">Chess [NOT WORKING]</a>
<a id="chess" href="/chess:chess:uqbar/">Chess</a>
</div>
<script>window.ourName = window.our = '${our}'</script>
<script>

View File

@ -1,7 +1,7 @@
#![feature(let_chains)]
use uqbar_process_lib::{
grant_messaging, http::bind_http_static_path, http::HttpServerError, println, receive, Address,
Message, ProcessId, Response,
await_message, grant_messaging, http::bind_http_static_path, http::HttpServerError, println, Address,
Message, ProcessId,
};
wit_bindgen::generate!({
@ -43,21 +43,38 @@ fn main(our: Address) -> anyhow::Result<()> {
.to_vec(),
)?;
bind_http_static_path(
"/our",
false,
false,
Some("text/html".to_string()),
our.node.clone().as_bytes().to_vec(),
)?;
bind_http_static_path(
"/our.js",
false,
false,
Some("application/javascript".to_string()),
format!("window.our = {{}}; window.our.node = '{}';", &our.node).as_bytes().to_vec(),
)?;
loop {
let Ok((ref source, ref message)) = receive() else {
let Ok(ref message) = await_message() else {
println!("homepage: got network error??");
continue;
};
if let Message::Response((ref msg, _)) = message
if let Message::Response { source, ipc, ..} = message
&& source.process == "http_server:sys:uqbar"
{
match serde_json::from_slice::<Result<(), HttpServerError>>(&msg.ipc) {
match serde_json::from_slice::<Result<(), HttpServerError>>(&ipc) {
Ok(Ok(())) => continue,
Ok(Err(e)) => println!("homepage: got error from http_server: {e}"),
Err(_e) => println!("homepage: got malformed message from http_server!"),
}
} else {
println!("homepage: got message from {source:?}: {message:?}");
println!("homepage: got message: {message:?}");
//println!("homepage: got message from {source:?}: {message:?}");
}
}
}

View File

@ -1,504 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "http"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "id-arena"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "idna"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
"equivalent",
"hashbrown",
"serde",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "key_value"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"serde",
"serde_json",
"thiserror",
"uqbar_process_lib",
"wit-bindgen",
]
[[package]]
name = "leb128"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "semver"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]]
name = "serde"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "smallvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "spdx"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71"
dependencies = [
"smallvec",
]
[[package]]
name = "syn"
version = "2.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uqbar_process_lib"
version = "0.3.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=5e1b94a#5e1b94ae2f85c66da33ec52117a72b90d53c4d22"
dependencies = [
"anyhow",
"bincode",
"http",
"rand",
"serde",
"serde_json",
"thiserror",
"url",
"wit-bindgen",
]
[[package]]
name = "url"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.10.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f"
dependencies = [
"anyhow",
"indexmap",
"serde",
"serde_derive",
"serde_json",
"spdx",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.116.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50"
dependencies = [
"indexmap",
"semver",
]
[[package]]
name = "wit-bindgen"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"bitflags",
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"anyhow",
"wit-component",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.13.2"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"anyhow",
"heck",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"anyhow",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
"wit-component",
]
[[package]]
name = "wit-component"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
]

View File

@ -1,31 +0,0 @@
[package]
name = "key_value"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.release]
panic = "abort"
opt-level = "s"
lto = true
[dependencies]
anyhow = "1.0"
bincode = "1.3.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" }
[lib]
crate-type = ["cdylib"]
[package.metadata.component]
package = "uqbar:process"
[package.metadata.component.target]
path = "wit"
[package.metadata.component.dependencies]

View File

@ -1 +0,0 @@
../../key_value_types.rs

View File

@ -1,191 +0,0 @@
use std::collections::HashMap;
// use serde::{Deserialize, Serialize};
use uqbar_process_lib::{Address, ProcessId, Request, Response};
use uqbar_process_lib::kernel_types as kt;
use uqbar_process_lib::uqbar::process::standard as wit;
wit_bindgen::generate!({
path: "../../../wit",
world: "process",
exports: {
world: Component,
},
});
mod key_value_types;
use key_value_types as kv;
const PREFIX: &str = "key_value-";
type DbToProcess = HashMap<String, ProcessId>;
fn make_vfs_cap(kind: &str, drive: &str) -> String {
serde_json::to_string(&serde_json::json!({
"kind": kind,
"drive": drive,
}))
.unwrap()
}
fn make_db_cap(kind: &str, db: &str) -> String {
serde_json::to_string(&serde_json::json!({
"kind": kind,
"db": db,
}))
.unwrap()
}
fn forward_if_have_cap(
our: &Address,
operation_type: &str,
db: &str,
ipc: Vec<u8>,
db_to_process: &mut DbToProcess,
) -> anyhow::Result<()> {
if wit::has_capability(&make_db_cap(operation_type, db)) {
// forward
let Some(process_id) = db_to_process.get(db) else {
return Err(kv::KeyValueError::DbDoesNotExist.into());
};
Request::new()
.target(wit::Address {
node: our.node.clone(),
process: process_id.clone(),
})
// .target(Address::new(our.node.clone(), process_id.clone()))?
.inherit(true)
.ipc(ipc)
.send()?;
return Ok(());
} else {
// reject
return Err(kv::KeyValueError::NoCap.into());
}
}
fn handle_message(our: &Address, db_to_process: &mut DbToProcess) -> anyhow::Result<()> {
let (source, message) = wit::receive().unwrap();
if our.node != source.node {
return Err(kv::KeyValueError::RejectForeign.into());
}
match message {
wit::Message::Response(_) => {
return Err(kv::KeyValueError::UnexpectedResponse.into());
}
wit::Message::Request(wit::Request { ipc, .. }) => {
match serde_json::from_slice(&ipc)? {
kv::KeyValueMessage::New { ref db } => {
// TODO: make atomic
// (1): create vfs drive
// (2): spin up worker, granting vfs caps
// (3): issue new caps
// (4): persist
if db_to_process.contains_key(db) {
return Err(kv::KeyValueError::DbAlreadyExists.into());
}
// (1)
let vfs_address = Address {
node: our.node.clone(),
process: ProcessId::new(Some("vfs"), "sys", "uqbar"),
};
let vfs_drive = format!("{}{}", PREFIX, db);
let _ = Request::new()
.target(vfs_address.clone())
.ipc(serde_json::to_vec(&kt::VfsRequest {
drive: vfs_drive.clone(),
action: kt::VfsAction::New,
})?)
.send_and_await_response(15)??;
// (2)
let vfs_read = wit::get_capability(
&vfs_address,
&make_vfs_cap("read", &vfs_drive)
).ok_or(anyhow::anyhow!("New failed: no vfs 'read' capability found"))?;
let vfs_write = wit::get_capability(
&vfs_address,
&make_vfs_cap("write", &vfs_drive)
).ok_or(anyhow::anyhow!("New failed: no vfs 'write' capability found"))?;
let messaging = wit::get_capability(
&source,
&"\"messaging\"".into(),
).ok_or(anyhow::anyhow!("New failed: no source 'messaging' capability found"))?;
let spawned_process_id = match wit::spawn(
None,
"/key_value_worker.wasm",
&wit::OnPanic::None, // TODO: notify us
&wit::Capabilities::Some(vec![vfs_read, vfs_write, messaging]),
false, // not public
) {
Ok(spawned_process_id) => spawned_process_id,
Err(e) => {
wit::print_to_terminal(0, &format!("couldn't spawn: {}", e));
panic!("couldn't spawn"); // TODO
}
};
// grant caps
wit::create_capability(&source.process, &make_db_cap("read", db));
wit::create_capability(&source.process, &make_db_cap("write", db));
// initialize worker
Request::new()
.target(wit::Address {
node: our.node.clone(),
process: spawned_process_id.clone(),
})
.ipc(ipc.clone())
.send()?;
// (4)
db_to_process.insert(db.into(), spawned_process_id);
// TODO: persistence?
Response::new()
.ipc(ipc)
.send()?;
}
kv::KeyValueMessage::Write { ref db, .. } => {
forward_if_have_cap(our, "write", db, ipc, db_to_process)?;
}
kv::KeyValueMessage::Read { ref db, .. } => {
forward_if_have_cap(our, "read", db, ipc, db_to_process)?;
}
kv::KeyValueMessage::Err { error } => {
return Err(error.into());
}
}
Ok(())
}
}
}
struct Component;
impl Guest for Component {
fn init(our: String) {
wit::print_to_terminal(0, "key_value: begin");
let our = Address::from_str(&our).unwrap();
let mut db_to_process: DbToProcess = HashMap::new();
loop {
match handle_message(&our, &mut db_to_process) {
Ok(()) => {}
Err(e) => {
wit::print_to_terminal(0, format!("key_value: error: {:?}", e,).as_str());
if let Some(e) = e.downcast_ref::<kv::KeyValueError>() {
Response::new()
.ipc(serde_json::to_vec(&e).unwrap())
.send()
.unwrap();
}
}
};
}
}
}

View File

@ -1,23 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub enum KeyValueMessage {
New { db: String },
Write { db: String, key: Vec<u8> },
Read { db: String, key: Vec<u8> },
Err { error: KeyValueError },
}
#[derive(Debug, Serialize, Deserialize, thiserror::Error)]
pub enum KeyValueError {
#[error("DbDoesNotExist")]
DbDoesNotExist,
#[error("DbAlreadyExists")]
DbAlreadyExists,
#[error("NoCap")]
NoCap,
#[error("RejectForeign")]
RejectForeign,
#[error("UnexpectedResponse")]
UnexpectedResponse,
}

View File

@ -1,644 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "backtrace"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "gimli"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
[[package]]
name = "hashbrown"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "http"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "id-arena"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "idna"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
"equivalent",
"hashbrown",
"serde",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "key_value_worker"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"redb",
"serde",
"serde_json",
"thiserror",
"uqbar_process_lib",
"wit-bindgen",
]
[[package]]
name = "leb128"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
[[package]]
name = "object"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project-lite"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pyo3-build-config"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "076c73d0bc438f7a4ef6fdd0c3bb4732149136abd952b110ac93e4edb13a6ba5"
dependencies = [
"once_cell",
"target-lexicon",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "redb"
version = "1.2.0"
source = "git+https://github.com/uqbar-dao/redb?rev=8e192d9#8e192d945295e1ddd331003bae215cd8d1564c4b"
dependencies = [
"libc",
"pyo3-build-config",
"rand",
"serde",
"serde_json",
"thiserror",
"tokio",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "semver"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]]
name = "serde"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "smallvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "spdx"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71"
dependencies = [
"smallvec",
]
[[package]]
name = "syn"
version = "2.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "target-lexicon"
version = "0.12.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a"
[[package]]
name = "thiserror"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
dependencies = [
"backtrace",
"pin-project-lite",
"tokio-macros",
]
[[package]]
name = "tokio-macros"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-bidi"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uqbar_process_lib"
version = "0.3.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=5e1b94a#5e1b94ae2f85c66da33ec52117a72b90d53c4d22"
dependencies = [
"anyhow",
"bincode",
"http",
"rand",
"serde",
"serde_json",
"thiserror",
"url",
"wit-bindgen",
]
[[package]]
name = "url"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.10.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f"
dependencies = [
"anyhow",
"indexmap",
"serde",
"serde_derive",
"serde_json",
"spdx",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.116.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50"
dependencies = [
"indexmap",
"semver",
]
[[package]]
name = "wit-bindgen"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"bitflags",
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"anyhow",
"wit-component",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.13.2"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"anyhow",
"heck",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"anyhow",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
"wit-component",
]
[[package]]
name = "wit-component"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
]

View File

@ -1,32 +0,0 @@
[package]
name = "key_value_worker"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.release]
panic = "abort"
opt-level = "s"
lto = true
[dependencies]
anyhow = "1.0"
bincode = "1.3.3"
redb = { git = "https://github.com/uqbar-dao/redb", rev = "8e192d9" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" }
[lib]
crate-type = ["cdylib"]
[package.metadata.component]
package = "uqbar:process"
[package.metadata.component.target]
path = "wit"
[package.metadata.component.dependencies]

View File

@ -1 +0,0 @@
../key_value_types.rs

View File

@ -1 +0,0 @@
../../key_value_types.rs

View File

@ -1,206 +0,0 @@
use std::collections::HashMap;
use redb::ReadableTable;
use serde::{Deserialize, Serialize};
use uqbar_process_lib::{Address, create_capability, ProcessId, Response};
use uqbar_process_lib::uqbar::process::standard as wit;
wit_bindgen::generate!({
path: "../../../wit",
world: "process",
exports: {
world: Component,
},
});
mod key_value_types;
use key_value_types as kv;
const PREFIX: &str = "key_value-";
const TABLE: redb::TableDefinition<&[u8], &[u8]> = redb::TableDefinition::new("process");
fn get_payload_wrapped() -> Option<(Option<String>, Vec<u8>)> {
match wit::get_payload() {
None => None,
Some(wit::Payload { mime, bytes }) => Some((mime, bytes)),
}
}
fn send_and_await_response_wrapped(
target_node: String,
target_process: String,
target_package: String,
target_publisher: String,
request_ipc: Vec<u8>,
request_metadata: Option<String>,
payload: Option<(Option<String>, Vec<u8>)>,
timeout: u64,
) -> (Vec<u8>, Option<String>) {
let payload = match payload {
None => None,
Some((mime, bytes)) => Some(wit::Payload { mime, bytes }),
};
let (
_,
wit::Message::Response((wit::Response { ipc, metadata, .. }, _)),
) = wit::send_and_await_response(
&wit::Address {
node: target_node,
process: ProcessId::new(
Some(&target_process),
&target_package,
&target_publisher,
),
},
&wit::Request {
inherit: false,
expects_response: Some(timeout),
ipc: request_ipc,
metadata: request_metadata,
},
match payload {
None => None,
Some(ref p) => Some(p),
},
).unwrap() else {
panic!("");
};
(ipc, metadata)
}
fn handle_message (
our: &wit::Address,
db_handle: &mut Option<redb::Database>,
) -> anyhow::Result<()> {
let (source, message) = wit::receive().unwrap();
if our.node != source.node {
return Err(kv::KeyValueError::RejectForeign.into());
}
match message {
wit::Message::Response(_) => { unimplemented!() },
wit::Message::Request(wit::Request { ipc, .. }) => {
match serde_json::from_slice(&ipc)? {
kv::KeyValueMessage::New { db } => {
let vfs_drive = format!("{}{}", PREFIX, db);
match db_handle {
Some(_) => {
return Err(kv::KeyValueError::DbAlreadyExists.into());
},
None => {
wit::print_to_terminal(1, "key_value_worker: Create");
*db_handle = Some(redb::Database::create(
format!("/{}.redb", db),
our.node.clone(),
vfs_drive,
get_payload_wrapped,
send_and_await_response_wrapped,
)?);
wit::print_to_terminal(1, "key_value_worker: Create done");
},
}
},
kv::KeyValueMessage::Write { ref key, .. } => {
let Some(db_handle) = db_handle else {
return Err(kv::KeyValueError::DbDoesNotExist.into());
};
let wit::Payload { ref bytes, .. } = wit::get_payload()
.ok_or(anyhow::anyhow!("couldnt get bytes for Write"))?;
let write_txn = db_handle.begin_write()?;
{
let mut table = write_txn.open_table(TABLE)?;
table.insert(&key[..], &bytes[..])?;
}
write_txn.commit()?;
Response::new()
.ipc(ipc)
.send()?;
},
kv::KeyValueMessage::Read { ref key, .. } => {
let Some(db_handle) = db_handle else {
return Err(kv::KeyValueError::DbDoesNotExist.into());
};
let read_txn = db_handle.begin_read()?;
let table = read_txn.open_table(TABLE)?;
match table.get(&key[..])? {
None => {
Response::new()
.ipc(ipc)
.send()?;
},
Some(v) => {
let bytes = v.value().to_vec();
wit::print_to_terminal(
1,
&format!(
"key_value_worker: key, val: {:?}, {}",
key,
if bytes.len() < 100 {
format!("{:?}", bytes)
} else {
"<elided>".into()
},
),
);
Response::new()
.ipc(ipc)
.payload(wit::Payload {
mime: None,
bytes,
})
.send()?;
},
};
},
kv::KeyValueMessage::Err { error } => {
return Err(error.into());
}
}
Ok(())
},
}
}
struct Component;
impl Guest for Component {
fn init(our: String) {
wit::print_to_terminal(1, "key_value_worker: begin");
let our = Address::from_str(&our).unwrap();
let mut db_handle: Option<redb::Database> = None;
let vfs_address = ProcessId::from_str("vfs:sys:uqbar").unwrap();
create_capability(
&vfs_address,
&"\"messaging\"".into(),
);
loop {
match handle_message(&our, &mut db_handle) {
Ok(()) => {},
Err(e) => {
wit::print_to_terminal(0, format!(
"key_value_worker: error: {:?}",
e,
).as_str());
if let Some(e) = e.downcast_ref::<kv::KeyValueError>() {
Response::new()
.ipc(serde_json::to_vec(&e).unwrap())
.send()
.unwrap();
}
panic!("");
},
};
}
}
}

View File

@ -1,12 +0,0 @@
[
{
"process_name": "key_value",
"process_wasm_path": "/key_value.wasm",
"on_panic": "Restart",
"request_networking": false,
"request_messaging": [
"vfs:sys:uqbar"
],
"public": true
}
]

View File

@ -1,5 +0,0 @@
{
"package": "key_value",
"publisher": "uqbar",
"version": [0, 1, 0]
}

View File

@ -42,7 +42,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
"syn-solidity",
"tiny-keccak",
]
@ -192,12 +192,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.5"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -223,9 +223,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
@ -234,9 +234,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.14.2"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
@ -299,9 +299,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "lazy_static"
@ -317,9 +317,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "libm"
@ -329,9 +329,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "linux-raw-sys"
version = "0.4.10"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
[[package]]
name = "log"
@ -369,18 +369,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.69"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "proptest"
version = "1.3.1"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e"
checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf"
dependencies = [
"bit-set",
"bit-vec",
@ -477,9 +477,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.7.5"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rmp"
@ -505,9 +505,9 @@ dependencies = [
[[package]]
name = "ruint"
version = "1.11.0"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "724fd11728a3804e9944b14cab63825024c40bf42f8af87c8b5d97c4bbacf426"
checksum = "608a5726529f2f0ef81b8fde9873c4bb829d6b5b5ca6be4d97345ddf0749c825"
dependencies = [
"proptest",
"rand",
@ -534,15 +534,15 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.21"
version = "0.38.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
dependencies = [
"bitflags 2.4.1",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -559,9 +559,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "semver"
@ -571,22 +571,22 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.192"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.192"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -602,9 +602,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.11.1"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "smol_str"
@ -637,9 +637,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.39"
version = "2.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
dependencies = [
"proc-macro2",
"quote",
@ -655,7 +655,7 @@ dependencies = [
"paste",
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -668,7 +668,7 @@ dependencies = [
"fastrand",
"redox_syscall",
"rustix",
"windows-sys",
"windows-sys 0.48.0",
]
[[package]]
@ -688,7 +688,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
]
[[package]]
@ -723,9 +723,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
[[package]]
name = "unicode-ident"
@ -756,8 +756,8 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uqbar_process_lib"
version = "0.3.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=5e1b94a#5e1b94ae2f85c66da33ec52117a72b90d53c4d22"
version = "0.4.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=1ce0d41#1ce0d412169e795c2a99464563b42ae2a2628d77"
dependencies = [
"anyhow",
"bincode",
@ -804,18 +804,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.36.2"
version = "0.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.10.11"
version = "0.10.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f"
checksum = "d835d67708f6374937c550ad8dd1d17c616ae009e3f00d7a0ac9f7825e78c36a"
dependencies = [
"anyhow",
"indexmap",
@ -829,9 +829,9 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.116.1"
version = "0.118.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50"
checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9"
dependencies = [
"indexmap",
"semver",
@ -843,7 +843,16 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
@ -852,13 +861,28 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
@ -867,46 +891,88 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "wit-bindgen"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"bitflags 2.4.1",
"wit-bindgen-rust-macro",
@ -914,8 +980,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-core"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"wit-component",
@ -924,8 +990,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust"
version = "0.13.2"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"heck",
@ -936,13 +1002,13 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"proc-macro2",
"quote",
"syn 2.0.39",
"syn 2.0.40",
"wit-bindgen-core",
"wit-bindgen-rust",
"wit-component",
@ -950,9 +1016,9 @@ dependencies = [
[[package]]
name = "wit-component"
version = "0.17.0"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e"
checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2"
dependencies = [
"anyhow",
"bitflags 2.4.1",
@ -969,9 +1035,9 @@ dependencies = [
[[package]]
name = "wit-parser"
version = "0.12.2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76"
checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3"
dependencies = [
"anyhow",
"id-arena",
@ -986,6 +1052,6 @@ dependencies = [
[[package]]
name = "zeroize"
version = "1.6.0"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"

View File

@ -17,10 +17,10 @@ alloy-sol-types = "0.3.2"
bincode = "1.3.3"
hex = "0.4.3"
rmp-serde = "1.1.2"
serde = {version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" }
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "1ce0d41" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
[lib]
crate-type = ["cdylib"]

View File

@ -2,7 +2,7 @@
{
"process_name": "qns_indexer",
"process_wasm_path": "/qns_indexer.wasm",
"on_panic": "Restart",
"on_exit": "Restart",
"request_networking": true,
"request_messaging": [
"net:sys:uqbar",

View File

@ -6,8 +6,8 @@ use serde_json::json;
use std::collections::HashMap;
use std::string::FromUtf8Error;
use uqbar_process_lib::{
get_typed_state, http, println, receive, set_state,
Address, Message, Payload, Request, Response,
await_message, get_typed_state, http, println, receive, set_state, Address, Message, Payload,
Request, Response,
};
wit_bindgen::generate!({
@ -147,7 +147,6 @@ impl Guest for Component {
}
fn main(our: Address, mut state: State) -> anyhow::Result<()> {
// shove all state into net::net
Request::new()
.target((&our.node, "net", "sys", "uqbar"))
@ -165,18 +164,18 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
http::bind_http_path("/node/:name", false, false)?;
loop {
let Ok((source, message)) = receive() else {
let Ok(message) = await_message() else {
println!("qns_indexer: got network error");
continue;
};
let Message::Request(request) = message else {
let Message::Request { source, ipc, .. } = message else {
// TODO we should store the subscription ID for eth_rpc
// incase we want to cancel/reset it
continue;
};
if source.process == "http_server:sys:uqbar" {
if let Ok(ipc_json) = serde_json::from_slice::<serde_json::Value>(&request.ipc) {
if let Ok(ipc_json) = serde_json::from_slice::<serde_json::Value>(&ipc) {
if ipc_json["path"].as_str().unwrap_or_default() == "/node/:name" {
if let Some(name) = ipc_json["url_params"]["name"].as_str() {
if let Some(node) = state.nodes.get(name) {
@ -219,12 +218,11 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
continue;
}
let Ok(msg) = serde_json::from_slice::<AllActions>(&request.ipc) else {
let Ok(msg) = serde_json::from_slice::<AllActions>(&ipc) else {
println!("qns_indexer: got invalid message");
continue;
};
match msg {
// Probably more message types later...maybe not...
AllActions::EventSubscription(e) => {
@ -232,10 +230,15 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
let nodeId = &e.topics[1];
let name = if decode_hex(&e.topics[0].clone()) == NodeRegistered::SIGNATURE_HASH {
let decoded = NodeRegistered::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
let decoded =
NodeRegistered::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
match dnswire_decode(decoded.0.clone()) {
Ok(name) => { state.names.insert(nodeId.to_string(), name.clone()); }
Err(_) => { println!("qns_indexer: failed to decode name: {:?}", decoded.0); }
Ok(name) => {
state.names.insert(nodeId.to_string(), name.clone());
}
Err(_) => {
println!("qns_indexer: failed to decode name: {:?}", decoded.0);
}
}
continue;
} else if let Some(name) = state.names.get(nodeId) {
@ -245,7 +248,10 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
continue;
};
let node = state.nodes.entry(name.clone()).or_insert_with(QnsUpdate::default);
let node = state
.nodes
.entry(name.clone())
.or_insert_with(QnsUpdate::default);
if node.name == "" {
node.name = name.clone();
@ -260,12 +266,14 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
match decode_hex(&e.topics[0].clone()) {
NodeRegistered::SIGNATURE_HASH => {}
KeyUpdate::SIGNATURE_HASH => {
let decoded = KeyUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
let decoded =
KeyUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
node.public_key = format!("0x{}", hex::encode(decoded.0));
send = true;
}
IpUpdate::SIGNATURE_HASH => {
let decoded = IpUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
let decoded =
IpUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
let ip = decoded.0;
node.ip = format!(
"{}.{}.{}.{}",
@ -277,23 +285,28 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
send = true;
}
WsUpdate::SIGNATURE_HASH => {
let decoded = WsUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
let decoded =
WsUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
node.port = decoded.0;
send = true;
}
WtUpdate::SIGNATURE_HASH => {
let decoded = WtUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
let decoded =
WtUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
}
TcpUpdate::SIGNATURE_HASH => {
let decoded = TcpUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
let decoded =
TcpUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
}
UdpUpdate::SIGNATURE_HASH => {
let decoded = UdpUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
UdpUpdate::SIGNATURE_HASH => {
let decoded =
UdpUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
}
RoutingUpdate::SIGNATURE_HASH => {
let decoded = RoutingUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
let decoded =
RoutingUpdate::decode_data(&decode_hex_to_vec(&e.data), true).unwrap();
let routers_raw = decoded.0;
node.routers = routers_raw
node.routers = routers_raw
.iter()
.map(|r| {
let key = format!("0x{}", hex::encode(r));
@ -311,14 +324,11 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
}
if send {
Request::new()
.target((&our.node, "net", "sys", "uqbar"))
.try_ipc(NetActions::QnsUpdate(node.clone()))?
.send()?;
}
}
}
set_state(&bincode::serialize(&state)?);

View File

@ -1,12 +0,0 @@
[
{
"process_name": "sqlite",
"process_wasm_path": "/sqlite.wasm",
"on_panic": "Restart",
"request_networking": false,
"request_messaging": [
"vfs:sys:uqbar"
],
"public": true
}
]

View File

@ -1,4 +0,0 @@
{
"package": "sqlite",
"publisher": "uqbar"
}

View File

@ -1,504 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "http"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "id-arena"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "idna"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
"equivalent",
"hashbrown",
"serde",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "leb128"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "semver"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]]
name = "serde"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "smallvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "spdx"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71"
dependencies = [
"smallvec",
]
[[package]]
name = "sqlite"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"serde",
"serde_json",
"thiserror",
"uqbar_process_lib",
"wit-bindgen",
]
[[package]]
name = "syn"
version = "2.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uqbar_process_lib"
version = "0.3.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=5e1b94a#5e1b94ae2f85c66da33ec52117a72b90d53c4d22"
dependencies = [
"anyhow",
"bincode",
"http",
"rand",
"serde",
"serde_json",
"thiserror",
"url",
"wit-bindgen",
]
[[package]]
name = "url"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.10.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f"
dependencies = [
"anyhow",
"indexmap",
"serde",
"serde_derive",
"serde_json",
"spdx",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.116.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50"
dependencies = [
"indexmap",
"semver",
]
[[package]]
name = "wit-bindgen"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"bitflags",
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"anyhow",
"wit-component",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.13.2"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"anyhow",
"heck",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"anyhow",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
"wit-component",
]
[[package]]
name = "wit-component"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
]

View File

@ -1,31 +0,0 @@
[package]
name = "sqlite"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.release]
panic = "abort"
opt-level = "s"
lto = true
[dependencies]
anyhow = "1.0"
bincode = "1.3.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" }
[lib]
crate-type = ["cdylib"]
[package.metadata.component]
package = "uqbar:process"
[package.metadata.component.target]
path = "wit"
[package.metadata.component.dependencies]

View File

@ -1,250 +0,0 @@
use std::collections::{HashMap, HashSet};
use uqbar_process_lib::{Address, ProcessId, Request, Response};
use uqbar_process_lib::kernel_types as kt;
use uqbar_process_lib::uqbar::process::standard as wit;
wit_bindgen::generate!({
path: "../../../wit",
world: "process",
exports: {
world: Component,
},
});
mod sqlite_types;
use sqlite_types as sq;
const PREFIX: &str = "sqlite-";
type DbToProcess = HashMap<String, ProcessId>;
fn make_vfs_cap(kind: &str, drive: &str) -> String {
serde_json::to_string(&serde_json::json!({
"kind": kind,
"drive": drive,
})).unwrap()
}
fn make_db_cap(kind: &str, db: &str) -> String {
serde_json::to_string(&serde_json::json!({
"kind": kind,
"db": db,
})).unwrap()
}
fn forward_if_have_cap(
our: &Address,
operation_type: &str,
// operation_type: OperationType,
db: &str,
ipc: Vec<u8>,
db_to_process: &mut DbToProcess,
) -> anyhow::Result<()> {
if wit::has_capability(&make_db_cap(operation_type, db)) {
// forward
let Some(process_id) = db_to_process.get(db) else {
return Err(sq::SqliteError::DbDoesNotExist.into());
};
Request::new()
.target(wit::Address {
node: our.node.clone(),
process: process_id.clone(),
})
// .target(Address::new(our.node.clone(), process_id.clone()))?
.inherit(true)
.ipc(ipc)
.send()?;
return Ok(());
} else {
// reject
return Err(sq::SqliteError::NoCap.into());
}
}
fn handle_message (
our: &Address,
db_to_process: &mut DbToProcess,
read_keywords: &HashSet<String>,
write_keywords: &HashSet<String>,
) -> anyhow::Result<()> {
let (source, message) = wit::receive().unwrap();
if our.node != source.node {
return Err(sq::SqliteError::RejectForeign.into());
}
match message {
wit::Message::Response(_) => {
return Err(sq::SqliteError::UnexpectedResponse.into());
},
wit::Message::Request(wit::Request { ipc, .. }) => {
match serde_json::from_slice(&ipc)? {
sq::SqliteMessage::New { ref db } => {
// TODO: make atomic
// (1): create vfs drive
// (2): spin up worker, granting vfs caps & msg_cap
// (3): issue new caps
if db_to_process.contains_key(db) {
return Err(sq::SqliteError::DbAlreadyExists.into());
}
// (1)
let vfs_address = Address {
node: our.node.clone(),
process: ProcessId::new(Some("vfs"), "sys", "uqbar"),
};
let vfs_drive = format!("{}{}", PREFIX, db);
let _ = Request::new()
.target(vfs_address.clone())
.ipc(serde_json::to_vec(&kt::VfsRequest {
drive: vfs_drive.clone(),
action: kt::VfsAction::New,
})?)
.send_and_await_response(15)??;
// (2)
let vfs_read = wit::get_capability(
&vfs_address,
&make_vfs_cap("read", &vfs_drive),
).ok_or(anyhow::anyhow!("New failed: no vfs 'read' capability found"))?;
let vfs_write = wit::get_capability(
&vfs_address,
&make_vfs_cap("write", &vfs_drive),
).ok_or(anyhow::anyhow!("New failed: no vfs 'write' capability found"))?;
let msg_cap = wit::get_capability(
&source,
&"\"messaging\"".into(),
).ok_or(anyhow::anyhow!("New failed: no msg capability passed"))?;
let spawned_process_id = match wit::spawn(
None,
"/sqlite_worker.wasm",
&wit::OnPanic::None,
&wit::Capabilities::Some(vec![vfs_read, vfs_write, msg_cap]),
false, // not public
) {
Ok(spawned_process_id) => spawned_process_id,
Err(e) => {
wit::print_to_terminal(0, &format!("couldn't spawn: {}", e));
panic!("couldn't spawn"); // TODO
},
};
// grant caps
wit::create_capability(&source.process, &make_db_cap("read", db));
wit::create_capability(&source.process, &make_db_cap("write", db));
// initialize worker
Request::new()
.target(wit::Address {
node: our.node.clone(),
process: spawned_process_id.clone(),
})
.ipc(ipc.clone())
.send()?;
db_to_process.insert(db.into(), spawned_process_id);
Response::new()
.ipc(ipc)
.send()?;
},
sq::SqliteMessage::Write { ref db, ref statement, .. } => {
let first_word = statement
.split_whitespace()
.next()
.map(|word| word.to_uppercase())
.unwrap_or("".to_string());
if !write_keywords.contains(&first_word) {
return Err(sq::SqliteError::NotAWriteKeyword.into())
}
forward_if_have_cap(our, "write", db, ipc, db_to_process)?;
},
sq::SqliteMessage::Read { ref db, ref query } => {
let first_word = query
.split_whitespace()
.next()
.map(|word| word.to_uppercase())
.unwrap_or("".to_string());
if !read_keywords.contains(&first_word) {
return Err(sq::SqliteError::NotAReadKeyword.into())
}
forward_if_have_cap(our, "read", db, ipc, db_to_process)?;
},
sq::SqliteMessage::Commit { ref db, .. } => {
forward_if_have_cap(our, "write", db, ipc, db_to_process)?;
},
}
Ok(())
},
}
}
struct Component;
impl Guest for Component {
fn init(our: String) {
wit::print_to_terminal(0, "sqlite: begin");
let our = Address::from_str(&our).unwrap();
let mut db_to_process: DbToProcess = HashMap::new();
let read_keywords: HashSet<String> = [
"ANALYZE",
"ATTACH",
"BEGIN",
"EXPLAIN",
"PRAGMA",
"SELECT",
"VALUES",
"WITH",
]
.iter()
.map(|x| x.to_string())
.collect();
let write_keywords: HashSet<String> = [
"ALTER",
"ANALYZE",
"COMMIT",
"CREATE",
"DELETE",
"DETACH",
"DROP",
"END",
"INSERT",
"REINDEX",
"RELEASE",
"RENAME",
"REPLACE",
"ROLLBACK",
"SAVEPOINT",
"UPDATE",
"VACUUM",
]
.iter()
.map(|x| x.to_string())
.collect();
loop {
match handle_message(&our, &mut db_to_process, &read_keywords, &write_keywords) {
Ok(()) => {},
Err(e) => {
wit::print_to_terminal(0, format!(
"sqlite: error: {:?}",
e,
).as_str());
if let Some(e) = e.downcast_ref::<sq::SqliteError>() {
Response::new()
.ipc(serde_json::to_vec(&e).unwrap())
.send()
.unwrap();
}
},
};
}
}
}

View File

@ -1 +0,0 @@
../../sqlite_types.rs

View File

@ -1,41 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub enum SqliteMessage {
New { db: String },
Write { db: String, statement: String, tx_id: Option<u64> },
Read { db: String, query: String },
Commit { db: String, tx_id: u64 },
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum SqlValue {
Integer(i64),
Real(f64),
Text(String),
Blob(Vec<u8>),
Boolean(bool),
Null,
}
#[derive(Debug, Serialize, Deserialize, thiserror::Error)]
pub enum SqliteError {
#[error("DbDoesNotExist")]
DbDoesNotExist,
#[error("DbAlreadyExists")]
DbAlreadyExists,
#[error("NoTx")]
NoTx,
#[error("NoCap")]
NoCap,
#[error("RejectForeign")]
RejectForeign,
#[error("UnexpectedResponse")]
UnexpectedResponse,
#[error("NotAWriteKeyword")]
NotAWriteKeyword,
#[error("NotAReadKeyword")]
NotAReadKeyword,
#[error("Invalid Parameters")]
InvalidParameters,
}

View File

@ -1,610 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
]
[[package]]
name = "allocator-api2"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashlink"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
dependencies = [
"hashbrown",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "http"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "id-arena"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "idna"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
"equivalent",
"hashbrown",
"serde",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "leb128"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "libsqlite3-sys"
version = "0.26.0"
source = "git+https://github.com/uqbar-dao/rusqlite?rev=8fb20a9#8fb20a9dedf4bea626036af8b43c92b050a24d9f"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pkg-config"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rusqlite"
version = "0.29.0"
source = "git+https://github.com/uqbar-dao/rusqlite?rev=8fb20a9#8fb20a9dedf4bea626036af8b43c92b050a24d9f"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"smallvec",
]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "semver"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]]
name = "serde"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "smallvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "spdx"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71"
dependencies = [
"smallvec",
]
[[package]]
name = "sqlite_worker"
version = "0.1.0"
dependencies = [
"anyhow",
"base64",
"bincode",
"rusqlite",
"serde",
"serde_json",
"thiserror",
"uqbar_process_lib",
"wit-bindgen",
]
[[package]]
name = "syn"
version = "2.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uqbar_process_lib"
version = "0.3.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=5e1b94a#5e1b94ae2f85c66da33ec52117a72b90d53c4d22"
dependencies = [
"anyhow",
"bincode",
"http",
"rand",
"serde",
"serde_json",
"thiserror",
"url",
"wit-bindgen",
]
[[package]]
name = "url"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.10.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f"
dependencies = [
"anyhow",
"indexmap",
"serde",
"serde_derive",
"serde_json",
"spdx",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.116.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50"
dependencies = [
"indexmap",
"semver",
]
[[package]]
name = "wit-bindgen"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"bitflags",
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"anyhow",
"wit-component",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.13.2"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"anyhow",
"heck",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
dependencies = [
"anyhow",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
"wit-component",
]
[[package]]
name = "wit-component"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
]

View File

@ -1,33 +0,0 @@
[package]
name = "sqlite_worker"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.release]
panic = "abort"
opt-level = "s"
lto = true
[dependencies]
anyhow = "1.0"
base64 = "0.13"
bincode = "1.3.3"
rusqlite = { git = "https://github.com/uqbar-dao/rusqlite", rev = "8fb20a9", features = ["bundled", "wasm32-wasi-vfs"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" }
[lib]
crate-type = ["cdylib"]
[package.metadata.component]
package = "uqbar:process"
[package.metadata.component.target]
path = "wit"
[package.metadata.component.dependencies]

View File

@ -1,66 +0,0 @@
#!/bin/bash
crossplatform_wget() {
curl -L "${1}" -o $(basename "${1}")
}
crossplatform_realpath_inner() {
python3 -c "import os; print(os.path.realpath('$1'))"
}
crossplatform_realpath() {
if [ -e "$1" ] || [ -L "$1" ]; then
crossplatform_realpath_inner "$1"
else
return 1
fi
}
# cd sqlite
# cargo build --release --no-default-features --target wasm32-wasi
#
# cd ../sqlite_worker
# Get special clang compiler required to build & link sqlite3 C lib.
mkdir -p target
cd target
WASI_VERSION=20
WASI_VERSION_FULL=${WASI_VERSION}.0
CC_PATH=$(crossplatform_realpath ./wasi-sdk-${WASI_VERSION_FULL}/bin/clang)
# Determine operating system
OS_TYPE="$(uname)"
if [ "$OS_TYPE" = "Darwin" ]; then
WASI_PLATFORM="macos"
elif [ "$OS_TYPE" = "Linux" ]; then
WASI_PLATFORM="linux"
else
echo "sqlite_worker build failed: Unsupported OS: $OS_TYPE"
exit 1
fi
if [ ! -e "$CC_PATH" ]; then
$(crossplatform_wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_VERSION}/wasi-sdk-${WASI_VERSION_FULL}-${WASI_PLATFORM}.tar.gz)
tar xvf wasi-sdk-${WASI_VERSION_FULL}-${WASI_PLATFORM}.tar.gz
fi
CC_PATH=$(crossplatform_realpath ./wasi-sdk-${WASI_VERSION_FULL}/bin/clang)
cd ..
# We write env vars to `.cargo/config.toml` here because:
# 1. Doing `export foo=/path && export bar=/path2 && RUSTFLAGS=baz cargo build ...`
# does not properly pass the RUSTFLAGS (cargo bug?).
# 2. Specifying `~/path` inside `.cargo/config.toml` doesn't expand.
mkdir -p .cargo
# Write to the .cargo/config.toml file
cat <<EOF > .cargo/config.toml
[env]
CC_wasm32_wasi = "$CC_PATH"
CARGO_TARGET_WASM32_WASI_LINKER = "$CC_PATH"
EOF
RUSTFLAGS="-C target-feature=-crt-static -C link-arg=-Wl,--no-entry,--export=init,--export=cabi_realloc" cargo build --release --no-default-features --target wasm32-wasi

View File

@ -1,575 +0,0 @@
use core::ffi::{c_char, c_int, c_ulonglong, CStr};
use std::ffi::CString;
use rusqlite::{types::FromSql, types::FromSqlError, types::ToSql, types::ValueRef};
use std::collections::HashMap;
use uqbar_process_lib::{Address, create_capability, ProcessId, Response};
use uqbar_process_lib::uqbar::process::standard as wit;
wit_bindgen::generate!({
path: "../../../wit",
world: "process",
exports: {
world: Component,
},
});
mod sqlite_types;
use sqlite_types as sq;
const PREFIX: &str = "sqlite-";
impl ToSql for sq::SqlValue {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
match self {
sq::SqlValue::Integer(i) => i.to_sql(),
sq::SqlValue::Real(f) => f.to_sql(),
sq::SqlValue::Text(ref s) => s.to_sql(),
sq::SqlValue::Blob(ref b) => b.to_sql(),
sq::SqlValue::Boolean(b) => b.to_sql(),
sq::SqlValue::Null => Ok(rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Null)),
}
}
}
impl FromSql for sq::SqlValue {
fn column_result(value: ValueRef<'_>) -> Result<Self, FromSqlError> {
match value {
ValueRef::Integer(i) => Ok(sq::SqlValue::Integer(i)),
ValueRef::Real(f) => Ok(sq::SqlValue::Real(f)),
ValueRef::Text(t) => {
let text_str = std::str::from_utf8(t).map_err(|_| FromSqlError::InvalidType)?;
Ok(sq::SqlValue::Text(text_str.to_string()))
},
ValueRef::Blob(b) => Ok(sq::SqlValue::Blob(b.to_vec())),
_ => Err(FromSqlError::InvalidType),
}
}
}
#[repr(C)]
pub struct CPreOptionStr {
is_empty: c_int, // 0 -> string is empty
string: CString,
}
#[repr(C)]
pub struct COptionStr {
is_empty: c_int, // 0 -> string is empty
string: *mut c_char,
}
#[repr(C)]
struct CBytes {
data: *mut u8,
len: usize,
}
#[repr(C)]
pub struct CPayload {
is_empty: c_int, // 0 -> payload is empty
mime: *mut COptionStr,
bytes: *mut CBytes,
}
#[repr(C)]
pub struct CPrePayload {
is_empty: c_int, // 0 -> payload is empty
mime: COptionStr,
bytes: CBytes,
}
#[repr(C)]
pub struct CProcessId {
process_name: *const c_char,
package_name: *const c_char,
publisher_node: *const c_char,
}
#[repr(C)]
pub struct CIpcMetadata {
ipc: *mut COptionStr,
metadata: *mut COptionStr,
}
impl CPreOptionStr {
fn new(s: Option<Vec<u8>>) -> Self {
let (is_empty, string) = match s {
None => (0, CString::new("").unwrap()),
Some(s) => (1, CString::new(s).unwrap()),
};
CPreOptionStr {
is_empty,
string,
}
}
}
impl COptionStr {
fn new(s: Option<Vec<u8>>) -> Self {
let (is_empty, string) = match s {
None => (0, CString::new("").unwrap()),
Some(s) => (1, CString::new(s).unwrap()),
};
COptionStr {
is_empty,
string: string.as_ptr() as *mut c_char,
}
}
}
fn from_coptionstr_to_bytes(s: *const COptionStr) -> Vec<u8> {
if unsafe { (*s).is_empty == 0 } {
vec![]
} else {
from_cstr_to_string(unsafe { (*s).string }).as_bytes().to_vec()
}
}
fn from_coptionstr_to_option_string(s: *const COptionStr) -> Option<String> {
if unsafe { (*s).is_empty == 0 } {
None
} else {
Some(from_cstr_to_string(unsafe { (*s).string }))
}
}
impl CBytes {
fn new(mut bytes: Vec<u8>) -> Self {
CBytes {
data: bytes.as_mut_ptr(),
len: bytes.len(),
}
}
fn new_empty() -> Self {
CBytes::new(Vec::with_capacity(0))
}
}
impl From<Vec<u8>> for CBytes {
fn from(bytes: Vec<u8>) -> Self {
CBytes::new(bytes)
}
}
impl From<CBytes> for Vec<u8> {
fn from(bytes: CBytes) -> Self {
let bytes = unsafe { Vec::from_raw_parts(bytes.data, bytes.len, bytes.len) };
bytes
}
}
fn from_cbytes_to_vec_u8(bytes: *mut CBytes) -> Vec<u8> {
// let bytes = unsafe { Vec::from_raw_parts((*bytes).data, (*bytes).len, (*bytes).len) };
let bytes = unsafe { std::slice::from_raw_parts((*bytes).data, (*bytes).len) };
let bytes = bytes.to_vec();
bytes
}
impl From<Option<wit::Payload>> for CPrePayload {
fn from(p: Option<wit::Payload>) -> Self {
let (is_empty, mime, bytes) = match p {
None => (0, COptionStr::new(None), CBytes::new_empty()),
Some(wit::Payload { mime, bytes }) => {
let mime = match mime {
Some(s) => Some(s.as_bytes().to_vec()),
None => None,
};
(1, COptionStr::new(mime), CBytes::new(bytes))
}
};
CPrePayload {
is_empty,
mime,
bytes,
}
}
}
impl From<CPayload> for Option<wit::Payload> {
fn from(p: CPayload) -> Self {
if p.is_empty == 0 {
None
} else {
let mime = from_coptionstr_to_option_string(p.mime);
let bytes = from_cbytes_to_vec_u8(p.bytes);
Some(wit::Payload {
mime,
bytes,
})
}
}
}
fn from_cpayload_to_option_payload(p: *const CPayload) -> Option<wit::Payload> {
if unsafe { (*p).is_empty == 0 } {
None
} else {
let mime = unsafe { from_coptionstr_to_option_string((*p).mime) };
let bytes = unsafe { from_cbytes_to_vec_u8((*p).bytes) };
Some(wit::Payload {
mime,
bytes,
})
}
}
fn from_cprocessid_to_processid(pid: *const CProcessId) -> ProcessId {
ProcessId {
process_name: from_cstr_to_string(unsafe { (*pid).process_name }),
package_name: from_cstr_to_string(unsafe { (*pid).package_name }),
publisher_node: from_cstr_to_string(unsafe { (*pid).publisher_node }),
}
}
fn from_cstr_to_string(s: *const c_char) -> String {
let cstr = unsafe { CStr::from_ptr(s) };
cstr.to_str().unwrap().into()
}
#[no_mangle]
pub extern "C" fn get_payload_wrapped(return_val: *mut CPayload) {
// TODO: remove this logic; just here to avoid writing to invalid places
// in memory due to an fs bug where chunk size may be bigger than requested
let max_len = unsafe { (*(*return_val).bytes).len.clone() };
let payload = wit::get_payload();
let mime_len = {
match payload {
None => None,
Some(ref payload) => {
match payload.mime {
None => None,
Some(ref mime) => {
Some(mime.len())
},
}
}
}
};
unsafe {
match payload {
None => {},
Some(payload) => {
(*return_val).is_empty = 1;
match payload.mime {
None => {},
Some(mime) => {
(*(*return_val).mime).is_empty = 1;
let Some(mime_len) = mime_len else { panic!("") };
let mime = CString::new(mime).unwrap();
std::ptr::copy_nonoverlapping(
mime.as_ptr(),
(*(*return_val).mime).string,
mime_len + 1,
);
},
}
(*(*return_val).bytes).len = std::cmp::min(max_len, payload.bytes.len());
std::ptr::copy_nonoverlapping(
payload.bytes.as_ptr(),
(*(*return_val).bytes).data,
std::cmp::min(max_len, payload.bytes.len()),
);
},
}
}
}
impl CIpcMetadata {
fn copy_to_ptr(ptr: *mut CIpcMetadata, ipc: CPreOptionStr, metadata: CPreOptionStr) {
unsafe {
(*(*ptr).ipc).is_empty = ipc.is_empty;
if ipc.is_empty == 1 {
std::ptr::copy_nonoverlapping(
ipc.string.as_ptr(),
(*(*ptr).ipc).string,
ipc.string.as_bytes_with_nul().len(),
);
}
(*(*ptr).metadata).is_empty = metadata.is_empty;
if metadata.is_empty == 1 {
std::ptr::copy_nonoverlapping(
metadata.string.as_ptr(),
(*(*ptr).metadata).string,
metadata.string.as_bytes_with_nul().len(),
);
}
}
}
}
#[no_mangle]
pub extern "C" fn send_and_await_response_wrapped(
target_node: *const c_char,
target_process: *const CProcessId,
request_ipc: *const COptionStr,
request_metadata: *const COptionStr,
payload: *const CPayload,
timeout: c_ulonglong,
return_val: *mut CIpcMetadata,
) {
let target_node = from_cstr_to_string(target_node);
let target_process = from_cprocessid_to_processid(target_process);
let payload = from_cpayload_to_option_payload(payload);
let request_ipc = from_coptionstr_to_bytes(request_ipc);
let request_metadata = from_coptionstr_to_option_string(request_metadata);
let (
_,
wit::Message::Response((wit::Response { ipc, metadata, .. }, _)),
) = wit::send_and_await_response(
&wit::Address {
node: target_node,
process: target_process,
},
&wit::Request {
inherit: false,
expects_response: Some(timeout),
ipc: request_ipc,
metadata: request_metadata,
},
match payload {
None => None,
Some(ref p) => Some(p),
},
).unwrap() else {
panic!("");
};
let ipc = CPreOptionStr::new(Some(ipc));
let metadata = CPreOptionStr::new(match metadata {
None => None,
Some(s) => Some(s.as_bytes().to_vec())
});
CIpcMetadata::copy_to_ptr(return_val, ipc, metadata);
}
fn json_to_sqlite(value: &serde_json::Value) -> Result<sq::SqlValue, sq::SqliteError> {
match value {
serde_json::Value::Number(n) => {
if let Some(int_val) = n.as_i64() {
Ok(sq::SqlValue::Integer(int_val))
} else if let Some(float_val) = n.as_f64() {
Ok(sq::SqlValue::Real(float_val))
} else {
Err(sq::SqliteError::InvalidParameters)
}
},
serde_json::Value::String(s) => {
match base64::decode(&s) {
Ok(decoded_bytes) => {
// Convert to SQLite Blob if it's a valid base64 string
Ok(sq::SqlValue::Blob(decoded_bytes))
},
Err(_) => {
// If it's not base64, just use the string itself
Ok(sq::SqlValue::Text(s.clone()))
}
}
},
serde_json::Value::Bool(b) => {
Ok(sq::SqlValue::Boolean(*b))
},
serde_json::Value::Null => {
Ok(sq::SqlValue::Null)
},
_ => {
Err(sq::SqliteError::InvalidParameters)
}
}
}
fn handle_message(
our: &wit::Address,
conn: &mut Option<rusqlite::Connection>,
txs: &mut HashMap<u64, Vec<(String, Vec<sq::SqlValue>)>>,
) -> anyhow::Result<()> {
let (source, message) = wit::receive().unwrap();
if our.node != source.node {
return Err(sq::SqliteError::RejectForeign.into());
}
match message {
wit::Message::Response(_) => { unimplemented!() },
wit::Message::Request(wit::Request { ipc, .. }) => {
match serde_json::from_slice(&ipc)? {
sq::SqliteMessage::New { db } => {
let vfs_drive = format!("{}{}", PREFIX, db);
match conn {
Some(_) => {
return Err(sq::SqliteError::DbAlreadyExists.into());
},
None => {
let flags = rusqlite::OpenFlags::default();
*conn = Some(rusqlite::Connection::open_with_flags_and_vfs(
format!(
"{}:{}:/{}.sql",
our.node,
vfs_drive,
db,
),
flags,
"uqbar",
)?);
},
}
},
sq::SqliteMessage::Write { ref statement, tx_id, .. } => {
let Some(ref conn) = conn else {
return Err(sq::SqliteError::DbDoesNotExist.into());
};
let parameters: Vec<sq::SqlValue> = match wit::get_payload() {
None => vec![],
Some(wit::Payload { mime: _, ref bytes }) => {
let json_params = serde_json::from_slice::<serde_json::Value>(bytes)?;
match json_params {
serde_json::Value::Array(vec) => {
vec.iter().map(|value| json_to_sqlite(value)).collect::<Result<Vec<_>, _>>()?
},
_ => {
return Err(sq::SqliteError::InvalidParameters.into());
}
}
},
};
match tx_id {
Some(tx_id) => {
txs.entry(tx_id)
.or_insert_with(Vec::new)
.push((statement.clone(), parameters));
},
None => {
let mut stmt = conn.prepare(statement)?;
stmt.execute(rusqlite::params_from_iter(parameters.iter()))?;
},
};
Response::new()
.ipc(ipc)
.send()?;
},
sq::SqliteMessage::Commit { ref tx_id, .. } => {
let Some(queries) = txs.remove(tx_id) else {
return Err(sq::SqliteError::NoTx.into());
};
let Some(ref mut conn) = conn else {
return Err(sq::SqliteError::DbDoesNotExist.into());
};
let tx = conn.transaction()?;
for (query, params) in queries {
tx.execute(&query, rusqlite::params_from_iter(params.iter()))?;
}
tx.commit()?;
Response::new()
.ipc(ipc)
.send()?;
},
sq::SqliteMessage::Read { ref query, .. } => {
let Some(ref db_handle) = conn else {
return Err(sq::SqliteError::DbDoesNotExist.into());
};
let parameters: Vec<sq::SqlValue> = match wit::get_payload() {
None => vec![],
Some(wit::Payload { mime: _, ref bytes }) => {
let json_params = serde_json::from_slice::<serde_json::Value>(bytes)?;
match json_params {
serde_json::Value::Array(vec) => {
vec.iter().map(|value| json_to_sqlite(value)).collect::<Result<Vec<_>, _>>()?
},
_ => {
return Err(sq::SqliteError::InvalidParameters.into());
}
}
},
};
let mut statement = db_handle.prepare(query)?;
let column_names: Vec<String> = statement
.column_names()
.iter()
.map(|c| c.to_string())
.collect();
let results: Vec<HashMap<String, serde_json::Value>> = statement
.query_map(rusqlite::params_from_iter(parameters.iter()), |row| {
let mut map = HashMap::new();
for (i, column_name) in column_names.iter().enumerate() {
let value: sq::SqlValue = row.get(i)?;
let value_json = match value {
sq::SqlValue::Integer(int) => serde_json::Value::Number(int.into()),
sq::SqlValue::Real(real) => serde_json::Value::Number(serde_json::Number::from_f64(real).unwrap()),
sq::SqlValue::Text(text) => serde_json::Value::String(text),
sq::SqlValue::Blob(blob) => serde_json::Value::String(base64::encode(blob)), // or another representation if you prefer
_ => serde_json::Value::Null,
};
map.insert(column_name.clone(), value_json);
}
Ok(map)
})?
.collect::<Result<Vec<_>, _>>()?;
let results = serde_json::json!(results).to_string();
let results_bytes = results.as_bytes().to_vec();
Response::new()
.ipc(ipc)
.payload(wit::Payload {
mime: None,
bytes: results_bytes,
})
.send()?;
},
}
Ok(())
},
}
}
struct Component;
impl Guest for Component {
fn init(our: String) {
wit::print_to_terminal(1, "sqlite_worker: begin");
let our = Address::from_str(&our).unwrap();
let mut conn: Option<rusqlite::Connection> = None;
let mut txs: HashMap<u64, Vec<(String, Vec<sq::SqlValue>)>> = HashMap::new();
let vfs_address = ProcessId::from_str("vfs:sys:uqbar").unwrap();
create_capability(
&vfs_address,
&"\"messaging\"".into(),
);
loop {
match handle_message(&our, &mut conn, &mut txs) {
Ok(()) => {},
Err(e) => {
// TODO: should we send an error on failure?
wit::print_to_terminal(0, format!(
"sqlite_worker: error: {:?}",
e,
).as_str());
if let Some(e) = e.downcast_ref::<sq::SqliteError>() {
Response::new()
.ipc(serde_json::to_vec(&e).unwrap())
.send()
.unwrap();
}
},
};
}
}
}

View File

@ -1 +0,0 @@
../../sqlite_types.rs

View File

@ -58,9 +58,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
@ -69,9 +69,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.14.2"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
@ -122,9 +122,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "leb128"
@ -134,9 +134,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.149"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "log"
@ -158,9 +158,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.69"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
@ -206,9 +206,9 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "semver"
@ -218,18 +218,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.190"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.190"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@ -249,9 +249,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.11.1"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "spdx"
@ -264,9 +264,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.38"
version = "2.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
dependencies = [
"proc-macro2",
"quote",
@ -322,9 +322,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
[[package]]
name = "unicode-ident"
@ -355,8 +355,8 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uqbar_process_lib"
version = "0.3.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=5e1b94a#5e1b94ae2f85c66da33ec52117a72b90d53c4d22"
version = "0.4.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=1ce0d41#1ce0d412169e795c2a99464563b42ae2a2628d77"
dependencies = [
"anyhow",
"bincode",
@ -388,18 +388,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.36.1"
version = "0.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53ae0be20bf87918df4fa831bfbbd0b491d24aee407ed86360eae4c2c5608d38"
checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.10.10"
version = "0.10.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5621910462c61a8efc3248fdfb1739bf649bb335b0df935c27b340418105f9d8"
checksum = "d835d67708f6374937c550ad8dd1d17c616ae009e3f00d7a0ac9f7825e78c36a"
dependencies = [
"anyhow",
"indexmap",
@ -413,9 +413,9 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.116.0"
version = "0.118.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53290b1276c5c2d47d694fb1a920538c01f51690e7e261acbe1d10c5fc306ea1"
checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9"
dependencies = [
"indexmap",
"semver",
@ -423,8 +423,8 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"bitflags",
"wit-bindgen-rust-macro",
@ -432,8 +432,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-core"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"wit-component",
@ -442,8 +442,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust"
version = "0.13.2"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"heck",
@ -454,8 +454,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"proc-macro2",
@ -468,9 +468,9 @@ dependencies = [
[[package]]
name = "wit-component"
version = "0.17.0"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e"
checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2"
dependencies = [
"anyhow",
"bitflags",
@ -487,9 +487,9 @@ dependencies = [
[[package]]
name = "wit-parser"
version = "0.12.2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76"
checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3"
dependencies = [
"anyhow",
"id-arena",

View File

@ -13,10 +13,10 @@ lto = true
[dependencies]
anyhow = "1.0"
bincode = "1.3.3"
serde = {version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" }
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "1ce0d41" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
[lib]
crate-type = ["cdylib"]

View File

@ -2,7 +2,7 @@
{
"process_name": "terminal",
"process_wasm_path": "/terminal.wasm",
"on_panic": "Restart",
"on_exit": "Restart",
"request_networking": true,
"request_messaging": [
"net:sys:uqbar",

View File

@ -2,7 +2,7 @@
{
"process_name": "tester",
"process_wasm_path": "/tester.wasm",
"on_panic": "Restart",
"on_exit": "Restart",
"request_networking": true,
"request_messaging": [
"net:sys:uqbar"

View File

@ -19,9 +19,9 @@ dependencies = [
[[package]]
name = "bitflags"
version = "2.4.0"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "bytes"
@ -58,9 +58,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
@ -69,9 +69,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.14.0"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
@ -111,9 +111,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.0.0"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
@ -122,9 +122,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "leb128"
@ -134,9 +134,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "log"
@ -158,9 +158,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.66"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
@ -206,30 +206,30 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "semver"
version = "1.0.18"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.188"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.188"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@ -238,9 +238,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.105"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
@ -249,9 +249,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.11.0"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "spdx"
@ -264,9 +264,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.31"
version = "2.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
dependencies = [
"proc-macro2",
"quote",
@ -323,15 +323,15 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
[[package]]
name = "unicode-ident"
version = "1.0.11"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-normalization"
@ -356,8 +356,8 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uqbar_process_lib"
version = "0.3.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=5e1b94a#5e1b94ae2f85c66da33ec52117a72b90d53c4d22"
version = "0.4.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=8342b1a#8342b1a131401fb5d141dab8c90e79aa6d2bc909"
dependencies = [
"anyhow",
"bincode",
@ -389,18 +389,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.36.2"
version = "0.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.10.11"
version = "0.10.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f"
checksum = "d835d67708f6374937c550ad8dd1d17c616ae009e3f00d7a0ac9f7825e78c36a"
dependencies = [
"anyhow",
"indexmap",
@ -414,9 +414,9 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.116.1"
version = "0.118.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50"
checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9"
dependencies = [
"indexmap",
"semver",
@ -424,8 +424,8 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"bitflags",
"wit-bindgen-rust-macro",
@ -433,8 +433,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-core"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"wit-component",
@ -443,8 +443,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust"
version = "0.13.2"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"heck",
@ -455,8 +455,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"proc-macro2",
@ -469,9 +469,9 @@ dependencies = [
[[package]]
name = "wit-component"
version = "0.17.0"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e"
checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2"
dependencies = [
"anyhow",
"bitflags",
@ -488,9 +488,9 @@ dependencies = [
[[package]]
name = "wit-parser"
version = "0.12.2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76"
checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3"
dependencies = [
"anyhow",
"id-arena",

View File

@ -13,19 +13,14 @@ lto = true
[dependencies]
anyhow = "1.0"
bincode = "1.3.3"
serde = {version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" }
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "8342b1a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
[lib]
crate-type = ["cdylib"]
[package.metadata.component]
package = "uqbar:process"
[package.metadata.component.target]
path = "wit"
[package.metadata.component.dependencies]

View File

@ -1,9 +1,9 @@
use serde::{Serialize, Deserialize};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use uqbar_process_lib::{Address, ProcessId, Request, Response};
use uqbar_process_lib::kernel_types as kt;
use uqbar_process_lib::uqbar::process::standard as wit;
use uqbar_process_lib::{spawn, Address, Message, OnExit, ProcessId, Request, Response};
mod tester_types;
use tester_types as tt;
@ -33,23 +33,29 @@ fn handle_message(our: &Address) -> anyhow::Result<()> {
match message {
wit::Message::Response(_) => {
return Err(tt::TesterError::UnexpectedResponse.into());
},
}
wit::Message::Request(wit::Request { ref ipc, .. }) => {
match serde_json::from_slice(ipc)? {
tt::TesterRequest::Run(_) => {
tt::TesterRequest::Run { test_timeout, .. } => {
wit::print_to_terminal(0, "test_runner: got Run");
let (_, response) = Request::new()
let response = Request::new()
.target(make_vfs_address(&our)?)
.ipc(serde_json::to_vec(&kt::VfsRequest {
drive: "tester:uqbar".into(),
action: kt::VfsAction::GetEntry("/".into()),
path: "/tester:uqbar/pkg".into(),
action: kt::VfsAction::ReadDir,
})?)
.send_and_await_response(5)??;
.send_and_await_response(test_timeout)?
.unwrap();
let wit::Message::Response((response, _)) = response else { panic!("") };
let kt::VfsResponse::GetEntry { children, .. } =
serde_json::from_slice(&response.ipc)? else { panic!("") };
let Message::Response { ipc: vfs_ipc, .. } = response else {
panic!("")
};
let kt::VfsResponse::ReadDir(children) =
serde_json::from_slice(&vfs_ipc)?
else {
panic!("")
};
let mut children: HashSet<_> = children.into_iter().collect();
children.remove("/manifest.json");
children.remove("/metadata.json");
@ -58,36 +64,49 @@ fn handle_message(our: &Address) -> anyhow::Result<()> {
wit::print_to_terminal(0, &format!("test_runner: running {:?}...", children));
// todo: are children returned as absolute paths.
for child in &children {
let child_process_id = match wit::spawn(
let child_process_id = match spawn(
None,
child,
&wit::OnPanic::None, // TODO: notify us
OnExit::None, // TODO: notify us
&wit::Capabilities::All,
false, // not public
) {
Ok(child_process_id) => child_process_id,
Err(e) => {
wit::print_to_terminal(0, &format!("couldn't spawn {}: {}", child, e));
wit::print_to_terminal(
0,
&format!("couldn't spawn {}: {}", child, e),
);
panic!("couldn't spawn"); // TODO
}
};
let (_, response) = Request::new()
let response = Request::new()
.target(Address {
node: our.node.clone(),
process: child_process_id,
})
.ipc(ipc.clone())
.send_and_await_response(5)??;
.send_and_await_response(test_timeout)?
.unwrap();
let wit::Message::Response((response, _)) = response else { panic!("") };
match serde_json::from_slice(&response.ipc)? {
tt::TesterResponse::Pass => {},
tt::TesterResponse::GetFullMessage(_) => {},
tt::TesterResponse::Fail { test, file, line, column } => {
let Message::Response { ipc, .. } = response else {
panic!("")
};
match serde_json::from_slice(&ipc)? {
tt::TesterResponse::Pass => {}
tt::TesterResponse::GetFullMessage(_) => {}
tt::TesterResponse::Fail {
test,
file,
line,
column,
} => {
fail!(test, file, line, column);
},
}
}
}
@ -97,11 +116,13 @@ fn handle_message(our: &Address) -> anyhow::Result<()> {
.ipc(serde_json::to_vec(&tt::TesterResponse::Pass).unwrap())
.send()
.unwrap();
},
tt::TesterRequest::KernelMessage(_) | tt::TesterRequest::GetFullMessage(_) => { unimplemented!() },
}
tt::TesterRequest::KernelMessage(_) | tt::TesterRequest::GetFullMessage(_) => {
unimplemented!()
}
}
Ok(())
},
}
}
}
@ -119,14 +140,11 @@ impl Guest for Component {
loop {
match handle_message(&our) {
Ok(()) => {},
Ok(()) => {}
Err(e) => {
wit::print_to_terminal(0, format!(
"test_runner: error: {:?}",
e,
).as_str());
wit::print_to_terminal(0, format!("test_runner: error: {:?}", e,).as_str());
fail!("test_runner");
},
}
};
}
}

View File

@ -19,9 +19,9 @@ dependencies = [
[[package]]
name = "bitflags"
version = "2.4.0"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "bytes"
@ -58,9 +58,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"libc",
@ -69,9 +69,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.14.2"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
@ -122,9 +122,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.9"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "leb128"
@ -134,9 +134,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "log"
@ -158,9 +158,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.66"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
@ -206,30 +206,30 @@ dependencies = [
[[package]]
name = "ryu"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "semver"
version = "1.0.18"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.188"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.188"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@ -238,9 +238,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.105"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
@ -249,9 +249,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.11.0"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "spdx"
@ -264,9 +264,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.31"
version = "2.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
dependencies = [
"proc-macro2",
"quote",
@ -324,15 +324,15 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
[[package]]
name = "unicode-ident"
version = "1.0.11"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-normalization"
@ -357,8 +357,8 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "uqbar_process_lib"
version = "0.3.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=5e1b94a#5e1b94ae2f85c66da33ec52117a72b90d53c4d22"
version = "0.4.0"
source = "git+ssh://git@github.com/uqbar-dao/process_lib.git?rev=a1dd690#a1dd690b2372ca9f249d96d0c46c97c7333a8b8b"
dependencies = [
"anyhow",
"bincode",
@ -390,18 +390,18 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.36.2"
version = "0.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-metadata"
version = "0.10.11"
version = "0.10.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2167ce53b2faa16a92c6cafd4942cff16c9a4fa0c5a5a0a41131ee4e49fc055f"
checksum = "d835d67708f6374937c550ad8dd1d17c616ae009e3f00d7a0ac9f7825e78c36a"
dependencies = [
"anyhow",
"indexmap",
@ -415,9 +415,9 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.116.1"
version = "0.118.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50"
checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9"
dependencies = [
"indexmap",
"semver",
@ -425,8 +425,8 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"bitflags",
"wit-bindgen-rust-macro",
@ -434,8 +434,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-core"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"wit-component",
@ -444,8 +444,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust"
version = "0.13.2"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"heck",
@ -456,8 +456,8 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.13.1"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=5390bab780733f1660d14c254ec985df2816bf1d#5390bab780733f1660d14c254ec985df2816bf1d"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"proc-macro2",
@ -470,9 +470,9 @@ dependencies = [
[[package]]
name = "wit-component"
version = "0.17.0"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "480cc1a078b305c1b8510f7c455c76cbd008ee49935f3a6c5fd5e937d8d95b1e"
checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2"
dependencies = [
"anyhow",
"bitflags",
@ -489,9 +489,9 @@ dependencies = [
[[package]]
name = "wit-parser"
version = "0.12.2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43771ee863a16ec4ecf9da0fc65c3bbd4a1235c8e3da5f094b562894843dfa76"
checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3"
dependencies = [
"anyhow",
"id-arena",

View File

@ -14,19 +14,14 @@ lto = true
anyhow = "1.0"
bincode = "1.3.3"
indexmap = "2.1"
serde = {version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "5e1b94a" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "5390bab780733f1660d14c254ec985df2816bf1d" }
uqbar_process_lib = { git = "ssh://git@github.com/uqbar-dao/process_lib.git", rev = "a1dd690" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
[lib]
crate-type = ["cdylib"]
[package.metadata.component]
package = "uqbar:process"
[package.metadata.component.target]
path = "wit"
[package.metadata.component.dependencies]

View File

@ -1,8 +1,10 @@
use indexmap::map::IndexMap;
use uqbar_process_lib::{Address, ProcessId, Request, Response};
use uqbar_process_lib::kernel_types as kt;
use uqbar_process_lib::uqbar::process::standard as wit;
use uqbar_process_lib::{
await_message, call_init, get_capability, println, share_capability, spawn, Address,
Capabilities, Message, OnExit, ProcessId, Request, Response,
};
mod tester_types;
use tester_types as tt;
@ -17,8 +19,8 @@ wit_bindgen::generate!({
type Messages = IndexMap<kt::Message, tt::KernelMessage>;
fn make_vfs_address(our: &wit::Address) -> anyhow::Result<Address> {
Ok(wit::Address {
fn make_vfs_address(our: &Address) -> anyhow::Result<Address> {
Ok(Address {
node: our.node.clone(),
process: ProcessId::from_str("vfs:sys:uqbar")?,
})
@ -29,29 +31,35 @@ fn handle_message(
_messages: &mut Messages,
node_names: &mut Vec<String>,
) -> anyhow::Result<()> {
let (source, message) = wit::receive().unwrap();
println!("handle_message");
let Ok(message) = await_message() else {
return Ok(());
};
match message {
wit::Message::Response((wit::Response { ipc, .. }, _)) => {
Message::Response { source, ipc, .. } => {
match serde_json::from_slice(&ipc)? {
tt::TesterResponse::Pass | tt::TesterResponse::Fail { .. } => {
if (source.process.package_name != "tester")
| (source.process.publisher_node != "uqbar") {
| (source.process.publisher_node != "uqbar")
{
return Err(tt::TesterError::UnexpectedResponse.into());
}
Response::new()
.ipc(ipc)
.send()
.unwrap();
},
tt::TesterResponse::GetFullMessage(_) => { unimplemented!() }
Response::new().ipc(ipc).send().unwrap();
}
tt::TesterResponse::GetFullMessage(_) => {
unimplemented!()
}
}
Ok(())
},
wit::Message::Request(wit::Request { ipc, .. }) => {
}
Message::Request { source, ipc, .. } => {
match serde_json::from_slice(&ipc)? {
tt::TesterRequest::Run(input_node_names) => {
wit::print_to_terminal(0, "tester: got Run");
tt::TesterRequest::Run {
input_node_names,
test_timeout,
} => {
println!("tester: got Run");
assert!(input_node_names.len() >= 1);
*node_names = input_node_names.clone();
@ -63,17 +71,17 @@ fn handle_message(
.unwrap();
} else {
// we are master node
let child = "/test_runner.wasm";
let child_process_id = match wit::spawn(
let child = "/tester:uqbar/pkg/test_runner.wasm";
let child_process_id = match spawn(
None,
child,
&wit::OnPanic::None, // TODO: notify us
&wit::Capabilities::All,
OnExit::None, // TODO: notify us
&Capabilities::All,
false, // not public
) {
Ok(child_process_id) => child_process_id,
Err(e) => {
wit::print_to_terminal(0, &format!("couldn't spawn {}: {}", child, e));
println!("couldn't spawn {}: {}", child, e);
panic!("couldn't spawn"); // TODO
}
};
@ -83,48 +91,48 @@ fn handle_message(
process: child_process_id,
})
.ipc(ipc)
.expects_response(15)
.expects_response(test_timeout)
.send()?;
}
},
tt::TesterRequest::KernelMessage(_) | tt::TesterRequest::GetFullMessage(_) => { unimplemented!() },
}
tt::TesterRequest::KernelMessage(_) | tt::TesterRequest::GetFullMessage(_) => {
unimplemented!()
}
}
Ok(())
},
}
}
struct Component;
impl Guest for Component {
fn init(our: String) {
wit::print_to_terminal(0, "tester: begin");
let our = Address::from_str(&our).unwrap();
let mut messages: Messages = IndexMap::new();
let mut node_names: Vec<String> = Vec::new();
// orchestrate tests using external scripts
// -> must give drive cap to rpc
let drive_cap = wit::get_capability(
&make_vfs_address(&our).unwrap(),
&serde_json::to_string(&serde_json::json!({
"kind": "write",
"drive": "tester:uqbar",
})).unwrap()
).unwrap();
wit::share_capability(&ProcessId::from_str("http_server:sys:uqbar").unwrap(), &drive_cap);
loop {
match handle_message(&our, &mut messages, &mut node_names) {
Ok(()) => {},
Err(e) => {
wit::print_to_terminal(0, format!(
"tester: error: {:?}",
e,
).as_str());
fail!("tester");
},
};
}
}
}
call_init!(init);
fn init(our: Address) {
println!("tester: begin");
let mut messages: Messages = IndexMap::new();
let mut node_names: Vec<String> = Vec::new();
// orchestrate tests using external scripts
// -> must give drive cap to rpc
let drive_cap = get_capability(
&make_vfs_address(&our).unwrap(),
&serde_json::to_string(&serde_json::json!({
"kind": "write",
"drive": "/tester:uqbar/pkg",
}))
.expect("couldn't serialize"),
)
.expect("couldn't get drive cap");
share_capability(
&ProcessId::from_str("http_server:sys:uqbar").expect("couldn't make pid"),
&drive_cap,
);
loop {
match handle_message(&our, &mut messages, &mut node_names) {
Ok(()) => {}
Err(e) => {
println!("tester: error: {:?}", e,);
}
};
}
}

View File

@ -19,7 +19,7 @@ pub struct KernelMessage {
#[derive(Debug, Serialize, Deserialize)]
pub enum TesterRequest {
Run(Vec<String>),
Run { input_node_names: Vec<String>, test_timeout: u64 },
KernelMessage(KernelMessage),
GetFullMessage(kt::Message),
}
@ -61,6 +61,7 @@ macro_rules! fail {
}).unwrap())
.send()
.unwrap();
panic!("")
};
($test:expr, $file:expr, $line:expr, $column:expr) => {
Response::new()
@ -72,6 +73,6 @@ macro_rules! fail {
}).unwrap())
.send()
.unwrap();
panic!("");
panic!("")
};
}

View File

@ -280,7 +280,7 @@ fn make_error_message(our_name: String, km: &KernelMessage, error: EthRpcError)
id: km.id,
source: Address {
node: our_name.clone(),
process: FILESYSTEM_PROCESS_ID.clone(),
process: ETH_RPC_PROCESS_ID.clone(),
},
target: match &km.rsvp {
None => km.source.clone(),

File diff suppressed because it is too large Load Diff

View File

@ -1,820 +0,0 @@
use crate::filesystem::manifest::{FileIdentifier, Manifest};
use crate::types::*;
/// log structured filesystem
use anyhow::Result;
use std::collections::{HashMap, HashSet, VecDeque};
use std::io::Read;
use std::sync::Arc;
use tokio::fs;
use tokio::sync::oneshot::{Receiver, Sender};
use tokio::sync::Mutex;
use tokio::time::{interval, Duration};
mod manifest;
pub async fn load_fs(
our_name: String,
home_directory_path: String,
file_key: Vec<u8>,
fs_config: FsConfig,
runtime_extensions: Vec<(ProcessId, MessageSender, bool)>,
) -> Result<(ProcessMap, Manifest, Vec<KernelMessage>), FsError> {
// load/create fs directory, manifest + log if none.
let fs_directory_path_str = format!("{}/fs", &home_directory_path);
if let Err(e) = fs::create_dir_all(&fs_directory_path_str).await {
panic!("failed creating fs dir! {:?}", e);
}
let fs_directory_path: std::path::PathBuf =
fs::canonicalize(fs_directory_path_str).await.unwrap();
// open and load manifest+log
let manifest_path = fs_directory_path.join("manifest.bin");
let manifest_file = fs::OpenOptions::new()
.append(true)
.read(true)
.create(true)
.open(&manifest_path)
.await
.expect("fs: failed to open manifest file");
let wal_path = fs_directory_path.join("wal.bin");
let wal_file = fs::OpenOptions::new()
.append(true)
.read(true)
.create(true)
.open(&wal_path)
.await
.expect("fs: failed to open WAL file");
// in memory details about files.
let mut manifest = Manifest::load(
manifest_file,
wal_file,
&fs_directory_path,
file_key,
fs_config,
)
.await
.expect("manifest load failed!");
// get kernel state for booting up
let kernel_process_id = FileIdentifier::Process(KERNEL_PROCESS_ID.clone());
let mut process_map: ProcessMap = HashMap::new();
// get current processes' wasm_bytes handles. GetState(kernel)
let vfs_messages = match manifest.read(&kernel_process_id, None, None).await {
Err(_) => {
// bootstrap filesystem
bootstrap(
&our_name,
&kernel_process_id,
runtime_extensions,
&mut process_map,
&mut manifest,
)
.await
.expect("fresh bootstrap failed!")
}
Ok(bytes) => {
process_map = bincode::deserialize(&bytes).expect("state map deserialization error!");
vec![]
}
};
Ok((process_map, manifest, vfs_messages))
}
/// function run only upon fresh boot.
///
/// for each folder in /modules, looks for a package.zip file, extracts the contents,
/// sends the contents to VFS, and reads the manifest.json.
///
/// the manifest.json contains instructions for which processes to boot and what
/// capabilities to give them. since we are inside runtime, can spawn those out of
/// thin air.
async fn bootstrap(
our_name: &str,
kernel_process_id: &FileIdentifier,
runtime_extensions: Vec<(ProcessId, MessageSender, bool)>,
process_map: &mut ProcessMap,
manifest: &mut Manifest,
) -> Result<Vec<KernelMessage>> {
println!("bootstrapping node...\r");
let mut runtime_caps: HashSet<Capability> = HashSet::new();
// kernel is a special case
runtime_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: ProcessId::from_str("kernel:sys:uqbar").unwrap(),
},
params: "\"messaging\"".into(),
});
// net is a special case
runtime_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: ProcessId::from_str("net:sys:uqbar").unwrap(),
},
params: "\"messaging\"".into(),
});
for runtime_module in runtime_extensions.clone() {
runtime_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: runtime_module.0,
},
params: "\"messaging\"".into(),
});
}
// give all runtime processes the ability to send messages across the network
runtime_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: KERNEL_PROCESS_ID.clone(),
},
params: "\"network\"".into(),
});
// finally, save runtime modules in state map as well, somewhat fakely
// special cases for kernel and net
process_map
.entry(ProcessId::from_str("kernel:sys:uqbar").unwrap())
.or_insert(PersistedProcess {
wasm_bytes_handle: 0,
on_panic: OnPanic::Restart,
capabilities: runtime_caps.clone(),
public: false,
});
process_map
.entry(ProcessId::from_str("net:sys:uqbar").unwrap())
.or_insert(PersistedProcess {
wasm_bytes_handle: 0,
on_panic: OnPanic::Restart,
capabilities: runtime_caps.clone(),
public: false,
});
for runtime_module in runtime_extensions {
process_map
.entry(runtime_module.0)
.or_insert(PersistedProcess {
wasm_bytes_handle: 0,
on_panic: OnPanic::Restart,
capabilities: runtime_caps.clone(),
public: runtime_module.2,
});
}
let packages: Vec<(String, zip::ZipArchive<std::io::Cursor<Vec<u8>>>)> =
get_zipped_packages().await;
let mut vfs_messages = Vec::new();
for (package_name, mut package) in packages {
// special case tester: only load it in if in simulation mode
if package_name == "tester" {
#[cfg(not(feature = "simulation-mode"))]
continue;
#[cfg(feature = "simulation-mode")]
{}
}
println!("fs: handling package {package_name}...\r");
// get and read metadata.json
let Ok(mut package_metadata_zip) = package.by_name("metadata.json") else {
println!(
"fs: missing metadata for package {}, skipping",
package_name
);
continue;
};
let mut metadata_content = Vec::new();
package_metadata_zip
.read_to_end(&mut metadata_content)
.unwrap();
drop(package_metadata_zip);
let package_metadata: serde_json::Value =
serde_json::from_slice(&metadata_content).expect("fs: metadata parse error");
println!("fs: found package metadata: {:?}\r", package_metadata);
let package_name = package_metadata["package"]
.as_str()
.expect("fs: metadata parse error: bad package name");
let package_publisher = package_metadata["publisher"]
.as_str()
.expect("fs: metadata parse error: bad publisher name");
// create a new package in VFS
let our_drive_name = [package_name, package_publisher].join(":");
vfs_messages.push(KernelMessage {
id: rand::random(),
source: Address {
node: our_name.to_string(),
process: FILESYSTEM_PROCESS_ID.clone(),
},
target: Address {
node: our_name.to_string(),
process: VFS_PROCESS_ID.clone(),
},
rsvp: None,
message: Message::Request(Request {
inherit: false,
expects_response: None,
ipc: serde_json::to_vec::<VfsRequest>(&VfsRequest {
drive: our_drive_name.clone(),
action: VfsAction::New,
})
.unwrap(),
metadata: None,
}),
payload: None,
signed_capabilities: None,
});
// for each file in package.zip, recursively through all dirs, send a newfile KM to VFS
for i in 0..package.len() {
let mut file = package.by_index(i).unwrap();
if file.is_file() {
let file_path = file
.enclosed_name()
.expect("fs: name error reading package.zip")
.to_owned();
let mut file_path = file_path.to_string_lossy().to_string();
if !file_path.starts_with('/') {
file_path = format!("/{}", file_path);
}
println!("fs: found file {}...\r", file_path);
let mut file_content = Vec::new();
file.read_to_end(&mut file_content).unwrap();
vfs_messages.push(KernelMessage {
id: rand::random(),
source: Address {
node: our_name.to_string(),
process: FILESYSTEM_PROCESS_ID.clone(),
},
target: Address {
node: our_name.to_string(),
process: VFS_PROCESS_ID.clone(),
},
rsvp: None,
message: Message::Request(Request {
inherit: false,
expects_response: None,
ipc: serde_json::to_vec::<VfsRequest>(&VfsRequest {
drive: our_drive_name.clone(),
action: VfsAction::Add {
full_path: file_path,
entry_type: AddEntryType::NewFile,
},
})
.unwrap(),
metadata: None,
}),
payload: Some(Payload {
mime: None,
bytes: file_content,
}),
signed_capabilities: None,
});
}
}
// get and read manifest.json
let Ok(mut package_manifest_zip) = package.by_name("manifest.json") else {
println!(
"fs: missing manifest for package {}, skipping",
package_name
);
continue;
};
let mut manifest_content = Vec::new();
package_manifest_zip
.read_to_end(&mut manifest_content)
.unwrap();
drop(package_manifest_zip);
let package_manifest = String::from_utf8(manifest_content)?;
let package_manifest = serde_json::from_str::<Vec<PackageManifestEntry>>(&package_manifest)
.expect("fs: manifest parse error");
// for each process-entry in manifest.json:
for mut entry in package_manifest {
let wasm_bytes = &mut Vec::new();
let mut file_path = entry.process_wasm_path.to_string();
if file_path.starts_with('/') {
file_path = file_path[1..].to_string();
}
package
.by_name(&file_path)
.expect("fs: no wasm found in package!")
.read_to_end(wasm_bytes)
.unwrap();
// spawn the requested capabilities
// remember: out of thin air, because this is the root distro
let mut requested_caps = HashSet::new();
let our_process_id = format!(
"{}:{}:{}",
entry.process_name, package_name, package_publisher
);
entry.request_messaging = Some(entry.request_messaging.unwrap_or_default());
if let Some(ref mut request_messaging) = entry.request_messaging {
request_messaging.push(our_process_id.clone());
for process_name in request_messaging {
requested_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: ProcessId::from_str(process_name).unwrap(),
},
params: "\"messaging\"".into(),
});
}
}
if entry.request_networking {
requested_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: KERNEL_PROCESS_ID.clone(),
},
params: "\"network\"".into(),
});
}
// give access to package_name vfs
requested_caps.insert(Capability {
issuer: Address {
node: our_name.into(),
process: VFS_PROCESS_ID.clone(),
},
params: serde_json::to_string(&serde_json::json!({
"kind": "read",
"drive": our_drive_name,
}))
.unwrap(),
});
requested_caps.insert(Capability {
issuer: Address {
node: our_name.into(),
process: VFS_PROCESS_ID.clone(),
},
params: serde_json::to_string(&serde_json::json!({
"kind": "write",
"drive": our_drive_name,
}))
.unwrap(),
});
let public_process = entry.public;
// save in process map
let file = FileIdentifier::new_uuid();
manifest.write(&file, wasm_bytes).await.unwrap();
let wasm_bytes_handle = file.to_uuid().unwrap();
process_map.insert(
ProcessId::new(Some(&entry.process_name), package_name, package_publisher),
PersistedProcess {
wasm_bytes_handle,
on_panic: entry.on_panic,
capabilities: requested_caps,
public: public_process,
},
);
}
}
// save kernel process state. FsAction::SetState(kernel)
let serialized_process_map =
bincode::serialize(&process_map).expect("state map serialization error!");
let process_map_hash: [u8; 32] = hash_bytes(&serialized_process_map);
if manifest.get_by_hash(&process_map_hash).await.is_none() {
let _ = manifest
.write(kernel_process_id, &serialized_process_map)
.await;
}
Ok(vfs_messages)
}
/// go into /target folder and get all .zip package files
async fn get_zipped_packages() -> Vec<(String, zip::ZipArchive<std::io::Cursor<Vec<u8>>>)> {
println!("fs: reading distro packages...\r");
let target_path = std::path::Path::new("target");
let mut packages = Vec::new();
if let Ok(mut entries) = fs::read_dir(target_path).await {
while let Ok(Some(entry)) = entries.next_entry().await {
if entry.file_name().to_string_lossy().ends_with(".zip") {
let package_name = entry
.file_name()
.to_string_lossy()
.trim_end_matches(".zip")
.to_string();
if let Ok(bytes) = fs::read(entry.path()).await {
if let Ok(zip) = zip::ZipArchive::new(std::io::Cursor::new(bytes)) {
// add to list of packages
println!("fs: found package: {}\r", package_name);
packages.push((package_name, zip));
}
}
}
}
}
packages
}
pub async fn fs_sender(
our_name: String,
manifest: Manifest,
send_to_loop: MessageSender,
send_to_terminal: PrintSender,
mut recv_in_fs: MessageReceiver,
mut recv_kill: Receiver<()>,
send_kill_confirm: Sender<()>,
) -> Result<()> {
// process queues for consistency
// todo: use file_drive for moar concurrency!
let process_queues = Arc::new(Mutex::new(
HashMap::<ProcessId, VecDeque<KernelMessage>>::new(),
));
// interval for deleting/(flushing), don't want to run immediately upon bootup.
// -> separate wal_flush and cold_flush
let mut interval = interval(Duration::from_secs(manifest.flush_cold_freq as u64));
let mut first_open = true;
// into main loop
loop {
tokio::select! {
Some(kernel_message) = recv_in_fs.recv() => {
if our_name != kernel_message.source.node {
println!(
"fs: request must come from our_name={}, got: {}",
our_name, &kernel_message,
);
continue;
}
// internal structures have Arc::clone setup.
let manifest_clone = manifest.clone();
let our_name = our_name.clone();
let mut source = kernel_message.source.clone();
let send_to_loop = send_to_loop.clone();
let send_to_terminal = send_to_terminal.clone();
let mut process_lock = process_queues.lock().await;
// optimization for get/set_state queues.
update_src_from_kernel_metadata(&mut source, &kernel_message);
if let Some(queue) = process_lock.get_mut(&source.process) {
queue.push_back(kernel_message.clone());
} else {
let mut new_queue = VecDeque::new();
new_queue.push_back(kernel_message.clone());
process_lock.insert(source.process.clone(), new_queue);
// clone Arc for thread
let process_lock_clone = process_queues.clone();
tokio::spawn(async move {
let mut process_lock = process_lock_clone.lock().await;
while let Some(km) = process_lock.get_mut(&source.process).and_then(|q| q.pop_front()) {
if let Err(e) = handle_request(
our_name.clone(),
km.clone(),
manifest_clone.clone(),
send_to_loop.clone(),
send_to_terminal.clone(),
)
.await
{
let _ = send_to_loop
.send(make_error_message(our_name.clone(), &km, e))
.await;
}
}
// Remove the process entry if no more tasks are left
if let Some(queue) = process_lock.get(&source.process) {
if queue.is_empty() {
process_lock.remove(&source.process);
}
}
});
}
}
_ = interval.tick() => {
if !first_open {
let manifest_clone = manifest.clone();
tokio::spawn(async move {
let _ = manifest_clone.flush_to_cold().await;
// let _ = manifest_clone.cleanup().await;
});
}
first_open = false;
}
_ = &mut recv_kill => {
let manifest_clone = manifest.clone();
let _ = manifest_clone.flush_to_wal_main().await;
let _ = send_kill_confirm.send(());
return Ok(());
}
}
}
}
async fn handle_request(
our_name: String,
kernel_message: KernelMessage,
manifest: Manifest,
send_to_loop: MessageSender,
_send_to_terminal: PrintSender,
) -> Result<(), FsError> {
let KernelMessage {
id,
source,
rsvp,
message,
payload,
..
} = kernel_message;
let Message::Request(Request {
expects_response,
ipc,
metadata, // for kernel
..
}) = message
else {
return Err(FsError::NoJson);
};
let action: FsAction = match serde_json::from_slice(&ipc) {
Ok(r) => r,
Err(e) => {
return Err(FsError::BadJson {
json: String::from_utf8(ipc).unwrap_or_default(),
error: format!("parse failed: {:?}", e),
})
}
};
// println!("got action! {:?}", action);
let (ipc, bytes) = match action {
FsAction::Write(maybe_file_id) => {
let Some(ref payload) = payload else {
return Err(FsError::BadBytes {
action: "Write".into(),
});
};
let file_uuid = match maybe_file_id {
Some(id) => FileIdentifier::Uuid(id),
None => FileIdentifier::new_uuid(),
};
match manifest.write(&file_uuid, &payload.bytes).await {
Ok(_) => (),
Err(e) => {
return Err(FsError::WriteFailed {
file: file_uuid.to_uuid().unwrap_or_default(),
error: e.to_string(),
})
}
}
(FsResponse::Write(file_uuid.to_uuid().unwrap()), None)
}
FsAction::WriteOffset((file_uuid, offset)) => {
let Some(ref payload) = payload else {
return Err(FsError::BadBytes {
action: "Write".into(),
});
};
let file_uuid = FileIdentifier::Uuid(file_uuid);
match manifest.write_at(&file_uuid, offset, &payload.bytes).await {
Ok(_) => (),
Err(e) => {
return Err(FsError::WriteFailed {
file: file_uuid.to_uuid().unwrap_or_default(),
error: format!("write_offset error: {}", e),
})
}
}
(FsResponse::Write(file_uuid.to_uuid().unwrap()), None)
}
FsAction::Read(file_uuid) => {
let file = FileIdentifier::Uuid(file_uuid);
match manifest.read(&file, None, None).await {
Err(e) => {
return Err(FsError::ReadFailed {
file: file.to_uuid().unwrap_or_default(),
error: e.to_string(),
})
}
Ok(bytes) => (FsResponse::Read(file_uuid), Some(bytes)),
}
}
FsAction::ReadChunk(req) => {
let file = FileIdentifier::Uuid(req.file);
match manifest
.read(&file, Some(req.start), Some(req.length))
.await
{
Err(e) => {
return Err(FsError::ReadFailed {
file: file.to_uuid().unwrap_or_default(),
error: e.to_string(),
})
}
Ok(bytes) => (FsResponse::Read(req.file), Some(bytes)),
}
}
FsAction::Delete(del) => {
let file = FileIdentifier::Uuid(del);
manifest.delete(&file).await?;
(FsResponse::Delete(del), None)
}
FsAction::Append(maybe_file_uuid) => {
let Some(ref payload) = payload else {
return Err(FsError::BadBytes {
action: "Append".into(),
});
};
let file_uuid = match maybe_file_uuid {
Some(uuid) => FileIdentifier::Uuid(uuid),
None => FileIdentifier::new_uuid(),
};
match manifest.append(&file_uuid, &payload.bytes).await {
Ok(_) => (),
Err(e) => {
return Err(FsError::WriteFailed {
file: file_uuid.to_uuid().unwrap_or_default(),
error: format!("append error: {}", e),
})
}
};
// note expecting file_uuid here, if we want process state to access append, we would change this.
(FsResponse::Append(file_uuid.to_uuid().unwrap()), None)
}
FsAction::Length(file_uuid) => {
let file = FileIdentifier::Uuid(file_uuid);
let length = manifest.get_length(&file).await;
match length {
Some(len) => (FsResponse::Length(len), None),
None => {
return Err(FsError::LengthError {
error: format!("file not found: {:?}", file_uuid),
})
}
}
}
FsAction::SetLength((file_uuid, length)) => {
let file = FileIdentifier::Uuid(file_uuid);
manifest.set_length(&file, length).await?;
// doublecheck if this is the type of return statement we want.
(FsResponse::Length(length), None)
}
// process state handlers
FsAction::SetState(process_id) => {
let Some(ref payload) = payload else {
return Err(FsError::BadBytes {
action: "SetState".into(),
});
};
// println!("setting state for process {:?} with len {:?}", process_id, &payload.bytes.len());
let file = FileIdentifier::Process(process_id);
match manifest.write(&file, &payload.bytes).await {
Ok(_) => (),
Err(e) => {
return Err(FsError::WriteFailed {
file: file.to_uuid().unwrap_or_default(),
error: format!("SetState error: {}", e),
})
}
};
(FsResponse::SetState, None)
}
FsAction::DeleteState(process_id) => {
let file = FileIdentifier::Process(process_id);
manifest.delete(&file).await?;
(FsResponse::Delete(0), None)
}
FsAction::GetState(process_id) => {
let file = FileIdentifier::Process(process_id);
match manifest.read(&file, None, None).await {
Err(e) => return Err(e),
Ok(bytes) => (FsResponse::GetState, Some(bytes)),
}
}
};
if expects_response.is_some() {
let response = KernelMessage {
id,
source: Address {
node: our_name.clone(),
process: FILESYSTEM_PROCESS_ID.clone(),
},
target: match rsvp {
None => source,
Some(rsvp) => rsvp,
},
rsvp: None,
message: Message::Response((
Response {
inherit: false,
ipc: serde_json::to_vec::<Result<FsResponse, FsError>>(&Ok(ipc)).unwrap(),
metadata, // for kernel
},
None,
)),
payload: bytes.map(|bytes| Payload { mime: None, bytes }),
signed_capabilities: None,
};
let _ = send_to_loop.send(response).await;
}
Ok(())
}
/// HELPERS
pub fn hash_bytes(bytes: &[u8]) -> [u8; 32] {
let mut hasher = blake3::Hasher::new();
let chunk_size: usize = 1024 * 256;
for chunk in bytes.chunks(chunk_size) {
let chunk_hash: [u8; 32] = blake3::hash(chunk).into();
hasher.update(&chunk_hash);
}
hasher.finalize().into()
}
fn update_src_from_kernel_metadata(source: &mut Address, kernel_message: &KernelMessage) {
if kernel_message.source.process == *KERNEL_PROCESS_ID {
if let Message::Request(request) = &kernel_message.message {
if let Some(process_id_str) = &request.metadata {
if let Ok(process_id) = ProcessId::from_str(process_id_str) {
source.process = process_id;
}
}
} else if let Message::Response((response, _)) = &kernel_message.message {
if let Some(process_id_str) = &response.metadata {
if let Ok(process_id) = ProcessId::from_str(process_id_str) {
source.process = process_id;
}
}
}
}
}
fn make_error_message(our_name: String, km: &KernelMessage, error: FsError) -> KernelMessage {
KernelMessage {
id: km.id,
source: Address {
node: our_name.clone(),
process: FILESYSTEM_PROCESS_ID.clone(),
},
target: match &km.rsvp {
None => km.source.clone(),
Some(rsvp) => rsvp.clone(),
},
rsvp: None,
message: Message::Response((
Response {
inherit: false,
ipc: serde_json::to_vec::<Result<FsResponse, FsError>>(&Err(error)).unwrap(),
metadata: None,
},
None,
)),
payload: None,
signed_capabilities: None,
}
}

View File

@ -136,38 +136,36 @@ async fn handle_message(
match client.execute(request).await {
Ok(response) => {
if expects_response.is_some() {
let _ = send_to_loop
.send(KernelMessage {
id,
source: Address {
node: our.to_string(),
process: ProcessId::new(Some("http_client"), "sys", "uqbar"),
let _ = send_to_loop
.send(KernelMessage {
id,
source: Address {
node: our.to_string(),
process: ProcessId::new(Some("http_client"), "sys", "uqbar"),
},
target,
rsvp: None,
message: Message::Response((
Response {
inherit: false,
ipc: serde_json::to_vec::<Result<HttpResponse, HttpClientError>>(&Ok(
HttpResponse {
status: response.status().as_u16(),
headers: serialize_headers(response.headers()),
},
))
.unwrap(),
metadata: None,
},
target,
rsvp: None,
message: Message::Response((
Response {
inherit: false,
ipc: serde_json::to_vec::<Result<HttpResponse, HttpClientError>>(
&Ok(HttpResponse {
status: response.status().as_u16(),
headers: serialize_headers(response.headers()),
}),
)
.unwrap(),
metadata: None,
},
None,
)),
payload: Some(Payload {
mime: None,
bytes: response.bytes().await.unwrap_or_default().to_vec(),
}),
signed_capabilities: None,
})
.await;
}
None,
)),
payload: Some(Payload {
mime: None,
bytes: response.bytes().await.unwrap_or_default().to_vec(),
}),
signed_capabilities: None,
})
.await;
}
Err(e) => {
make_error_message(

298
src/http/login.html Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,13 @@
use crate::http::types::*;
use crate::http::utils::*;
use crate::register;
use crate::types::*;
use crate::{keygen, register};
use anyhow::Result;
use dashmap::DashMap;
use futures::{SinkExt, StreamExt};
use http::uri::Authority;
use route_recognizer::Router;
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::Arc;
@ -14,7 +16,12 @@ use warp::http::{header::HeaderValue, StatusCode};
use warp::ws::{WebSocket, Ws};
use warp::{Filter, Reply};
#[cfg(not(feature = "simulation-mode"))]
const HTTP_SELF_IMPOSED_TIMEOUT: u64 = 15;
#[cfg(feature = "simulation-mode")]
const HTTP_SELF_IMPOSED_TIMEOUT: u64 = 600;
const LOGIN_HTML: &str = include_str!("login.html");
/// mapping from a given HTTP request (assigned an ID) to the oneshot
/// channel that will get a response from the app that handles the request,
@ -29,14 +36,23 @@ type WebSocketSenders = Arc<DashMap<u32, (ProcessId, WebSocketSender)>>;
type WebSocketSender = tokio::sync::mpsc::Sender<warp::ws::Message>;
type PathBindings = Arc<RwLock<Router<BoundPath>>>;
type WsPathBindings = Arc<RwLock<Router<BoundWsPath>>>;
struct BoundPath {
pub app: ProcessId,
pub secure_subdomain: Option<String>,
pub authenticated: bool,
pub local_only: bool,
pub static_content: Option<Payload>, // TODO store in filesystem and cache
}
struct BoundWsPath {
pub app: ProcessId,
pub secure_subdomain: Option<String>,
pub authenticated: bool,
pub encrypted: bool, // TODO use
}
/// HTTP server: a runtime module that handles HTTP requests at a given port.
/// The server accepts bindings-requests from apps. These can be used in two ways:
///
@ -55,34 +71,41 @@ struct BoundPath {
pub async fn http_server(
our_name: String,
our_port: u16,
encoded_keyfile: Vec<u8>,
jwt_secret_bytes: Vec<u8>,
mut recv_in_server: MessageReceiver,
send_to_loop: MessageSender,
print_tx: PrintSender,
) -> Result<()> {
let our_name = Arc::new(our_name);
let encoded_keyfile = Arc::new(encoded_keyfile);
let jwt_secret_bytes = Arc::new(jwt_secret_bytes);
let http_response_senders: HttpResponseSenders = Arc::new(DashMap::new());
let ws_senders: WebSocketSenders = Arc::new(DashMap::new());
// Add RPC path
// add RPC path
let mut bindings_map: Router<BoundPath> = Router::new();
let rpc_bound_path = BoundPath {
app: ProcessId::from_str("rpc:sys:uqbar").unwrap(),
secure_subdomain: None, // TODO maybe RPC should have subdomain?
authenticated: false,
local_only: true,
static_content: None,
};
bindings_map.add("/rpc:sys:uqbar/message", rpc_bound_path);
let path_bindings: PathBindings = Arc::new(RwLock::new(bindings_map));
// ws path bindings
let ws_path_bindings: WsPathBindings = Arc::new(RwLock::new(Router::new()));
tokio::spawn(serve(
our_name.clone(),
our_port,
http_response_senders.clone(),
path_bindings.clone(),
ws_path_bindings.clone(),
ws_senders.clone(),
encoded_keyfile.clone(),
jwt_secret_bytes.clone(),
send_to_loop.clone(),
print_tx.clone(),
@ -94,10 +117,9 @@ pub async fn http_server(
km,
http_response_senders.clone(),
path_bindings.clone(),
ws_path_bindings.clone(),
ws_senders.clone(),
jwt_secret_bytes.clone(),
send_to_loop.clone(),
print_tx.clone(),
)
.await;
}
@ -111,7 +133,9 @@ async fn serve(
our_port: u16,
http_response_senders: HttpResponseSenders,
path_bindings: PathBindings,
ws_path_bindings: WsPathBindings,
ws_senders: WebSocketSenders,
encoded_keyfile: Arc<Vec<u8>>,
jwt_secret_bytes: Arc<Vec<u8>>,
send_to_loop: MessageSender,
print_tx: PrintSender,
@ -123,42 +147,42 @@ async fn serve(
})
.await;
// Filter to receive websockets
// filter to receive websockets
let cloned_msg_tx = send_to_loop.clone();
let cloned_our = our.clone();
let cloned_jwt_secret_bytes = jwt_secret_bytes.clone();
let cloned_print_tx = print_tx.clone();
let ws_route = warp::path::end()
.and(warp::ws())
let ws_route = warp::ws()
.and(warp::path::full())
.and(warp::filters::host::optional())
.and(warp::filters::header::headers_cloned())
.and(warp::any().map(move || cloned_our.clone()))
.and(warp::any().map(move || cloned_jwt_secret_bytes.clone()))
.and(warp::any().map(move || ws_senders.clone()))
.and(warp::any().map(move || ws_path_bindings.clone()))
.and(warp::any().map(move || cloned_msg_tx.clone()))
.and(warp::any().map(move || cloned_print_tx.clone()))
.map(
|ws_connection: Ws,
our: Arc<String>,
jwt_secret_bytes: Arc<Vec<u8>>,
ws_senders: WebSocketSenders,
send_to_loop: MessageSender,
print_tx: PrintSender| {
ws_connection.on_upgrade(move |ws: WebSocket| async move {
maintain_websocket(
ws,
our,
jwt_secret_bytes,
ws_senders,
send_to_loop,
print_tx,
)
.await
})
},
);
// Filter to receive HTTP requests
.and_then(ws_handler);
// filter to receive and handle login requests
let cloned_our = our.clone();
let login = warp::path("login").and(warp::path::end()).and(
warp::get()
.map(|| warp::reply::with_status(warp::reply::html(LOGIN_HTML), StatusCode::OK))
.or(warp::post()
.and(warp::body::content_length_limit(1024 * 16))
.and(warp::body::json())
.and(warp::any().map(move || cloned_our.clone()))
.and(warp::any().map(move || encoded_keyfile.clone()))
.and_then(login_handler)),
);
// filter to receive all other HTTP requests
let filter = warp::filters::method::method()
.and(warp::addr::remote())
.and(warp::filters::host::optional())
.and(warp::path::full())
.and(warp::query::<HashMap<String, String>>())
.and(warp::filters::header::headers_cloned())
.and(warp::filters::body::bytes())
.and(warp::any().map(move || our.clone()))
@ -166,18 +190,148 @@ async fn serve(
.and(warp::any().map(move || path_bindings.clone()))
.and(warp::any().map(move || jwt_secret_bytes.clone()))
.and(warp::any().map(move || send_to_loop.clone()))
.and(warp::any().map(move || print_tx.clone()))
.and_then(http_handler);
let filter_with_ws = ws_route.or(filter);
let filter_with_ws = ws_route.or(login).or(filter);
warp::serve(filter_with_ws)
.run(([0, 0, 0, 0], our_port))
.await;
}
/// handle non-GET requests on /login. if POST, validate password
/// and return auth token, which will be stored in a cookie.
/// then redirect to wherever they were trying to go.
async fn login_handler(
info: LoginInfo,
our: Arc<String>,
encoded_keyfile: Arc<Vec<u8>>,
) -> Result<impl warp::Reply, warp::Rejection> {
match keygen::decode_keyfile(&encoded_keyfile, &info.password) {
Ok(keyfile) => {
let token = match register::generate_jwt(&keyfile.jwt_secret_bytes, our.as_ref()) {
Some(token) => token,
None => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Failed to generate JWT"),
StatusCode::SERVICE_UNAVAILABLE,
)
.into_response())
}
};
let mut response = warp::reply::with_status(
warp::reply::json(&base64::encode(encoded_keyfile.to_vec())),
StatusCode::FOUND,
)
.into_response();
match HeaderValue::from_str(&format!("uqbar-auth_{}={};", our.as_ref(), &token)) {
Ok(v) => {
response.headers_mut().append(http::header::SET_COOKIE, v);
Ok(response)
}
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Failed to generate Auth JWT"),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response())
}
}
}
Err(_) => Ok(warp::reply::with_status(
warp::reply::json(&"Failed to decode keyfile"),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response()),
}
}
async fn ws_handler(
ws_connection: Ws,
path: warp::path::FullPath,
host: Option<Authority>,
headers: warp::http::HeaderMap,
our: Arc<String>,
jwt_secret_bytes: Arc<Vec<u8>>,
ws_senders: WebSocketSenders,
ws_path_bindings: WsPathBindings,
send_to_loop: MessageSender,
print_tx: PrintSender,
) -> Result<impl warp::Reply, warp::Rejection> {
let original_path = normalize_path(path.as_str());
let _ = print_tx.send(Printout {
verbosity: 1,
content: format!("got ws request for {original_path}"),
});
let serialized_headers = serialize_headers(&headers);
let ws_path_bindings = ws_path_bindings.read().await;
let Ok(route) = ws_path_bindings.recognize(&original_path) else {
return Err(warp::reject::not_found());
};
let bound_path = route.handler();
if let Some(ref subdomain) = bound_path.secure_subdomain {
let _ = print_tx
.send(Printout {
verbosity: 1,
content: format!(
"got request for path {original_path} bound by subdomain {subdomain}"
),
})
.await;
// assert that host matches what this app wants it to be
if host.is_none() {
return Err(warp::reject::not_found());
}
let host = host.as_ref().unwrap();
// parse out subdomain from host (there can only be one)
let request_subdomain = host.host().split('.').next().unwrap_or("");
if request_subdomain != subdomain {
return Err(warp::reject::not_found());
}
}
if bound_path.authenticated {
let Some(auth_token) = serialized_headers.get("cookie") else {
return Err(warp::reject::not_found());
};
if !auth_cookie_valid(&our, &auth_token, &jwt_secret_bytes) {
return Err(warp::reject::not_found());
}
}
let app = bound_path.app.clone();
Ok(ws_connection.on_upgrade(move |ws: WebSocket| async move {
maintain_websocket(
ws,
our.clone(),
app,
// remove process id from beginning of path by splitting into segments
// separated by "/" and taking all but the first
original_path
.split('/')
.skip(1)
.collect::<Vec<&str>>()
.join("/"),
jwt_secret_bytes.clone(),
ws_senders.clone(),
send_to_loop.clone(),
print_tx.clone(),
)
.await;
}))
}
async fn http_handler(
method: warp::http::Method,
socket_addr: Option<SocketAddr>,
host: Option<Authority>,
path: warp::path::FullPath,
query_params: HashMap<String, String>,
headers: warp::http::HeaderMap,
body: warp::hyper::body::Bytes,
our: Arc<String>,
@ -185,11 +339,18 @@ async fn http_handler(
path_bindings: PathBindings,
jwt_secret_bytes: Arc<Vec<u8>>,
send_to_loop: MessageSender,
print_tx: PrintSender,
) -> Result<impl warp::Reply, warp::Rejection> {
// TODO this is all so dirty. Figure out what actually matters.
// trim trailing "/"
let original_path = normalize_path(path.as_str());
let _ = print_tx
.send(Printout {
verbosity: 1,
content: format!("got request for path {original_path}"),
})
.await;
let id: u64 = rand::random();
let serialized_headers = serialize_headers(&headers);
let path_bindings = path_bindings.read().await;
@ -200,11 +361,55 @@ async fn http_handler(
let bound_path = route.handler();
if bound_path.authenticated {
let auth_token = serialized_headers
.get("cookie")
.cloned()
.unwrap_or_default();
if !auth_cookie_valid(&our, &auth_token, &jwt_secret_bytes) {
match serialized_headers.get("cookie") {
Some(auth_token) => {
// they have an auth token, validate
if !auth_cookie_valid(&our, &auth_token, &jwt_secret_bytes) {
return Ok(
warp::reply::with_status(vec![], StatusCode::UNAUTHORIZED).into_response()
);
}
}
None => {
// redirect to login page so they can get an auth token
let _ = print_tx
.send(Printout {
verbosity: 1,
content: format!("redirecting request from {socket_addr:?} to login page"),
})
.await;
return Ok(warp::http::Response::builder()
.status(StatusCode::TEMPORARY_REDIRECT)
.header(
"Location",
format!(
"http://{}/login",
host.unwrap_or(Authority::from_static("localhost"))
),
)
.body(vec![])
.into_response());
}
}
}
if let Some(ref subdomain) = bound_path.secure_subdomain {
let _ = print_tx
.send(Printout {
verbosity: 1,
content: format!(
"got request for path {original_path} bound by subdomain {subdomain}"
),
})
.await;
// assert that host matches what this app wants it to be
if host.is_none() {
return Ok(warp::reply::with_status(vec![], StatusCode::UNAUTHORIZED).into_response());
}
let host = host.as_ref().unwrap();
// parse out subdomain from host (there can only be one)
let request_subdomain = host.host().split('.').next().unwrap_or("");
if request_subdomain != subdomain {
return Ok(warp::reply::with_status(vec![], StatusCode::UNAUTHORIZED).into_response());
}
}
@ -258,14 +463,20 @@ async fn http_handler(
message: Message::Request(Request {
inherit: false,
expects_response: Some(HTTP_SELF_IMPOSED_TIMEOUT),
ipc: serde_json::to_vec(&IncomingHttpRequest {
ipc: serde_json::to_vec(&HttpServerRequest::Http(IncomingHttpRequest {
source_socket_addr: socket_addr.map(|addr| addr.to_string()),
method: method.to_string(),
raw_path: format!("http://localhost{}", original_path),
raw_path: format!(
"http://{}{}",
host.unwrap_or(Authority::from_static("localhost"))
.to_string(),
original_path
),
headers: serialized_headers,
})
query_params,
}))
.unwrap(),
metadata: None,
metadata: Some("http".into()),
}),
payload: Some(Payload {
mime: None,
@ -387,57 +598,25 @@ async fn handle_rpc_message(
async fn maintain_websocket(
ws: WebSocket,
our: Arc<String>,
jwt_secret_bytes: Arc<Vec<u8>>,
app: ProcessId,
path: String,
_jwt_secret_bytes: Arc<Vec<u8>>, // TODO use for encrypted channels
ws_senders: WebSocketSenders,
send_to_loop: MessageSender,
_print_tx: PrintSender,
print_tx: PrintSender,
) {
let (mut write_stream, mut read_stream) = ws.split();
let _ = print_tx
.send(Printout {
verbosity: 1,
content: format!("got new client websocket connection"),
})
.await;
// first, receive a message from client that contains the target process
// and the auth token
let Some(Ok(register_msg)) = read_stream.next().await else {
// stream closed, exit
let stream = write_stream.reunite(read_stream).unwrap();
let _ = stream.close().await;
return;
};
let Ok(ws_register) = serde_json::from_slice::<WsRegister>(register_msg.as_bytes()) else {
// stream error, exit
let stream = write_stream.reunite(read_stream).unwrap();
let _ = stream.close().await;
return;
};
let Ok(owner_process) = ProcessId::from_str(&ws_register.target_process) else {
// invalid process id, exit
let stream = write_stream.reunite(read_stream).unwrap();
let _ = stream.close().await;
return;
};
let Ok(our_name) = verify_auth_token(&ws_register.auth_token, &jwt_secret_bytes) else {
// invalid auth token, exit
let stream = write_stream.reunite(read_stream).unwrap();
let _ = stream.close().await;
return;
};
if our_name != *our {
// invalid auth token, exit
let stream = write_stream.reunite(read_stream).unwrap();
let _ = stream.close().await;
return;
}
let ws_channel_id: u32 = rand::random();
let channel_id: u32 = rand::random();
let (ws_sender, mut ws_receiver) = tokio::sync::mpsc::channel(100);
ws_senders.insert(ws_channel_id, (owner_process.clone(), ws_sender));
ws_senders.insert(channel_id, (app.clone(), ws_sender));
// send a message to the process associated with this channel
// notifying them that the channel is now open
let _ = send_to_loop
.send(KernelMessage {
id: rand::random(),
@ -446,91 +625,69 @@ async fn maintain_websocket(
process: HTTP_SERVER_PROCESS_ID.clone(),
},
target: Address {
node: our.to_string(),
process: owner_process.clone(),
node: our.clone().to_string(),
process: app.clone(),
},
rsvp: None,
message: Message::Request(Request {
inherit: false,
expects_response: None,
ipc: serde_json::to_vec(&HttpServerAction::WebSocketOpen(ws_channel_id)).unwrap(),
metadata: None,
ipc: serde_json::to_vec(&HttpServerRequest::WebSocketOpen { path, channel_id })
.unwrap(),
metadata: Some("ws".into()),
}),
payload: None,
signed_capabilities: None,
})
.await;
// respond to the client notifying them that the channel is now open
let Ok(()) = write_stream
.send(warp::ws::Message::text(
serde_json::to_string(&WsRegisterResponse {
channel_id: ws_channel_id,
})
.unwrap(),
))
.await
else {
// stream error, exit
let stream = write_stream.reunite(read_stream).unwrap();
let _ = stream.close().await;
return;
};
let _ = print_tx.send(Printout {
verbosity: 1,
content: format!("websocket channel {channel_id} opened"),
});
loop {
tokio::select! {
read = read_stream.next() => {
match read {
None => {
// stream closed, remove and exit
websocket_close(ws_channel_id, owner_process, &ws_senders, &send_to_loop).await;
break;
}
Some(Err(_e)) => {
// stream error, remove and exit
websocket_close(ws_channel_id, owner_process, &ws_senders, &send_to_loop).await;
break;
}
Some(Ok(msg)) => {
// forward message to process associated with this channel
let _ = send_to_loop
.send(KernelMessage {
id: rand::random(),
source: Address {
node: our.to_string(),
process: HTTP_SERVER_PROCESS_ID.clone(),
},
target: Address {
node: our.to_string(),
process: owner_process.clone(),
},
rsvp: None,
message: Message::Request(Request {
inherit: false,
expects_response: None,
ipc: serde_json::to_vec(&HttpServerAction::WebSocketPush {
channel_id: ws_channel_id,
message_type: WsMessageType::Binary,
}).unwrap(),
metadata: None,
}),
payload: Some(Payload {
mime: None,
bytes: msg.into_bytes(),
}),
signed_capabilities: None,
})
.await;
let _ = send_to_loop.send(KernelMessage {
id: rand::random(),
source: Address {
node: our.to_string(),
process: HTTP_SERVER_PROCESS_ID.clone(),
},
target: Address {
node: our.to_string(),
process: app.clone(),
},
rsvp: None,
message: Message::Request(Request {
inherit: false,
expects_response: None,
ipc: serde_json::to_vec(&HttpServerRequest::WebSocketPush {
channel_id,
message_type: WsMessageType::Binary,
}).unwrap(),
metadata: Some("ws".into()),
}),
payload: Some(Payload {
mime: None,
bytes: msg.into_bytes(),
}),
signed_capabilities: None,
});
}
_ => {
websocket_close(channel_id, app.clone(), &ws_senders, &send_to_loop).await;
break;
}
}
}
Some(outgoing) = ws_receiver.recv() => {
// forward message to websocket
match write_stream.send(outgoing).await {
Ok(()) => continue,
Err(_e) => {
// stream error, remove and exit
websocket_close(ws_channel_id, owner_process, &ws_senders, &send_to_loop).await;
Err(_) => {
websocket_close(channel_id, app.clone(), &ws_senders, &send_to_loop).await;
break;
}
}
@ -563,8 +720,8 @@ async fn websocket_close(
message: Message::Request(Request {
inherit: false,
expects_response: None,
ipc: serde_json::to_vec(&HttpServerAction::WebSocketClose(channel_id)).unwrap(),
metadata: None,
ipc: serde_json::to_vec(&HttpServerRequest::WebSocketClose(channel_id)).unwrap(),
metadata: Some("ws".into()),
}),
payload: Some(Payload {
mime: None,
@ -583,10 +740,9 @@ async fn handle_app_message(
km: KernelMessage,
http_response_senders: HttpResponseSenders,
path_bindings: PathBindings,
ws_path_bindings: WsPathBindings,
ws_senders: WebSocketSenders,
jwt_secret_bytes: Arc<Vec<u8>>,
send_to_loop: MessageSender,
print_tx: PrintSender,
) {
// when we get a Response, try to match it to an outstanding HTTP
// request and send it there.
@ -618,60 +774,10 @@ async fn handle_app_message(
.unwrap(),
));
} else {
let Ok(mut response) = serde_json::from_slice::<HttpResponse>(&response.ipc) else {
let Ok(response) = serde_json::from_slice::<HttpResponse>(&response.ipc) else {
// the receiver will automatically trigger a 503 when sender is dropped.
return;
};
// XX REFACTOR THIS:
// for the login case, todo refactor out?
let segments: Vec<&str> = path
.split('/')
.filter(|&segment| !segment.is_empty())
.collect();
// If we're getting back a /login from a proxy (or our own node),
// then we should generate a jwt from the secret + the name of the ship,
// and then attach it to a header.
if response.status < 400
&& (segments.len() == 1 || segments.len() == 4)
&& matches!(segments.last(), Some(&"login"))
{
if let Some(auth_cookie) = response.headers.get("set-cookie") {
let mut ws_auth_username = km.source.node.clone();
if segments.len() == 4
&& matches!(segments.first(), Some(&"http-proxy"))
&& matches!(segments.get(1), Some(&"serve"))
{
if let Some(segment) = segments.get(2) {
ws_auth_username = segment.to_string();
}
}
if let Some(token) = register::generate_jwt(
jwt_secret_bytes.to_vec().as_slice(),
ws_auth_username.clone(),
) {
let auth_cookie_with_ws = format!(
"{}; uqbar-ws-auth_{}={};",
auth_cookie,
ws_auth_username.clone(),
token
);
response
.headers
.insert("set-cookie".to_string(), auth_cookie_with_ws);
let _ = print_tx
.send(Printout {
verbosity: 2,
content: format!(
"SET WS AUTH COOKIE WITH USERNAME: {}",
ws_auth_username
),
})
.await;
}
}
}
let _ = sender.send((
HttpResponse {
status: response.status,
@ -722,6 +828,7 @@ async fn handle_app_message(
&normalize_path(&path),
BoundPath {
app: km.source.process.clone(),
secure_subdomain: None,
authenticated,
local_only,
static_content: None,
@ -743,6 +850,7 @@ async fn handle_app_message(
&normalize_path(&path),
BoundPath {
app: km.source.process.clone(),
secure_subdomain: None,
authenticated,
local_only,
static_content: Some(payload),
@ -751,7 +859,99 @@ async fn handle_app_message(
}
send_action_response(km.id, km.source, &send_to_loop, Ok(())).await;
}
HttpServerAction::WebSocketOpen(_) => {
HttpServerAction::SecureBind { path, cache } => {
// the process ID is hashed to generate a unique subdomain
// only the first 32 chars, or 128 bits are used.
// we hash because the process ID can contain many more than
// simply alphanumeric characters that will cause issues as a subdomain.
let process_id_hash =
format!("{:x}", Sha256::digest(km.source.process.to_string()));
let subdomain = process_id_hash.split_at(32).0.to_owned();
let mut path_bindings = path_bindings.write().await;
if !cache {
// trim trailing "/"
path_bindings.add(
&normalize_path(&path),
BoundPath {
app: km.source.process.clone(),
secure_subdomain: Some(subdomain),
authenticated: true,
local_only: false,
static_content: None,
},
);
} else {
let Some(payload) = km.payload else {
send_action_response(
km.id,
km.source,
&send_to_loop,
Err(HttpServerError::NoPayload),
)
.await;
return;
};
// trim trailing "/"
path_bindings.add(
&normalize_path(&path),
BoundPath {
app: km.source.process.clone(),
secure_subdomain: Some(subdomain),
authenticated: true,
local_only: false,
static_content: Some(payload),
},
);
}
send_action_response(km.id, km.source, &send_to_loop, Ok(())).await;
}
HttpServerAction::WebSocketBind {
mut path,
authenticated,
encrypted,
} => {
path = if path.starts_with('/') {
format!("/{}{}", km.source.process, path)
} else {
format!("/{}/{}", km.source.process, path)
};
let mut ws_path_bindings = ws_path_bindings.write().await;
ws_path_bindings.add(
&normalize_path(&path),
BoundWsPath {
app: km.source.process.clone(),
secure_subdomain: None,
authenticated,
encrypted,
},
);
send_action_response(km.id, km.source, &send_to_loop, Ok(())).await;
}
HttpServerAction::WebSocketSecureBind {
mut path,
encrypted,
} => {
path = if path.starts_with('/') {
format!("/{}{}", km.source.process, path)
} else {
format!("/{}/{}", km.source.process, path)
};
let process_id_hash =
format!("{:x}", Sha256::digest(km.source.process.to_string()));
let subdomain = process_id_hash.split_at(32).0.to_owned();
let mut ws_path_bindings = ws_path_bindings.write().await;
ws_path_bindings.add(
&normalize_path(&path),
BoundWsPath {
app: km.source.process.clone(),
secure_subdomain: Some(subdomain),
authenticated: true,
encrypted,
},
);
send_action_response(km.id, km.source, &send_to_loop, Ok(())).await;
}
HttpServerAction::WebSocketOpen { .. } => {
// we cannot receive these, only send them to processes
send_action_response(
km.id,

View File

@ -5,12 +5,35 @@ use thiserror::Error;
/// HTTP Request type that can be shared over WASM boundary to apps.
/// This is the one you receive from the `http_server:sys:uqbar` service.
#[derive(Debug, Serialize, Deserialize)]
pub enum HttpServerRequest {
Http(IncomingHttpRequest),
/// Processes will receive this kind of request when a client connects to them.
/// If a process does not want this websocket open, they should issue a *request*
/// containing a [`type@HttpServerAction::WebSocketClose`] message and this channel ID.
WebSocketOpen {
path: String,
channel_id: u32,
},
/// Processes can both SEND and RECEIVE this kind of request
/// (send as [`type@HttpServerAction::WebSocketPush`]).
/// When received, will contain the message bytes as payload.
WebSocketPush {
channel_id: u32,
message_type: WsMessageType,
},
/// Receiving will indicate that the client closed the socket. Can be sent to close
/// from the server-side, as [`type@HttpServerAction::WebSocketClose`].
WebSocketClose(u32),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct IncomingHttpRequest {
pub source_socket_addr: Option<String>, // will parse to SocketAddr
pub method: String, // will parse to http::Method
pub raw_path: String,
pub headers: HashMap<String, String>,
pub query_params: HashMap<String, String>,
// BODY is stored in the payload, as bytes
}
@ -56,8 +79,8 @@ pub enum HttpClientError {
}
/// Request type sent to `http_server:sys:uqbar` in order to configure it.
/// You can also send [`WebSocketPush`], which allows you to push messages
/// across an existing open WebSocket connection.
/// You can also send [`type@HttpServerAction::WebSocketPush`], which
/// allows you to push messages across an existing open WebSocket connection.
///
/// If a response is expected, all HttpServerActions will return a Response
/// with the shape Result<(), HttpServerActionError> serialized to JSON.
@ -67,23 +90,53 @@ pub enum HttpServerAction {
/// be the static file to serve at this path.
Bind {
path: String,
/// Set whether the HTTP request needs a valid login cookie, AKA, whether
/// the user needs to be logged in to access this path.
authenticated: bool,
/// Set whether requests can be fielded from anywhere, or only the loopback address.
local_only: bool,
/// Set whether to bind the payload statically to this path. That is, take the
/// payload bytes and serve them as the response to any request to this path.
cache: bool,
},
/// SecureBind expects a payload if and only if `cache` is TRUE. The payload should
/// be the static file to serve at this path.
///
/// SecureBind is the same as Bind, except that it forces requests to be made from
/// the unique subdomain of the process that bound the path. These requests are
/// *always* authenticated, and *never* local_only. The purpose of SecureBind is to
/// serve elements of an app frontend or API in an exclusive manner, such that other
/// apps installed on this node cannot access them. Since the subdomain is unique, it
/// will require the user to be logged in separately to the general domain authentication.
SecureBind {
path: String,
/// Set whether to bind the payload statically to this path. That is, take the
/// payload bytes and serve them as the response to any request to this path.
cache: bool,
},
/// Bind a path to receive incoming WebSocket connections.
/// Doesn't need a cache since does not serve assets.
WebSocketBind {
path: String,
authenticated: bool,
encrypted: bool,
},
/// SecureBind is the same as Bind, except that it forces new connections to be made
/// from the unique subdomain of the process that bound the path. These are *always*
/// authenticated. Since the subdomain is unique, it will require the user to be
/// logged in separately to the general domain authentication.
WebSocketSecureBind { path: String, encrypted: bool },
/// Processes will RECEIVE this kind of request when a client connects to them.
/// If a process does not want this websocket open, they should issue a *request*
/// containing a [`enum@HttpServerAction::WebSocketClose`] message and this channel ID.
WebSocketOpen(u32),
/// Processes can both SEND and RECEIVE this kind of request.
/// containing a [`type@HttpServerAction::WebSocketClose`] message and this channel ID.
WebSocketOpen { path: String, channel_id: u32 },
/// When sent, expects a payload containing the WebSocket message bytes to send.
WebSocketPush {
channel_id: u32,
message_type: WsMessageType,
},
/// Processes can both SEND and RECEIVE this kind of request. Sending will
/// close a socket the process controls. Receiving will indicate that the
/// client closed the socket.
/// Sending will close a socket the process controls.
WebSocketClose(u32),
}

View File

@ -21,11 +21,13 @@ pub struct RpcMessage {
}
/// Ingest an auth token given from client and return the node name or an error.
pub fn verify_auth_token(auth_token: &str, jwt_secret: &[u8]) -> Result<String, jwt::Error> {
pub fn _verify_auth_token(auth_token: &str, jwt_secret: &[u8]) -> Result<String, jwt::Error> {
let Ok(secret) = Hmac::<Sha256>::new_from_slice(jwt_secret) else {
return Err(jwt::Error::Format);
};
println!("hello\r");
let claims: Result<JwtClaims, jwt::Error> = auth_token.verify_with_key(&secret);
match claims {

View File

@ -1,5 +1,5 @@
use crate::types as t;
use crate::FILESYSTEM_PROCESS_ID;
use crate::types::STATE_PROCESS_ID;
use crate::types::{self as t, VFS_PROCESS_ID};
use crate::KERNEL_PROCESS_ID;
use anyhow::Result;
use ring::signature::{self, KeyPair};
@ -57,13 +57,14 @@ async fn persist_state(
},
target: t::Address {
node: our_name.to_string(),
process: FILESYSTEM_PROCESS_ID.clone(),
process: STATE_PROCESS_ID.clone(),
},
rsvp: None,
message: t::Message::Request(t::Request {
inherit: true,
expects_response: None,
ipc: serde_json::to_vec(&t::FsAction::SetState(KERNEL_PROCESS_ID.clone())).unwrap(),
ipc: serde_json::to_vec(&t::StateAction::SetState(KERNEL_PROCESS_ID.clone()))
.unwrap(),
metadata: None,
}),
payload: Some(t::Payload { mime: None, bytes }),
@ -142,7 +143,7 @@ async fn handle_kernel_request(
t::KernelCommand::InitializeProcess {
id,
wasm_bytes_handle,
on_panic,
on_exit,
initial_capabilities,
public,
} => {
@ -248,7 +249,7 @@ async fn handle_kernel_request(
process_id: id,
persisted: t::PersistedProcess {
wasm_bytes_handle,
on_panic,
on_exit,
capabilities: valid_capabilities,
public,
},
@ -453,8 +454,8 @@ async fn handle_kernel_response(
.await;
return;
};
// ignore responses that aren't filesystem responses
if km.source.process != *FILESYSTEM_PROCESS_ID {
// ignore responses that aren't filesystem or state responses
if km.source.process != *STATE_PROCESS_ID && km.source.process != *VFS_PROCESS_ID {
return;
}
let Some(ref metadata) = response.metadata else {
@ -571,8 +572,8 @@ async fn start_process(
node: our_name.clone(),
process: id.clone(),
},
wasm_bytes_handle: process_metadata.persisted.wasm_bytes_handle,
on_panic: process_metadata.persisted.on_panic.clone(),
wasm_bytes_handle: process_metadata.persisted.wasm_bytes_handle.clone(),
on_exit: process_metadata.persisted.on_exit.clone(),
public: process_metadata.persisted.public,
};
process_handles.insert(
@ -659,7 +660,7 @@ pub async fn kernel(
for (process_id, persisted) in &process_map {
// runtime extensions will have a bytes_handle of 0, because they have no
// WASM code saved in filesystem.
if persisted.on_panic.is_restart() && persisted.wasm_bytes_handle != 0 {
if persisted.on_exit.is_restart() && persisted.wasm_bytes_handle != "" {
send_to_loop
.send(t::KernelMessage {
id: rand::random(),
@ -669,14 +670,17 @@ pub async fn kernel(
},
target: t::Address {
node: our.name.clone(),
process: FILESYSTEM_PROCESS_ID.clone(),
process: VFS_PROCESS_ID.clone(),
},
rsvp: None,
message: t::Message::Request(t::Request {
inherit: true,
expects_response: Some(5), // TODO evaluate
ipc: serde_json::to_vec(&t::FsAction::Read(persisted.wasm_bytes_handle))
.unwrap(),
ipc: serde_json::to_vec(&t::VfsRequest {
path: persisted.wasm_bytes_handle.clone(),
action: t::VfsAction::Read,
})
.unwrap(),
metadata: Some(
serde_json::to_string(&StartProcessMetadata {
source: t::Address {
@ -696,7 +700,7 @@ pub async fn kernel(
.await
.expect("event loop: fatal: sender died");
}
if let t::OnPanic::Requests(requests) = &persisted.on_panic {
if let t::OnExit::Requests(requests) = &persisted.on_exit {
// if a persisted process had on-death-requests, we should perform them now
// even in death, a process can only message processes it has capabilities for
for (address, request, payload) in requests {
@ -851,7 +855,7 @@ pub async fn kernel(
verbosity: 0,
content: format!(
"event loop: don't have {} amongst registered processes (got message for it from network)",
kernel_message.source.process,
kernel_message.target.process,
)
})
.await;
@ -881,7 +885,7 @@ pub async fn kernel(
// enforce that local process has capability to message a target process of this name
// kernel and filesystem can ALWAYS message any local process
if kernel_message.source.process != *KERNEL_PROCESS_ID
&& kernel_message.source.process != *FILESYSTEM_PROCESS_ID
&& kernel_message.source.process != *STATE_PROCESS_ID
{
let Some(persisted_source) = process_map.get(&kernel_message.source.process) else {
continue

View File

@ -534,11 +534,11 @@ pub async fn make_process_loop(
.await
.expect("event loop: fatal: sender died");
// fulfill the designated OnPanic behavior
match metadata.on_panic {
t::OnPanic::None => {}
// fulfill the designated OnExit behavior
match metadata.on_exit {
t::OnExit::None => {}
// if restart, tell ourselves to init the app again, with same capabilities
t::OnPanic::Restart => {
t::OnExit::Restart => {
send_to_loop
.send(t::KernelMessage {
id: rand::random(),
@ -551,7 +551,7 @@ pub async fn make_process_loop(
ipc: serde_json::to_vec(&t::KernelCommand::InitializeProcess {
id: metadata.our.process.clone(),
wasm_bytes_handle: metadata.wasm_bytes_handle,
on_panic: metadata.on_panic,
on_exit: metadata.on_exit,
initial_capabilities,
public: metadata.public,
})
@ -569,7 +569,7 @@ pub async fn make_process_loop(
}
// if requests, fire them
// even in death, a process can only message processes it has capabilities for
t::OnPanic::Requests(requests) => {
t::OnExit::Requests(requests) => {
for (address, mut request, payload) in requests {
request.expects_response = None;
let (tx, rx) = tokio::sync::oneshot::channel();

View File

@ -1,7 +1,7 @@
use crate::kernel::process;
use crate::kernel::process::uqbar::process::standard as wit;
use crate::types as t;
use crate::FILESYSTEM_PROCESS_ID;
use crate::types::STATE_PROCESS_ID;
use crate::KERNEL_PROCESS_ID;
use crate::VFS_PROCESS_ID;
use anyhow::Result;
@ -40,11 +40,15 @@ impl StandardHost for process::ProcessWasi {
//
/// TODO critical: move to kernel logic to enable persistence of choice made here
async fn set_on_panic(&mut self, on_panic: wit::OnPanic) -> Result<()> {
self.process.metadata.on_panic = t::de_wit_on_panic(on_panic);
async fn set_on_exit(&mut self, on_exit: wit::OnExit) -> Result<()> {
self.process.metadata.on_exit = t::OnExit::de_wit(on_exit);
Ok(())
}
async fn get_on_exit(&mut self) -> Result<wit::OnExit> {
Ok(self.process.metadata.on_exit.en_wit())
}
/// create a message from the *kernel* to the filesystem,
/// asking it to fetch the current state saved under this process
async fn get_state(&mut self) -> Result<Option<Vec<u8>>> {
@ -57,12 +61,12 @@ impl StandardHost for process::ProcessWasi {
}),
wit::Address {
node: self.process.metadata.our.node.clone(),
process: FILESYSTEM_PROCESS_ID.en_wit(),
process: STATE_PROCESS_ID.en_wit(),
},
wit::Request {
inherit: false,
expects_response: Some(5),
ipc: serde_json::to_vec(&t::FsAction::GetState(
ipc: serde_json::to_vec(&t::StateAction::GetState(
self.process.metadata.our.process.clone(),
))
.unwrap(),
@ -98,12 +102,12 @@ impl StandardHost for process::ProcessWasi {
}),
wit::Address {
node: self.process.metadata.our.node.clone(),
process: FILESYSTEM_PROCESS_ID.en_wit(),
process: STATE_PROCESS_ID.en_wit(),
},
wit::Request {
inherit: false,
expects_response: Some(5),
ipc: serde_json::to_vec(&t::FsAction::SetState(
ipc: serde_json::to_vec(&t::StateAction::SetState(
self.process.metadata.our.process.clone(),
))
.unwrap(),
@ -137,12 +141,12 @@ impl StandardHost for process::ProcessWasi {
}),
wit::Address {
node: self.process.metadata.our.node.clone(),
process: FILESYSTEM_PROCESS_ID.en_wit(),
process: STATE_PROCESS_ID.en_wit(),
},
wit::Request {
inherit: false,
expects_response: Some(5),
ipc: serde_json::to_vec(&t::FsAction::DeleteState(
ipc: serde_json::to_vec(&t::StateAction::DeleteState(
self.process.metadata.our.process.clone(),
))
.unwrap(),
@ -171,7 +175,7 @@ impl StandardHost for process::ProcessWasi {
&mut self,
name: Option<String>,
wasm_path: String, // must be located within package's drive
on_panic: wit::OnPanic,
on_exit: wit::OnExit,
capabilities: wit::Capabilities,
public: bool,
) -> Result<Result<wit::ProcessId, wit::SpawnError>> {
@ -181,11 +185,18 @@ impl StandardHost for process::ProcessWasi {
node: self.process.metadata.our.node.clone(),
process: VFS_PROCESS_ID.en_wit(),
};
let our_drive_name = [
// TODO: note, drive could just be your basic process maybe.
let path = format!(
"/{}/{}/{}",
self.process.metadata.our.process.to_string(),
self.process.metadata.our.process.package(),
self.process.metadata.our.process.publisher(),
]
.join(":");
wasm_path
);
// let our_drive_name = [
// self.process.metadata.our.process.package(),
// self.process.metadata.our.process.publisher(),
// ]
// .join(":");
let Ok(Ok((_, hash_response))) = process::send_and_await_response(
self,
None,
@ -194,8 +205,8 @@ impl StandardHost for process::ProcessWasi {
inherit: false,
expects_response: Some(5),
ipc: serde_json::to_vec(&t::VfsRequest {
drive: our_drive_name.clone(),
action: t::VfsAction::GetHash(wasm_path.clone()),
path: path.clone(),
action: t::VfsAction::Read,
})
.unwrap(),
metadata: None,
@ -214,29 +225,7 @@ impl StandardHost for process::ProcessWasi {
self.process.last_payload = old_last_payload;
return Ok(Err(wit::SpawnError::NoFileAtPath));
};
let t::VfsResponse::GetHash(Some(hash)) = serde_json::from_slice(&ipc).unwrap() else {
// reset payload to what it was
self.process.last_payload = old_last_payload;
return Ok(Err(wit::SpawnError::NoFileAtPath));
};
let Ok(Ok(_)) = process::send_and_await_response(
self,
None,
vfs_address,
wit::Request {
inherit: false,
expects_response: Some(5),
ipc: serde_json::to_vec(&t::VfsRequest {
drive: our_drive_name,
action: t::VfsAction::GetEntry(wasm_path.clone()),
})
.unwrap(),
metadata: None,
},
None,
)
.await
else {
let t::VfsResponse::Read = serde_json::from_slice(&ipc).unwrap() else {
// reset payload to what it was
self.process.last_payload = old_last_payload;
return Ok(Err(wit::SpawnError::NoFileAtPath));
@ -271,8 +260,8 @@ impl StandardHost for process::ProcessWasi {
expects_response: Some(5), // TODO evaluate
ipc: serde_json::to_vec(&t::KernelCommand::InitializeProcess {
id: new_process_id.clone(),
wasm_bytes_handle: hash,
on_panic: t::de_wit_on_panic(on_panic),
wasm_bytes_handle: path,
on_exit: t::OnExit::de_wit(on_exit),
initial_capabilities: match capabilities {
wit::Capabilities::None => HashSet::new(),
wit::Capabilities::All => {

View File

@ -23,7 +23,7 @@ pub fn encode_keyfile(
password: String,
username: String,
routers: Vec<String>,
networking_key: Document,
networking_key: &[u8],
jwt: Vec<u8>,
file_key: Vec<u8>,
) -> Vec<u8> {
@ -49,9 +49,7 @@ pub fn encode_keyfile(
let jwt_nonce = Aes256Gcm::generate_nonce(&mut OsRng);
let file_nonce = Aes256Gcm::generate_nonce(&mut OsRng);
let keyciphertext: Vec<u8> = cipher
.encrypt(&network_nonce, networking_key.as_ref())
.unwrap();
let keyciphertext: Vec<u8> = cipher.encrypt(&network_nonce, networking_key).unwrap();
let jwtciphertext: Vec<u8> = cipher.encrypt(&jwt_nonce, jwt.as_ref()).unwrap();
let fileciphertext: Vec<u8> = cipher.encrypt(&file_nonce, file_key.as_ref()).unwrap();
@ -66,9 +64,9 @@ pub fn encode_keyfile(
.unwrap()
}
pub fn decode_keyfile(keyfile: Vec<u8>, password: &str) -> Result<Keyfile, &'static str> {
pub fn decode_keyfile(keyfile: &[u8], password: &str) -> Result<Keyfile, &'static str> {
let (username, routers, salt, key_enc, jwt_enc, file_enc) =
bincode::deserialize::<(String, Vec<String>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>)>(&keyfile)
bincode::deserialize::<(String, Vec<String>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>)>(keyfile)
.map_err(|_| "failed to deserialize keyfile")?;
// rederive disk key
@ -112,9 +110,9 @@ pub fn decode_keyfile(keyfile: Vec<u8>, password: &str) -> Result<Keyfile, &'sta
})
}
pub fn get_username_and_routers(keyfile: Vec<u8>) -> Result<(String, Vec<String>), &'static str> {
pub fn get_username_and_routers(keyfile: &[u8]) -> Result<(String, Vec<String>), &'static str> {
let (username, routers, _salt, _key_enc, _jwt_enc, _file_enc) =
bincode::deserialize::<(String, Vec<String>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>)>(&keyfile)
bincode::deserialize::<(String, Vec<String>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>)>(keyfile)
.map_err(|_| "failed to deserialize keyfile")?;
Ok((username, routers))

520
src/kv.rs Normal file
View File

@ -0,0 +1,520 @@
use anyhow::Result;
use dashmap::DashMap;
// use rocksdb::checkpoint::Checkpoint;
use rocksdb::OptimisticTransactionDB;
use std::collections::{HashMap, VecDeque};
use std::sync::Arc;
use tokio::fs;
use tokio::sync::Mutex;
use crate::types::*;
pub async fn kv(
our_node: String,
send_to_loop: MessageSender,
send_to_terminal: PrintSender,
mut recv_from_loop: MessageReceiver,
send_to_caps_oracle: CapMessageSender,
home_directory_path: String,
) -> anyhow::Result<()> {
let kv_path = format!("{}/kv", &home_directory_path);
if let Err(e) = fs::create_dir_all(&kv_path).await {
panic!("failed creating kv dir! {:?}", e);
}
let open_kvs: Arc<DashMap<(PackageId, String), OptimisticTransactionDB>> =
Arc::new(DashMap::new());
let txs: Arc<DashMap<u64, Vec<(KvAction, Option<Vec<u8>>)>>> = Arc::new(DashMap::new());
let mut process_queues: HashMap<ProcessId, Arc<Mutex<VecDeque<KernelMessage>>>> =
HashMap::new();
loop {
tokio::select! {
Some(km) = recv_from_loop.recv() => {
if our_node.clone() != km.source.node {
println!(
"kv: request must come from our_node={}, got: {}",
our_node,
km.source.node,
);
continue;
}
let queue = process_queues
.entry(km.source.process.clone())
.or_insert_with(|| Arc::new(Mutex::new(VecDeque::new())))
.clone();
{
let mut queue_lock = queue.lock().await;
queue_lock.push_back(km.clone());
}
// clone Arcs
let our_node = our_node.clone();
let send_to_caps_oracle = send_to_caps_oracle.clone();
let send_to_terminal = send_to_terminal.clone();
let send_to_loop = send_to_loop.clone();
let open_kvs = open_kvs.clone();
let txs = txs.clone();
let kv_path = kv_path.clone();
tokio::spawn(async move {
let mut queue_lock = queue.lock().await;
if let Some(km) = queue_lock.pop_front() {
if let Err(e) = handle_request(
our_node.clone(),
km.clone(),
open_kvs.clone(),
txs.clone(),
send_to_loop.clone(),
send_to_terminal.clone(),
send_to_caps_oracle.clone(),
kv_path.clone(),
)
.await
{
let _ = send_to_loop
.send(make_error_message(our_node.clone(), &km, e))
.await;
}
}
});
}
}
}
}
async fn handle_request(
our_node: String,
km: KernelMessage,
open_kvs: Arc<DashMap<(PackageId, String), OptimisticTransactionDB>>,
txs: Arc<DashMap<u64, Vec<(KvAction, Option<Vec<u8>>)>>>,
send_to_loop: MessageSender,
send_to_terminal: PrintSender,
send_to_caps_oracle: CapMessageSender,
kv_path: String,
) -> Result<(), KvError> {
let KernelMessage {
id,
source,
message,
payload,
..
} = km.clone();
let Message::Request(Request {
ipc,
expects_response,
metadata,
..
}) = message.clone()
else {
return Err(KvError::InputError {
error: "not a request".into(),
});
};
let request: KvRequest = match serde_json::from_slice(&ipc) {
Ok(r) => r,
Err(e) => {
println!("kv: got invalid Request: {}", e);
return Err(KvError::InputError {
error: "didn't serialize to KvAction.".into(),
});
}
};
check_caps(
our_node.clone(),
source.clone(),
open_kvs.clone(),
send_to_caps_oracle.clone(),
&request,
kv_path.clone(),
)
.await?;
let (ipc, bytes) = match &request.action {
KvAction::New => {
// handled in check_caps.
(serde_json::to_vec(&KvResponse::Ok).unwrap(), None)
}
KvAction::Get { key } => {
let db = match open_kvs.get(&(request.package_id, request.db)) {
None => {
return Err(KvError::NoDb);
}
Some(db) => db,
};
match db.get(&key) {
Ok(Some(value)) => (
serde_json::to_vec(&KvResponse::Get { key: key.to_vec() }).unwrap(),
Some(value),
),
Ok(None) => {
return Err(KvError::KeyNotFound);
}
Err(e) => {
return Err(KvError::RocksDBError {
action: request.action.to_string(),
error: e.to_string(),
})
}
}
}
KvAction::BeginTx => {
let tx_id = rand::random::<u64>();
txs.insert(tx_id, Vec::new());
(
serde_json::to_vec(&KvResponse::BeginTx { tx_id }).unwrap(),
None,
)
}
KvAction::Set { key, tx_id } => {
let db = match open_kvs.get(&(request.package_id, request.db)) {
None => {
return Err(KvError::NoDb);
}
Some(db) => db,
};
let Some(payload) = payload else {
return Err(KvError::InputError {
error: "no payload".into(),
});
};
match tx_id {
None => {
db.put(key, payload.bytes)?;
}
Some(tx_id) => {
let mut tx = match txs.get_mut(&tx_id) {
None => {
return Err(KvError::NoTx);
}
Some(tx) => tx,
};
tx.push((request.action.clone(), Some(payload.bytes)));
}
}
(serde_json::to_vec(&KvResponse::Ok).unwrap(), None)
}
KvAction::Delete { key, tx_id } => {
let db = match open_kvs.get(&(request.package_id, request.db)) {
None => {
return Err(KvError::NoDb);
}
Some(db) => db,
};
match tx_id {
None => {
db.delete(key)?;
}
Some(tx_id) => {
let mut tx = match txs.get_mut(&tx_id) {
None => {
return Err(KvError::NoTx);
}
Some(tx) => tx,
};
tx.push((request.action.clone(), None));
}
}
(serde_json::to_vec(&KvResponse::Ok).unwrap(), None)
}
KvAction::Commit { tx_id } => {
let db = match open_kvs.get(&(request.package_id, request.db)) {
None => {
return Err(KvError::NoDb);
}
Some(db) => db,
};
let txs = match txs.remove(&tx_id).map(|(_, tx)| tx) {
None => {
return Err(KvError::NoTx);
}
Some(tx) => tx,
};
let tx = db.transaction();
for (action, payload) in txs {
match action {
KvAction::Set { key, .. } => {
if let Some(payload) = payload {
tx.put(&key, &payload)?;
}
}
KvAction::Delete { key, .. } => {
tx.delete(&key)?;
}
_ => {}
}
}
match tx.commit() {
Ok(_) => (serde_json::to_vec(&KvResponse::Ok).unwrap(), None),
Err(e) => {
return Err(KvError::RocksDBError {
action: request.action.to_string(),
error: e.to_string(),
})
}
}
}
KvAction::Backup => {
// loop through all db directories and backup.
//
(serde_json::to_vec(&KvResponse::Ok).unwrap(), None)
}
};
if let Some(target) = km.rsvp.or_else(|| {
expects_response.map(|_| Address {
node: our_node.clone(),
process: source.process.clone(),
})
}) {
let response = KernelMessage {
id,
source: Address {
node: our_node.clone(),
process: KV_PROCESS_ID.clone(),
},
target,
rsvp: None,
message: Message::Response((
Response {
inherit: false,
ipc,
metadata,
},
None,
)),
payload: bytes.map(|bytes| Payload {
mime: Some("application/octet-stream".into()),
bytes,
}),
signed_capabilities: None,
};
let _ = send_to_loop.send(response).await;
} else {
send_to_terminal
.send(Printout {
verbosity: 2,
content: format!(
"kv: not sending response: {:?}",
serde_json::from_slice::<KvResponse>(&ipc)
),
})
.await
.unwrap();
}
Ok(())
}
async fn check_caps(
our_node: String,
source: Address,
open_kvs: Arc<DashMap<(PackageId, String), OptimisticTransactionDB>>,
mut send_to_caps_oracle: CapMessageSender,
request: &KvRequest,
kv_path: String,
) -> Result<(), KvError> {
let (send_cap_bool, recv_cap_bool) = tokio::sync::oneshot::channel();
let src_package_id = PackageId::new(source.process.package(), source.process.publisher());
match &request.action {
KvAction::Delete { .. }
| KvAction::Set { .. }
| KvAction::BeginTx
| KvAction::Commit { .. } => {
send_to_caps_oracle
.send(CapMessage::Has {
on: source.process.clone(),
cap: Capability {
issuer: Address {
node: our_node.clone(),
process: KV_PROCESS_ID.clone(),
},
params: serde_json::to_string(&serde_json::json!({
"kind": "write",
"db": request.db.to_string(),
}))
.unwrap(),
},
responder: send_cap_bool,
})
.await?;
let has_cap = recv_cap_bool.await?;
if !has_cap {
return Err(KvError::NoCap {
error: request.action.to_string(),
});
}
Ok(())
}
KvAction::Get { .. } => {
send_to_caps_oracle
.send(CapMessage::Has {
on: source.process.clone(),
cap: Capability {
issuer: Address {
node: our_node.clone(),
process: KV_PROCESS_ID.clone(),
},
params: serde_json::to_string(&serde_json::json!({
"kind": "read",
"db": request.db.to_string(),
}))
.unwrap(),
},
responder: send_cap_bool,
})
.await?;
let has_cap = recv_cap_bool.await?;
if !has_cap {
return Err(KvError::NoCap {
error: request.action.to_string(),
});
}
Ok(())
}
KvAction::New { .. } => {
if src_package_id != request.package_id {
return Err(KvError::NoCap {
error: request.action.to_string(),
});
}
add_capability(
"read",
&request.db.to_string(),
&our_node,
&source,
&mut send_to_caps_oracle,
)
.await?;
add_capability(
"write",
&request.db.to_string(),
&our_node,
&source,
&mut send_to_caps_oracle,
)
.await?;
let db_path = format!("{}{}", kv_path, request.db.to_string());
fs::create_dir_all(&db_path).await?;
let db = OptimisticTransactionDB::open_default(&db_path)?;
open_kvs.insert((request.package_id.clone(), request.db.clone()), db);
Ok(())
}
KvAction::Backup { .. } => {
if source.process != *STATE_PROCESS_ID {
return Err(KvError::NoCap {
error: request.action.to_string(),
});
}
Ok(())
}
}
}
async fn add_capability(
kind: &str,
db: &str,
our_node: &str,
source: &Address,
send_to_caps_oracle: &mut CapMessageSender,
) -> Result<(), KvError> {
let cap = Capability {
issuer: Address {
node: our_node.to_string(),
process: KV_PROCESS_ID.clone(),
},
params: serde_json::to_string(&serde_json::json!({ "kind": kind, "db": db })).unwrap(),
};
let (send_cap_bool, recv_cap_bool) = tokio::sync::oneshot::channel();
send_to_caps_oracle
.send(CapMessage::Add {
on: source.process.clone(),
cap,
responder: send_cap_bool,
})
.await?;
let _ = recv_cap_bool.await?;
Ok(())
}
fn make_error_message(our_name: String, km: &KernelMessage, error: KvError) -> KernelMessage {
KernelMessage {
id: km.id,
source: Address {
node: our_name.clone(),
process: KV_PROCESS_ID.clone(),
},
target: match &km.rsvp {
None => km.source.clone(),
Some(rsvp) => rsvp.clone(),
},
rsvp: None,
message: Message::Response((
Response {
inherit: false,
ipc: serde_json::to_vec(&KvResponse::Err { error: error }).unwrap(),
metadata: None,
},
None,
)),
payload: None,
signed_capabilities: None,
}
}
impl std::fmt::Display for KvAction {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<tokio::sync::oneshot::error::RecvError> for KvError {
fn from(err: tokio::sync::oneshot::error::RecvError) -> Self {
KvError::NoCap {
error: err.to_string(),
}
}
}
impl From<tokio::sync::mpsc::error::SendError<CapMessage>> for KvError {
fn from(err: tokio::sync::mpsc::error::SendError<CapMessage>) -> Self {
KvError::NoCap {
error: err.to_string(),
}
}
}
impl From<std::io::Error> for KvError {
fn from(err: std::io::Error) -> Self {
KvError::IOError {
error: err.to_string(),
}
}
}
impl From<rocksdb::Error> for KvError {
fn from(error: rocksdb::Error) -> Self {
KvError::RocksDBError {
action: "".into(),
error: error.to_string(),
}
}
}

View File

@ -1,7 +0,0 @@
# Local LLM Integration
1. Clone and build [llama.cpp](https://github.com/ggerganov/llama.cpp) on the same machine where you will run your uqbar node
- follow their README for details on how to do this. In most cases simply running `make` works
- make sure to get your model as a .gguf file
2. Within the llama.cpp directory, run this command in llama.cpp on the same machine you will run your uqbar node: `./server --port <PORT>`
- Note: you can pass in whatever other command line arguments to the llama cpp server you want depending on your preferences/hardware/model/etc.
3. Run your Uqbar node with `--features llm` and `--llm http://localhost:<PORT>`. For example `cargo +nightly run --features llm --release home --rpc wss://eth-sepolia.g.alchemy.com/v2/<YOUR_API_KEY> --llm http://localhost:<PORT>`

View File

@ -1,180 +0,0 @@
use crate::llm::types::*;
use crate::types::*;
use anyhow::Result;
use reqwest::Response as ReqwestResponse;
mod types;
pub async fn llm(
our_name: String,
send_to_loop: MessageSender,
mut recv_in_client: MessageReceiver,
llm_url: String,
print_tx: PrintSender,
) -> Result<()> {
while let Some(message) = recv_in_client.recv().await {
let KernelMessage {
id,
source,
rsvp,
message:
Message::Request(Request {
expects_response,
ipc,
..
}),
..
} = message.clone()
else {
return Err(anyhow::anyhow!("llm: bad message"));
};
let our_name = our_name.clone();
let llm_url = llm_url.clone();
let send_to_loop = send_to_loop.clone();
let print_tx = print_tx.clone();
tokio::spawn(async move {
if let Err(e) = handle_message(
our_name.clone(),
send_to_loop.clone(),
llm_url.clone(),
id,
rsvp,
expects_response,
source.clone(),
ipc,
print_tx.clone(),
)
.await
{
send_to_loop
.send(make_error_message(our_name.clone(), id, source, e))
.await
.unwrap();
}
});
}
Err(anyhow::anyhow!("llm: exited"))
}
async fn handle_message(
our: String,
send_to_loop: MessageSender,
llm_url: String,
id: u64,
rsvp: Option<Address>,
expects_response: Option<u64>,
source: Address,
json: Vec<u8>,
_print_tx: PrintSender,
) -> Result<(), LlmError> {
let target = if expects_response.is_some() {
source.clone()
} else if source.process == ProcessId::from_str("terminal:terminal:uqbar").unwrap() {
source.clone()
} else {
let Some(rsvp) = rsvp else {
return Err(LlmError::BadRsvp);
};
rsvp.clone()
};
let req: LlmPrompt = match serde_json::from_slice(&json) {
Ok(req) => req,
Err(e) => {
return Err(LlmError::BadJson {
json: String::from_utf8(json).unwrap_or_default(),
error: format!("{}", e),
})
}
};
let client = reqwest::Client::new();
let res: ReqwestResponse = match client
.post(&format!("{}/completion", llm_url))
.json(&req)
.send()
.await
{
Ok(res) => res,
Err(e) => {
return Err(LlmError::RequestFailed {
error: format!("{}", e),
});
}
};
let llm_response = match res.json::<LlmResponse>().await {
Ok(response) => response,
Err(e) => {
return Err(LlmError::DeserializationToLlmResponseFailed {
error: format!("{}", e),
});
}
};
let _ = _print_tx
.send(Printout {
verbosity: 0,
content: format!("llm: {:?}", llm_response.clone().content),
})
.await;
let message = KernelMessage {
id,
source: Address {
node: our,
process: ProcessId::new(Some("llm"), "sys", "uqbar"),
},
target,
rsvp: None,
message: Message::Response((
Response {
inherit: false,
ipc: serde_json::to_vec::<Result<LlmResponse, LlmError>>(&Ok(llm_response))
.unwrap(),
metadata: None,
},
None,
)),
payload: None,
signed_capabilities: None,
};
send_to_loop.send(message).await.unwrap();
Ok(())
}
//
// helpers
//
fn make_error_message(
our_name: String,
id: u64,
source: Address,
error: LlmError,
) -> KernelMessage {
KernelMessage {
id,
source: source.clone(),
target: Address {
node: our_name.clone(),
process: source.process.clone(),
},
rsvp: None,
message: Message::Response((
Response {
inherit: false,
ipc: serde_json::to_vec::<Result<HttpClientResponse, LlmError>>(&Err(error))
.unwrap(),
metadata: None,
},
None,
)),
payload: None,
signed_capabilities: None,
}
}

View File

@ -1,114 +0,0 @@
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Serialize, Deserialize)]
pub struct LlmPrompt {
prompt: String, // TODO can be a string or an array of strings
temperature: Option<f64>,
top_k: Option<usize>,
top_p: Option<f64>,
n_predict: Option<isize>, // isize to accommodate -1
n_keep: Option<isize>, // isize to accommodate -1
stream: Option<bool>,
stop: Option<Vec<String>>,
tfs_z: Option<f64>,
typical_p: Option<f64>,
repeat_penalty: Option<f64>,
repeat_last_n: Option<isize>, // isize to accommodate -1
penalize_nl: Option<bool>,
presence_penalty: Option<f64>,
frequency_penalty: Option<f64>,
mirostat: Option<u8>, // u8 as it's 0, 1, or 2
mirostat_tau: Option<f64>,
mirostat_eta: Option<f64>,
grammar: Option<String>,
seed: Option<isize>, // isize to accommodate -1
ignore_eos: Option<bool>,
logit_bias: Option<Vec<(usize, f64)>>,
n_probs: Option<usize>,
image_data: Option<Vec<ImageData>>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ImageData {
data: String, // Base64 string
id: usize,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct LlmResponse {
pub content: String,
pub generation_settings: GenerationSettings,
pub model: String,
pub prompt: String,
pub slot_id: u64,
pub stop: bool,
pub stopped_eos: bool,
pub stopped_limit: bool,
pub stopped_word: bool,
pub stopping_word: String,
pub timings: Timings,
pub tokens_cached: u64,
pub tokens_evaluated: u64,
pub tokens_predicted: u64,
pub truncated: bool,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct GenerationSettings {
pub frequency_penalty: f64,
pub grammar: String,
pub ignore_eos: bool,
pub logit_bias: Vec<serde_json::Value>, // This should be changed to the appropriate type
pub mirostat: u64,
pub mirostat_eta: f64,
pub mirostat_tau: f64,
pub model: String,
pub n_ctx: u64,
pub n_keep: u64,
pub n_predict: u64,
pub n_probs: u64,
pub penalize_nl: bool,
pub presence_penalty: f64,
pub repeat_last_n: u64,
pub repeat_penalty: f64,
pub seed: u64,
pub stop: Vec<serde_json::Value>, // This should be changed to the appropriate type
pub stream: bool,
pub temp: f64,
pub tfs_z: f64,
pub top_k: u64,
pub top_p: f64,
pub typical_p: f64,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Timings {
pub predicted_ms: f64,
pub predicted_n: u64,
pub predicted_per_second: f64,
pub predicted_per_token_ms: f64,
pub prompt_ms: f64,
pub prompt_n: u64,
pub prompt_per_second: f64,
pub prompt_per_token_ms: f64,
}
#[derive(Error, Debug, Serialize, Deserialize)]
pub enum LlmError {
#[error("llm: rsvp is None but message is expecting response")]
BadRsvp,
#[error("llm: no json in request")]
NoJson,
#[error(
"llm: JSON payload could not be parsed to LlmPrompt: {error}. Got {:?}.",
json
)]
BadJson { json: String, error: String },
#[error("llm: http method not supported: {:?}", method)]
BadMethod { method: String },
#[error("llm: failed to execute request {:?}", error)]
RequestFailed { error: String },
#[error("llm: failed to deserialize response {:?}", error)]
DeserializationToLlmResponseFailed { error: String },
}

View File

@ -12,37 +12,34 @@ use tokio::{fs, time::timeout};
use ring::{rand::SystemRandom, signature, signature::KeyPair};
mod eth_rpc;
mod filesystem;
mod http;
mod kernel;
mod keygen;
mod kv;
mod net;
mod register;
mod sqlite;
mod state;
mod terminal;
mod timer;
mod types;
mod vfs;
// extensions
#[cfg(feature = "llm")]
mod llm;
const EVENT_LOOP_CHANNEL_CAPACITY: usize = 10_000;
const EVENT_LOOP_DEBUG_CHANNEL_CAPACITY: usize = 50;
const TERMINAL_CHANNEL_CAPACITY: usize = 32;
const WEBSOCKET_SENDER_CHANNEL_CAPACITY: usize = 32;
const FILESYSTEM_CHANNEL_CAPACITY: usize = 32;
const HTTP_CHANNEL_CAPACITY: usize = 32;
const HTTP_CLIENT_CHANNEL_CAPACITY: usize = 32;
const ETH_RPC_CHANNEL_CAPACITY: usize = 32;
const VFS_CHANNEL_CAPACITY: usize = 1_000;
const CAP_CHANNEL_CAPACITY: usize = 1_000;
#[cfg(feature = "llm")]
const LLM_CHANNEL_CAPACITY: usize = 32;
const KV_CHANNEL_CAPACITY: usize = 1_000;
const SQLITE_CHANNEL_CAPACITY: usize = 1_000;
const VERSION: &str = env!("CARGO_PKG_VERSION");
/// This can and should be an environment variable / setting. It configures networking
/// Tshis can and should be an environment variable / setting. It configures networking
/// such that indirect nodes always use routers, even when target is a direct node,
/// such that only their routers can ever see their physical networking details.
const REVEAL_IP: bool = true;
@ -52,7 +49,7 @@ async fn serve_register_fe(
our_ip: String,
http_server_port: u16,
rpc_url: String,
) -> (Identity, Keyfile) {
) -> (Identity, Vec<u8>, Keyfile) {
// check if we have keys saved on disk, encrypted
// if so, prompt user for "password" to decrypt with
@ -66,10 +63,9 @@ async fn serve_register_fe(
// that updates their PKI info on-chain.
let (kill_tx, kill_rx) = oneshot::channel::<bool>();
let disk_keyfile = match fs::read(format!("{}/.keys", home_directory_path)).await {
Ok(keyfile) => keyfile,
Err(_) => Vec::new(),
};
let disk_keyfile: Option<Vec<u8>> = fs::read(format!("{}/.keys", home_directory_path))
.await
.ok();
let (tx, mut rx) = mpsc::channel::<(Identity, Keyfile, Vec<u8>)>(1);
let (our, decoded_keyfile, encoded_keyfile) = tokio::select! {
@ -81,20 +77,16 @@ async fn serve_register_fe(
}
};
println!(
"saving encrypted networking keys to {}/.keys",
home_directory_path
);
fs::write(format!("{}/.keys", home_directory_path), encoded_keyfile)
.await
.unwrap();
println!("registration complete!");
fs::write(
format!("{}/.keys", home_directory_path),
encoded_keyfile.clone(),
)
.await
.unwrap();
let _ = kill_tx.send(true);
(our, decoded_keyfile)
(our, encoded_keyfile, decoded_keyfile)
}
#[tokio::main]
@ -124,9 +116,6 @@ async fn main() {
.value_parser(value_parser!(u16)),
);
#[cfg(feature = "llm")]
let app = app.arg(arg!(--llm <LLM_URL> "LLM endpoint"));
let matches = app.get_matches();
let home_directory_path = matches.get_one::<String>("home").unwrap();
@ -146,9 +135,6 @@ async fn main() {
matches.get_one::<String>("fake-node-name"),
);
#[cfg(feature = "llm")]
let llm_url = matches.get_one::<String>("llm").unwrap();
if let Err(e) = fs::create_dir_all(home_directory_path).await {
panic!("failed to create home directory: {:?}", e);
}
@ -169,9 +155,15 @@ async fn main() {
// websocket sender receives send messages via this channel, kernel send messages
let (net_message_sender, net_message_receiver): (MessageSender, MessageReceiver) =
mpsc::channel(WEBSOCKET_SENDER_CHANNEL_CAPACITY);
// filesystem receives request messages via this channel, kernel sends messages
let (fs_message_sender, fs_message_receiver): (MessageSender, MessageReceiver) =
mpsc::channel(FILESYSTEM_CHANNEL_CAPACITY);
// kernel_state sender and receiver
let (state_sender, state_receiver): (MessageSender, MessageReceiver) =
mpsc::channel(VFS_CHANNEL_CAPACITY);
// kv sender and receiver
let (kv_sender, kv_receiver): (MessageSender, MessageReceiver) =
mpsc::channel(KV_CHANNEL_CAPACITY);
// sqlite sender and receiver
let (sqlite_sender, sqlite_receiver): (MessageSender, MessageReceiver) =
mpsc::channel(SQLITE_CHANNEL_CAPACITY);
// http server channel w/ websockets (eyre)
let (http_server_sender, http_server_receiver): (MessageSender, MessageReceiver) =
mpsc::channel(HTTP_CHANNEL_CAPACITY);
@ -188,75 +180,6 @@ async fn main() {
// terminal receives prints via this channel, all other modules send prints
let (print_sender, print_receiver): (PrintSender, PrintReceiver) =
mpsc::channel(TERMINAL_CHANNEL_CAPACITY);
// optional llm extension
#[cfg(feature = "llm")]
let (llm_sender, llm_receiver): (MessageSender, MessageReceiver) =
mpsc::channel(LLM_CHANNEL_CAPACITY);
// fs config in .env file (todo add -- arguments cleanly (with clap?))
dotenv::dotenv().ok();
let mem_buffer_limit = env::var("MEM_BUFFER_LIMIT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(1024 * 1024 * 5); // 5mb default
let read_cache_limit = env::var("READ_CACHE_LIMIT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(1024 * 1024 * 5); // 5mb default
let chunk_size = env::var("CHUNK_SIZE")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(1024 * 256); // 256kb default
let flush_to_cold_interval = env::var("FLUSH_TO_COLD_INTERVAL")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(60); // 60s default
let encryption = env::var("ENCRYPTION")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(true); // default true
let cloud_enabled = env::var("CLOUD_ENABLED")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(false); // default false
let s3_config = if let (Ok(access_key), Ok(secret_key), Ok(region), Ok(bucket), Ok(endpoint)) = (
env::var("S3_ACCESS_KEY"),
env::var("S3_SECRET_KEY"),
env::var("S3_REGION"),
env::var("S3_BUCKET"),
env::var("S3_ENDPOINT"),
) {
Some(S3Config {
access_key,
secret_key,
region,
bucket,
endpoint,
})
} else {
None
};
let fs_config = FsConfig {
s3_config,
mem_buffer_limit,
read_cache_limit,
chunk_size,
flush_to_cold_interval,
encryption,
cloud_enabled,
};
// shutdown signal send and await to fs
let (fs_kill_send, fs_kill_recv) = oneshot::channel::<()>();
let (fs_kill_confirm_send, fs_kill_confirm_recv) = oneshot::channel::<()>();
println!("finding public IP address...");
let our_ip: std::net::Ipv4Addr = {
@ -272,13 +195,21 @@ async fn main() {
};
let http_server_port = http::utils::find_open_port(port).await.unwrap();
println!("runtime bound port {}\r", http_server_port);
if http_server_port != port {
let error_message = format!(
"uqbar: couldn't bind {}; first available port found {}. Set an available port with `--port` and try again.",
port,
http_server_port,
);
println!("{error_message}");
panic!("{error_message}");
}
println!(
"login or register at http://localhost:{}\r",
http_server_port
);
#[cfg(not(feature = "simulation-mode"))]
let (our, decoded_keyfile) = serve_register_fe(
let (our, encoded_keyfile, decoded_keyfile) = serve_register_fe(
&home_directory_path,
our_ip.to_string(),
http_server_port.clone(),
@ -286,7 +217,7 @@ async fn main() {
)
.await;
#[cfg(feature = "simulation-mode")]
let (our, decoded_keyfile) = match fake_node_name {
let (our, encoded_keyfile, decoded_keyfile) = match fake_node_name {
None => {
match password {
None => match rpc_url {
@ -305,7 +236,7 @@ async fn main() {
match fs::read(format!("{}/.keys", home_directory_path)).await {
Err(e) => panic!("could not read keyfile: {}", e),
Ok(keyfile) => {
match keygen::decode_keyfile(keyfile, &password) {
match keygen::decode_keyfile(&keyfile, &password) {
Err(e) => panic!("could not decode keyfile: {}", e),
Ok(decoded_keyfile) => {
let our = Identity {
@ -322,7 +253,7 @@ async fn main() {
ws_routing: None, // TODO
allowed_routers: decoded_keyfile.routers.clone(),
};
(our, decoded_keyfile)
(our, keyfile, decoded_keyfile)
}
}
}
@ -363,16 +294,19 @@ async fn main() {
password,
name.clone(),
decoded_keyfile.routers.clone(),
networking_keypair,
networking_keypair.as_ref(),
decoded_keyfile.jwt_secret_bytes.clone(),
decoded_keyfile.file_key.clone(),
);
fs::write(format!("{}/.keys", home_directory_path), encoded_keyfile)
.await
.unwrap();
fs::write(
format!("{}/.keys", home_directory_path),
encoded_keyfile.clone(),
)
.await
.unwrap();
(our, decoded_keyfile)
(our, encoded_keyfile, decoded_keyfile)
}
};
@ -380,11 +314,6 @@ async fn main() {
// where public means that any process can always message it.
#[allow(unused_mut)]
let mut runtime_extensions = vec![
(
ProcessId::new(Some("filesystem"), "sys", "uqbar"),
fs_message_sender,
false,
),
(
ProcessId::new(Some("http_server"), "sys", "uqbar"),
http_server_sender,
@ -410,6 +339,17 @@ async fn main() {
vfs_message_sender,
true,
),
(
ProcessId::new(Some("state"), "sys", "uqbar"),
state_sender,
true,
),
(ProcessId::new(Some("kv"), "sys", "uqbar"), kv_sender, true),
(
ProcessId::new(Some("sqlite"), "sys", "uqbar"),
sqlite_sender,
true,
),
];
#[cfg(feature = "llm")]
@ -419,15 +359,13 @@ async fn main() {
true,
));
let (kernel_process_map, manifest, vfs_messages) = filesystem::load_fs(
let (kernel_process_map, db) = state::load_state(
our.name.clone(),
home_directory_path.clone(),
decoded_keyfile.file_key,
fs_config,
runtime_extensions.clone(),
)
.await
.expect("fs load failed!");
.expect("state load failed!");
/*
* the kernel module will handle our userspace processes and receives
@ -471,18 +409,34 @@ async fn main() {
kernel_message_sender.clone(),
net_message_receiver,
));
tasks.spawn(filesystem::fs_sender(
tasks.spawn(state::state_sender(
our.name.clone(),
manifest,
kernel_message_sender.clone(),
print_sender.clone(),
fs_message_receiver,
fs_kill_recv,
fs_kill_confirm_send,
state_receiver,
db,
home_directory_path.clone(),
));
tasks.spawn(kv::kv(
our.name.clone(),
kernel_message_sender.clone(),
print_sender.clone(),
kv_receiver,
caps_oracle_sender.clone(),
home_directory_path.clone(),
));
tasks.spawn(sqlite::sqlite(
our.name.clone(),
kernel_message_sender.clone(),
print_sender.clone(),
sqlite_receiver,
caps_oracle_sender.clone(),
home_directory_path.clone(),
));
tasks.spawn(http::server::http_server(
our.name.clone(),
http_server_port,
encoded_keyfile,
decoded_keyfile.jwt_secret_bytes.clone(),
http_server_receiver,
kernel_message_sender.clone(),
@ -514,18 +468,8 @@ async fn main() {
print_sender.clone(),
vfs_message_receiver,
caps_oracle_sender.clone(),
vfs_messages,
home_directory_path.clone(),
));
#[cfg(feature = "llm")]
{
tasks.spawn(llm::llm(
our.name.clone(),
kernel_message_sender.clone(),
llm_receiver,
llm_url.to_string(),
print_sender.clone(),
));
}
// if a runtime task exits, try to recover it,
// unless it was terminal signaling a quit
let quit_msg: String = tokio::select! {
@ -551,10 +495,6 @@ async fn main() {
}
};
// shutdown signal to fs for flush
let _ = fs_kill_send.send(());
let _ = fs_kill_confirm_recv.await;
// gracefully abort all running processes in kernel
let _ = kernel_message_sender
.send(KernelMessage {

View File

@ -65,7 +65,7 @@ pub async fn maintain_connection(
if km.source.node != peer_name {
let _ = print_tx.send(Printout {
verbosity: 0,
content: format!("net: got message with spoofed source from {peer_name}")
content: format!("net: got message with spoofed source from {peer_name}!")
}).await;
break
} else {
@ -136,7 +136,9 @@ pub async fn maintain_passthrough(mut conn: PassthroughConnection) {
maybe_recv = conn.read_stream_1.next() => {
match maybe_recv {
Some(Ok(msg)) => {
conn.write_stream_2.send(msg).await.expect("net error: fatal: kernel died");
let Ok(()) = conn.write_stream_2.send(msg).await else {
break
};
last_message = std::time::Instant::now();
}
_ => break,
@ -145,7 +147,9 @@ pub async fn maintain_passthrough(mut conn: PassthroughConnection) {
maybe_recv = conn.read_stream_2.next() => {
match maybe_recv {
Some(Ok(msg)) => {
conn.write_stream_1.send(msg).await.expect("net error: fatal: kernel died");
let Ok(()) = conn.write_stream_1.send(msg).await else {
break
};
last_message = std::time::Instant::now();
}
_ => break,

View File

@ -1,15 +1,13 @@
use aes_gcm::aead::KeyInit;
use ethers::prelude::{abigen, namehash, Address as EthAddress, Provider, U256};
use ethers_providers::Ws;
use hmac::Hmac;
use jwt::SignWithKey;
use ring::pkcs8::Document;
use ring::rand::SystemRandom;
use ring::signature;
use ring::signature::KeyPair;
use sha2::Sha256;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use tokio::sync::{mpsc, oneshot};
use warp::{
http::{
@ -77,14 +75,14 @@ fn _hex_string_to_u8_array(hex_str: &str) -> Result<[u8; 32], &'static str> {
Ok(bytes)
}
pub fn generate_jwt(jwt_secret_bytes: &[u8], username: String) -> Option<String> {
pub fn generate_jwt(jwt_secret_bytes: &[u8], username: &str) -> Option<String> {
let jwt_secret: Hmac<Sha256> = match Hmac::new_from_slice(jwt_secret_bytes) {
Ok(secret) => secret,
Err(_) => return None,
};
let claims = crate::http::types::JwtClaims {
username: username.clone(),
username: username.to_string(),
expiration: 0,
};
@ -101,16 +99,34 @@ pub async fn register(
ip: String,
port: u16,
rpc_url: String,
keyfile: Vec<u8>,
keyfile: Option<Vec<u8>>,
) {
let our_temp_arc = Arc::new(Mutex::new(None)); // Networking info is generated and passed to the UI, but not used until confirmed
let our_ws_info = our_temp_arc.clone();
// Networking info is generated and passed to the UI, but not used until confirmed
let (public_key, serialized_networking_keypair) = keygen::generate_networking_key();
let net_keypair = Arc::new(serialized_networking_keypair.as_ref().to_vec());
let tx = Arc::new(tx);
let net_keypair_arc = Arc::new(Mutex::new(None));
let net_keypair_ws_info = net_keypair_arc.clone();
// TODO: if IP is localhost, don't allow registration as direct
let ws_port = crate::http::utils::find_open_port(9000).await.unwrap();
let keyfile_arc = Arc::new(Mutex::new(Some(keyfile)));
let keyfile_vet = keyfile_arc.clone();
// This is a temporary identity, passed to the UI. If it is confirmed through a /boot or /confirm-change-network-keys, then it will be used to replace the current identity
let our_temp_id = Arc::new(Identity {
networking_key: format!("0x{}", public_key),
name: "".to_string(),
ws_routing: Some((ip.clone(), ws_port)),
allowed_routers: vec![
"uqbar-router-1.uq".into(), // "0x8d9e54427c50660c6d4802f63edca86a9ca5fd6a78070c4635950e9d149ed441".into(),
"uqbar-router-2.uq".into(), // "0x06d331ed65843ecf0860c73292005d8103af20820546b2f8f9007d01f60595b1".into(),
"uqbar-router-3.uq".into(), // "0xe6ab611eb62e8aee0460295667f8179cda4315982717db4b0b3da6022deecac1".into(),
],
});
let keyfile = warp::any().map(move || keyfile.clone());
let our_temp_id = warp::any().map(move || our_temp_id.clone());
let net_keypair = warp::any().map(move || net_keypair.clone());
let tx = warp::any().map(move || tx.clone());
let ip = warp::any().map(move || ip.clone());
let rpc_url = warp::any().map(move || rpc_url.clone());
let static_files = warp::path("static").and(warp::fs::dir("./src/register-ui/build/static/"));
@ -118,73 +134,60 @@ pub async fn register(
.and(warp::get())
.and(warp::fs::file("./src/register-ui/build/index.html"));
let keyfile_info_copy = keyfile_arc.clone();
let boot_tx = tx.clone();
let boot_our_arc = our_temp_arc.clone();
let boot_net_keypair_arc = net_keypair_arc.clone();
let import_tx = tx.clone();
let import_ip = ip.clone();
let import_rpc_url = rpc_url.clone();
let login_tx = tx.clone();
let login_keyfile_arc = keyfile_arc.clone();
let generate_keys_ip = ip.clone();
let api = warp::path("info")
.and(
warp::get()
.and(warp::any().map(move || keyfile_info_copy.clone()))
.and(keyfile.clone())
.and_then(get_unencrypted_info),
)
.or(warp::path("generate-networking-info").and(
warp::post()
.and(warp::any().map(move || generate_keys_ip.clone()))
.and(warp::any().map(move || our_ws_info.clone()))
.and(warp::any().map(move || net_keypair_ws_info.clone()))
.and(our_temp_id.clone())
.and_then(generate_networking_info),
))
.or(warp::path("vet-keyfile").and(
warp::post()
.and(warp::body::content_length_limit(1024 * 16))
.and(warp::body::json())
.and(warp::any().map(move || keyfile_vet.clone()))
.and(keyfile.clone())
.and_then(handle_keyfile_vet),
))
.or(warp::path("boot").and(
warp::post()
.and(warp::body::content_length_limit(1024 * 16))
.and(warp::body::json())
.and(warp::any().map(move || boot_tx.clone()))
.and(warp::any().map(move || boot_our_arc.lock().unwrap().take().unwrap()))
.and(warp::any().map(move || boot_net_keypair_arc.lock().unwrap().take().unwrap()))
.and(tx.clone())
.and(our_temp_id.clone())
.and(net_keypair.clone())
.and_then(handle_boot),
))
.or(warp::path("import-keyfile").and(
warp::post()
.and(warp::body::content_length_limit(1024 * 16))
.and(warp::body::json())
.and(warp::any().map(move || import_ip.clone()))
.and(warp::any().map(move || import_rpc_url.clone()))
.and(warp::any().map(move || import_tx.clone()))
.and(ip.clone())
.and(rpc_url.clone())
.and(tx.clone())
.and_then(handle_import_keyfile),
))
.or(warp::path("login").and(
warp::post()
.and(warp::body::content_length_limit(1024 * 16))
.and(warp::body::json())
.and(warp::any().map(move || ip.clone()))
.and(warp::any().map(move || rpc_url.clone()))
.and(warp::any().map(move || login_tx.clone()))
.and(warp::any().map(move || login_keyfile_arc.lock().unwrap().take().unwrap()))
.and(ip)
.and(rpc_url)
.and(tx.clone())
.and(keyfile.clone())
.and_then(handle_login),
))
.or(warp::path("confirm-change-network-keys").and(
warp::post()
.and(warp::body::content_length_limit(1024 * 16))
.and(warp::body::json())
.and(warp::any().map(move || tx.clone()))
.and(warp::any().map(move || our_temp_arc.lock().unwrap().take().unwrap()))
.and(warp::any().map(move || net_keypair_arc.lock().unwrap().take().unwrap()))
.and(warp::any().map(move || keyfile_arc.lock().unwrap().take().unwrap()))
.and(tx)
.and(our_temp_id)
.and(net_keypair)
.and(keyfile)
.and_then(confirm_change_network_keys),
));
@ -208,101 +211,72 @@ pub async fn register(
.await;
}
async fn get_unencrypted_info(
keyfile_arc: Arc<Mutex<Option<Vec<u8>>>>,
) -> Result<impl Reply, Rejection> {
async fn get_unencrypted_info(keyfile: Option<Vec<u8>>) -> Result<impl Reply, Rejection> {
let (name, allowed_routers) = {
match keyfile_arc.lock().unwrap().clone() {
Some(encoded_keyfile) => match keygen::get_username_and_routers(encoded_keyfile) {
match keyfile {
Some(encoded_keyfile) => match keygen::get_username_and_routers(&encoded_keyfile) {
Ok(k) => k,
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Failed to decode keyfile".to_string()),
StatusCode::INTERNAL_SERVER_ERROR,
warp::reply::json(&"Incorrect password"),
StatusCode::UNAUTHORIZED,
)
.into_response())
}
},
None => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Keyfile not present".to_string()),
warp::reply::json(&"Keyfile not present"),
StatusCode::NOT_FOUND,
)
.into_response())
}
}
};
let our = UnencryptedIdentity {
name,
allowed_routers,
};
Ok(warp::reply::with_status(Ok(warp::reply::json(&our)), StatusCode::OK).into_response())
Ok(warp::reply::with_status(
Ok(warp::reply::json(&UnencryptedIdentity {
name,
allowed_routers,
})),
StatusCode::OK,
)
.into_response())
}
async fn generate_networking_info(
ip: String,
our_temp_arc: Arc<Mutex<Option<Identity>>>,
networking_keypair_arc: Arc<Mutex<Option<Document>>>,
) -> Result<impl Reply, Rejection> {
let (public_key, serialized_networking_keypair) = keygen::generate_networking_key();
*networking_keypair_arc.lock().unwrap() = Some(serialized_networking_keypair);
// TODO: if IP is localhost, don't allow registration as direct
let ws_port = crate::http::utils::find_open_port(9000).await.unwrap();
// This is a temporary identity, passed to the UI. If it is confirmed through a /boot or /confirm-change-network-keys, then it will be used to replace the current identity
let our_temp = Identity {
networking_key: format!("0x{}", public_key),
name: "".to_string(),
ws_routing: Some((ip, ws_port)),
allowed_routers: vec![
"uqbar-router-1.uq".into(), // "0x8d9e54427c50660c6d4802f63edca86a9ca5fd6a78070c4635950e9d149ed441".into(),
"uqbar-router-2.uq".into(), // "0x06d331ed65843ecf0860c73292005d8103af20820546b2f8f9007d01f60595b1".into(),
"uqbar-router-3.uq".into(), // "0xe6ab611eb62e8aee0460295667f8179cda4315982717db4b0b3da6022deecac1".into(),
],
};
*our_temp_arc.lock().unwrap() = Some(our_temp.clone());
Ok(warp::reply::json(&our_temp))
async fn generate_networking_info(our_temp_id: Arc<Identity>) -> Result<impl Reply, Rejection> {
Ok(warp::reply::json(our_temp_id.as_ref()))
}
async fn handle_keyfile_vet(
payload: KeyfileVet,
keyfile_arc: Arc<Mutex<Option<Vec<u8>>>>,
keyfile: Option<Vec<u8>>,
) -> Result<impl Reply, Rejection> {
let encoded_keyfile = match payload.keyfile.is_empty() {
true => keyfile_arc.lock().unwrap().clone().unwrap(),
false => base64::decode(payload.keyfile).unwrap(),
true => keyfile.ok_or(warp::reject())?,
false => base64::decode(payload.keyfile).map_err(|_| warp::reject())?,
};
let decoded_keyfile = match keygen::decode_keyfile(encoded_keyfile, &payload.password) {
Ok(k) => k,
Err(_) => return Err(warp::reject()),
};
let decoded_keyfile =
keygen::decode_keyfile(&encoded_keyfile, &payload.password).map_err(|_| warp::reject())?;
let keyfile_vetted = KeyfileVetted {
Ok(warp::reply::json(&KeyfileVetted {
username: decoded_keyfile.username,
networking_key: format!(
"0x{}",
hex::encode(decoded_keyfile.networking_keypair.public_key().as_ref())
),
routers: decoded_keyfile.routers,
};
Ok(warp::reply::json(&keyfile_vetted))
}))
}
async fn handle_boot(
info: BootInfo,
sender: RegistrationSender,
mut our: Identity,
networking_keypair: Document,
sender: Arc<RegistrationSender>,
our: Arc<Identity>,
networking_keypair: Arc<Vec<u8>>,
) -> Result<impl Reply, Rejection> {
let mut our = our.as_ref().clone();
our.name = info.username;
if info.direct {
our.allowed_routers = vec![];
} else {
@ -326,35 +300,26 @@ async fn handle_boot(
info.password,
decoded_keyfile.username.clone(),
decoded_keyfile.routers.clone(),
networking_keypair,
networking_keypair.as_ref(),
decoded_keyfile.jwt_secret_bytes.clone(),
decoded_keyfile.file_key.clone(),
);
let encoded_keyfile_str = base64::encode(encoded_keyfile.clone());
success_response(
sender,
our,
decoded_keyfile,
encoded_keyfile,
encoded_keyfile_str,
)
.await
success_response(sender, our, decoded_keyfile, encoded_keyfile).await
}
async fn handle_import_keyfile(
info: ImportKeyfileInfo,
ip: String,
_rpc_url: String,
sender: RegistrationSender,
sender: Arc<RegistrationSender>,
) -> Result<impl Reply, Rejection> {
// if keyfile was not present in node and is present from user upload
let encoded_keyfile = match base64::decode(info.keyfile.clone()) {
Ok(k) => k,
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Keyfile not valid base64".to_string()),
warp::reply::json(&"Keyfile not valid base64"),
StatusCode::BAD_REQUEST,
)
.into_response())
@ -363,39 +328,38 @@ async fn handle_import_keyfile(
let Some(ws_port) = crate::http::utils::find_open_port(9000).await else {
return Ok(warp::reply::with_status(
warp::reply::json(&"Unable to find free port".to_string()),
warp::reply::json(&"Unable to find free port"),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response());
};
let (decoded_keyfile, our) =
match keygen::decode_keyfile(encoded_keyfile.clone(), &info.password) {
Ok(k) => {
let our = Identity {
name: k.username.clone(),
networking_key: format!(
"0x{}",
hex::encode(k.networking_keypair.public_key().as_ref())
),
ws_routing: if k.routers.is_empty() {
Some((ip, ws_port))
} else {
None
},
allowed_routers: k.routers.clone(),
};
let (decoded_keyfile, our) = match keygen::decode_keyfile(&encoded_keyfile, &info.password) {
Ok(k) => {
let our = Identity {
name: k.username.clone(),
networking_key: format!(
"0x{}",
hex::encode(k.networking_keypair.public_key().as_ref())
),
ws_routing: if k.routers.is_empty() {
Some((ip, ws_port))
} else {
None
},
allowed_routers: k.routers.clone(),
};
(k, our)
}
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Failed to decode keyfile".to_string()),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response())
}
};
(k, our)
}
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Incorrect Password".to_string()),
StatusCode::UNAUTHORIZED,
)
.into_response())
}
};
// if !networking_info_valid(rpc_url, ip, ws_port, &our).await {
// return Ok(warp::reply::with_status(
@ -405,114 +369,89 @@ async fn handle_import_keyfile(
// .into_response());
// }
let encoded_keyfile_str = info.keyfile.clone();
success_response(
sender,
our,
decoded_keyfile,
encoded_keyfile,
encoded_keyfile_str,
)
.await
success_response(sender, our, decoded_keyfile, encoded_keyfile).await
}
async fn handle_login(
info: LoginInfo,
ip: String,
_rpc_url: String,
sender: RegistrationSender,
encoded_keyfile: Vec<u8>,
sender: Arc<RegistrationSender>,
encoded_keyfile: Option<Vec<u8>>,
) -> Result<impl Reply, Rejection> {
if encoded_keyfile.is_empty() {
if encoded_keyfile.is_none() {
return Ok(warp::reply::with_status(
warp::reply::json(&"Keyfile not present".to_string()),
warp::reply::json(&"Keyfile not present"),
StatusCode::NOT_FOUND,
)
.into_response());
}
let encoded_keyfile = encoded_keyfile.unwrap();
let Some(ws_port) = crate::http::utils::find_open_port(9000).await else {
return Ok(warp::reply::with_status(
warp::reply::json(&"Unable to find free port".to_string()),
warp::reply::json(&"Unable to find free port"),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response());
};
let (decoded_keyfile, our) =
match keygen::decode_keyfile(encoded_keyfile.clone(), &info.password) {
Ok(k) => {
let our = Identity {
name: k.username.clone(),
networking_key: format!(
"0x{}",
hex::encode(k.networking_keypair.public_key().as_ref())
),
ws_routing: if k.routers.is_empty() {
Some((ip, ws_port))
} else {
None
},
allowed_routers: k.routers.clone(),
};
let (decoded_keyfile, our) = match keygen::decode_keyfile(&encoded_keyfile, &info.password) {
Ok(k) => {
let our = Identity {
name: k.username.clone(),
networking_key: format!(
"0x{}",
hex::encode(k.networking_keypair.public_key().as_ref())
),
ws_routing: if k.routers.is_empty() {
Some((ip, ws_port))
} else {
None
},
allowed_routers: k.routers.clone(),
};
(k, our)
}
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Failed to decode keyfile".to_string()),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response())
}
};
(k, our)
}
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Incorrect Password"),
StatusCode::UNAUTHORIZED,
)
.into_response())
}
};
// if !networking_info_valid(rpc_url, ip, ws_port, &our).await {
// return Ok(warp::reply::with_status(
// warp::reply::json(&"Networking info invalid".to_string()),
// StatusCode::UNAUTHORIZED,
// )
// .into_response());
// }
let encoded_keyfile_str = base64::encode(encoded_keyfile.clone());
success_response(
sender,
our,
decoded_keyfile,
encoded_keyfile,
encoded_keyfile_str,
)
.await
success_response(sender, our, decoded_keyfile, encoded_keyfile).await
}
async fn confirm_change_network_keys(
info: LoginAndResetInfo,
sender: RegistrationSender,
mut our: Identity, // the arc of our temporary identity
networking_keypair: Document,
encoded_keyfile: Vec<u8>,
sender: Arc<RegistrationSender>,
our: Arc<Identity>,
networking_keypair: Arc<Vec<u8>>,
encoded_keyfile: Option<Vec<u8>>,
) -> Result<impl Reply, Rejection> {
if encoded_keyfile.is_empty() {
if encoded_keyfile.is_none() {
return Ok(warp::reply::with_status(
warp::reply::json(&"Keyfile not present".to_string()),
warp::reply::json(&"Keyfile not present"),
StatusCode::NOT_FOUND,
)
.into_response());
}
let encoded_keyfile = encoded_keyfile.unwrap();
let mut our = our.as_ref().clone();
// Get our name from our current keyfile
let old_decoded_keyfile = match keygen::decode_keyfile(encoded_keyfile.clone(), &info.password)
{
let old_decoded_keyfile = match keygen::decode_keyfile(&encoded_keyfile, &info.password) {
Ok(k) => {
our.name = k.username.clone();
k
}
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Invalid password".to_string()),
warp::reply::json(&"Invalid password"),
StatusCode::UNAUTHORIZED,
)
.into_response());
@ -539,35 +478,26 @@ async fn confirm_change_network_keys(
info.password,
decoded_keyfile.username.clone(),
decoded_keyfile.routers.clone(),
networking_keypair,
networking_keypair.as_ref(),
decoded_keyfile.jwt_secret_bytes.clone(),
decoded_keyfile.file_key.clone(),
);
let encoded_keyfile_str = base64::encode(encoded_keyfile.clone());
success_response(
sender,
our,
decoded_keyfile,
encoded_keyfile,
encoded_keyfile_str,
)
.await
success_response(sender, our.clone(), decoded_keyfile, encoded_keyfile).await
}
async fn success_response(
sender: RegistrationSender,
sender: Arc<RegistrationSender>,
our: Identity,
decoded_keyfile: Keyfile,
encoded_keyfile: Vec<u8>,
encoded_keyfile_str: String,
) -> Result<warp::reply::Response, Rejection> {
let token = match generate_jwt(&decoded_keyfile.jwt_secret_bytes, our.name.clone()) {
let encoded_keyfile_str = base64::encode(&encoded_keyfile);
let token = match generate_jwt(&decoded_keyfile.jwt_secret_bytes, &our.name) {
Some(token) => token,
None => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Failed to generate JWT".to_string()),
warp::reply::json(&"Failed to generate JWT"),
StatusCode::SERVICE_UNAVAILABLE,
)
.into_response())
@ -591,26 +521,13 @@ async fn success_response(
}
Err(_) => {
return Ok(warp::reply::with_status(
warp::reply::json(&"Failed to generate Auth JWT".to_string()),
warp::reply::json(&"Failed to generate Auth JWT"),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response())
}
}
// match HeaderValue::from_str(&format!("uqbar-ws-auth_{}={};", &our.name, &token)) {
// Ok(v) => {
// headers.append(SET_COOKIE, v);
// },
// Err(_) => {
// return Ok(warp::reply::with_status(
// warp::reply::json(&"Failed to generate WS JWT".to_string()),
// StatusCode::INTERNAL_SERVER_ERROR,
// )
// .into_response())
// }
// }
Ok(response)
}

570
src/sqlite.rs Normal file
View File

@ -0,0 +1,570 @@
use anyhow::Result;
use dashmap::DashMap;
use rusqlite::types::{FromSql, FromSqlError, ToSql, ValueRef};
use rusqlite::Connection;
use std::collections::{HashMap, VecDeque};
use std::sync::Arc;
use tokio::fs;
use tokio::sync::Mutex;
use crate::types::*;
pub async fn sqlite(
our_node: String,
send_to_loop: MessageSender,
send_to_terminal: PrintSender,
mut recv_from_loop: MessageReceiver,
send_to_caps_oracle: CapMessageSender,
home_directory_path: String,
) -> anyhow::Result<()> {
let sqlite_path = format!("{}/sqlite", &home_directory_path);
if let Err(e) = fs::create_dir_all(&sqlite_path).await {
panic!("failed creating sqlite dir! {:?}", e);
}
let open_dbs: Arc<DashMap<(PackageId, String), Mutex<Connection>>> = Arc::new(DashMap::new());
let txs: Arc<DashMap<u64, Vec<(String, Vec<SqlValue>)>>> = Arc::new(DashMap::new());
let mut process_queues: HashMap<ProcessId, Arc<Mutex<VecDeque<KernelMessage>>>> =
HashMap::new();
loop {
tokio::select! {
Some(km) = recv_from_loop.recv() => {
if our_node.clone() != km.source.node {
println!(
"sqlite: request must come from our_node={}, got: {}",
our_node,
km.source.node,
);
continue;
}
let queue = process_queues
.entry(km.source.process.clone())
.or_insert_with(|| Arc::new(Mutex::new(VecDeque::new())))
.clone();
{
let mut queue_lock = queue.lock().await;
queue_lock.push_back(km.clone());
}
// clone Arcs
let our_node = our_node.clone();
let send_to_caps_oracle = send_to_caps_oracle.clone();
let send_to_terminal = send_to_terminal.clone();
let send_to_loop = send_to_loop.clone();
let open_dbs = open_dbs.clone();
let txs = txs.clone();
let sqlite_path = sqlite_path.clone();
tokio::spawn(async move {
let mut queue_lock = queue.lock().await;
if let Some(km) = queue_lock.pop_front() {
if let Err(e) = handle_request(
our_node.clone(),
km.clone(),
open_dbs.clone(),
txs.clone(),
send_to_loop.clone(),
send_to_terminal.clone(),
send_to_caps_oracle.clone(),
sqlite_path.clone(),
)
.await
{
let _ = send_to_loop
.send(make_error_message(our_node.clone(), &km, e))
.await;
}
}
});
}
}
}
}
async fn handle_request(
our_node: String,
km: KernelMessage,
open_dbs: Arc<DashMap<(PackageId, String), Mutex<Connection>>>,
txs: Arc<DashMap<u64, Vec<(String, Vec<SqlValue>)>>>,
send_to_loop: MessageSender,
send_to_terminal: PrintSender,
send_to_caps_oracle: CapMessageSender,
sqlite_path: String,
) -> Result<(), SqliteError> {
let KernelMessage {
id,
source,
message,
payload,
..
} = km.clone();
let Message::Request(Request {
ipc,
expects_response,
metadata,
..
}) = message.clone()
else {
return Err(SqliteError::InputError {
error: "not a request".into(),
});
};
let request: SqliteRequest = match serde_json::from_slice(&ipc) {
Ok(r) => r,
Err(e) => {
println!("sqlite: got invalid Request: {}", e);
return Err(SqliteError::InputError {
error: "didn't serialize to SqliteRequest.".into(),
});
}
};
check_caps(
our_node.clone(),
source.clone(),
open_dbs.clone(),
send_to_caps_oracle.clone(),
&request,
sqlite_path.clone(),
)
.await?;
let (ipc, bytes) = match request.action {
SqliteAction::New => {
// handled in check_caps
//
(serde_json::to_vec(&SqliteResponse::Ok).unwrap(), None)
}
SqliteAction::Read { query } => {
let db = match open_dbs.get(&(request.package_id, request.db)) {
Some(db) => db,
None => {
return Err(SqliteError::NoDb);
}
};
let db = db.lock().await;
let parameters = get_json_params(payload)?;
let mut statement = db.prepare(&query)?;
let column_names: Vec<String> = statement
.column_names()
.iter()
.map(|c| c.to_string())
.collect();
let results: Vec<HashMap<String, serde_json::Value>> = statement
.query_map(rusqlite::params_from_iter(parameters.iter()), |row| {
let mut map = HashMap::new();
for (i, column_name) in column_names.iter().enumerate() {
let value: SqlValue = row.get(i)?;
let value_json = match value {
SqlValue::Integer(int) => serde_json::Value::Number(int.into()),
SqlValue::Real(real) => serde_json::Value::Number(
serde_json::Number::from_f64(real).unwrap(),
),
SqlValue::Text(text) => serde_json::Value::String(text),
SqlValue::Blob(blob) => serde_json::Value::String(base64::encode(blob)), // or another representation if you prefer
_ => serde_json::Value::Null,
};
map.insert(column_name.clone(), value_json);
}
Ok(map)
})?
.collect::<Result<Vec<_>, _>>()?;
let results = serde_json::json!(results).to_string();
let results_bytes = results.as_bytes().to_vec();
(
serde_json::to_vec(&SqliteResponse::Read).unwrap(),
Some(results_bytes),
)
}
SqliteAction::Write { statement, tx_id } => {
let db = match open_dbs.get(&(request.package_id, request.db)) {
Some(db) => db,
None => {
return Err(SqliteError::NoDb);
}
};
let db = db.lock().await;
let parameters = get_json_params(payload)?;
match tx_id {
Some(tx_id) => {
txs.entry(tx_id)
.or_insert_with(Vec::new)
.push((statement.clone(), parameters));
}
None => {
let mut stmt = db.prepare(&statement)?;
stmt.execute(rusqlite::params_from_iter(parameters.iter()))?;
}
};
(serde_json::to_vec(&SqliteResponse::Ok).unwrap(), None)
}
SqliteAction::BeginTx => {
let tx_id = rand::random::<u64>();
txs.insert(tx_id, Vec::new());
(
serde_json::to_vec(&SqliteResponse::BeginTx { tx_id }).unwrap(),
None,
)
}
SqliteAction::Commit { tx_id } => {
let db = match open_dbs.get(&(request.package_id, request.db)) {
Some(db) => db,
None => {
return Err(SqliteError::NoDb);
}
};
let mut db = db.lock().await;
let txs = match txs.remove(&tx_id).map(|(_, tx)| tx) {
None => {
return Err(SqliteError::NoTx);
}
Some(tx) => tx,
};
let tx = db.transaction()?;
for (query, params) in txs {
tx.execute(&query, rusqlite::params_from_iter(params.iter()))?;
}
tx.commit()?;
(serde_json::to_vec(&SqliteResponse::Ok).unwrap(), None)
}
SqliteAction::Backup => {
// execute WAL flush.
//
(serde_json::to_vec(&SqliteResponse::Ok).unwrap(), None)
}
};
if let Some(target) = km.rsvp.or_else(|| {
expects_response.map(|_| Address {
node: our_node.clone(),
process: source.process.clone(),
})
}) {
let response = KernelMessage {
id,
source: Address {
node: our_node.clone(),
process: SQLITE_PROCESS_ID.clone(),
},
target,
rsvp: None,
message: Message::Response((
Response {
inherit: false,
ipc,
metadata,
},
None,
)),
payload: bytes.map(|bytes| Payload {
mime: Some("application/octet-stream".into()),
bytes,
}),
signed_capabilities: None,
};
let _ = send_to_loop.send(response).await;
} else {
send_to_terminal
.send(Printout {
verbosity: 2,
content: format!(
"sqlite: not sending response: {:?}",
serde_json::from_slice::<SqliteResponse>(&ipc)
),
})
.await
.unwrap();
}
Ok(())
}
async fn check_caps(
our_node: String,
source: Address,
open_dbs: Arc<DashMap<(PackageId, String), Mutex<Connection>>>,
mut send_to_caps_oracle: CapMessageSender,
request: &SqliteRequest,
sqlite_path: String,
) -> Result<(), SqliteError> {
let (send_cap_bool, recv_cap_bool) = tokio::sync::oneshot::channel();
let src_package_id = PackageId::new(source.process.package(), source.process.publisher());
match &request.action {
SqliteAction::Write { .. } | SqliteAction::BeginTx | SqliteAction::Commit { .. } => {
send_to_caps_oracle
.send(CapMessage::Has {
on: source.process.clone(),
cap: Capability {
issuer: Address {
node: our_node.clone(),
process: SQLITE_PROCESS_ID.clone(),
},
params: serde_json::to_string(&serde_json::json!({
"kind": "write",
"db": request.db.to_string(),
}))
.unwrap(),
},
responder: send_cap_bool,
})
.await?;
let has_cap = recv_cap_bool.await?;
if !has_cap {
return Err(SqliteError::NoCap {
error: request.action.to_string(),
});
}
Ok(())
}
SqliteAction::Read { .. } => {
send_to_caps_oracle
.send(CapMessage::Has {
on: source.process.clone(),
cap: Capability {
issuer: Address {
node: our_node.clone(),
process: SQLITE_PROCESS_ID.clone(),
},
params: serde_json::to_string(&serde_json::json!({
"kind": "read",
"db": request.db.to_string(),
}))
.unwrap(),
},
responder: send_cap_bool,
})
.await?;
let has_cap = recv_cap_bool.await?;
if !has_cap {
return Err(SqliteError::NoCap {
error: request.action.to_string(),
});
}
Ok(())
}
SqliteAction::New => {
if src_package_id != request.package_id {
return Err(SqliteError::NoCap {
error: request.action.to_string(),
});
}
add_capability(
"read",
&request.db.to_string(),
&our_node,
&source,
&mut send_to_caps_oracle,
)
.await?;
add_capability(
"write",
&request.db.to_string(),
&our_node,
&source,
&mut send_to_caps_oracle,
)
.await?;
let db_path = format!("{}{}", sqlite_path, request.db.to_string());
fs::create_dir_all(&db_path).await?;
let db = Connection::open(&db_path)?;
db.execute("PRAGMA journal_mode=WAL;", [])?;
open_dbs.insert(
(request.package_id.clone(), request.db.clone()),
Mutex::new(db),
);
Ok(())
}
SqliteAction::Backup => {
if source.process != *STATE_PROCESS_ID {
return Err(SqliteError::NoCap {
error: request.action.to_string(),
});
}
Ok(())
}
}
}
async fn add_capability(
kind: &str,
db: &str,
our_node: &str,
source: &Address,
send_to_caps_oracle: &mut CapMessageSender,
) -> Result<(), SqliteError> {
let cap = Capability {
issuer: Address {
node: our_node.to_string(),
process: SQLITE_PROCESS_ID.clone(),
},
params: serde_json::to_string(&serde_json::json!({ "kind": kind, "db": db })).unwrap(),
};
let (send_cap_bool, recv_cap_bool) = tokio::sync::oneshot::channel();
send_to_caps_oracle
.send(CapMessage::Add {
on: source.process.clone(),
cap,
responder: send_cap_bool,
})
.await?;
let _ = recv_cap_bool.await?;
Ok(())
}
fn json_to_sqlite(value: &serde_json::Value) -> Result<SqlValue, SqliteError> {
match value {
serde_json::Value::Number(n) => {
if let Some(int_val) = n.as_i64() {
Ok(SqlValue::Integer(int_val))
} else if let Some(float_val) = n.as_f64() {
Ok(SqlValue::Real(float_val))
} else {
Err(SqliteError::InvalidParameters)
}
}
serde_json::Value::String(s) => {
match base64::decode(&s) {
Ok(decoded_bytes) => {
// convert to SQLite Blob if it's a valid base64 string
Ok(SqlValue::Blob(decoded_bytes))
}
Err(_) => {
// if it's not base64, just use the string itself
Ok(SqlValue::Text(s.clone()))
}
}
}
serde_json::Value::Bool(b) => Ok(SqlValue::Boolean(*b)),
serde_json::Value::Null => Ok(SqlValue::Null),
_ => Err(SqliteError::InvalidParameters),
}
}
fn get_json_params(payload: Option<Payload>) -> Result<Vec<SqlValue>, SqliteError> {
match payload {
None => Ok(vec![]),
Some(payload) => match serde_json::from_slice::<serde_json::Value>(&payload.bytes) {
Ok(serde_json::Value::Array(vec)) => vec
.iter()
.map(|value| json_to_sqlite(value))
.collect::<Result<Vec<_>, _>>(),
_ => Err(SqliteError::InvalidParameters),
},
}
}
fn make_error_message(our_name: String, km: &KernelMessage, error: SqliteError) -> KernelMessage {
KernelMessage {
id: km.id,
source: Address {
node: our_name.clone(),
process: KV_PROCESS_ID.clone(),
},
target: match &km.rsvp {
None => km.source.clone(),
Some(rsvp) => rsvp.clone(),
},
rsvp: None,
message: Message::Response((
Response {
inherit: false,
ipc: serde_json::to_vec(&SqliteResponse::Err { error: error }).unwrap(),
metadata: None,
},
None,
)),
payload: None,
signed_capabilities: None,
}
}
impl ToSql for SqlValue {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
match self {
SqlValue::Integer(i) => i.to_sql(),
SqlValue::Real(f) => f.to_sql(),
SqlValue::Text(ref s) => s.to_sql(),
SqlValue::Blob(ref b) => b.to_sql(),
SqlValue::Boolean(b) => b.to_sql(),
SqlValue::Null => Ok(rusqlite::types::ToSqlOutput::Owned(
rusqlite::types::Value::Null,
)),
}
}
}
impl FromSql for SqlValue {
fn column_result(value: ValueRef<'_>) -> Result<Self, FromSqlError> {
match value {
ValueRef::Integer(i) => Ok(SqlValue::Integer(i)),
ValueRef::Real(f) => Ok(SqlValue::Real(f)),
ValueRef::Text(t) => {
let text_str = std::str::from_utf8(t).map_err(|_| FromSqlError::InvalidType)?;
Ok(SqlValue::Text(text_str.to_string()))
}
ValueRef::Blob(b) => Ok(SqlValue::Blob(b.to_vec())),
_ => Err(FromSqlError::InvalidType),
}
}
}
impl std::fmt::Display for SqliteAction {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<std::io::Error> for SqliteError {
fn from(err: std::io::Error) -> Self {
SqliteError::IOError {
error: err.to_string(),
}
}
}
impl From<rusqlite::Error> for SqliteError {
fn from(err: rusqlite::Error) -> Self {
SqliteError::RusqliteError {
error: err.to_string(),
}
}
}
impl From<tokio::sync::oneshot::error::RecvError> for SqliteError {
fn from(err: tokio::sync::oneshot::error::RecvError) -> Self {
SqliteError::NoCap {
error: err.to_string(),
}
}
}
impl From<tokio::sync::mpsc::error::SendError<CapMessage>> for SqliteError {
fn from(err: tokio::sync::mpsc::error::SendError<CapMessage>) -> Self {
SqliteError::NoCap {
error: err.to_string(),
}
}
}

650
src/state.rs Normal file
View File

@ -0,0 +1,650 @@
use anyhow::Result;
use rocksdb::checkpoint::Checkpoint;
use rocksdb::{Options, DB};
use std::collections::{HashMap, HashSet, VecDeque};
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use tokio::fs;
use tokio::sync::Mutex;
use crate::types::*;
pub async fn load_state(
our_name: String,
home_directory_path: String,
runtime_extensions: Vec<(ProcessId, MessageSender, bool)>,
) -> Result<(ProcessMap, DB), StateError> {
let state_path = format!("{}/kernel", &home_directory_path);
if let Err(e) = fs::create_dir_all(&state_path).await {
panic!("failed creating kernel state dir! {:?}", e);
}
// more granular kernel_state in column families
// let mut options = Option::default().unwrap();
// options.create_if_missing(true);
//let db = DB::open_default(&state_directory_path_str).unwrap();
let mut opts = Options::default();
opts.create_if_missing(true);
// let cf_name = "kernel_state";
// let cf_descriptor = ColumnFamilyDescriptor::new(cf_name, Options::default());
let db = DB::open_default(state_path).unwrap();
let mut process_map: ProcessMap = HashMap::new();
let kernel_id = process_to_vec(KERNEL_PROCESS_ID.clone());
match db.get(&kernel_id) {
Ok(Some(value)) => {
process_map = bincode::deserialize::<ProcessMap>(&value).unwrap();
}
Ok(None) => {
bootstrap(
&our_name,
home_directory_path.clone(),
runtime_extensions.clone(),
&mut process_map,
)
.await
.unwrap();
db.put(&kernel_id, bincode::serialize(&process_map).unwrap())
.unwrap();
}
Err(e) => {
panic!("failed to load kernel state from db: {:?}", e);
}
}
Ok((process_map, db))
}
pub async fn state_sender(
our_name: String,
send_to_loop: MessageSender,
send_to_terminal: PrintSender,
mut recv_state: MessageReceiver,
db: DB,
home_directory_path: String,
) -> Result<(), anyhow::Error> {
let db = Arc::new(db);
let mut process_queues: HashMap<ProcessId, Arc<Mutex<VecDeque<KernelMessage>>>> =
HashMap::new();
loop {
tokio::select! {
Some(km) = recv_state.recv() => {
if our_name != km.source.node {
println!(
"state: request must come from our_name={}, got: {}",
our_name, &km,
);
continue;
}
let queue = process_queues
.entry(km.source.process.clone())
.or_insert_with(|| Arc::new(Mutex::new(VecDeque::new())))
.clone();
{
let mut queue_lock = queue.lock().await;
queue_lock.push_back(km.clone());
}
let db_clone = db.clone();
let send_to_loop = send_to_loop.clone();
let send_to_terminal = send_to_terminal.clone();
let our_name = our_name.clone();
let home_directory_path = home_directory_path.clone();
tokio::spawn(async move {
let mut queue_lock = queue.lock().await;
if let Some(km) = queue_lock.pop_front() {
if let Err(e) = handle_request(
our_name.clone(),
km.clone(),
db_clone,
send_to_loop.clone(),
send_to_terminal,
home_directory_path,
)
.await
{
let _ = send_to_loop
.send(make_error_message(our_name.clone(), &km, e))
.await;
}
}
});
}
}
}
}
async fn handle_request(
our_name: String,
kernel_message: KernelMessage,
db: Arc<DB>,
send_to_loop: MessageSender,
_send_to_terminal: PrintSender,
home_directory_path: String,
) -> Result<(), StateError> {
let KernelMessage {
id,
source,
rsvp,
message,
payload,
..
} = kernel_message;
let Message::Request(Request {
expects_response,
ipc,
metadata, // for kernel
..
}) = message
else {
return Err(StateError::BadRequest {
error: "not a request".into(),
});
};
let action: StateAction = match serde_json::from_slice(&ipc) {
Ok(r) => r,
Err(e) => {
return Err(StateError::BadJson {
error: format!("parse into StateAction failed: {:?}", e),
})
}
};
let (ipc, bytes) = match action {
StateAction::SetState(process_id) => {
let key = process_to_vec(process_id);
let Some(ref payload) = payload else {
return Err(StateError::BadBytes {
action: "SetState".into(),
});
};
db.put(key, &payload.bytes)
.map_err(|e| StateError::RocksDBError {
action: "SetState".into(),
error: e.to_string(),
})?;
(serde_json::to_vec(&StateResponse::SetState).unwrap(), None)
}
StateAction::GetState(process_id) => {
let key = process_to_vec(process_id.clone());
match db.get(key) {
Ok(Some(value)) => (
serde_json::to_vec(&StateResponse::GetState).unwrap(),
Some(value),
),
Ok(None) => {
return Err(StateError::NotFound {
process_id: process_id.clone(),
});
}
Err(e) => {
println!("get state error: {:?}", e);
return Err(StateError::RocksDBError {
action: "GetState".into(),
error: e.to_string(),
});
}
}
}
StateAction::DeleteState(process_id) => {
let key = process_to_vec(process_id);
match db.delete(key) {
Ok(_) => (
serde_json::to_vec(&StateResponse::DeleteState).unwrap(),
None,
),
Err(e) => {
println!("delete state error: {:?}", e);
return Err(StateError::RocksDBError {
action: "DeleteState".into(),
error: e.to_string(),
});
}
}
}
StateAction::Backup => {
// handle Backup action
println!("got backup");
let checkpoint_dir = format!("{}/kernel/checkpoint", &home_directory_path);
if Path::new(&checkpoint_dir).exists() {
let _ = fs::remove_dir_all(&checkpoint_dir).await;
}
let checkpoint = Checkpoint::new(&db).unwrap();
checkpoint.create_checkpoint(&checkpoint_dir).unwrap();
(serde_json::to_vec(&StateResponse::Backup).unwrap(), None)
}
};
if let Some(target) = rsvp.or_else(|| {
expects_response.map(|_| Address {
node: our_name.clone(),
process: source.process.clone(),
})
}) {
let response = KernelMessage {
id,
source: Address {
node: our_name.clone(),
process: STATE_PROCESS_ID.clone(),
},
target,
rsvp: None,
message: Message::Response((
Response {
inherit: false,
ipc,
metadata,
},
None,
)),
payload: bytes.map(|bytes| Payload {
mime: Some("application/octet-stream".into()),
bytes,
}),
signed_capabilities: None,
};
let _ = send_to_loop.send(response).await;
};
Ok(())
}
/// function run only upon fresh boot.
///
/// for each folder in /modules, looks for a package.zip file, extracts the contents,
/// sends the contents to VFS, and reads the manifest.json.
///
/// the manifest.json contains instructions for which processes to boot and what
/// capabilities to give them. since we are inside runtime, can spawn those out of
/// thin air.
async fn bootstrap(
our_name: &str,
home_directory_path: String,
runtime_extensions: Vec<(ProcessId, MessageSender, bool)>,
process_map: &mut ProcessMap,
) -> Result<()> {
println!("bootstrapping node...\r");
let mut runtime_caps: HashSet<Capability> = HashSet::new();
// kernel is a special case
runtime_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: ProcessId::from_str("kernel:sys:uqbar").unwrap(),
},
params: "\"messaging\"".into(),
});
// net is a special case
runtime_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: ProcessId::from_str("net:sys:uqbar").unwrap(),
},
params: "\"messaging\"".into(),
});
for runtime_module in runtime_extensions.clone() {
runtime_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: runtime_module.0,
},
params: "\"messaging\"".into(),
});
}
// give all runtime processes the ability to send messages across the network
runtime_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: KERNEL_PROCESS_ID.clone(),
},
params: "\"network\"".into(),
});
// finally, save runtime modules in state map as well, somewhat fakely
// special cases for kernel and net
process_map
.entry(ProcessId::from_str("kernel:sys:uqbar").unwrap())
.or_insert(PersistedProcess {
wasm_bytes_handle: "".into(),
on_exit: OnExit::Restart,
capabilities: runtime_caps.clone(),
public: false,
});
process_map
.entry(ProcessId::from_str("net:sys:uqbar").unwrap())
.or_insert(PersistedProcess {
wasm_bytes_handle: "".into(),
on_exit: OnExit::Restart,
capabilities: runtime_caps.clone(),
public: false,
});
for runtime_module in runtime_extensions {
process_map
.entry(runtime_module.0)
.or_insert(PersistedProcess {
wasm_bytes_handle: "".into(),
on_exit: OnExit::Restart,
capabilities: runtime_caps.clone(),
public: runtime_module.2,
});
}
let packages: Vec<(String, zip::ZipArchive<std::io::Cursor<Vec<u8>>>)> =
get_zipped_packages().await;
for (package_name, mut package) in packages {
// special case tester: only load it in if in simulation mode
if package_name == "tester" {
#[cfg(not(feature = "simulation-mode"))]
continue;
#[cfg(feature = "simulation-mode")]
{}
}
println!("fs: handling package {package_name}...\r");
// get and read metadata.json
let Ok(mut package_metadata_zip) = package.by_name("metadata.json") else {
println!(
"fs: missing metadata for package {}, skipping",
package_name
);
continue;
};
let mut metadata_content = Vec::new();
package_metadata_zip
.read_to_end(&mut metadata_content)
.unwrap();
drop(package_metadata_zip);
let package_metadata: serde_json::Value =
serde_json::from_slice(&metadata_content).expect("fs: metadata parse error");
println!("fs: found package metadata: {:?}\r", package_metadata);
let package_name = package_metadata["package"]
.as_str()
.expect("fs: metadata parse error: bad package name");
let package_publisher = package_metadata["publisher"]
.as_str()
.expect("fs: metadata parse error: bad publisher name");
// create a new package in VFS
let our_drive_name = [package_name, package_publisher].join(":");
let pkg_path = format!("{}/vfs/{}/pkg", &home_directory_path, &our_drive_name);
fs::create_dir_all(&pkg_path)
.await
.expect("bootstrap vfs dir pkg creation failed!");
let drive_path = format!("/{}/pkg", &our_drive_name);
// for each file in package.zip, recursively through all dirs, send a newfile KM to VFS
for i in 0..package.len() {
let mut file = package.by_index(i).unwrap();
if file.is_file() {
let file_path = file
.enclosed_name()
.expect("fs: name error reading package.zip")
.to_owned();
let mut file_path = file_path.to_string_lossy().to_string();
if !file_path.starts_with('/') {
file_path = format!("/{}", file_path);
}
println!("fs: found file {}...\r", file_path);
let mut file_content = Vec::new();
file.read_to_end(&mut file_content).unwrap();
let path = format!("{}/{}", &pkg_path, file_path);
fs::write(&path, file_content).await.unwrap();
}
}
// get and read manifest.json
let Ok(mut package_manifest_zip) = package.by_name("manifest.json") else {
println!(
"fs: missing manifest for package {}, skipping",
package_name
);
continue;
};
let mut manifest_content = Vec::new();
package_manifest_zip
.read_to_end(&mut manifest_content)
.unwrap();
drop(package_manifest_zip);
let package_manifest = String::from_utf8(manifest_content)?;
let package_manifest = serde_json::from_str::<Vec<PackageManifestEntry>>(&package_manifest)
.expect("fs: manifest parse error");
// for each process-entry in manifest.json:
for mut entry in package_manifest {
let wasm_bytes = &mut Vec::new();
let mut file_path = entry.process_wasm_path.to_string();
if file_path.starts_with('/') {
file_path = file_path[1..].to_string();
}
package
.by_name(&file_path)
.expect("fs: no wasm found in package!")
.read_to_end(wasm_bytes)
.unwrap();
// spawn the requested capabilities
// remember: out of thin air, because this is the root distro
let mut requested_caps = HashSet::new();
let our_process_id = format!(
"{}:{}:{}",
entry.process_name, package_name, package_publisher
);
entry.request_messaging = Some(entry.request_messaging.unwrap_or_default());
if let Some(ref mut request_messaging) = entry.request_messaging {
request_messaging.push(serde_json::Value::String(our_process_id.clone()));
for value in request_messaging {
match value {
serde_json::Value::String(process_name) => {
requested_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: ProcessId::from_str(process_name).unwrap(),
},
params: "\"messaging\"".into(),
});
}
serde_json::Value::Object(map) => {
if let Some(process_name) = map.get("process") {
if let Some(params) = map.get("params") {
requested_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: ProcessId::from_str(
process_name.as_str().unwrap(),
)
.unwrap(),
},
params: params.to_string(),
});
}
}
}
_ => {
// other json types
continue;
}
}
}
}
// grant capabilities to other initially spawned processes, distro
if let Some(to_grant) = &entry.grant_messaging {
for value in to_grant {
let mut capability = None;
let mut to_process = None;
match value {
serde_json::Value::String(process_name) => {
if let Ok(parsed_process_id) = ProcessId::from_str(process_name) {
capability = Some(Capability {
issuer: Address {
node: our_name.to_string(),
process: ProcessId::from_str(process_name).unwrap(),
},
params: "\"messaging\"".into(),
});
to_process = Some(parsed_process_id);
}
}
serde_json::Value::Object(map) => {
if let Some(process_name) = map.get("process") {
if let Ok(parsed_process_id) =
ProcessId::from_str(&process_name.as_str().unwrap())
{
if let Some(params) = map.get("params") {
capability = Some(Capability {
issuer: Address {
node: our_name.to_string(),
process: ProcessId::from_str(
process_name.as_str().unwrap(),
)
.unwrap(),
},
params: params.to_string(),
});
to_process = Some(parsed_process_id);
}
}
}
}
_ => {
continue;
}
}
if let Some(cap) = capability {
if let Some(process) = process_map.get_mut(&to_process.unwrap()) {
process.capabilities.insert(cap);
}
}
}
}
if entry.request_networking {
requested_caps.insert(Capability {
issuer: Address {
node: our_name.to_string(),
process: KERNEL_PROCESS_ID.clone(),
},
params: "\"network\"".into(),
});
}
// give access to package_name vfs
requested_caps.insert(Capability {
issuer: Address {
node: our_name.into(),
process: VFS_PROCESS_ID.clone(),
},
params: serde_json::to_string(&serde_json::json!({
"kind": "read",
"drive": drive_path,
}))
.unwrap(),
});
requested_caps.insert(Capability {
issuer: Address {
node: our_name.into(),
process: VFS_PROCESS_ID.clone(),
},
params: serde_json::to_string(&serde_json::json!({
"kind": "write",
"drive": drive_path,
}))
.unwrap(),
});
let public_process = entry.public;
let wasm_bytes_handle = format!("{}/{}", &drive_path, &file_path);
process_map.insert(
ProcessId::new(Some(&entry.process_name), package_name, package_publisher),
PersistedProcess {
wasm_bytes_handle,
on_exit: entry.on_exit,
capabilities: requested_caps,
public: public_process,
},
);
}
}
Ok(())
}
/// go into /target folder and get all .zip package files
async fn get_zipped_packages() -> Vec<(String, zip::ZipArchive<std::io::Cursor<Vec<u8>>>)> {
println!("fs: reading distro packages...\r");
let target_path = std::path::Path::new("target");
let mut packages = Vec::new();
if let Ok(mut entries) = fs::read_dir(target_path).await {
while let Ok(Some(entry)) = entries.next_entry().await {
if entry.file_name().to_string_lossy().ends_with(".zip") {
let package_name = entry
.file_name()
.to_string_lossy()
.trim_end_matches(".zip")
.to_string();
if let Ok(bytes) = fs::read(entry.path()).await {
if let Ok(zip) = zip::ZipArchive::new(std::io::Cursor::new(bytes)) {
// add to list of packages
println!("fs: found package: {}\r", package_name);
packages.push((package_name, zip));
}
}
}
}
}
packages
}
fn make_error_message(our_name: String, km: &KernelMessage, error: StateError) -> KernelMessage {
KernelMessage {
id: km.id,
source: Address {
node: our_name.clone(),
process: STATE_PROCESS_ID.clone(),
},
target: match &km.rsvp {
None => km.source.clone(),
Some(rsvp) => rsvp.clone(),
},
rsvp: None,
message: Message::Response((
Response {
inherit: false,
ipc: serde_json::to_vec(&StateResponse::Err(error)).unwrap(),
metadata: None,
},
None,
)),
payload: None,
signed_capabilities: None,
}
}
fn process_to_vec(process: ProcessId) -> Vec<u8> {
process.to_string().as_bytes().to_vec()
}

View File

@ -146,7 +146,13 @@ pub async fn terminal(
stdout,
cursor::MoveTo(0, win_rows - 1),
terminal::Clear(ClearType::CurrentLine),
Print(format!("{} {}/{} {:02}:{:02} ",
Print(format!("{}{} {}/{} {:02}:{:02} ",
match printout.verbosity {
0 => "",
1 => "1",
2 => "2",
_ => "3",
},
now.weekday(),
now.month(),
now.day(),

View File

@ -7,13 +7,15 @@ use thiserror::Error;
lazy_static::lazy_static! {
pub static ref ENCRYPTOR_PROCESS_ID: ProcessId = ProcessId::new(Some("encryptor"), "sys", "uqbar");
pub static ref ETH_RPC_PROCESS_ID: ProcessId = ProcessId::new(Some("eth_rpc"), "sys", "uqbar");
pub static ref FILESYSTEM_PROCESS_ID: ProcessId = ProcessId::new(Some("filesystem"), "sys", "uqbar");
pub static ref HTTP_CLIENT_PROCESS_ID: ProcessId = ProcessId::new(Some("http_client"), "sys", "uqbar");
pub static ref HTTP_SERVER_PROCESS_ID: ProcessId = ProcessId::new(Some("http_server"), "sys", "uqbar");
pub static ref KERNEL_PROCESS_ID: ProcessId = ProcessId::new(Some("kernel"), "sys", "uqbar");
pub static ref TERMINAL_PROCESS_ID: ProcessId = ProcessId::new(Some("terminal"), "terminal", "uqbar");
pub static ref TIMER_PROCESS_ID: ProcessId = ProcessId::new(Some("timer"), "sys", "uqbar");
pub static ref VFS_PROCESS_ID: ProcessId = ProcessId::new(Some("vfs"), "sys", "uqbar");
pub static ref STATE_PROCESS_ID: ProcessId = ProcessId::new(Some("state"), "sys", "uqbar");
pub static ref KV_PROCESS_ID: ProcessId = ProcessId::new(Some("kv"), "sys", "uqbar");
pub static ref SQLITE_PROCESS_ID: ProcessId = ProcessId::new(Some("sqlite"), "sys", "uqbar");
}
//
@ -66,13 +68,13 @@ pub struct PackageId {
}
impl PackageId {
pub fn _new(package_name: &str, publisher_node: &str) -> Self {
pub fn new(package_name: &str, publisher_node: &str) -> Self {
PackageId {
package_name: package_name.into(),
publisher_node: publisher_node.into(),
}
}
pub fn _from_str(input: &str) -> Result<Self, ProcessIdParseError> {
pub fn from_str(input: &str) -> Result<Self, ProcessIdParseError> {
// split string on colons into 2 segments
let mut segments = input.split(':');
let package_name = segments
@ -428,18 +430,54 @@ pub enum SendErrorKind {
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum OnPanic {
pub enum OnExit {
None,
Restart,
Requests(Vec<(Address, Request, Option<Payload>)>),
}
impl OnPanic {
impl OnExit {
pub fn is_restart(&self) -> bool {
match self {
OnPanic::None => false,
OnPanic::Restart => true,
OnPanic::Requests(_) => false,
OnExit::None => false,
OnExit::Restart => true,
OnExit::Requests(_) => false,
}
}
pub fn en_wit(&self) -> wit::OnExit {
match self {
OnExit::None => wit::OnExit::None,
OnExit::Restart => wit::OnExit::Restart,
OnExit::Requests(reqs) => wit::OnExit::Requests(
reqs.iter()
.map(|(address, request, payload)| {
(
address.en_wit(),
en_wit_request(request.clone()),
en_wit_payload(payload.clone()),
)
})
.collect(),
),
}
}
pub fn de_wit(wit: wit::OnExit) -> Self {
match wit {
wit::OnExit::None => OnExit::None,
wit::OnExit::Restart => OnExit::Restart,
wit::OnExit::Requests(reqs) => OnExit::Requests(
reqs.into_iter()
.map(|(address, request, payload)| {
(
Address::de_wit(address),
de_wit_request(request),
de_wit_payload(payload),
)
})
.collect(),
),
}
}
}
@ -585,23 +623,6 @@ pub fn en_wit_send_error_kind(kind: SendErrorKind) -> wit::SendErrorKind {
}
}
pub fn de_wit_on_panic(wit: wit::OnPanic) -> OnPanic {
match wit {
wit::OnPanic::None => OnPanic::None,
wit::OnPanic::Restart => OnPanic::Restart,
wit::OnPanic::Requests(reqs) => OnPanic::Requests(
reqs.into_iter()
.map(|(address, request, payload)| {
(
Address::de_wit(address),
de_wit_request(request),
de_wit_payload(payload),
)
})
.collect(),
),
}
}
//
// END SYNC WITH process_lib
//
@ -715,8 +736,8 @@ pub struct IdentityTransaction {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ProcessMetadata {
pub our: Address,
pub wasm_bytes_handle: u128,
pub on_panic: OnPanic,
pub wasm_bytes_handle: String,
pub on_exit: OnExit,
pub public: bool,
}
@ -791,8 +812,8 @@ pub enum KernelCommand {
/// for the new process if `public` is false.
InitializeProcess {
id: ProcessId,
wasm_bytes_handle: u128,
on_panic: OnPanic,
wasm_bytes_handle: String,
on_exit: OnExit,
initial_capabilities: HashSet<SignedCapability>,
public: bool,
},
@ -846,10 +867,8 @@ pub type ProcessMap = HashMap<ProcessId, PersistedProcess>;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PersistedProcess {
pub wasm_bytes_handle: u128,
// pub drive: String,
// pub full_path: String,
pub on_panic: OnPanic,
pub wasm_bytes_handle: String,
pub on_exit: OnExit,
pub capabilities: HashSet<Capability>,
pub public: bool, // marks if a process allows messages from any process
}
@ -870,6 +889,7 @@ pub struct PackageMetadata {
pub package: String,
pub publisher: String,
pub version: PackageVersion,
pub wit_version: Option<(u32, u32, u32)>,
pub description: Option<String>,
pub website: Option<String>,
}
@ -879,214 +899,266 @@ pub struct PackageMetadata {
pub struct PackageManifestEntry {
pub process_name: String,
pub process_wasm_path: String,
pub on_panic: OnPanic,
pub on_exit: OnExit,
pub request_networking: bool,
pub request_messaging: Option<Vec<String>>,
pub grant_messaging: Option<Vec<String>>,
pub request_messaging: Option<Vec<serde_json::Value>>,
pub grant_messaging: Option<Vec<serde_json::Value>>,
pub public: bool,
}
#[derive(Serialize, Deserialize, Debug)]
pub enum FsAction {
Write(Option<u128>),
WriteOffset((u128, u64)),
Append(Option<u128>),
Read(u128),
ReadChunk(ReadChunkRequest),
Delete(u128),
Length(u128),
SetLength((u128, u64)),
pub enum StateAction {
GetState(ProcessId),
SetState(ProcessId),
DeleteState(ProcessId),
Backup,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ReadChunkRequest {
pub file: u128,
pub start: u64,
pub length: u64,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum FsResponse {
Write(u128),
Read(u128),
ReadChunk(u128), // TODO: remove?
Append(u128),
Delete(u128),
Length(u64),
pub enum StateResponse {
GetState,
SetState,
}
#[derive(Debug)]
pub struct S3Config {
pub access_key: String,
pub secret_key: String,
pub region: String,
pub bucket: String,
pub endpoint: String,
}
#[derive(Debug)]
pub struct FsConfig {
pub s3_config: Option<S3Config>,
pub mem_buffer_limit: usize,
pub read_cache_limit: usize,
pub chunk_size: usize,
pub flush_to_cold_interval: usize,
pub encryption: bool,
pub cloud_enabled: bool,
// pub flush_to_wal_interval: usize,
DeleteState,
Backup,
Err(StateError),
}
#[derive(Error, Debug, Serialize, Deserialize)]
pub enum FsError {
#[error("fs: Bytes payload required for {action}.")]
pub enum StateError {
#[error("kernel_state: rocksdb internal error: {error}")]
RocksDBError { action: String, error: String },
#[error("kernel_state: startup error")]
StartupError { action: String },
#[error("kernel_state: bytes payload required for {action}")]
BadBytes { action: String },
#[error(
"fs: JSON payload could not be parsed to FsAction: {:?}, error: {:?}.",
json,
error
)]
BadJson { json: String, error: String },
#[error("fs: No JSON payload.")]
NoJson,
#[error("fs: Read failed to file {file}: {error}.")]
ReadFailed { file: u128, error: String },
#[error("fs: Write failed to file {file}: {error}.")]
WriteFailed { file: u128, error: String },
#[error("fs: file not found: {file}")]
NotFound { file: u128 },
#[error("fs: S3 error: {error}")]
S3Error { error: String },
#[error("fs: IO error: {error}")]
#[error("kernel_state: bad request error: {error}")]
BadRequest { error: String },
#[error("kernel_state: Bad JSON payload: {error}")]
BadJson { error: String },
#[error("kernel_state: state not found for ProcessId {process_id}")]
NotFound { process_id: ProcessId },
#[error("kernel_state: IO error: {error}")]
IOError { error: String },
#[error("fs: Encryption error: {error}")]
EncryptionError { error: String },
#[error("fs: Limit error: {error}")]
LimitError { error: String },
#[error("fs: memory buffer error: {error}")]
MemoryBufferError { error: String },
#[error("fs: length operation error: {error}")]
LengthError { error: String },
#[error("fs: creating fs dir failed at path: {path}: {error}")]
CreateInitialDirError { path: String, error: String },
}
#[allow(dead_code)]
impl FsError {
impl StateError {
pub fn kind(&self) -> &str {
match *self {
FsError::BadBytes { .. } => "BadBytes",
FsError::BadJson { .. } => "BadJson",
FsError::NoJson { .. } => "NoJson",
FsError::ReadFailed { .. } => "ReadFailed",
FsError::WriteFailed { .. } => "WriteFailed",
FsError::S3Error { .. } => "S3Error",
FsError::IOError { .. } => "IOError",
FsError::EncryptionError { .. } => "EncryptionError",
FsError::LimitError { .. } => "LimitError",
FsError::MemoryBufferError { .. } => "MemoryBufferError",
FsError::NotFound { .. } => "NotFound",
FsError::LengthError { .. } => "LengthError",
FsError::CreateInitialDirError { .. } => "CreateInitialDirError",
StateError::RocksDBError { .. } => "RocksDBError",
StateError::StartupError { .. } => "StartupError",
StateError::BadBytes { .. } => "BadBytes",
StateError::BadRequest { .. } => "BadRequest",
StateError::BadJson { .. } => "NoJson",
StateError::NotFound { .. } => "NotFound",
StateError::IOError { .. } => "IOError",
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct VfsRequest {
pub drive: String,
pub path: String,
pub action: VfsAction,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum VfsAction {
New,
Add {
full_path: String,
entry_type: AddEntryType,
},
Rename {
full_path: String,
new_full_path: String,
},
Delete(String),
WriteOffset {
full_path: String,
offset: u64,
},
Append(String),
SetSize {
full_path: String,
size: u64,
},
GetPath(u128),
GetHash(String),
GetEntry(String),
GetFileChunk {
full_path: String,
offset: u64,
length: u64,
},
GetEntryLength(String),
CreateDrive,
CreateDir,
CreateDirAll,
CreateFile,
OpenFile,
CloseFile,
WriteAll,
Write,
ReWrite,
WriteAt(u64),
Append,
SyncAll,
Read,
ReadDir,
ReadToEnd,
ReadExact(u64),
ReadToString,
Seek(SeekFrom),
RemoveFile,
RemoveDir,
RemoveDirAll,
Rename(String),
AddZip,
Len,
SetLen(u64),
Hash,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum AddEntryType {
Dir,
NewFile, // add a new file to fs and add name in vfs
ExistingFile { hash: u128 }, // link an existing file in fs to a new name in vfs
ZipArchive,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum GetEntryType {
Dir,
File,
pub enum SeekFrom {
Start(u64),
End(i64),
Current(i64),
}
#[derive(Debug, Serialize, Deserialize)]
pub enum VfsResponse {
Ok,
Err(VfsError),
GetPath(Option<String>),
GetHash(Option<u128>),
GetEntry {
// file bytes in payload, if entry was a file
is_file: bool,
children: Vec<String>,
},
GetFileChunk, // chunk in payload, if file exists
GetEntryLength(Option<u64>),
Read,
ReadDir(Vec<String>),
ReadToString(String),
Len(u64),
Hash([u8; 32]),
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Error, Debug, Serialize, Deserialize)]
pub enum VfsError {
BadJson,
BadPayload,
BadDriveName,
BadDescriptor,
NoCap,
EntryNotFound,
PersistError,
InternalError, // String
#[error("vfs: No capability for action {action} at path {path}")]
NoCap { action: String, path: String },
#[error("vfs: Bytes payload required for {action} at path {path}")]
BadBytes { action: String, path: String },
#[error("vfs: bad request error: {error}")]
BadRequest { error: String },
#[error("vfs: error parsing path: {path}, error: {error}")]
ParseError { error: String, path: String },
#[error("vfs: IO error: {error}, at path {path}")]
IOError { error: String, path: String },
#[error("vfs: kernel capability channel error: {error}")]
CapChannelFail { error: String },
#[error("vfs: Bad JSON payload: {error}")]
BadJson { error: String },
#[error("vfs: File not found at path {path}")]
NotFound { path: String },
#[error("vfs: Creating directory failed at path: {path}: {error}")]
CreateDirError { path: String, error: String },
}
#[allow(dead_code)]
impl VfsError {
pub fn kind(&self) -> &str {
match *self {
VfsError::BadJson => "BadJson",
VfsError::BadPayload => "BadPayload",
VfsError::BadDriveName => "BadDriveName",
VfsError::BadDescriptor => "BadDescriptor",
VfsError::NoCap => "NoCap",
VfsError::EntryNotFound => "EntryNotFound",
VfsError::PersistError => "PersistError",
VfsError::InternalError => "InternalError",
VfsError::NoCap { .. } => "NoCap",
VfsError::BadBytes { .. } => "BadBytes",
VfsError::BadRequest { .. } => "BadRequest",
VfsError::ParseError { .. } => "ParseError",
VfsError::IOError { .. } => "IOError",
VfsError::CapChannelFail { .. } => "CapChannelFail",
VfsError::BadJson { .. } => "NoJson",
VfsError::NotFound { .. } => "NotFound",
VfsError::CreateDirError { .. } => "CreateDirError",
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct KvRequest {
pub package_id: PackageId,
pub db: String,
pub action: KvAction,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum KvAction {
New,
Set { key: Vec<u8>, tx_id: Option<u64> },
Delete { key: Vec<u8>, tx_id: Option<u64> },
Get { key: Vec<u8> },
BeginTx,
Commit { tx_id: u64 },
Backup,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum KvResponse {
Ok,
BeginTx { tx_id: u64 },
Get { key: Vec<u8> },
Err { error: KvError },
}
#[derive(Debug, Serialize, Deserialize, Error)]
pub enum KvError {
#[error("kv: DbDoesNotExist")]
NoDb,
#[error("kv: DbAlreadyExists")]
DbAlreadyExists,
#[error("kv: KeyNotFound")]
KeyNotFound,
#[error("kv: no Tx found")]
NoTx,
#[error("kv: No capability: {error}")]
NoCap { error: String },
#[error("kv: rocksdb internal error: {error}")]
RocksDBError { action: String, error: String },
#[error("kv: input bytes/json/key error: {error}")]
InputError { error: String },
#[error("kv: IO error: {error}")]
IOError { error: String },
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SqliteRequest {
pub package_id: PackageId,
pub db: String,
pub action: SqliteAction,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum SqliteAction {
New,
Write {
statement: String,
tx_id: Option<u64>,
},
Read {
query: String,
},
BeginTx,
Commit {
tx_id: u64,
},
Backup,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum SqliteResponse {
Ok,
Read,
BeginTx { tx_id: u64 },
Err { error: SqliteError },
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum SqlValue {
Integer(i64),
Real(f64),
Text(String),
Blob(Vec<u8>),
Boolean(bool),
Null,
}
#[derive(Debug, Serialize, Deserialize, Error)]
pub enum SqliteError {
#[error("sqlite: DbDoesNotExist")]
NoDb,
#[error("sqlite: DbAlreadyExists")]
DbAlreadyExists,
#[error("sqlite: NoTx")]
NoTx,
#[error("sqlite: No capability: {error}")]
NoCap { error: String },
#[error("sqlite: UnexpectedResponse")]
UnexpectedResponse,
#[error("sqlite: NotAWriteKeyword")]
NotAWriteKeyword,
#[error("sqlite: NotAReadKeyword")]
NotAReadKeyword,
#[error("sqlite: Invalid Parameters")]
InvalidParameters,
#[error("sqlite: IO error: {error}")]
IOError { error: String },
#[error("sqlite: rusqlite error: {error}")]
RusqliteError { error: String },
#[error("sqlite: input bytes/json/key error: {error}")]
InputError { error: String },
}

2092
src/vfs.rs

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,188 +0,0 @@
package uqbar:process@0.4.0;
interface standard {
// JSON is passed over WASM boundary as a string.
type json = string;
type node-id = string;
// context, like ipc, is a protocol-defined serialized byte array.
// it is used when building a Request to save information
// that will not be part of a Response, in order to more
// easily handle ("contextualize") that Response.
type context = list<u8>;
record process-id {
process-name: string,
package-name: string,
publisher-node: node-id,
}
// TODO better name for this
record address {
node: node-id,
process: process-id,
}
record payload {
mime: option<string>,
bytes: list<u8>,
}
record request {
// if true, this request inherits context AND payload of incipient
// request, and cannot have its own context.
inherit: bool,
// if Some, this request expects a response in the number of seconds given
expects-response: option<u64>,
ipc: list<u8>,
metadata: option<json>,
// to grab payload, use get_payload()
}
record response {
inherit: bool,
ipc: list<u8>,
metadata: option<json>,
// to grab payload, use get_payload()
}
// a message can be a request or a response.
// within a response, there is a result which surfaces any error
// that happened because of a request.
// a successful response will contain the context of the request
// it matches, if any was set.
variant message {
request(request),
response(tuple<response, option<context>>),
}
variant capabilities {
none,
all,
some(list<signed-capability>),
}
record signed-capability {
issuer: address,
params: json,
signature: list<u8>,
}
// on-panic is a setting that determines what happens when a process panics.
// NOTE: requests should have expects-response set to false, will always be set to that by kernel
variant on-panic {
none,
restart,
requests(list<tuple<address, request, option<payload>>>),
}
// network errors come from trying to send a message to another node.
// a message can fail by timing out, or by the node being entirely unreachable (offline).
// in either case, the message is not delivered, and the process that sent it
// receives that message along with any assigned context and/or payload,
// and is free to handle it as it sees fit.
// note that if the message is a response, the process can issue a response again,
// and it will be directed to the same (remote) request as the original.
record send-error {
kind: send-error-kind,
message: message,
payload: option<payload>,
}
enum send-error-kind {
offline,
timeout,
}
enum spawn-error {
name-taken,
no-file-at-path,
// TODO more here?
}
// system utils:
print-to-terminal: func(verbosity: u8, message: string);
// **more will be added here with regard to blockchains**
get-eth-block: func() -> u64;
// process management:
set-on-panic: func(on-panic: on-panic);
get-state: func() -> option<list<u8>>;
set-state: func(bytes: list<u8>);
clear-state: func();
spawn: func(
name: option<string>,
wasm-path: string, // must be located within package's drive
on-panic: on-panic,
capabilities: capabilities,
public: bool
) -> result<process-id, spawn-error>;
// capabilities management
// gives us all our signed capabilities so we can send them to others
get-capabilities: func() -> list<signed-capability>;
// gets a single specific capability
get-capability: func(issuer: address, params: json) -> option<signed-capability>;
// attaches a specific signed capability to our next message
attach-capability: func(capability: signed-capability);
// saves capabilities to our store, so we can use them
save-capabilities: func(capabilities: list<signed-capability>);
// check to see if the sender of a prompting message has a given capability, issued by us
// if the prompting message has a remote source, they must have attached it.
has-capability: func(params: json) -> bool;
// generates a new capability with our process as the issuer and gives it to the target,
// which must be a locally-running process.
create-capability: func(to: process-id, params: json);
// take a signed capability and save it to a given locally-running process
share-capability: func(to: process-id, capability: signed-capability);
// message I/O:
// ingest next message when it arrives along with its source.
// almost all long-running processes will call this in a loop
receive: func() -> result<tuple<address, message>, tuple<send-error, option<context>>>;
// gets payload, if any, of the message we just received
get-payload: func() -> option<payload>;
// send message(s) to target(s)
send-request:
func(target: address, request: request, context: option<context>, payload: option<payload>);
send-requests:
func(requests: list<tuple<address, request, option<context>, option<payload>>>);
send-response:
func(response: response, payload: option<payload>);
// send a single request, then block (internally) until its response
// the type is Message but will always contain Response
send-and-await-response:
func(target: address, request: request, payload: option<payload>) ->
result<tuple<address, message>, send-error>;
}
world lib {
import standard;
}
world process {
include lib;
export init: func(our: string);
}