mirror of
https://github.com/uqbar-dao/nectar.git
synced 2025-01-09 03:00:48 +03:00
commit
acc1a942e1
12
.gitignore
vendored
12
.gitignore
vendored
@ -1,5 +1,8 @@
|
||||
target/
|
||||
wit/
|
||||
**/target/
|
||||
**/wit/
|
||||
**/*.wasm
|
||||
.vscode
|
||||
.app-signing
|
||||
.DS_Store
|
||||
@ -7,9 +10,8 @@ wit/
|
||||
*.swo
|
||||
*.zip
|
||||
/home
|
||||
modules/**/pkg/*.wasm
|
||||
modules/**/wit
|
||||
target.wasm
|
||||
world
|
||||
packages/**/pkg/*.wasm
|
||||
packages/**/wit
|
||||
.env
|
||||
src/bootstrapped_processes.rs
|
||||
kinode/src/bootstrapped_processes.rs
|
||||
kinode/packages/**/wasi_snapshot_preview1.wasm
|
||||
|
2864
Cargo.lock
generated
2864
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
90
Cargo.toml
90
Cargo.toml
@ -1,75 +1,33 @@
|
||||
[package]
|
||||
name = "kinode"
|
||||
name = "kinode_lib"
|
||||
authors = ["KinodeDAO"]
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
edition = "2021"
|
||||
description = "A general-purpose sovereign cloud computing platform"
|
||||
homepage = "https://kinode.org"
|
||||
repository = "https://github.com/kinode-dao/kinode"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[build-dependencies]
|
||||
reqwest = { version = "0.11.22", features = ["blocking"] }
|
||||
sha2 = "0.10"
|
||||
walkdir = "2.4"
|
||||
zip = "0.6"
|
||||
|
||||
[features]
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
aes-gcm = "0.10.2"
|
||||
anyhow = "1.0.71"
|
||||
async-trait = "0.1.71"
|
||||
base64 = "0.13"
|
||||
bincode = "1.3.3"
|
||||
blake3 = "1.4.1"
|
||||
bytes = "1.4.0"
|
||||
cap-std = "2.0.0"
|
||||
chacha20poly1305 = "0.10.1"
|
||||
chrono = "0.4.31"
|
||||
clap = { version = "4.4", features = ["derive"] }
|
||||
crossterm = { version = "0.26.1", features = ["event-stream", "bracketed-paste"] }
|
||||
dashmap = "5.5.3"
|
||||
digest = "0.10"
|
||||
elliptic-curve = { version = "0.13.8", features = ["ecdh"] }
|
||||
ethers = "2.0"
|
||||
ethers-providers = "2.0.9"
|
||||
flate2 = "1.0"
|
||||
futures = "0.3"
|
||||
generic-array = "0.14"
|
||||
getrandom = "0.2.10"
|
||||
hex = "0.4.3"
|
||||
hkdf = "0.12.3"
|
||||
hmac = "0.12"
|
||||
http = "0.2.9"
|
||||
jwt = "0.16"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.20"
|
||||
nohash-hasher = "0.2.0"
|
||||
num-traits = "0.2"
|
||||
open = "5.0.0"
|
||||
public-ip = "0.2.2"
|
||||
rand = "0.8.4"
|
||||
reqwest = "0.11.18"
|
||||
ring = "0.16.20"
|
||||
rmp-serde = "1.1.2"
|
||||
rocksdb = { version = "0.21.0", features = ["multi-threaded-cf"] }
|
||||
route-recognizer = "0.3.1"
|
||||
rusqlite = { version = "0.30.0", features = ["bundled"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_urlencoded = "0.7"
|
||||
sha2 = "0.10"
|
||||
snow = { version = "0.9.3", features = ["ring-resolver"] }
|
||||
static_dir = "0.2.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.28", features = ["fs", "macros", "rt-multi-thread", "signal", "sync"] }
|
||||
tokio-stream = "0.1.14"
|
||||
tokio-tungstenite = "0.20.1"
|
||||
url = "2.4.1"
|
||||
uuid = { version = "1.1.2", features = ["serde", "v4"] }
|
||||
warp = "0.3.5"
|
||||
wasmtime = "15.0.1"
|
||||
wasmtime-wasi = "15.0.1"
|
||||
zip = "0.6"
|
||||
lib = { path = "lib" }
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"lib", "kinode",
|
||||
"kinode/packages/app_store/app_store", "kinode/packages/app_store/ft_worker",
|
||||
"kinode/packages/app_store/download", "kinode/packages/app_store/install", "kinode/packages/app_store/uninstall",
|
||||
"kinode/packages/chess/chess",
|
||||
"kinode/packages/homepage/homepage",
|
||||
"kinode/packages/kns_indexer/kns_indexer",
|
||||
"kinode/packages/terminal/terminal",
|
||||
"kinode/packages/terminal/alias", "kinode/packages/terminal/cat", "kinode/packages/terminal/echo", "kinode/packages/terminal/hi", "kinode/packages/terminal/m", "kinode/packages/terminal/top",
|
||||
"kinode/packages/tester/tester", "kinode/packages/tester/test_runner",
|
||||
]
|
||||
default-members = ["lib"]
|
||||
resolver = "2"
|
||||
|
||||
[profile.release]
|
||||
strip = "symbols"
|
||||
lto = true
|
||||
panic = "abort"
|
||||
codegen-units = 1
|
||||
|
11
README.md
11
README.md
@ -20,10 +20,6 @@ If you have questions, join the [Kinode discord](https://discord.gg/TCgdca5Bjt)
|
||||
|
||||
git clone git@github.com:kinode-dao/kinode.git
|
||||
|
||||
# Configure dependency retrieval from GitHub
|
||||
mkdir .cargo
|
||||
echo "net.git-fetch-with-cli = true" > .cargo/config
|
||||
|
||||
# Get some stuff so we can build Wasm.
|
||||
|
||||
cd kinode
|
||||
@ -33,10 +29,10 @@ rustup target add wasm32-wasi
|
||||
rustup target add wasm32-wasi --toolchain nightly
|
||||
cargo install cargo-wasi
|
||||
|
||||
# Build the runtime, along with a number of booted-at-startup WASM modules including terminal and key_value
|
||||
# Build the runtime, along with a number of "distro" WASM modules
|
||||
# OPTIONAL: --release flag
|
||||
|
||||
cargo +nightly build --release
|
||||
cargo +nightly build -p kinode
|
||||
```
|
||||
|
||||
### Boot
|
||||
@ -46,7 +42,8 @@ Make sure not to use the same home directory for two nodes at once! You can use
|
||||
|
||||
TODO: document feature flags `--simulation-mode`
|
||||
```bash
|
||||
cargo +nightly run --release -- home --rpc wss://eth-sepolia.g.alchemy.com/v2/<your-api-key> --tesnet
|
||||
# OPTIONAL: --release flag
|
||||
cargo +nightly run -p kinode -- home --rpc wss://<your-api-url> --testnet
|
||||
```
|
||||
|
||||
On boot you will be prompted to navigate to `localhost:8080`. Make sure your ETH wallet is connected to the Sepolia test network. Login should be straightforward, just submit the transactions and follow the flow. If you want to register a new ID you will either need [Sepolia testnet tokens](https://www.infura.io/faucet/sepolia) or an invite code.
|
||||
|
230
build.rs
230
build.rs
@ -1,230 +0,0 @@
|
||||
use std::process::Command;
|
||||
use std::{
|
||||
fs, io,
|
||||
io::{Read, Write},
|
||||
};
|
||||
|
||||
fn run_command(cmd: &mut Command) -> io::Result<()> {
|
||||
let status = cmd.status()?;
|
||||
if status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Command failed"))
|
||||
}
|
||||
}
|
||||
|
||||
fn file_outdated<P1, P2>(input: P1, output: P2) -> io::Result<bool>
|
||||
where
|
||||
P1: AsRef<std::path::Path>,
|
||||
P2: AsRef<std::path::Path>,
|
||||
{
|
||||
let out_meta = std::fs::metadata(output);
|
||||
if let Ok(meta) = out_meta {
|
||||
let output_mtime = meta.modified()?;
|
||||
|
||||
// if input file is more recent than our output, we are outdated
|
||||
let input_meta = fs::metadata(input)?;
|
||||
let input_mtime = input_meta.modified()?;
|
||||
|
||||
Ok(input_mtime > output_mtime)
|
||||
} else {
|
||||
// output file not found, we are outdated
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
fn build_app(target_path: &str, name: &str, parent_pkg_path: Option<&str>) {
|
||||
let pwd = std::env::current_dir().unwrap();
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
// if and only if module's wit is outdated, re-set-up build environment
|
||||
if file_outdated(
|
||||
format!("{}/target.wasm", pwd.display()),
|
||||
format!("{}/target/bindings/{}/target.wasm", target_path, name),
|
||||
)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
// create target/bindings directory
|
||||
fs::create_dir_all(format!("{}/target/bindings/{}", target_path, name,)).unwrap();
|
||||
// copy newly-made target.wasm into target/bindings
|
||||
run_command(Command::new("cp").args([
|
||||
"target.wasm",
|
||||
&format!("{}/target/bindings/{}/", target_path, name,),
|
||||
]))
|
||||
.unwrap();
|
||||
// copy newly-made world into target/bindings
|
||||
run_command(Command::new("cp").args([
|
||||
"world",
|
||||
&format!("{}/target/bindings/{}/", target_path, name,),
|
||||
]))
|
||||
.unwrap();
|
||||
}
|
||||
// Build the module targeting wasm32-wasi
|
||||
let bash_build_path = &format!("{}/build.sh", target_path);
|
||||
if std::path::Path::new(&bash_build_path).exists() {
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
std::env::set_current_dir(target_path).unwrap();
|
||||
run_command(Command::new("/bin/bash").arg("build.sh")).unwrap();
|
||||
std::env::set_current_dir(cwd).unwrap();
|
||||
} else {
|
||||
run_command(Command::new("cargo").args([
|
||||
"+nightly",
|
||||
"build",
|
||||
"--release",
|
||||
"--no-default-features",
|
||||
&format!("--manifest-path={}/Cargo.toml", target_path),
|
||||
"--target",
|
||||
"wasm32-wasi",
|
||||
]))
|
||||
.unwrap();
|
||||
}
|
||||
// Adapt module to component with adapter based on wasi_snapshot_preview1.wasm
|
||||
run_command(Command::new("wasm-tools").args([
|
||||
"component",
|
||||
"new",
|
||||
&format!("{}/target/wasm32-wasi/release/{}.wasm", target_path, name),
|
||||
"-o",
|
||||
&format!(
|
||||
"{}/target/wasm32-wasi/release/{}_adapted.wasm",
|
||||
target_path, name
|
||||
),
|
||||
"--adapt",
|
||||
&format!("{}/wasi_snapshot_preview1.wasm", pwd.display()),
|
||||
]))
|
||||
.unwrap();
|
||||
|
||||
// Determine the destination for the .wasm file after embedding wit
|
||||
let wasm_dest_path = if let Some(parent_pkg) = parent_pkg_path {
|
||||
format!("{}/{}.wasm", parent_pkg, name)
|
||||
} else {
|
||||
let pkg_folder = format!("{}/pkg/", target_path);
|
||||
let _ = run_command(Command::new("mkdir").args(["-p", &pkg_folder]));
|
||||
format!("{}/{}.wasm", pkg_folder, name)
|
||||
};
|
||||
|
||||
// Embed "wit" into the component
|
||||
run_command(Command::new("wasm-tools").args([
|
||||
"component",
|
||||
"embed",
|
||||
"wit",
|
||||
"--world",
|
||||
"process",
|
||||
&format!(
|
||||
"{}/target/wasm32-wasi/release/{}_adapted.wasm",
|
||||
target_path, name
|
||||
),
|
||||
"-o",
|
||||
&wasm_dest_path,
|
||||
]))
|
||||
.unwrap();
|
||||
|
||||
let end = std::time::Instant::now();
|
||||
println!(
|
||||
"cargo:warning=building {} took {:?}",
|
||||
target_path,
|
||||
end.duration_since(start)
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if std::env::var("SKIP_BUILD_SCRIPT").is_ok() {
|
||||
println!("Skipping build script");
|
||||
return;
|
||||
}
|
||||
|
||||
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("kinode.wit");
|
||||
if !wit_file.exists() {
|
||||
// TODO: cache in better way
|
||||
let mut wit_file = std::fs::File::create(&wit_file).unwrap();
|
||||
let kinode_wit_url =
|
||||
"https://raw.githubusercontent.com/kinode-dao/kinode-wit/master/kinode.wit";
|
||||
let mut response = reqwest::blocking::get(kinode_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",
|
||||
"wit",
|
||||
&format!("{}/wit/", pwd.display()),
|
||||
"-o",
|
||||
"target.wasm",
|
||||
"--wasm",
|
||||
]))
|
||||
.unwrap();
|
||||
run_command(Command::new("touch").args([&format!("{}/world", pwd.display())])).unwrap();
|
||||
|
||||
// Build wasm32-wasi apps, zip, and add to bootstrapped_processes.rs
|
||||
let mut bootstrapped_processes =
|
||||
fs::File::create(format!("{}/src/bootstrapped_processes.rs", pwd.display(),)).unwrap();
|
||||
writeln!(
|
||||
bootstrapped_processes,
|
||||
"pub static BOOTSTRAPPED_PROCESSES: &[(&str, &[u8])] = &[",
|
||||
)
|
||||
.unwrap();
|
||||
let modules_dir = format!("{}/modules", pwd.display());
|
||||
for entry in std::fs::read_dir(modules_dir).unwrap() {
|
||||
let entry_path = entry.unwrap().path();
|
||||
|
||||
// Build the app
|
||||
let parent_pkg_path = format!("{}/pkg", entry_path.display());
|
||||
fs::create_dir_all(&parent_pkg_path).unwrap();
|
||||
|
||||
// Otherwise, consider it a directory containing subdirectories with potential apps
|
||||
for sub_entry in std::fs::read_dir(&entry_path).unwrap() {
|
||||
let sub_entry_path = sub_entry.unwrap().path();
|
||||
if sub_entry_path.join("Cargo.toml").exists() {
|
||||
build_app(
|
||||
&sub_entry_path.display().to_string(),
|
||||
sub_entry_path.file_name().unwrap().to_str().unwrap(),
|
||||
Some(&parent_pkg_path),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// After processing all sub-apps, zip the parent's pkg/ directory
|
||||
let zip_filename = format!("{}.zip", entry_path.file_name().unwrap().to_str().unwrap(),);
|
||||
let zip_path = format!("{}/target/{}", pwd.display(), zip_filename,);
|
||||
let writer = std::fs::File::create(&zip_path).unwrap();
|
||||
let options = zip::write::FileOptions::default()
|
||||
.compression_method(zip::CompressionMethod::Stored)
|
||||
.unix_permissions(0o755);
|
||||
let mut zip = zip::ZipWriter::new(writer);
|
||||
for sub_entry in walkdir::WalkDir::new(&parent_pkg_path) {
|
||||
let sub_entry = sub_entry.unwrap();
|
||||
let path = sub_entry.path();
|
||||
let name = path
|
||||
.strip_prefix(std::path::Path::new(&parent_pkg_path))
|
||||
.unwrap();
|
||||
|
||||
// Write a directory or file to the ZIP archive
|
||||
if path.is_file() {
|
||||
zip.start_file(name.to_string_lossy().into_owned(), options)
|
||||
.unwrap();
|
||||
let mut file = std::fs::File::open(path).unwrap();
|
||||
let mut buffer = Vec::new();
|
||||
file.read_to_end(&mut buffer).unwrap();
|
||||
zip.write_all(&buffer).unwrap();
|
||||
} else if !name.as_os_str().is_empty() {
|
||||
zip.add_directory(name.to_string_lossy().into_owned(), options)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
zip.finish().unwrap();
|
||||
|
||||
// Add zip bytes to bootstrapped_processes.rs
|
||||
writeln!(
|
||||
bootstrapped_processes,
|
||||
" (\"{}\", include_bytes!(\"{}\")),",
|
||||
zip_filename, zip_path,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
writeln!(bootstrapped_processes, "];").unwrap();
|
||||
}
|
6161
kinode/Cargo.lock
generated
Normal file
6161
kinode/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
83
kinode/Cargo.toml
Normal file
83
kinode/Cargo.toml
Normal file
@ -0,0 +1,83 @@
|
||||
[package]
|
||||
name = "kinode"
|
||||
authors = ["KinodeDAO"]
|
||||
version = "0.5.3"
|
||||
edition = "2021"
|
||||
description = "A general-purpose sovereign cloud computing platform"
|
||||
homepage = "https://kinode.org"
|
||||
repository = "https://github.com/kinode-dao/kinode"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[[bin]]
|
||||
name = "kinode"
|
||||
path = "src/main.rs"
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.71"
|
||||
kit = { git = "https://github.com/kinode-dao/kit", rev = "15a4681" }
|
||||
reqwest = { version = "0.11.22", features = ["blocking"] }
|
||||
sha2 = "0.10"
|
||||
tokio = { version = "1.28", features = ["macros"] }
|
||||
walkdir = "2.4"
|
||||
zip = "0.6"
|
||||
|
||||
[features]
|
||||
simulation-mode = []
|
||||
|
||||
[dependencies]
|
||||
aes-gcm = "0.10.2"
|
||||
anyhow = "1.0.71"
|
||||
async-trait = "0.1.71"
|
||||
base64 = "0.13"
|
||||
bincode = "1.3.3"
|
||||
blake3 = "1.4.1"
|
||||
bytes = "1.4.0"
|
||||
cap-std = "2.0.0"
|
||||
chacha20poly1305 = "0.10.1"
|
||||
chrono = "0.4.31"
|
||||
clap = { version = "4.4", features = ["derive"] }
|
||||
crossterm = { version = "0.26.1", features = ["event-stream", "bracketed-paste"] }
|
||||
dashmap = "5.5.3"
|
||||
digest = "0.10"
|
||||
elliptic-curve = { version = "0.13.8", features = ["ecdh"] }
|
||||
ethers = "2.0.13"
|
||||
ethers-providers = "2.0.13"
|
||||
flate2 = "1.0"
|
||||
futures = "0.3"
|
||||
generic-array = "0.14"
|
||||
getrandom = "0.2.10"
|
||||
hex = "0.4.3"
|
||||
hkdf = "0.12.3"
|
||||
hmac = "0.12"
|
||||
http = "0.2.9"
|
||||
jwt = "0.16"
|
||||
lib = { path = "../lib" }
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.20"
|
||||
nohash-hasher = "0.2.0"
|
||||
num-traits = "0.2"
|
||||
open = "5.0.0"
|
||||
public-ip = "0.2.2"
|
||||
rand = "0.8.4"
|
||||
reqwest = "0.11.18"
|
||||
ring = "0.16.20"
|
||||
rmp-serde = "1.1.2"
|
||||
rocksdb = { version = "0.21.0", features = ["multi-threaded-cf"] }
|
||||
route-recognizer = "0.3.1"
|
||||
rusqlite = { version = "0.30.0", features = ["bundled"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_urlencoded = "0.7"
|
||||
sha2 = "0.10"
|
||||
snow = { version = "0.9.3", features = ["ring-resolver"] }
|
||||
static_dir = "0.2.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.28", features = ["fs", "macros", "rt-multi-thread", "signal", "sync"] }
|
||||
tokio-stream = "0.1.14"
|
||||
tokio-tungstenite = "0.20.1"
|
||||
url = "2.4.1"
|
||||
uuid = { version = "1.1.2", features = ["serde", "v4"] }
|
||||
warp = "0.3.5"
|
||||
wasmtime = "15.0.1"
|
||||
wasmtime-wasi = "15.0.1"
|
||||
zip = "0.6"
|
90
kinode/build.rs
Normal file
90
kinode/build.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use std::{
|
||||
fs,
|
||||
io::{Cursor, Read, Write},
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
if std::env::var("SKIP_BUILD_SCRIPT").is_ok() {
|
||||
println!("Skipping build script");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let pwd = std::env::current_dir().unwrap();
|
||||
let parent_dir = pwd.parent().unwrap();
|
||||
|
||||
// Build wasm32-wasi apps, zip, and add to bootstrapped_processes.rs
|
||||
let mut bootstrapped_processes = Vec::new();
|
||||
writeln!(
|
||||
bootstrapped_processes,
|
||||
"pub static BOOTSTRAPPED_PROCESSES: &[(&str, &[u8])] = &[",
|
||||
)
|
||||
.unwrap();
|
||||
let packages_dir = format!("{}/packages", pwd.display());
|
||||
eprintln!("{packages_dir:?}");
|
||||
for entry in std::fs::read_dir(packages_dir).unwrap() {
|
||||
let entry_path = entry.unwrap().path();
|
||||
let parent_pkg_path = format!("{}/pkg", entry_path.display());
|
||||
|
||||
kit::build::execute(&entry_path, false, false, false, true).await?;
|
||||
|
||||
// After processing all sub-apps, zip the parent's pkg/ directory
|
||||
let mut writer = Cursor::new(Vec::new());
|
||||
{
|
||||
let options = zip::write::FileOptions::default()
|
||||
.compression_method(zip::CompressionMethod::Stored)
|
||||
.unix_permissions(0o755);
|
||||
let mut zip = zip::ZipWriter::new(&mut writer);
|
||||
for sub_entry in walkdir::WalkDir::new(&parent_pkg_path) {
|
||||
let sub_entry = sub_entry.unwrap();
|
||||
let path = sub_entry.path();
|
||||
let name = path
|
||||
.strip_prefix(std::path::Path::new(&parent_pkg_path))
|
||||
.unwrap();
|
||||
|
||||
// Write a directory or file to the ZIP archive
|
||||
if path.is_file() {
|
||||
zip.start_file(name.to_string_lossy().into_owned(), options)
|
||||
.unwrap();
|
||||
let mut file = std::fs::File::open(path).unwrap();
|
||||
let mut buffer = Vec::new();
|
||||
file.read_to_end(&mut buffer).unwrap();
|
||||
zip.write_all(&buffer).unwrap();
|
||||
} else if !name.as_os_str().is_empty() {
|
||||
zip.add_directory(name.to_string_lossy().into_owned(), options)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
zip.finish().unwrap();
|
||||
}
|
||||
let zip_contents = writer.into_inner();
|
||||
let zip_filename = format!("{}.zip", entry_path.file_name().unwrap().to_str().unwrap(),);
|
||||
let zip_path = format!("{}/target/{}", parent_dir.display(), zip_filename);
|
||||
if !std::path::Path::new(&zip_path).exists() {
|
||||
fs::write(&zip_path, zip_contents)?;
|
||||
} else {
|
||||
let existing_zip_contents = fs::read(&zip_path)?;
|
||||
if zip_contents != existing_zip_contents {
|
||||
fs::write(&zip_path, zip_contents)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Add zip bytes to bootstrapped_processes.rs
|
||||
writeln!(
|
||||
bootstrapped_processes,
|
||||
" (\"{}\", include_bytes!(\"{}\")),",
|
||||
zip_filename, zip_path,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
writeln!(bootstrapped_processes, "];").unwrap();
|
||||
let bootstrapped_processes_path = pwd.join("src/bootstrapped_processes.rs");
|
||||
if bootstrapped_processes_path.exists() {
|
||||
let existing_bootstrapped_processes = fs::read(&bootstrapped_processes_path)?;
|
||||
if bootstrapped_processes == existing_bootstrapped_processes {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
fs::write(&bootstrapped_processes_path, bootstrapped_processes)?;
|
||||
Ok(())
|
||||
}
|
@ -1,23 +1,25 @@
|
||||
[package]
|
||||
name = "ft_worker"
|
||||
version = "0.2.0"
|
||||
name = "app_store"
|
||||
version = "0.3.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]
|
||||
alloy-primitives = "0.5.1"
|
||||
alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy.git", rev = "3b1c310" }
|
||||
alloy-sol-types = "0.5.1"
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.5-alpha" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha", features = ["eth"] }
|
||||
rand = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
sha2 = "0.10.8"
|
||||
sha3 = "0.10.8"
|
||||
url = "2.4.1"
|
||||
urlencoding = "2.1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
||||
zip = { version = "0.6.6", default-features = false }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
140
kinode/packages/app_store/app_store/src/api.rs
Normal file
140
kinode/packages/app_store/app_store/src/api.rs
Normal file
@ -0,0 +1,140 @@
|
||||
use kinode_process_lib::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
//
|
||||
// app store API
|
||||
//
|
||||
|
||||
/// Remote requests, those sent between instantiations of this process
|
||||
/// on different nodes, take this form. Will add more here in the future
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum RemoteRequest {
|
||||
/// Request a package from another node who we expect to
|
||||
/// be mirroring it. If the remote node is mirroring the package,
|
||||
/// they must respond with RemoteResponse::DownloadApproved,
|
||||
/// at which point requester can expect an FTWorkerRequest::Receive.
|
||||
Download {
|
||||
package_id: PackageId,
|
||||
desired_version_hash: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
/// The response expected from sending a [`RemoteRequest`].
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum RemoteResponse {
|
||||
DownloadApproved,
|
||||
DownloadDenied, // TODO expand on why
|
||||
Metadata,
|
||||
}
|
||||
|
||||
/// Local requests sent to the app store take this form.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum LocalRequest {
|
||||
/// Expects a zipped package as blob, and creates a new package from it.
|
||||
///
|
||||
/// If requested, will return a NewPackageResponse indicating success/failure.
|
||||
/// This is used for locally installing a package.
|
||||
NewPackage {
|
||||
package: PackageId,
|
||||
/// Sets whether we will mirror this package for others
|
||||
mirror: bool,
|
||||
},
|
||||
/// Try to download a package from a specified node.
|
||||
///
|
||||
/// If requested, will return a DownloadResponse indicating success/failure.
|
||||
/// No blob is expected.
|
||||
Download {
|
||||
package: PackageId,
|
||||
download_from: NodeId,
|
||||
/// Sets whether we will mirror this package for others
|
||||
mirror: bool,
|
||||
/// Sets whether we will try to automatically update this package
|
||||
/// when a new version is posted to the listings contract
|
||||
auto_update: bool,
|
||||
/// The version hash we're looking for. If None, will download the latest.
|
||||
desired_version_hash: Option<String>,
|
||||
},
|
||||
/// Select a downloaded package and install it. Will only succeed if the
|
||||
/// package is currently in the filesystem. If the package has *already*
|
||||
/// been installed, this will kill the running package and reset it with
|
||||
/// what's on disk.
|
||||
///
|
||||
/// If requested, will return an InstallResponse indicating success/failure.
|
||||
/// No blob is expected.
|
||||
Install(PackageId),
|
||||
/// Select an installed package and uninstall it.
|
||||
/// This will kill the processes in the **manifest** of the package,
|
||||
/// but not the processes that were spawned by those processes! Take
|
||||
/// care to kill those processes yourself. This will also delete the drive
|
||||
/// containing the source code for this package. This does not guarantee
|
||||
/// that other data created by this package will be removed from places such
|
||||
/// as the key-value store.
|
||||
///
|
||||
/// If requested, will return an UninstallResponse indicating success/failure.
|
||||
/// No blob is expected.
|
||||
Uninstall(PackageId),
|
||||
/// Start mirroring a package. This will fail if the package has not been downloaded.
|
||||
StartMirroring(PackageId),
|
||||
/// Stop mirroring a package. This will fail if the package has not been downloaded.
|
||||
StopMirroring(PackageId),
|
||||
/// Turn on automatic updates to a package. This will fail if the package has not been downloaded.
|
||||
StartAutoUpdate(PackageId),
|
||||
/// Turn off automatic updates to a package. This will fail if the package has not been downloaded.
|
||||
StopAutoUpdate(PackageId),
|
||||
/// This is an expensive operation! Throw away our state and rebuild from scratch.
|
||||
/// Re-index the locally downloaded/installed packages AND the onchain data.
|
||||
RebuildIndex,
|
||||
}
|
||||
|
||||
/// Local responses take this form.
|
||||
/// The variant of `LocalResponse` given will match the `LocalRequest` it is
|
||||
/// responding to.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum LocalResponse {
|
||||
NewPackageResponse(NewPackageResponse),
|
||||
DownloadResponse(DownloadResponse),
|
||||
InstallResponse(InstallResponse),
|
||||
UninstallResponse(UninstallResponse),
|
||||
MirrorResponse(MirrorResponse),
|
||||
AutoUpdateResponse(AutoUpdateResponse),
|
||||
RebuiltIndex,
|
||||
}
|
||||
|
||||
// TODO for all: expand these to elucidate why something failed
|
||||
// these are locally-given responses to local requests
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum NewPackageResponse {
|
||||
Success,
|
||||
Failure,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum DownloadResponse {
|
||||
Started,
|
||||
Failure,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum InstallResponse {
|
||||
Success,
|
||||
Failure,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum UninstallResponse {
|
||||
Success,
|
||||
Failure,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum MirrorResponse {
|
||||
Success,
|
||||
Failure,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum AutoUpdateResponse {
|
||||
Success,
|
||||
Failure,
|
||||
}
|
381
kinode/packages/app_store/app_store/src/http_api.rs
Normal file
381
kinode/packages/app_store/app_store/src/http_api.rs
Normal file
@ -0,0 +1,381 @@
|
||||
use crate::{DownloadResponse, PackageListing, PackageState, RequestedPackage, State};
|
||||
use kinode_process_lib::{
|
||||
http::{send_response, IncomingHttpRequest, Method, StatusCode},
|
||||
print_to_terminal, Address, NodeId, PackageId,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Actions supported over HTTP:
|
||||
/// - get all downloaded apps: GET /apps
|
||||
/// - get all listed apps: GET /apps/listed
|
||||
/// - get some subset of listed apps, via search or filter: ?
|
||||
/// - get detail about a specific downloaded app: GET /apps/:id
|
||||
/// - get capabilities for a specific downloaded app: GET /apps/:id/caps
|
||||
/// - get detail about a specific listed app: GET /apps/listed/:id
|
||||
///
|
||||
/// - download a listed app: POST /apps/listed/:id
|
||||
/// - install a downloaded app: POST /apps/:id
|
||||
/// - uninstall/delete a downloaded app: DELETE /apps/:id
|
||||
/// - update a downloaded app: PUT /apps/:id
|
||||
/// - approve capabilities for a downloaded app: POST /apps/:id/caps
|
||||
/// - start mirroring a downloaded app: PUT /apps/:id/mirror
|
||||
/// - stop mirroring a downloaded app: DELETE /apps/:id/mirror
|
||||
/// - start auto-updating a downloaded app: PUT /apps/:id/auto-update
|
||||
/// - stop auto-updating a downloaded app: DELETE /apps/:id/auto-update
|
||||
pub fn handle_http_request(
|
||||
our: &Address,
|
||||
state: &mut State,
|
||||
requested_packages: &mut HashMap<PackageId, RequestedPackage>,
|
||||
req: &IncomingHttpRequest,
|
||||
) -> anyhow::Result<()> {
|
||||
match serve_paths(our, state, requested_packages, req) {
|
||||
Ok((status_code, headers, body)) => send_response(
|
||||
status_code,
|
||||
Some(HashMap::from([(
|
||||
String::from("Content-Type"),
|
||||
String::from("application/json"),
|
||||
)])),
|
||||
body,
|
||||
),
|
||||
Err(e) => {
|
||||
print_to_terminal(1, &format!("http error: {:?}", e));
|
||||
send_response(StatusCode::INTERNAL_SERVER_ERROR, None, vec![])
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn gen_package_info(
|
||||
id: &PackageId,
|
||||
listing: Option<&PackageListing>,
|
||||
state: Option<&PackageState>,
|
||||
) -> serde_json::Value {
|
||||
json!({
|
||||
"owner": match &listing {
|
||||
Some(listing) => Some(&listing.owner),
|
||||
None => None,
|
||||
},
|
||||
"package": id.package().to_string(),
|
||||
"publisher": id.publisher(),
|
||||
"installed": match &state {
|
||||
Some(state) => state.installed,
|
||||
None => false,
|
||||
},
|
||||
"metadata_hash": match &listing {
|
||||
Some(listing) => Some(&listing.metadata_hash),
|
||||
None => None,
|
||||
},
|
||||
"metadata": match &listing {
|
||||
Some(listing) => Some(&listing.metadata),
|
||||
None => match state {
|
||||
Some(state) => Some(&state.metadata),
|
||||
None => None,
|
||||
},
|
||||
},
|
||||
"state": match &state {
|
||||
Some(state) => json!({
|
||||
"mirrored_from": state.mirrored_from,
|
||||
"our_version": state.our_version,
|
||||
"caps_approved": state.caps_approved,
|
||||
"mirroring": state.mirroring,
|
||||
"auto_update": state.auto_update,
|
||||
}),
|
||||
None => json!(null),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn serve_paths(
|
||||
our: &Address,
|
||||
state: &mut State,
|
||||
requested_packages: &mut HashMap<PackageId, RequestedPackage>,
|
||||
req: &IncomingHttpRequest,
|
||||
) -> anyhow::Result<(StatusCode, Option<HashMap<String, String>>, Vec<u8>)> {
|
||||
let path = req.path()?;
|
||||
let method = req.method()?;
|
||||
|
||||
// TODO get rid of this workaround when we change `IncomingHttpRequest`
|
||||
let bound_path: &str = if path.ends_with("auto-update") {
|
||||
"/apps/:id/auto-update"
|
||||
} else if path.ends_with("mirror") {
|
||||
"/apps/:id/mirror"
|
||||
} else if path.ends_with("caps") {
|
||||
"/apps/:id/caps"
|
||||
} else if path.starts_with("/apps/listed/") {
|
||||
"/apps/listed/:id"
|
||||
} else if &path == "/apps/listed" || &path == "/apps" {
|
||||
&path
|
||||
} else {
|
||||
"/apps/:id"
|
||||
};
|
||||
|
||||
// print_to_terminal(0, &format!("HTTP {method} {path} {bound_path}"));
|
||||
|
||||
match bound_path {
|
||||
// GET all downloaded apps
|
||||
"/apps" => {
|
||||
if method != Method::GET {
|
||||
return Ok((
|
||||
StatusCode::METHOD_NOT_ALLOWED,
|
||||
None,
|
||||
format!("Invalid method {method} for {path}").into_bytes(),
|
||||
));
|
||||
}
|
||||
let all: Vec<serde_json::Value> = state
|
||||
.downloaded_packages
|
||||
.iter()
|
||||
.map(|(package_id, package_state)| {
|
||||
let listing = state.get_listing(package_id);
|
||||
gen_package_info(package_id, listing, Some(package_state))
|
||||
})
|
||||
.collect();
|
||||
return Ok((StatusCode::OK, None, serde_json::to_vec(&all)?));
|
||||
}
|
||||
// GET all listed apps
|
||||
"/apps/listed" => {
|
||||
if method != Method::GET {
|
||||
return Ok((
|
||||
StatusCode::METHOD_NOT_ALLOWED,
|
||||
None,
|
||||
format!("Invalid method {method} for {path}").into_bytes(),
|
||||
));
|
||||
}
|
||||
let all: Vec<serde_json::Value> = state
|
||||
.listed_packages
|
||||
.iter()
|
||||
.map(|(_hash, listing)| {
|
||||
let package_id = PackageId::new(&listing.name, &listing.publisher);
|
||||
let state = state.downloaded_packages.get(&package_id);
|
||||
gen_package_info(&package_id, Some(listing), state)
|
||||
})
|
||||
.collect();
|
||||
return Ok((StatusCode::OK, None, serde_json::to_vec(&all)?));
|
||||
}
|
||||
// GET detail about a specific downloaded app
|
||||
// install a downloaded app: POST
|
||||
// update a downloaded app: PUT
|
||||
// uninstall/delete a downloaded app: DELETE
|
||||
"/apps/:id" => {
|
||||
let package_id = path
|
||||
.split("/")
|
||||
.last()
|
||||
.unwrap_or_default()
|
||||
.parse::<PackageId>()?;
|
||||
match method {
|
||||
Method::GET => {
|
||||
let Some(pkg) = state.downloaded_packages.get(&package_id) else {
|
||||
return Ok((
|
||||
StatusCode::NOT_FOUND,
|
||||
None,
|
||||
format!("App not found: {package_id}").into_bytes(),
|
||||
));
|
||||
};
|
||||
let listing = state.get_listing(&package_id);
|
||||
Ok((
|
||||
StatusCode::OK,
|
||||
None,
|
||||
gen_package_info(&package_id, listing, Some(pkg))
|
||||
.to_string()
|
||||
.into_bytes(),
|
||||
))
|
||||
}
|
||||
Method::POST => {
|
||||
// install an app
|
||||
crate::handle_install(our, state, &package_id)?;
|
||||
Ok((StatusCode::CREATED, None, format!("Installed").into_bytes()))
|
||||
}
|
||||
Method::PUT => {
|
||||
// update an app
|
||||
// TODO
|
||||
Ok((StatusCode::NO_CONTENT, None, format!("TODO").into_bytes()))
|
||||
}
|
||||
Method::DELETE => {
|
||||
// uninstall an app
|
||||
state.uninstall(&package_id)?;
|
||||
Ok((
|
||||
StatusCode::NO_CONTENT,
|
||||
None,
|
||||
format!("Uninstalled").into_bytes(),
|
||||
))
|
||||
}
|
||||
_ => Ok((
|
||||
StatusCode::METHOD_NOT_ALLOWED,
|
||||
None,
|
||||
format!("Invalid method {method} for {path}").into_bytes(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
// GET detail about a specific listed app
|
||||
// download a listed app: POST
|
||||
"/apps/listed/:id" => {
|
||||
let package_id = path
|
||||
.split("/")
|
||||
.last()
|
||||
.unwrap_or_default()
|
||||
.parse::<PackageId>()?;
|
||||
match method {
|
||||
Method::GET => {
|
||||
let Some(listing) = state.get_listing(&package_id) else {
|
||||
return Ok((
|
||||
StatusCode::NOT_FOUND,
|
||||
None,
|
||||
format!("App not found: {package_id}").into_bytes(),
|
||||
));
|
||||
};
|
||||
let downloaded = state.downloaded_packages.get(&package_id);
|
||||
Ok((
|
||||
StatusCode::OK,
|
||||
None,
|
||||
gen_package_info(&package_id, Some(listing), downloaded)
|
||||
.to_string()
|
||||
.into_bytes(),
|
||||
))
|
||||
}
|
||||
Method::POST => {
|
||||
// download an app
|
||||
// TODO get fields from POST body
|
||||
let pkg_listing: &PackageListing = state
|
||||
.get_listing(&package_id)
|
||||
.ok_or(anyhow::anyhow!("No package"))?;
|
||||
let mirrors: &Vec<NodeId> = pkg_listing
|
||||
.metadata
|
||||
.as_ref()
|
||||
.ok_or(anyhow::anyhow!("No metadata for package {package_id}"))?
|
||||
.mirrors
|
||||
.as_ref()
|
||||
.ok_or(anyhow::anyhow!("No mirrors for package {package_id}"))?;
|
||||
// TODO select on FE
|
||||
let download_from = mirrors
|
||||
.first()
|
||||
.ok_or(anyhow::anyhow!("No mirrors for package {package_id}"))?;
|
||||
// TODO select on FE
|
||||
let mirror = false;
|
||||
let auto_update = false;
|
||||
let desired_version_hash = None;
|
||||
match crate::start_download(
|
||||
our,
|
||||
requested_packages,
|
||||
&package_id,
|
||||
download_from,
|
||||
mirror,
|
||||
auto_update,
|
||||
&desired_version_hash,
|
||||
) {
|
||||
DownloadResponse::Started => Ok((
|
||||
StatusCode::CREATED,
|
||||
None,
|
||||
format!("Downloading").into_bytes(),
|
||||
)),
|
||||
DownloadResponse::Failure => Ok((
|
||||
StatusCode::SERVICE_UNAVAILABLE,
|
||||
None,
|
||||
format!("Failed to download").into_bytes(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => Ok((
|
||||
StatusCode::METHOD_NOT_ALLOWED,
|
||||
None,
|
||||
format!("Invalid method {method} for {path}").into_bytes(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
// GET caps for a specific downloaded app
|
||||
// approve capabilities for a downloaded app: POST
|
||||
"/apps/:id/caps" => {
|
||||
let package_id = path
|
||||
.split("/")
|
||||
.nth(2)
|
||||
.unwrap_or_default()
|
||||
.parse::<PackageId>()?;
|
||||
match method {
|
||||
// return the capabilities for that app
|
||||
Method::GET => Ok(match crate::fetch_package_manifest(&package_id) {
|
||||
Ok(manifest) => (StatusCode::OK, None, serde_json::to_vec(&manifest)?),
|
||||
Err(_) => (
|
||||
StatusCode::NOT_FOUND,
|
||||
None,
|
||||
format!("App manifest not found: {package_id}").into_bytes(),
|
||||
),
|
||||
}),
|
||||
// approve the capabilities for that app
|
||||
Method::POST => Ok(
|
||||
match state.update_downloaded_package(&package_id, |pkg| {
|
||||
pkg.caps_approved = true;
|
||||
}) {
|
||||
true => (StatusCode::OK, None, vec![]),
|
||||
false => (
|
||||
StatusCode::NOT_FOUND,
|
||||
None,
|
||||
format!("App not found: {package_id}").into_bytes(),
|
||||
),
|
||||
},
|
||||
),
|
||||
_ => Ok((
|
||||
StatusCode::METHOD_NOT_ALLOWED,
|
||||
None,
|
||||
format!("Invalid method {method} for {path}").into_bytes(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
// start mirroring a downloaded app: PUT
|
||||
// stop mirroring a downloaded app: DELETE
|
||||
"/apps/:id/mirror" => {
|
||||
let package_id = path
|
||||
.split("/")
|
||||
.nth(2)
|
||||
.unwrap_or_default()
|
||||
.parse::<PackageId>()?;
|
||||
match method {
|
||||
// start mirroring an app
|
||||
Method::PUT => {
|
||||
state.start_mirroring(&package_id);
|
||||
Ok((StatusCode::OK, None, vec![]))
|
||||
}
|
||||
// stop mirroring an app
|
||||
Method::DELETE => {
|
||||
state.stop_mirroring(&package_id);
|
||||
Ok((StatusCode::OK, None, vec![]))
|
||||
}
|
||||
_ => Ok((
|
||||
StatusCode::METHOD_NOT_ALLOWED,
|
||||
None,
|
||||
format!("Invalid method {method} for {path}").into_bytes(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
// start auto-updating a downloaded app: PUT
|
||||
// stop auto-updating a downloaded app: DELETE
|
||||
"/apps/:id/auto-update" => {
|
||||
let package_id = path
|
||||
.split("/")
|
||||
.nth(2)
|
||||
.unwrap_or_default()
|
||||
.parse::<PackageId>()?;
|
||||
match method {
|
||||
// start auto-updating an app
|
||||
Method::PUT => {
|
||||
state.start_auto_update(&package_id);
|
||||
Ok((StatusCode::OK, None, vec![]))
|
||||
}
|
||||
// stop auto-updating an app
|
||||
Method::DELETE => {
|
||||
state.stop_auto_update(&package_id);
|
||||
Ok((StatusCode::OK, None, vec![]))
|
||||
}
|
||||
_ => Ok((
|
||||
StatusCode::METHOD_NOT_ALLOWED,
|
||||
None,
|
||||
format!("Invalid method {method} for {path}").into_bytes(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => Ok((
|
||||
StatusCode::NOT_FOUND,
|
||||
None,
|
||||
format!("Path not found: {}", path).into_bytes(),
|
||||
)),
|
||||
}
|
||||
}
|
706
kinode/packages/app_store/app_store/src/lib.rs
Normal file
706
kinode/packages/app_store/app_store/src/lib.rs
Normal file
@ -0,0 +1,706 @@
|
||||
use kinode_process_lib::eth::{EthAction, EthAddress, EthSubEvent, SubscribeLogsRequest};
|
||||
use kinode_process_lib::http::{bind_http_path, serve_ui, HttpServerRequest};
|
||||
use kinode_process_lib::kernel_types as kt;
|
||||
use kinode_process_lib::*;
|
||||
use kinode_process_lib::{call_init, println};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::str::FromStr;
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
||||
},
|
||||
});
|
||||
|
||||
mod api;
|
||||
mod http_api;
|
||||
use api::*;
|
||||
mod types;
|
||||
use types::*;
|
||||
mod ft_worker_lib;
|
||||
use ft_worker_lib::{
|
||||
spawn_receive_transfer, spawn_transfer, FTWorkerCommand, FTWorkerResult, FileTransferContext,
|
||||
};
|
||||
|
||||
/// App Store:
|
||||
/// acts as both a local package manager and a protocol to share packages across the network.
|
||||
/// packages are apps; apps are packages. we use an onchain app listing contract to determine
|
||||
/// what apps are available to download and what node(s) to download them from.
|
||||
///
|
||||
/// once we know that list, we can request a package from a node and download it locally.
|
||||
/// (we can also manually download an "untracked" package if we know its name and distributor node)
|
||||
/// packages that are downloaded can then be installed!
|
||||
///
|
||||
/// installed packages can be managed:
|
||||
/// - given permissions (necessary to complete install)
|
||||
/// - uninstalled + deleted
|
||||
/// - set to automatically update if a new version is available
|
||||
|
||||
const CONTRACT_ADDRESS: &str = "0x18c39eB547A0060C6034f8bEaFB947D1C16eADF1";
|
||||
|
||||
const EVENTS: [&str; 3] = [
|
||||
"AppRegistered(uint256,string,bytes,string,bytes32)",
|
||||
"AppMetadataUpdated(uint256,string,bytes32)",
|
||||
"Transfer(address,address,uint256)",
|
||||
];
|
||||
|
||||
// internal types
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)] // untagged as a meta-type for all incoming requests
|
||||
pub enum Req {
|
||||
LocalRequest(LocalRequest),
|
||||
RemoteRequest(RemoteRequest),
|
||||
FTWorkerCommand(FTWorkerCommand),
|
||||
FTWorkerResult(FTWorkerResult),
|
||||
Eth(EthSubEvent),
|
||||
Http(HttpServerRequest),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)] // untagged as a meta-type for all incoming responses
|
||||
pub enum Resp {
|
||||
LocalResponse(LocalResponse),
|
||||
RemoteResponse(RemoteResponse),
|
||||
FTWorkerResult(FTWorkerResult),
|
||||
}
|
||||
|
||||
call_init!(init);
|
||||
fn init(our: Address) {
|
||||
println!("{}: started", our.package());
|
||||
|
||||
for path in [
|
||||
"/apps",
|
||||
"/apps/listed",
|
||||
"/apps/:id",
|
||||
"/apps/listed/:id",
|
||||
"/apps/:id/caps",
|
||||
"/apps/:id/mirror",
|
||||
"/apps/:id/auto-update",
|
||||
] {
|
||||
bind_http_path(path, true, false).expect("failed to bind http path");
|
||||
}
|
||||
serve_ui(
|
||||
&our,
|
||||
"ui",
|
||||
true,
|
||||
false,
|
||||
vec!["/", "/my-apps", "/app-details/:id", "/publish"],
|
||||
)
|
||||
.expect("failed to serve static UI");
|
||||
|
||||
// load in our saved state or initalize a new one if none exists
|
||||
let mut state = get_typed_state(|bytes| Ok(bincode::deserialize(bytes)?))
|
||||
.unwrap_or(State::new(CONTRACT_ADDRESS.to_string()).unwrap());
|
||||
|
||||
if state.contract_address != CONTRACT_ADDRESS {
|
||||
println!("app store: warning: contract address mismatch--overwriting saved state");
|
||||
state = State::new(CONTRACT_ADDRESS.to_string()).unwrap();
|
||||
}
|
||||
|
||||
println!(
|
||||
"app store: indexing on contract address {}",
|
||||
state.contract_address
|
||||
);
|
||||
|
||||
let mut requested_packages: HashMap<PackageId, RequestedPackage> = HashMap::new();
|
||||
|
||||
// subscribe to events on the app store contract
|
||||
SubscribeLogsRequest::new(1) // subscription id 1
|
||||
.address(EthAddress::from_str(&state.contract_address).unwrap())
|
||||
.from_block(state.last_saved_block - 1)
|
||||
.events(EVENTS)
|
||||
.send()
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
match await_message() {
|
||||
Err(send_error) => {
|
||||
// TODO handle these based on what they are triggered by
|
||||
println!("app store: got network error: {send_error}");
|
||||
}
|
||||
Ok(message) => {
|
||||
if let Err(e) = handle_message(&our, &mut state, &mut requested_packages, &message)
|
||||
{
|
||||
println!("app store: error handling message: {:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// message router: parse into our Req and Resp types, then pass to
|
||||
/// function defined for each kind of message. check whether the source
|
||||
/// of the message is allowed to send that kind of message to us.
|
||||
/// finally, fire a response if expected from a request.
|
||||
fn handle_message(
|
||||
our: &Address,
|
||||
mut state: &mut State,
|
||||
mut requested_packages: &mut HashMap<PackageId, RequestedPackage>,
|
||||
message: &Message,
|
||||
) -> anyhow::Result<()> {
|
||||
match message {
|
||||
Message::Request {
|
||||
source,
|
||||
expects_response,
|
||||
body,
|
||||
..
|
||||
} => match serde_json::from_slice::<Req>(&body)? {
|
||||
Req::LocalRequest(local_request) => {
|
||||
if our.node != source.node {
|
||||
return Err(anyhow::anyhow!("local request from non-local node"));
|
||||
}
|
||||
let resp =
|
||||
handle_local_request(&our, &local_request, &mut state, &mut requested_packages);
|
||||
if expects_response.is_some() {
|
||||
Response::new().body(serde_json::to_vec(&resp)?).send()?;
|
||||
}
|
||||
}
|
||||
Req::RemoteRequest(remote_request) => {
|
||||
let resp = handle_remote_request(&our, &source, &remote_request, &mut state);
|
||||
if expects_response.is_some() {
|
||||
Response::new().body(serde_json::to_vec(&resp)?).send()?;
|
||||
}
|
||||
}
|
||||
Req::FTWorkerResult(FTWorkerResult::ReceiveSuccess(name)) => {
|
||||
handle_receive_download(&our, &mut state, &name, &mut requested_packages)?;
|
||||
}
|
||||
Req::FTWorkerCommand(_) => {
|
||||
spawn_receive_transfer(&our, &body)?;
|
||||
}
|
||||
Req::FTWorkerResult(r) => {
|
||||
println!("app store: got weird ft_worker result: {r:?}");
|
||||
}
|
||||
Req::Eth(e) => {
|
||||
if source.node() != our.node() || source.process != "eth:distro:sys" {
|
||||
return Err(anyhow::anyhow!("eth sub event from weird addr: {source}"));
|
||||
}
|
||||
handle_eth_sub_event(&mut state, e)?;
|
||||
}
|
||||
Req::Http(incoming) => {
|
||||
if source.node() != our.node()
|
||||
|| &source.process.to_string() != "http_server:distro:sys"
|
||||
{
|
||||
return Err(anyhow::anyhow!("http_server from non-local node"));
|
||||
}
|
||||
if let HttpServerRequest::Http(req) = incoming {
|
||||
http_api::handle_http_request(&our, &mut state, requested_packages, &req)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
Message::Response { body, context, .. } => {
|
||||
// the only kind of response we care to handle here!
|
||||
let Some(context) = context else {
|
||||
return Err(anyhow::anyhow!("app store: missing context"));
|
||||
};
|
||||
handle_ft_worker_result(body, context)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// so far just fielding requests to download packages from us
|
||||
fn handle_remote_request(
|
||||
our: &Address,
|
||||
source: &Address,
|
||||
request: &RemoteRequest,
|
||||
state: &mut State,
|
||||
) -> Resp {
|
||||
match request {
|
||||
RemoteRequest::Download {
|
||||
package_id,
|
||||
desired_version_hash,
|
||||
} => {
|
||||
let Some(package_state) = state.get_downloaded_package(package_id) else {
|
||||
return Resp::RemoteResponse(RemoteResponse::DownloadDenied);
|
||||
};
|
||||
if !package_state.mirroring {
|
||||
return Resp::RemoteResponse(RemoteResponse::DownloadDenied);
|
||||
}
|
||||
if let Some(hash) = desired_version_hash {
|
||||
if &package_state.our_version != hash {
|
||||
return Resp::RemoteResponse(RemoteResponse::DownloadDenied);
|
||||
}
|
||||
}
|
||||
let file_name = format!("/{}.zip", package_id);
|
||||
// get the .zip from VFS and attach as blob to response
|
||||
let file_path = format!("/{}/pkg/{}.zip", package_id, package_id);
|
||||
let Ok(Ok(_)) = Request::to(("our", "vfs", "distro", "sys"))
|
||||
.body(
|
||||
serde_json::to_vec(&vfs::VfsRequest {
|
||||
path: file_path,
|
||||
action: vfs::VfsAction::Read,
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
.send_and_await_response(5)
|
||||
else {
|
||||
return Resp::RemoteResponse(RemoteResponse::DownloadDenied);
|
||||
};
|
||||
// transfer will *inherit* the blob bytes we receive from VFS
|
||||
match spawn_transfer(&our, &file_name, None, 60, &source) {
|
||||
Ok(()) => Resp::RemoteResponse(RemoteResponse::DownloadApproved),
|
||||
Err(_e) => Resp::RemoteResponse(RemoteResponse::DownloadDenied),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// only `our.node` can call this
|
||||
fn handle_local_request(
|
||||
our: &Address,
|
||||
request: &LocalRequest,
|
||||
state: &mut State,
|
||||
requested_packages: &mut HashMap<PackageId, RequestedPackage>,
|
||||
) -> LocalResponse {
|
||||
match request {
|
||||
LocalRequest::NewPackage { package, mirror } => {
|
||||
let Some(blob) = get_blob() else {
|
||||
return LocalResponse::NewPackageResponse(NewPackageResponse::Failure);
|
||||
};
|
||||
// set the version hash for this new local package
|
||||
let our_version = generate_version_hash(&blob.bytes);
|
||||
|
||||
let package_state = PackageState {
|
||||
mirrored_from: Some(our.node.clone()),
|
||||
our_version,
|
||||
installed: false,
|
||||
caps_approved: true, // TODO see if we want to auto-approve local installs
|
||||
mirroring: *mirror,
|
||||
auto_update: false, // can't auto-update a local package
|
||||
metadata: None, // TODO
|
||||
};
|
||||
let Ok(()) = state.add_downloaded_package(package, package_state, Some(blob.bytes))
|
||||
else {
|
||||
return LocalResponse::NewPackageResponse(NewPackageResponse::Failure);
|
||||
};
|
||||
LocalResponse::NewPackageResponse(NewPackageResponse::Success)
|
||||
}
|
||||
LocalRequest::Download {
|
||||
package: package_id,
|
||||
download_from,
|
||||
mirror,
|
||||
auto_update,
|
||||
desired_version_hash,
|
||||
} => LocalResponse::DownloadResponse(start_download(
|
||||
our,
|
||||
requested_packages,
|
||||
package_id,
|
||||
download_from,
|
||||
*mirror,
|
||||
*auto_update,
|
||||
desired_version_hash,
|
||||
)),
|
||||
LocalRequest::Install(package) => match handle_install(our, state, package) {
|
||||
Ok(()) => LocalResponse::InstallResponse(InstallResponse::Success),
|
||||
Err(_) => LocalResponse::InstallResponse(InstallResponse::Failure),
|
||||
},
|
||||
LocalRequest::Uninstall(package) => match state.uninstall(package) {
|
||||
Ok(()) => LocalResponse::UninstallResponse(UninstallResponse::Success),
|
||||
Err(_) => LocalResponse::UninstallResponse(UninstallResponse::Failure),
|
||||
},
|
||||
LocalRequest::StartMirroring(package) => match state.start_mirroring(package) {
|
||||
true => LocalResponse::MirrorResponse(MirrorResponse::Success),
|
||||
false => LocalResponse::MirrorResponse(MirrorResponse::Failure),
|
||||
},
|
||||
LocalRequest::StopMirroring(package) => match state.stop_mirroring(package) {
|
||||
true => LocalResponse::MirrorResponse(MirrorResponse::Success),
|
||||
false => LocalResponse::MirrorResponse(MirrorResponse::Failure),
|
||||
},
|
||||
LocalRequest::StartAutoUpdate(package) => match state.start_auto_update(package) {
|
||||
true => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Success),
|
||||
false => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Failure),
|
||||
},
|
||||
LocalRequest::StopAutoUpdate(package) => match state.stop_auto_update(package) {
|
||||
true => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Success),
|
||||
false => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Failure),
|
||||
},
|
||||
LocalRequest::RebuildIndex => {
|
||||
*state = State::new(CONTRACT_ADDRESS.to_string()).unwrap();
|
||||
// kill our old subscription and build a new one.
|
||||
Request::to(("our", "eth", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&EthAction::UnsubscribeLogs(1)).unwrap())
|
||||
.send()
|
||||
.unwrap();
|
||||
SubscribeLogsRequest::new(1) // subscription id 1
|
||||
.address(EthAddress::from_str(&state.contract_address).unwrap())
|
||||
.from_block(state.last_saved_block - 1)
|
||||
.events(EVENTS)
|
||||
.send()
|
||||
.unwrap();
|
||||
LocalResponse::RebuiltIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_download(
|
||||
our: &Address,
|
||||
requested_packages: &mut HashMap<PackageId, RequestedPackage>,
|
||||
package_id: &PackageId,
|
||||
download_from: &NodeId,
|
||||
mirror: bool,
|
||||
auto_update: bool,
|
||||
desired_version_hash: &Option<String>,
|
||||
) -> DownloadResponse {
|
||||
match Request::to((download_from.as_str(), our.process.clone()))
|
||||
.inherit(true)
|
||||
.body(
|
||||
serde_json::to_vec(&RemoteRequest::Download {
|
||||
package_id: package_id.clone(),
|
||||
desired_version_hash: desired_version_hash.clone(),
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
.send_and_await_response(5)
|
||||
{
|
||||
Ok(Ok(Message::Response { body, .. })) => match serde_json::from_slice::<Resp>(&body) {
|
||||
Ok(Resp::RemoteResponse(RemoteResponse::DownloadApproved)) => {
|
||||
requested_packages.insert(
|
||||
package_id.clone(),
|
||||
RequestedPackage {
|
||||
from: download_from.clone(),
|
||||
mirror,
|
||||
auto_update,
|
||||
desired_version_hash: desired_version_hash.clone(),
|
||||
},
|
||||
);
|
||||
DownloadResponse::Started
|
||||
}
|
||||
_ => DownloadResponse::Failure,
|
||||
},
|
||||
_ => DownloadResponse::Failure,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_receive_download(
|
||||
our: &Address,
|
||||
state: &mut State,
|
||||
package_name: &str,
|
||||
requested_packages: &mut HashMap<PackageId, RequestedPackage>,
|
||||
) -> anyhow::Result<()> {
|
||||
// remove leading / and .zip from file name to get package ID
|
||||
let package_name = package_name[1..].trim_end_matches(".zip");
|
||||
let Ok(package_id) = package_name.parse::<PackageId>() else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"app store: bad package filename fron download: {package_name}"
|
||||
));
|
||||
};
|
||||
println!("app store: successfully received {}", package_id);
|
||||
// only save the package if we actually requested it
|
||||
let Some(requested_package) = requested_packages.remove(&package_id) else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"app store: received unrequested package--rejecting!"
|
||||
));
|
||||
};
|
||||
let Some(blob) = get_blob() else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"app store: received download but found no blob"
|
||||
));
|
||||
};
|
||||
// check the version hash for this download against requested!!
|
||||
// for now we can reject if it's not latest.
|
||||
let download_hash = generate_version_hash(&blob.bytes);
|
||||
match requested_package.desired_version_hash {
|
||||
Some(hash) => {
|
||||
if download_hash != hash {
|
||||
return Err(anyhow::anyhow!(
|
||||
"app store: downloaded package is not latest version--rejecting download!"
|
||||
));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// check against latest from listing
|
||||
let Some(package_listing) = state.get_listing(&package_id) else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"app store: downloaded package cannot be found in manager--rejecting download!"
|
||||
));
|
||||
};
|
||||
if let Some(metadata) = &package_listing.metadata {
|
||||
if let Some(latest_hash) = metadata.versions.clone().unwrap_or(vec![]).last() {
|
||||
if &download_hash != latest_hash {
|
||||
return Err(anyhow::anyhow!(
|
||||
"app store: downloaded package is not latest version--rejecting download!"
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"app store: downloaded package has no versions in manager--rejecting download!"
|
||||
));
|
||||
}
|
||||
} else {
|
||||
println!("app store: warning: downloaded package has no listing metadata to check validity against!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.add_downloaded_package(
|
||||
&package_id,
|
||||
PackageState {
|
||||
mirrored_from: Some(requested_package.from),
|
||||
our_version: download_hash,
|
||||
installed: false,
|
||||
caps_approved: false,
|
||||
mirroring: requested_package.mirror,
|
||||
auto_update: requested_package.auto_update,
|
||||
metadata: None, // TODO
|
||||
},
|
||||
Some(blob.bytes),
|
||||
)
|
||||
}
|
||||
|
||||
fn handle_ft_worker_result(body: &[u8], context: &[u8]) -> anyhow::Result<()> {
|
||||
if let Ok(Resp::FTWorkerResult(ft_worker_result)) = serde_json::from_slice::<Resp>(body) {
|
||||
let context = serde_json::from_slice::<FileTransferContext>(context)?;
|
||||
if let FTWorkerResult::SendSuccess = ft_worker_result {
|
||||
println!(
|
||||
"app store: successfully shared {} in {:.4}s",
|
||||
context.file_name,
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(context.start_time)
|
||||
.unwrap()
|
||||
.as_secs_f64(),
|
||||
);
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("app store: failed to share app"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_eth_sub_event(state: &mut State, event: EthSubEvent) -> anyhow::Result<()> {
|
||||
let EthSubEvent::Log(log) = event else {
|
||||
return Err(anyhow::anyhow!("app store: got non-log event"));
|
||||
};
|
||||
state.ingest_listings_contract_event(log)
|
||||
}
|
||||
|
||||
fn fetch_package_manifest(package: &PackageId) -> anyhow::Result<Vec<kt::PackageManifestEntry>> {
|
||||
let drive_path = format!("/{}/pkg", package);
|
||||
Request::to(("our", "vfs", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&vfs::VfsRequest {
|
||||
path: format!("{}/manifest.json", drive_path),
|
||||
action: vfs::VfsAction::Read,
|
||||
})?)
|
||||
.send_and_await_response(5)??;
|
||||
let Some(blob) = get_blob() else {
|
||||
return Err(anyhow::anyhow!("no blob"));
|
||||
};
|
||||
let manifest = String::from_utf8(blob.bytes)?;
|
||||
Ok(serde_json::from_str::<Vec<kt::PackageManifestEntry>>(
|
||||
&manifest,
|
||||
)?)
|
||||
}
|
||||
|
||||
/// the steps to take an existing package on disk and install/start it
|
||||
/// make sure you have reviewed and approved caps in manifest before calling this
|
||||
pub fn handle_install(
|
||||
our: &Address,
|
||||
state: &mut State,
|
||||
package_id: &PackageId,
|
||||
) -> anyhow::Result<()> {
|
||||
let drive_path = format!("/{package_id}/pkg");
|
||||
let manifest = fetch_package_manifest(package_id)?;
|
||||
// always grant read/write to their drive, which we created for them
|
||||
let Some(read_cap) = get_capability(
|
||||
&Address::new(&our.node, ("vfs", "distro", "sys")),
|
||||
&serde_json::to_string(&serde_json::json!({
|
||||
"kind": "read",
|
||||
"drive": drive_path,
|
||||
}))?,
|
||||
) else {
|
||||
return Err(anyhow::anyhow!("app store: no read cap"));
|
||||
};
|
||||
let Some(write_cap) = get_capability(
|
||||
&Address::new(&our.node, ("vfs", "distro", "sys")),
|
||||
&serde_json::to_string(&serde_json::json!({
|
||||
"kind": "write",
|
||||
"drive": drive_path,
|
||||
}))?,
|
||||
) else {
|
||||
return Err(anyhow::anyhow!("app store: no write cap"));
|
||||
};
|
||||
let Some(networking_cap) = get_capability(
|
||||
&Address::new(&our.node, ("kernel", "distro", "sys")),
|
||||
&"\"network\"".to_string(),
|
||||
) else {
|
||||
return Err(anyhow::anyhow!("app store: no net cap"));
|
||||
};
|
||||
// first, for each process in manifest, initialize it
|
||||
// then, once all have been initialized, grant them requested caps
|
||||
// and finally start them.
|
||||
for entry in &manifest {
|
||||
let wasm_path = if entry.process_wasm_path.starts_with("/") {
|
||||
entry.process_wasm_path.clone()
|
||||
} else {
|
||||
format!("/{}", entry.process_wasm_path)
|
||||
};
|
||||
let wasm_path = format!("{}{}", drive_path, wasm_path);
|
||||
let process_id = format!("{}:{}", entry.process_name, package_id);
|
||||
let Ok(parsed_new_process_id) = process_id.parse::<ProcessId>() else {
|
||||
return Err(anyhow::anyhow!("app store: invalid process id!"));
|
||||
};
|
||||
// kill process if it already exists
|
||||
Request::to(("our", "kernel", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&kt::KernelCommand::KillProcess(
|
||||
parsed_new_process_id.clone(),
|
||||
))?)
|
||||
.send()?;
|
||||
|
||||
let _bytes_response = Request::to(("our", "vfs", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&vfs::VfsRequest {
|
||||
path: wasm_path.clone(),
|
||||
action: vfs::VfsAction::Read,
|
||||
})?)
|
||||
.send_and_await_response(5)??;
|
||||
|
||||
Request::new()
|
||||
.target(("our", "kernel", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&kt::KernelCommand::InitializeProcess {
|
||||
id: parsed_new_process_id.clone(),
|
||||
wasm_bytes_handle: wasm_path,
|
||||
wit_version: None,
|
||||
on_exit: entry.on_exit.clone(),
|
||||
initial_capabilities: HashSet::new(),
|
||||
public: entry.public,
|
||||
})?)
|
||||
.inherit(true)
|
||||
.send_and_await_response(5)??;
|
||||
// build initial caps
|
||||
let mut requested_capabilities: Vec<kt::Capability> = vec![];
|
||||
for value in &entry.request_capabilities {
|
||||
match value {
|
||||
serde_json::Value::String(process_name) => {
|
||||
if let Ok(parsed_process_id) = process_name.parse::<ProcessId>() {
|
||||
requested_capabilities.push(kt::Capability {
|
||||
issuer: Address {
|
||||
node: our.node.clone(),
|
||||
process: parsed_process_id.clone(),
|
||||
},
|
||||
params: "\"messaging\"".into(),
|
||||
});
|
||||
} else {
|
||||
println!(
|
||||
"app-store: invalid cap: {} for {} to request!",
|
||||
value.to_string(),
|
||||
package_id
|
||||
);
|
||||
}
|
||||
}
|
||||
serde_json::Value::Object(map) => {
|
||||
if let Some(process_name) = map.get("process") {
|
||||
if let Ok(parsed_process_id) = process_name
|
||||
.as_str()
|
||||
.unwrap_or_default()
|
||||
.parse::<ProcessId>()
|
||||
{
|
||||
if let Some(params) = map.get("params") {
|
||||
requested_capabilities.push(kt::Capability {
|
||||
issuer: Address {
|
||||
node: our.node.clone(),
|
||||
process: parsed_process_id.clone(),
|
||||
},
|
||||
params: params.to_string(),
|
||||
});
|
||||
} else {
|
||||
println!(
|
||||
"app-store: invalid cap: {} for {} to request!",
|
||||
value.to_string(),
|
||||
package_id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if entry.request_networking {
|
||||
requested_capabilities.push(kt::de_wit_capability(networking_cap.clone()));
|
||||
}
|
||||
requested_capabilities.push(kt::de_wit_capability(read_cap.clone()));
|
||||
requested_capabilities.push(kt::de_wit_capability(write_cap.clone()));
|
||||
Request::new()
|
||||
.target(("our", "kernel", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&kt::KernelCommand::GrantCapabilities {
|
||||
target: parsed_new_process_id.clone(),
|
||||
capabilities: requested_capabilities,
|
||||
})?)
|
||||
.send()?;
|
||||
}
|
||||
// THEN, *after* all processes have been initialized, grant caps in manifest
|
||||
// TODO for both grants and requests: make the vector of caps
|
||||
// and then do one GrantCapabilities message at the end. much faster.
|
||||
for entry in &manifest {
|
||||
let process_id = format!("{}:{}", entry.process_name, package_id);
|
||||
let Ok(parsed_new_process_id) = process_id.parse::<ProcessId>() else {
|
||||
return Err(anyhow::anyhow!("app store: invalid process id!"));
|
||||
};
|
||||
for value in &entry.grant_capabilities {
|
||||
match value {
|
||||
serde_json::Value::String(process_name) => {
|
||||
if let Ok(parsed_process_id) = process_name.parse::<ProcessId>() {
|
||||
Request::to(("our", "kernel", "distro", "sys"))
|
||||
.body(
|
||||
serde_json::to_vec(&kt::KernelCommand::GrantCapabilities {
|
||||
target: parsed_process_id,
|
||||
capabilities: vec![kt::Capability {
|
||||
issuer: Address {
|
||||
node: our.node.clone(),
|
||||
process: parsed_new_process_id.clone(),
|
||||
},
|
||||
params: "\"messaging\"".into(),
|
||||
}],
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
.send()?;
|
||||
}
|
||||
}
|
||||
serde_json::Value::Object(map) => {
|
||||
if let Some(process_name) = map.get("process") {
|
||||
if let Ok(parsed_process_id) = process_name
|
||||
.as_str()
|
||||
.unwrap_or_default()
|
||||
.parse::<ProcessId>()
|
||||
{
|
||||
if let Some(params) = map.get("params") {
|
||||
Request::to(("our", "kernel", "distro", "sys"))
|
||||
.body(serde_json::to_vec(
|
||||
&kt::KernelCommand::GrantCapabilities {
|
||||
target: parsed_process_id,
|
||||
capabilities: vec![kt::Capability {
|
||||
issuer: Address {
|
||||
node: our.node.clone(),
|
||||
process: parsed_new_process_id.clone(),
|
||||
},
|
||||
params: params.to_string(),
|
||||
}],
|
||||
},
|
||||
)?)
|
||||
.send()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
Request::to(("our", "kernel", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&kt::KernelCommand::RunProcess(
|
||||
parsed_new_process_id,
|
||||
))?)
|
||||
.send_and_await_response(5)??;
|
||||
}
|
||||
// finally set the package as installed
|
||||
state.update_downloaded_package(package_id, |package_state| {
|
||||
package_state.installed = true;
|
||||
});
|
||||
Ok(())
|
||||
}
|
585
kinode/packages/app_store/app_store/src/types.rs
Normal file
585
kinode/packages/app_store/app_store/src/types.rs
Normal file
@ -0,0 +1,585 @@
|
||||
use alloy_rpc_types::Log;
|
||||
use alloy_sol_types::{sol, SolEvent};
|
||||
use kinode_process_lib::kernel_types as kt;
|
||||
use kinode_process_lib::{println, *};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
sol! {
|
||||
event AppRegistered(
|
||||
uint256 indexed package,
|
||||
string packageName,
|
||||
bytes publisherName,
|
||||
string metadataUrl,
|
||||
bytes32 metadataHash
|
||||
);
|
||||
event AppMetadataUpdated(
|
||||
uint256 indexed package,
|
||||
string metadataUrl,
|
||||
bytes32 metadataHash
|
||||
);
|
||||
event Transfer(
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256 indexed tokenId
|
||||
);
|
||||
}
|
||||
|
||||
/// from kns_indexer:sys
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum IndexerRequests {
|
||||
/// return the human readable name for a namehash
|
||||
/// returns an Option<String>
|
||||
NamehashToName { hash: String, block: u64 },
|
||||
/// return the most recent on-chain routing information for a node name.
|
||||
/// returns an Option<KnsUpdate>
|
||||
NodeInfo { name: String, block: u64 },
|
||||
}
|
||||
|
||||
//
|
||||
// app store types
|
||||
//
|
||||
|
||||
pub type PackageHash = String;
|
||||
|
||||
/// listing information derived from metadata hash in listing event
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct PackageListing {
|
||||
pub owner: String, // eth address
|
||||
pub name: String,
|
||||
pub publisher: NodeId,
|
||||
pub metadata_hash: String,
|
||||
pub metadata: Option<OnchainPackageMetadata>,
|
||||
}
|
||||
|
||||
/// metadata derived from metadata hash in listing event
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct OnchainPackageMetadata {
|
||||
pub name: Option<String>,
|
||||
pub subtitle: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub image: Option<String>,
|
||||
pub version: Option<String>,
|
||||
pub license: Option<String>,
|
||||
pub website: Option<String>,
|
||||
pub screenshots: Option<Vec<String>>,
|
||||
pub mirrors: Option<Vec<NodeId>>,
|
||||
pub versions: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct RequestedPackage {
|
||||
pub from: NodeId,
|
||||
pub mirror: bool,
|
||||
pub auto_update: bool,
|
||||
// if none, we're requesting the latest version onchain
|
||||
pub desired_version_hash: Option<String>,
|
||||
}
|
||||
|
||||
/// state of an individual package we have downloaded
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct PackageState {
|
||||
/// the node we last downloaded the package from
|
||||
/// this is "us" if we don't know the source (usually cause it's a local install)
|
||||
pub mirrored_from: Option<NodeId>,
|
||||
/// the version of the package we have downloaded
|
||||
pub our_version: String,
|
||||
pub installed: bool,
|
||||
pub caps_approved: bool,
|
||||
/// are we serving this package to others?
|
||||
pub mirroring: bool,
|
||||
/// if we get a listing data update, will we try to download it?
|
||||
pub auto_update: bool,
|
||||
pub metadata: Option<OnchainPackageMetadata>,
|
||||
}
|
||||
|
||||
/// this process's saved state
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct State {
|
||||
/// the address of the contract we are using to read package listings
|
||||
pub contract_address: String,
|
||||
/// the last block at which we saved the state of the listings to disk.
|
||||
/// we don't want to save the state every time we get a new listing,
|
||||
/// so we only save it every so often and then mark the block at which
|
||||
/// that last occurred here. when we boot, we can read logs starting
|
||||
/// from this block and rebuild latest state.
|
||||
pub last_saved_block: u64,
|
||||
/// we keep the full state of the package manager here, calculated from
|
||||
/// the listings contract logs. in the future, we'll offload this and
|
||||
/// only track a certain number of packages...
|
||||
pub package_hashes: HashMap<PackageId, PackageHash>, // TODO migrate to sqlite db
|
||||
pub listed_packages: HashMap<PackageHash, PackageListing>, // TODO migrate to sqlite db
|
||||
/// we keep the full state of the packages we have downloaded here.
|
||||
/// in order to keep this synchronized with our filesystem, we will
|
||||
/// ingest apps on disk if we have to rebuild our state. this is also
|
||||
/// updated every time we download, create, or uninstall a package.
|
||||
pub downloaded_packages: HashMap<PackageId, PackageState>, // TODO migrate to sqlite db
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// To create a new state, we populate the downloaded_packages map
|
||||
/// with all packages parseable from our filesystem.
|
||||
pub fn new(contract_address: String) -> anyhow::Result<Self> {
|
||||
crate::print_to_terminal(1, "app store: producing new state");
|
||||
let mut state = State {
|
||||
contract_address,
|
||||
last_saved_block: 1,
|
||||
package_hashes: HashMap::new(),
|
||||
listed_packages: HashMap::new(),
|
||||
downloaded_packages: HashMap::new(),
|
||||
};
|
||||
crate::print_to_terminal(
|
||||
1,
|
||||
&format!("populate: {:?}", state.populate_packages_from_filesystem()),
|
||||
);
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub fn get_listing(&self, package_id: &PackageId) -> Option<&PackageListing> {
|
||||
self.listed_packages
|
||||
.get(self.package_hashes.get(package_id)?)
|
||||
}
|
||||
|
||||
fn get_listing_with_hash_mut(
|
||||
&mut self,
|
||||
package_hash: &PackageHash,
|
||||
) -> Option<&mut PackageListing> {
|
||||
self.listed_packages.get_mut(package_hash)
|
||||
}
|
||||
|
||||
/// Done in response to any new onchain listing update other than 'delete'
|
||||
fn insert_listing(&mut self, package_hash: PackageHash, listing: PackageListing) {
|
||||
self.package_hashes.insert(
|
||||
PackageId::new(&listing.name, &listing.publisher),
|
||||
package_hash.clone(),
|
||||
);
|
||||
self.listed_packages.insert(package_hash, listing);
|
||||
}
|
||||
|
||||
/// Done in response to an onchain listing update of 'delete'
|
||||
fn delete_listing(&mut self, package_hash: &PackageHash) {
|
||||
if let Some(old) = self.listed_packages.remove(package_hash) {
|
||||
self.package_hashes
|
||||
.remove(&PackageId::new(&old.name, &old.publisher));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_downloaded_package(&self, package_id: &PackageId) -> Option<PackageState> {
|
||||
self.downloaded_packages.get(package_id).cloned()
|
||||
}
|
||||
|
||||
pub fn add_downloaded_package(
|
||||
&mut self,
|
||||
package_id: &PackageId,
|
||||
package_state: PackageState,
|
||||
package_bytes: Option<Vec<u8>>,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(package_bytes) = package_bytes {
|
||||
let drive_name = format!("/{package_id}/pkg");
|
||||
let blob = LazyLoadBlob {
|
||||
mime: Some("application/zip".to_string()),
|
||||
bytes: package_bytes,
|
||||
};
|
||||
|
||||
// create a new drive for this package in VFS
|
||||
// this is possible because we have root access
|
||||
Request::to(("our", "vfs", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&vfs::VfsRequest {
|
||||
path: drive_name.clone(),
|
||||
action: vfs::VfsAction::CreateDrive,
|
||||
})?)
|
||||
.send_and_await_response(5)??;
|
||||
|
||||
// convert the zip to a new package drive
|
||||
let response = Request::to(("our", "vfs", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&vfs::VfsRequest {
|
||||
path: drive_name.clone(),
|
||||
action: vfs::VfsAction::AddZip,
|
||||
})?)
|
||||
.blob(blob.clone())
|
||||
.send_and_await_response(5)??;
|
||||
let vfs::VfsResponse::Ok = serde_json::from_slice::<vfs::VfsResponse>(response.body())?
|
||||
else {
|
||||
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_id>.zip
|
||||
let zip_path = format!("{}/{}.zip", drive_name, package_id);
|
||||
Request::to(("our", "vfs", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&vfs::VfsRequest {
|
||||
path: zip_path,
|
||||
action: vfs::VfsAction::Write,
|
||||
})?)
|
||||
.blob(blob)
|
||||
.send_and_await_response(5)??;
|
||||
}
|
||||
self.downloaded_packages
|
||||
.insert(package_id.to_owned(), package_state);
|
||||
crate::set_state(&bincode::serialize(self)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// returns True if the package was found and updated, False otherwise
|
||||
pub fn update_downloaded_package(
|
||||
&mut self,
|
||||
package_id: &PackageId,
|
||||
fn_: impl FnOnce(&mut PackageState),
|
||||
) -> bool {
|
||||
let res = self
|
||||
.downloaded_packages
|
||||
.get_mut(package_id)
|
||||
.map(|package_state| {
|
||||
fn_(package_state);
|
||||
true
|
||||
})
|
||||
.unwrap_or(false);
|
||||
crate::set_state(&bincode::serialize(self).unwrap());
|
||||
res
|
||||
}
|
||||
|
||||
pub fn start_mirroring(&mut self, package_id: &PackageId) -> bool {
|
||||
self.update_downloaded_package(package_id, |package_state| {
|
||||
package_state.mirroring = true;
|
||||
})
|
||||
}
|
||||
|
||||
pub fn stop_mirroring(&mut self, package_id: &PackageId) -> bool {
|
||||
self.update_downloaded_package(package_id, |package_state| {
|
||||
package_state.mirroring = false;
|
||||
})
|
||||
}
|
||||
|
||||
pub fn start_auto_update(&mut self, package_id: &PackageId) -> bool {
|
||||
self.update_downloaded_package(package_id, |package_state| {
|
||||
package_state.auto_update = true;
|
||||
})
|
||||
}
|
||||
|
||||
pub fn stop_auto_update(&mut self, package_id: &PackageId) -> bool {
|
||||
self.update_downloaded_package(package_id, |package_state| {
|
||||
package_state.auto_update = false;
|
||||
})
|
||||
}
|
||||
|
||||
/// saves state
|
||||
pub fn populate_packages_from_filesystem(&mut self) -> anyhow::Result<()> {
|
||||
let Message::Response { body, .. } = Request::to(("our", "vfs", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&vfs::VfsRequest {
|
||||
path: "/".to_string(),
|
||||
action: vfs::VfsAction::ReadDir,
|
||||
})?)
|
||||
.send_and_await_response(3)??
|
||||
else {
|
||||
return Err(anyhow::anyhow!("vfs: bad response"));
|
||||
};
|
||||
let response = serde_json::from_slice::<vfs::VfsResponse>(&body)?;
|
||||
crate::print_to_terminal(1, &format!("vfs response: {:?}", response));
|
||||
let vfs::VfsResponse::ReadDir(entries) = response else {
|
||||
return Err(anyhow::anyhow!("vfs: unexpected response: {:?}", response));
|
||||
};
|
||||
for entry in entries {
|
||||
crate::print_to_terminal(1, &format!("entry: {:?}", entry));
|
||||
// ignore non-package dirs
|
||||
let Ok(package_id) = entry.path.parse::<PackageId>() else {
|
||||
continue;
|
||||
};
|
||||
if entry.file_type == vfs::FileType::Directory {
|
||||
let zip_file = vfs::File {
|
||||
path: format!("/{}/pkg/{}.zip", package_id, package_id),
|
||||
};
|
||||
let Ok(zip_file_bytes) = zip_file.read() else {
|
||||
continue;
|
||||
};
|
||||
// generate entry from this data
|
||||
// for the version hash, take the SHA-256 hash of the zip file
|
||||
let our_version = generate_version_hash(&zip_file_bytes);
|
||||
// the user will need to turn mirroring and auto-update back on if they
|
||||
// have to reset the state of their app store for some reason. the apps
|
||||
// themselves will remain on disk unless explicitly deleted.
|
||||
self.add_downloaded_package(
|
||||
&package_id,
|
||||
PackageState {
|
||||
mirrored_from: None,
|
||||
our_version,
|
||||
installed: true,
|
||||
caps_approved: true, // since it's already installed this must be true
|
||||
mirroring: false,
|
||||
auto_update: false,
|
||||
metadata: None,
|
||||
},
|
||||
None,
|
||||
)?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn uninstall(&mut self, package_id: &PackageId) -> anyhow::Result<()> {
|
||||
let drive_path = format!("/{package_id}/pkg");
|
||||
Request::new()
|
||||
.target(("our", "vfs", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&vfs::VfsRequest {
|
||||
path: format!("{}/manifest.json", drive_path),
|
||||
action: vfs::VfsAction::Read,
|
||||
})?)
|
||||
.send_and_await_response(5)??;
|
||||
let Some(blob) = get_blob() else {
|
||||
return Err(anyhow::anyhow!("no blob"));
|
||||
};
|
||||
let manifest = String::from_utf8(blob.bytes)?;
|
||||
let manifest = serde_json::from_str::<Vec<kt::PackageManifestEntry>>(&manifest)?;
|
||||
// reading from the package manifest, kill every process
|
||||
for entry in &manifest {
|
||||
let process_id = format!("{}:{}", entry.process_name, package_id);
|
||||
let Ok(parsed_new_process_id) = process_id.parse::<ProcessId>() else {
|
||||
continue;
|
||||
};
|
||||
Request::new()
|
||||
.target(("our", "kernel", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&kt::KernelCommand::KillProcess(
|
||||
parsed_new_process_id,
|
||||
))?)
|
||||
.send()?;
|
||||
}
|
||||
// then, delete the drive
|
||||
Request::new()
|
||||
.target(("our", "vfs", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&vfs::VfsRequest {
|
||||
path: drive_path,
|
||||
action: vfs::VfsAction::RemoveDirAll,
|
||||
})?)
|
||||
.send_and_await_response(5)??;
|
||||
|
||||
// finally, remove from downloaded packages
|
||||
self.downloaded_packages.remove(package_id);
|
||||
crate::set_state(&bincode::serialize(self)?);
|
||||
|
||||
println!("app store: uninstalled {package_id}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// only saves state if last_saved_block is more than 1000 blocks behind
|
||||
pub fn ingest_listings_contract_event(&mut self, log: Log) -> anyhow::Result<()> {
|
||||
let block_number: u64 = log
|
||||
.block_number
|
||||
.ok_or(anyhow::anyhow!("app store: got log with no block number"))?
|
||||
.try_into()?;
|
||||
|
||||
// let package_hash: alloy_primitives::U256 = log.topics[1].into();
|
||||
// let package_hash = package_hash.to_string();
|
||||
|
||||
match log.topics[0] {
|
||||
AppRegistered::SIGNATURE_HASH => {
|
||||
let package_hash = log.topics[1];
|
||||
let (package_name, publisher_dnswire, metadata_url, metadata_hash) =
|
||||
AppRegistered::abi_decode_data(&log.data, true)?;
|
||||
let package_hash = package_hash.to_string();
|
||||
let metadata_hash = metadata_hash.to_string();
|
||||
|
||||
crate::print_to_terminal(
|
||||
1,
|
||||
&format!(
|
||||
"app registered with publisher_dnswire {:?}, package_hash {}, package_name {}, metadata_url {}, metadata_hash {}",
|
||||
publisher_dnswire, package_hash, package_name, metadata_url, metadata_hash
|
||||
)
|
||||
);
|
||||
|
||||
if generate_package_hash(&package_name, publisher_dnswire.as_slice())
|
||||
!= package_hash
|
||||
{
|
||||
return Err(anyhow::anyhow!(
|
||||
"app store: got log with mismatched package hash"
|
||||
));
|
||||
}
|
||||
|
||||
let Ok(publisher_name) = dnswire_decode(publisher_dnswire.as_slice()) else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"app store: got log with invalid publisher name"
|
||||
));
|
||||
};
|
||||
|
||||
let metadata = fetch_metadata(&metadata_url, &metadata_hash).ok();
|
||||
|
||||
let listing = match self.get_listing_with_hash_mut(&package_hash) {
|
||||
Some(current_listing) => {
|
||||
current_listing.name = package_name;
|
||||
current_listing.publisher = publisher_name;
|
||||
current_listing.metadata_hash = metadata_hash;
|
||||
current_listing.metadata = metadata;
|
||||
current_listing.clone()
|
||||
}
|
||||
None => PackageListing {
|
||||
owner: "".to_string(),
|
||||
name: package_name,
|
||||
publisher: publisher_name,
|
||||
metadata_hash,
|
||||
metadata,
|
||||
},
|
||||
};
|
||||
self.insert_listing(package_hash, listing);
|
||||
}
|
||||
AppMetadataUpdated::SIGNATURE_HASH => {
|
||||
let package_hash = log.topics[1].to_string();
|
||||
let (metadata_url, metadata_hash) =
|
||||
AppMetadataUpdated::abi_decode_data(&log.data, false)?;
|
||||
let metadata_hash = metadata_hash.to_string();
|
||||
|
||||
crate::print_to_terminal(
|
||||
1,
|
||||
&format!(
|
||||
"app metadata updated with package_hash {}, metadata_url {}, metadata_hash {}",
|
||||
package_hash, metadata_url, metadata_hash
|
||||
)
|
||||
);
|
||||
|
||||
let current_listing = self
|
||||
.get_listing_with_hash_mut(&package_hash.to_string())
|
||||
.ok_or(anyhow::anyhow!(
|
||||
"app store: got log with no matching listing"
|
||||
))?;
|
||||
|
||||
let metadata = match fetch_metadata(&metadata_url, &metadata_hash) {
|
||||
Ok(metadata) => Some(metadata),
|
||||
Err(e) => {
|
||||
crate::print_to_terminal(
|
||||
1,
|
||||
&format!("app store: failed to fetch metadata: {e:?}"),
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
current_listing.metadata_hash = metadata_hash;
|
||||
current_listing.metadata = metadata;
|
||||
}
|
||||
Transfer::SIGNATURE_HASH => {
|
||||
let from = alloy_primitives::Address::from_word(log.topics[1]);
|
||||
let to = alloy_primitives::Address::from_word(log.topics[2]);
|
||||
let package_hash = log.topics[3].to_string();
|
||||
|
||||
crate::print_to_terminal(
|
||||
1,
|
||||
&format!(
|
||||
"handling transfer from {} to {} of pkghash {}",
|
||||
from, to, package_hash
|
||||
),
|
||||
);
|
||||
|
||||
if from == alloy_primitives::Address::ZERO {
|
||||
crate::print_to_terminal(1, "transfer from 0 address: new app listed");
|
||||
match self.get_listing_with_hash_mut(&package_hash) {
|
||||
Some(current_listing) => {
|
||||
current_listing.owner = to.to_string();
|
||||
}
|
||||
None => {
|
||||
let listing = PackageListing {
|
||||
owner: to.to_string(),
|
||||
name: "".to_string(),
|
||||
publisher: "".to_string(),
|
||||
metadata_hash: "".to_string(),
|
||||
metadata: None,
|
||||
};
|
||||
self.insert_listing(package_hash, listing);
|
||||
}
|
||||
}
|
||||
} else if to == alloy_primitives::Address::ZERO {
|
||||
crate::print_to_terminal(1, "transfer to 0 address: deleting listing");
|
||||
self.delete_listing(&package_hash);
|
||||
} else {
|
||||
crate::print_to_terminal(1, "transferring listing");
|
||||
let current_listing =
|
||||
self.get_listing_with_hash_mut(&package_hash)
|
||||
.ok_or(anyhow::anyhow!(
|
||||
"app store: got log with no matching listing"
|
||||
))?;
|
||||
current_listing.owner = to.to_string();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if block_number > self.last_saved_block + 1000 {
|
||||
self.last_saved_block = block_number;
|
||||
crate::set_state(&bincode::serialize(self)?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// take a DNSwire-formatted node ID from chain and convert it to a String
|
||||
fn dnswire_decode(wire_format_bytes: &[u8]) -> Result<String, std::string::FromUtf8Error> {
|
||||
let mut i = 0;
|
||||
let mut result = Vec::new();
|
||||
|
||||
while i < wire_format_bytes.len() {
|
||||
let len = wire_format_bytes[i] as usize;
|
||||
if len == 0 {
|
||||
break;
|
||||
}
|
||||
let end = i + len + 1;
|
||||
let mut span = wire_format_bytes[i + 1..end].to_vec();
|
||||
span.push('.' as u8);
|
||||
result.push(span);
|
||||
i = end;
|
||||
}
|
||||
|
||||
let flat: Vec<_> = result.into_iter().flatten().collect();
|
||||
|
||||
let name = String::from_utf8(flat)?;
|
||||
|
||||
// Remove the trailing '.' if it exists (it should always exist)
|
||||
if name.ends_with('.') {
|
||||
Ok(name[0..name.len() - 1].to_string())
|
||||
} else {
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
|
||||
/// fetch metadata from metadata_url and verify it matches metadata_hash
|
||||
fn fetch_metadata(
|
||||
metadata_url: &str,
|
||||
metadata_hash: &str,
|
||||
) -> anyhow::Result<OnchainPackageMetadata> {
|
||||
let url = url::Url::parse(metadata_url)?;
|
||||
let _response = http::send_request_await_response(http::Method::GET, url, None, 5, vec![])?;
|
||||
let Some(body) = get_blob() else {
|
||||
return Err(anyhow::anyhow!("no blob"));
|
||||
};
|
||||
let hash = generate_metadata_hash(&body.bytes);
|
||||
if &hash == metadata_hash {
|
||||
Ok(serde_json::from_slice::<OnchainPackageMetadata>(
|
||||
&body.bytes,
|
||||
)?)
|
||||
} else {
|
||||
Err(anyhow::anyhow!(
|
||||
"metadata hash mismatch: got {hash}, expected {metadata_hash}"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// generate a Keccak-256 hash of the metadata bytes
|
||||
fn generate_metadata_hash(metadata: &[u8]) -> String {
|
||||
use sha3::{Digest, Keccak256};
|
||||
let mut hasher = Keccak256::new();
|
||||
hasher.update(metadata);
|
||||
format!("0x{:x}", hasher.finalize())
|
||||
}
|
||||
|
||||
/// generate a Keccak-256 hash of the package name and publisher (match onchain)
|
||||
fn generate_package_hash(name: &str, publisher_dnswire: &[u8]) -> String {
|
||||
use sha3::{Digest, Keccak256};
|
||||
let mut hasher = Keccak256::new();
|
||||
hasher.update([name.as_bytes(), publisher_dnswire].concat());
|
||||
let hash = hasher.finalize();
|
||||
format!("0x{:x}", hash)
|
||||
}
|
||||
|
||||
/// generate a SHA-256 hash of the zip bytes to act as a version hash
|
||||
pub fn generate_version_hash(zip_bytes: &[u8]) -> String {
|
||||
use sha2::{Digest, Sha256};
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(zip_bytes);
|
||||
format!("{:x}", hasher.finalize())
|
||||
}
|
@ -3,14 +3,10 @@ name = "download"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "329c7a8" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
1
kinode/packages/app_store/download/src/api.rs
Symbolic link
1
kinode/packages/app_store/download/src/api.rs
Symbolic link
@ -0,0 +1 @@
|
||||
../../app_store/src/api.rs
|
@ -1,36 +1,18 @@
|
||||
use kinode_process_lib::{
|
||||
await_next_request_body, call_init, println, Address, Message, NodeId, PackageId, Request,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod api;
|
||||
use api::*;
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
||||
},
|
||||
});
|
||||
|
||||
/// grabbed from main:app_store:sys
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum LocalRequest {
|
||||
Download {
|
||||
package: PackageId,
|
||||
install_from: NodeId,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum LocalResponse {
|
||||
DownloadResponse(DownloadResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum DownloadResponse {
|
||||
Started,
|
||||
Failure,
|
||||
}
|
||||
|
||||
call_init!(init);
|
||||
|
||||
fn init(our: Address) {
|
||||
@ -60,7 +42,10 @@ fn init(our: Address) {
|
||||
.body(
|
||||
serde_json::to_vec(&LocalRequest::Download {
|
||||
package: package_id.clone(),
|
||||
install_from: download_from.clone(),
|
||||
download_from: download_from.clone(),
|
||||
mirror: true,
|
||||
auto_update: true,
|
||||
desired_version_hash: None,
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
@ -82,5 +67,9 @@ fn init(our: Address) {
|
||||
LocalResponse::DownloadResponse(DownloadResponse::Failure) => {
|
||||
println!("failed to download package {package_id} from {download_from}");
|
||||
}
|
||||
_ => {
|
||||
println!("download: unexpected response from app_store..!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,16 @@
|
||||
[package]
|
||||
name = "app_store"
|
||||
name = "ft_worker"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.5-alpha" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
rand = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
sha2 = "0.10.8"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
||||
|
||||
[lib]
|
@ -6,7 +6,7 @@ mod ft_worker_lib;
|
||||
use ft_worker_lib::*;
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
@ -3,14 +3,10 @@ name = "install"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "329c7a8" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
1
kinode/packages/app_store/install/src/api.rs
Symbolic link
1
kinode/packages/app_store/install/src/api.rs
Symbolic link
@ -0,0 +1 @@
|
||||
../../app_store/src/api.rs
|
@ -1,33 +1,18 @@
|
||||
use kinode_process_lib::{
|
||||
await_next_request_body, call_init, println, Address, Message, NodeId, PackageId, Request,
|
||||
await_next_request_body, call_init, println, Address, Message, PackageId, Request,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod api;
|
||||
use api::*;
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
||||
},
|
||||
});
|
||||
|
||||
/// grabbed from main:app_store:sys
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum LocalRequest {
|
||||
Install(PackageId),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum LocalResponse {
|
||||
InstallResponse(InstallResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum InstallResponse {
|
||||
Success,
|
||||
Failure,
|
||||
}
|
||||
|
||||
call_init!(init);
|
||||
|
||||
fn init(our: Address) {
|
||||
@ -72,5 +57,9 @@ fn init(our: Address) {
|
||||
println!("failed to install package {package_id}");
|
||||
println!("make sure that the package has been downloaded!")
|
||||
}
|
||||
_ => {
|
||||
println!("install: unexpected response from app_store..!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@ -25,7 +25,10 @@
|
||||
}
|
||||
],
|
||||
"grant_capabilities": [
|
||||
"eth:distro:sys",
|
||||
"http_client:distro:sys",
|
||||
"http_server:distro:sys",
|
||||
"kns_indexer:kns_indexer:sys",
|
||||
"terminal:terminal:sys",
|
||||
"vfs:distro:sys"
|
||||
],
|
@ -2,33 +2,33 @@
|
||||
"download.wasm": {
|
||||
"root": false,
|
||||
"public": false,
|
||||
"requestNetworking": false,
|
||||
"requestCapabilities": [
|
||||
"request_networking": false,
|
||||
"request_capabilities": [
|
||||
"main:app_store:sys"
|
||||
],
|
||||
"grantCapabilities": [
|
||||
"grant_capabilities": [
|
||||
"main:app_store:sys"
|
||||
]
|
||||
},
|
||||
"install.wasm": {
|
||||
"root": false,
|
||||
"public": false,
|
||||
"requestNetworking": false,
|
||||
"requestCapabilities": [
|
||||
"request_networking": false,
|
||||
"request_capabilities": [
|
||||
"main:app_store:sys"
|
||||
],
|
||||
"grantCapabilities": [
|
||||
"grant_capabilities": [
|
||||
"main:app_store:sys"
|
||||
]
|
||||
},
|
||||
"uninstall.wasm": {
|
||||
"root": false,
|
||||
"public": false,
|
||||
"requestNetworking": false,
|
||||
"requestCapabilities": [
|
||||
"request_networking": false,
|
||||
"request_capabilities": [
|
||||
"main:app_store:sys"
|
||||
],
|
||||
"grantCapabilities": [
|
||||
"grant_capabilities": [
|
||||
"main:app_store:sys"
|
||||
]
|
||||
}
|
File diff suppressed because one or more lines are too long
90
kinode/packages/app_store/pkg/ui/assets/index-pkTLhk2L.js
Normal file
90
kinode/packages/app_store/pkg/ui/assets/index-pkTLhk2L.js
Normal file
File diff suppressed because one or more lines are too long
1
kinode/packages/app_store/pkg/ui/assets/vite.svg
Normal file
1
kinode/packages/app_store/pkg/ui/assets/vite.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
27
kinode/packages/app_store/pkg/ui/index.html
Normal file
27
kinode/packages/app_store/pkg/ui/index.html
Normal file
File diff suppressed because one or more lines are too long
@ -3,14 +3,10 @@ name = "uninstall"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "329c7a8" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
1
kinode/packages/app_store/uninstall/src/api.rs
Symbolic link
1
kinode/packages/app_store/uninstall/src/api.rs
Symbolic link
@ -0,0 +1 @@
|
||||
../../app_store/src/api.rs
|
@ -1,33 +1,18 @@
|
||||
use kinode_process_lib::{
|
||||
await_next_request_body, call_init, println, Address, Message, NodeId, PackageId, Request,
|
||||
await_next_request_body, call_init, println, Address, Message, PackageId, Request,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod api;
|
||||
use api::*;
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
||||
},
|
||||
});
|
||||
|
||||
/// grabbed from main:app_store:sys
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum LocalRequest {
|
||||
Uninstall(PackageId),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum LocalResponse {
|
||||
UninstallResponse(UninstallResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum UninstallResponse {
|
||||
Success,
|
||||
Failure,
|
||||
}
|
||||
|
||||
call_init!(init);
|
||||
|
||||
fn init(our: Address) {
|
||||
@ -71,5 +56,9 @@ fn init(our: Address) {
|
||||
LocalResponse::UninstallResponse(UninstallResponse::Failure) => {
|
||||
println!("failed to uninstall package {package_id}!");
|
||||
}
|
||||
_ => {
|
||||
println!("uninstall: unexpected response from app_store..!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,9 +4,9 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
@ -46,9 +46,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.1"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
@ -88,36 +88,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.3"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.15"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.16"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
@ -154,9 +146,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@ -180,9 +172,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
|
||||
checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
@ -213,9 +205,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@ -230,8 +222,13 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?tag=v0.5.5-alpha#722f2dbfbcc4d1bf5da1fa5db137632a3cede44c"
|
||||
<<<<<<< HEAD:modules/chess/chess/Cargo.lock
|
||||
version = "0.5.7"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?tag=v0.5.9-alpha#c1ac7227951fbd8cabf6568704f0ce11e8558c8a"
|
||||
=======
|
||||
version = "0.5.6"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=fccb6a0#fccb6a0c07ebda3e385bff7f76e4984b741f01c7"
|
||||
>>>>>>> develop:kinode/packages/chess/chess/Cargo.lock
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -259,9 +256,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.151"
|
||||
version = "0.2.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@ -269,15 +266,6 @@ version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
@ -338,18 +326,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -492,9 +480,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
||||
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
@ -502,9 +490,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.0"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
@ -525,32 +513,26 @@ version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.20"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.193"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.193"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -559,9 +541,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.108"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@ -570,24 +552,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.2"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "spdx"
|
||||
version = "0.10.2"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b19b32ed6d899ab23174302ff105c1577e45a06b08d4fe0a9dd13ce804bbbf71"
|
||||
checksum = "62bde1398b09b9f93fc2fc9b9da86e362693e999d3a54a8ac47a99a5a73f638b"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.41"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -596,18 +578,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.50"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
|
||||
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.50"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -640,9 +622,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.14"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
|
||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
@ -704,10 +686,19 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.10.14"
|
||||
name = "wasm-encoder"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d835d67708f6374937c550ad8dd1d17c616ae009e3f00d7a0ac9f7825e78c36a"
|
||||
checksum = "e09bca7d6388637d27fb5edbeab11f56bfabcef8743c55ae34370e1e5030a071"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.10.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c853d3809fc9fccf3bc0ad63f4f51d8eefad0bacf88f957aa991c1d9b88b016e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
@ -715,8 +706,8 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"spdx",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
"wasm-encoder 0.41.0",
|
||||
"wasmparser 0.121.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -729,6 +720,17 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.121.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "953cf6a7606ab31382cb1caa5ae403e77ba70c7f8e12eeda167e7040d42bfda8"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@ -756,7 +758,7 @@ name = "wit-bindgen"
|
||||
version = "0.16.0"
|
||||
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.4.2",
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
@ -803,23 +805,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.4.2",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-encoder 0.38.1",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wasmparser 0.118.1",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.13.0"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3"
|
||||
checksum = "df4913a2219096373fd6512adead1fb77ecdaa59d7fc517972a7d30b12f625be"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
@ -3,16 +3,12 @@ name = "chess"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
base64 = "0.13"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.5-alpha" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
pleco = "0.5"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
@ -96,7 +96,7 @@ fn send_ws_update(our: &Address, game: &Game, open_channels: &HashSet<u32>) -> a
|
||||
|
||||
// Boilerplate: generate the wasm bindings for a process
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
||||
@ -111,7 +111,8 @@ fn initialize(our: Address) {
|
||||
println!("{}: started", our.package());
|
||||
|
||||
// Serve the index.html and other UI files found in pkg/ui at the root path.
|
||||
http::serve_ui(&our, "ui").unwrap();
|
||||
// authenticated=true, local_only=false
|
||||
http::serve_ui(&our, "ui", true, false, vec!["/"]).unwrap();
|
||||
|
||||
// Allow HTTP requests to be made to /games; they will be handled dynamically.
|
||||
http::bind_http_path("/games", true, false).unwrap();
|
||||
@ -183,12 +184,12 @@ fn handle_request(our: &Address, message: &Message, state: &mut ChessState) -> a
|
||||
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(),
|
||||
)
|
||||
);
|
||||
Err(anyhow::anyhow!("chess: error handling http request: {e:?}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -419,35 +420,48 @@ fn handle_http_request(
|
||||
http_request: &http::IncomingHttpRequest,
|
||||
) -> anyhow::Result<()> {
|
||||
if http_request.path()? != "/games" {
|
||||
return http::send_response(
|
||||
http::send_response(
|
||||
http::StatusCode::NOT_FOUND,
|
||||
None,
|
||||
"Not Found".to_string().as_bytes().to_vec(),
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
match http_request.method()?.as_str() {
|
||||
// on GET: give the frontend all of our active games
|
||||
"GET" => http::send_response(
|
||||
"GET" => Ok(http::send_response(
|
||||
http::StatusCode::OK,
|
||||
Some(HashMap::from([(
|
||||
String::from("Content-Type"),
|
||||
String::from("application/json"),
|
||||
)])),
|
||||
serde_json::to_vec(&state.games)?,
|
||||
),
|
||||
)),
|
||||
// on POST: create a new game
|
||||
"POST" => {
|
||||
let Some(blob) = get_blob() else {
|
||||
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
|
||||
return Ok(http::send_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
None,
|
||||
vec![],
|
||||
));
|
||||
};
|
||||
let blob_json = serde_json::from_slice::<serde_json::Value>(&blob.bytes)?;
|
||||
let Some(game_id) = blob_json["id"].as_str() else {
|
||||
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
|
||||
return Ok(http::send_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
None,
|
||||
vec![],
|
||||
));
|
||||
};
|
||||
if let Some(game) = state.games.get(game_id)
|
||||
&& !game.ended
|
||||
{
|
||||
return http::send_response(http::StatusCode::CONFLICT, None, vec![]);
|
||||
return Ok(http::send_response(
|
||||
http::StatusCode::CONFLICT,
|
||||
None,
|
||||
vec![],
|
||||
));
|
||||
};
|
||||
|
||||
let player_white = blob_json["white"]
|
||||
@ -495,34 +509,63 @@ fn handle_http_request(
|
||||
String::from("application/json"),
|
||||
)])),
|
||||
body,
|
||||
)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
// on PUT: make a move
|
||||
"PUT" => {
|
||||
let Some(blob) = get_blob() else {
|
||||
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
|
||||
return Ok(http::send_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
None,
|
||||
vec![],
|
||||
));
|
||||
};
|
||||
let blob_json = serde_json::from_slice::<serde_json::Value>(&blob.bytes)?;
|
||||
let Some(game_id) = blob_json["id"].as_str() else {
|
||||
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
|
||||
return Ok(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::NOT_FOUND, None, vec![]);
|
||||
return Ok(http::send_response(
|
||||
http::StatusCode::NOT_FOUND,
|
||||
None,
|
||||
vec![],
|
||||
));
|
||||
};
|
||||
if (game.turns % 2 == 0 && game.white != our.node)
|
||||
|| (game.turns % 2 == 1 && game.black != our.node)
|
||||
{
|
||||
return http::send_response(http::StatusCode::FORBIDDEN, None, vec![]);
|
||||
return Ok(http::send_response(
|
||||
http::StatusCode::FORBIDDEN,
|
||||
None,
|
||||
vec![],
|
||||
));
|
||||
} else if game.ended {
|
||||
return http::send_response(http::StatusCode::CONFLICT, None, vec![]);
|
||||
return Ok(http::send_response(
|
||||
http::StatusCode::CONFLICT,
|
||||
None,
|
||||
vec![],
|
||||
));
|
||||
}
|
||||
let Some(move_str) = blob_json["move"].as_str() else {
|
||||
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
|
||||
return Ok(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![]);
|
||||
return Ok(http::send_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
None,
|
||||
vec![],
|
||||
));
|
||||
}
|
||||
// send the move to the other player
|
||||
// check if the game is over
|
||||
@ -559,15 +602,24 @@ fn handle_http_request(
|
||||
String::from("application/json"),
|
||||
)])),
|
||||
body,
|
||||
)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
// on DELETE: end the game
|
||||
"DELETE" => {
|
||||
let Some(game_id) = http_request.query_params().get("id") else {
|
||||
return http::send_response(http::StatusCode::BAD_REQUEST, None, vec![]);
|
||||
return Ok(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![]);
|
||||
return Ok(http::send_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
None,
|
||||
vec![],
|
||||
));
|
||||
};
|
||||
// send the other player an end game request
|
||||
Request::new()
|
||||
@ -584,9 +636,14 @@ fn handle_http_request(
|
||||
String::from("application/json"),
|
||||
)])),
|
||||
body,
|
||||
)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
// Any other method will be rejected.
|
||||
_ => http::send_response(http::StatusCode::METHOD_NOT_ALLOWED, None, vec![]),
|
||||
_ => Ok(http::send_response(
|
||||
http::StatusCode::METHOD_NOT_ALLOWED,
|
||||
None,
|
||||
vec![],
|
||||
)),
|
||||
}
|
||||
}
|
@ -83,9 +83,11 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hi"
|
||||
name = "homepage"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -121,9 +123,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@ -138,8 +140,8 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=329c7a8#329c7a8314973c857db38c7b712318de9349eb6e"
|
||||
version = "0.5.6"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=fccb6a0#fccb6a0c07ebda3e385bff7f76e4984b741f01c7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -201,9 +203,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -261,18 +263,18 @@ checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.195"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.195"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -281,9 +283,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.111"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@ -292,9 +294,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.12.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "spdx"
|
||||
@ -427,18 +429,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.39.0"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "111495d6204760238512f57a9af162f45086504da332af210f2f75dd80b34f1d"
|
||||
checksum = "e09bca7d6388637d27fb5edbeab11f56bfabcef8743c55ae34370e1e5030a071"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.10.15"
|
||||
version = "0.10.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "818931c85b1d197909699d36c509fa89550ccfa0d66932ba3c1726faddb4d0c7"
|
||||
checksum = "c853d3809fc9fccf3bc0ad63f4f51d8eefad0bacf88f957aa991c1d9b88b016e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
@ -446,8 +448,8 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"spdx",
|
||||
"wasm-encoder 0.39.0",
|
||||
"wasmparser 0.119.0",
|
||||
"wasm-encoder 0.41.0",
|
||||
"wasmparser 0.121.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -462,9 +464,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.119.0"
|
||||
version = "0.121.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c35daf77afb4f9b14016625144a391085ec2ca99ca9cc53ed291bb53ab5278d"
|
||||
checksum = "953cf6a7606ab31382cb1caa5ae403e77ba70c7f8e12eeda167e7040d42bfda8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"indexmap",
|
19
kinode/packages/homepage/homepage/Cargo.toml
Normal file
19
kinode/packages/homepage/homepage/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "homepage"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.component]
|
||||
package = "kinode:process"
|
@ -9,7 +9,7 @@
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover" />
|
||||
<link href='https://fonts.googleapis.com/css?family=Montserrat' rel='stylesheet'>
|
||||
<link href='https://fonts.googleapis.com/css?family=Montserrat' rel='stylesheet'>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
@ -197,7 +197,8 @@
|
||||
<h3 id="uq-name">Welcome ${our}!</h3>
|
||||
|
||||
<h4>Apps:</h4>
|
||||
<!-- <a id="file-transfer" href="/file-transfer">File Transfer</a> -->
|
||||
<a id="app-store" href="/main:app_store:sys/">App Store</a>
|
||||
<br />
|
||||
<a id="chess" href="/chess:chess:sys/">Chess</a>
|
||||
</div>
|
||||
<script>window.ourName = window.our = '${our}'</script>
|
||||
@ -215,8 +216,8 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// const fileTransferLink = document.getElementById('file-transfer')
|
||||
// fileTransferLink.attributes.href.value = (window.location.pathname + fileTransferLink.attributes.href.value).replace('//', '/')
|
||||
const chessLink = document.getElementById('chess')
|
||||
chessLink.attributes.href.value = (window.location.pathname + chessLink.attributes.href.value).replace('//', '/')
|
||||
// const chessLink = document.getElementById('chess')
|
||||
// chessLink.attributes.href.value = (window.location.pathname + chessLink.attributes.href.value).replace('//', '/')
|
||||
|
||||
// const webSocket = new WebSocket('ws://' + window.location.host) // Handle proxying later
|
||||
// webSocket.onopen = () => {
|
@ -4,7 +4,7 @@ use kinode_process_lib::{
|
||||
};
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
@ -3,12 +3,6 @@ name = "kns_indexer"
|
||||
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"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
@ -17,7 +11,7 @@ alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy.git", rev = "3b1c31
|
||||
alloy-sol-types = "0.5.1"
|
||||
bincode = "1.3.3"
|
||||
hex = "0.4.3"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.5-alpha", features = ["eth"] }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha", features = ["eth"] }
|
||||
rmp-serde = "1.1.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
@ -14,7 +14,7 @@ use std::str::FromStr;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
@ -3,14 +3,10 @@ name = "alias"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "329c7a8" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
@ -3,14 +3,10 @@ name = "cat"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "329c7a8" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
@ -3,7 +3,7 @@ use kinode_process_lib::{
|
||||
};
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
@ -3,14 +3,10 @@ name = "echo"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "329c7a8" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
@ -1,7 +1,7 @@
|
||||
use kinode_process_lib::{await_next_request_body, call_init, println, Address, Response};
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
@ -3,13 +3,9 @@ name = "hi"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "329c7a8" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
@ -3,7 +3,7 @@ use kinode_process_lib::{
|
||||
};
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
@ -3,15 +3,11 @@ name = "m"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
clap = "4.4.18"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "329c7a8" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
regex = "1.10.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
@ -3,7 +3,7 @@ use kinode_process_lib::{await_next_request_body, call_init, println, Address, R
|
||||
use regex::Regex;
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
@ -2,24 +2,24 @@
|
||||
"alias.wasm": {
|
||||
"root": false,
|
||||
"public": false,
|
||||
"requestNetworking": false,
|
||||
"requestCapabilities": [
|
||||
"request_networking": false,
|
||||
"request_capabilities": [
|
||||
"terminal:terminal:sys"
|
||||
],
|
||||
"grantCapabilities": []
|
||||
"grant_capabilities": []
|
||||
},
|
||||
"echo.wasm": {
|
||||
"root": false,
|
||||
"public": false,
|
||||
"requestNetworking": false,
|
||||
"requestCapabilities": [],
|
||||
"grantCapabilities": []
|
||||
"request_networking": false,
|
||||
"request_capabilities": [],
|
||||
"grant_capabilities": []
|
||||
},
|
||||
"cat.wasm": {
|
||||
"root": false,
|
||||
"public": false,
|
||||
"requestNetworking": false,
|
||||
"requestCapabilities": [
|
||||
"request_networking": false,
|
||||
"request_capabilities": [
|
||||
"vfs:distro:sys",
|
||||
{
|
||||
"process": "vfs:distro:sys",
|
||||
@ -28,31 +28,31 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"grantCapabilities": []
|
||||
"grant_capabilities": []
|
||||
},
|
||||
"hi.wasm": {
|
||||
"root": false,
|
||||
"public": false,
|
||||
"requestNetworking": true,
|
||||
"requestCapabilities": [
|
||||
"request_networking": true,
|
||||
"request_capabilities": [
|
||||
"net:distro:sys"
|
||||
],
|
||||
"grantCapabilities": [
|
||||
"grant_capabilities": [
|
||||
"net:distro:sys"
|
||||
]
|
||||
},
|
||||
"top.wasm": {
|
||||
"root": false,
|
||||
"public": false,
|
||||
"requestNetworking": false,
|
||||
"requestCapabilities": [
|
||||
"request_networking": false,
|
||||
"request_capabilities": [
|
||||
"kernel:distro:sys"
|
||||
],
|
||||
"grantCapabilities": []
|
||||
"grant_capabilities": []
|
||||
},
|
||||
"m.wasm": {
|
||||
"root": true,
|
||||
"public": false,
|
||||
"requestNetworking": true
|
||||
"public": true,
|
||||
"request_networking": true
|
||||
}
|
||||
}
|
@ -3,14 +3,12 @@
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "alias"
|
||||
version = "0.1.0"
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -30,9 +28,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.1"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
@ -122,9 +120,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@ -139,8 +137,8 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=329c7a8#329c7a8314973c857db38c7b712318de9349eb6e"
|
||||
version = "0.5.6"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=fccb6a0#fccb6a0c07ebda3e385bff7f76e4984b741f01c7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -172,6 +170,12 @@ version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
@ -202,9 +206,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -248,6 +252,35 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
@ -262,18 +295,18 @@ checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.195"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.195"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -282,9 +315,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.111"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@ -293,9 +326,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.12.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "spdx"
|
||||
@ -317,6 +350,20 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib",
|
||||
"rand",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.56"
|
||||
@ -363,9 +410,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.14"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
|
||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
@ -428,18 +475,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.39.0"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "111495d6204760238512f57a9af162f45086504da332af210f2f75dd80b34f1d"
|
||||
checksum = "e09bca7d6388637d27fb5edbeab11f56bfabcef8743c55ae34370e1e5030a071"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.10.15"
|
||||
version = "0.10.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "818931c85b1d197909699d36c509fa89550ccfa0d66932ba3c1726faddb4d0c7"
|
||||
checksum = "c853d3809fc9fccf3bc0ad63f4f51d8eefad0bacf88f957aa991c1d9b88b016e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
@ -447,8 +494,8 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"spdx",
|
||||
"wasm-encoder 0.39.0",
|
||||
"wasmparser 0.119.0",
|
||||
"wasm-encoder 0.41.0",
|
||||
"wasmparser 0.121.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -463,9 +510,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.119.0"
|
||||
version = "0.121.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c35daf77afb4f9b14016625144a391085ec2ca99ca9cc53ed291bb53ab5278d"
|
||||
checksum = "953cf6a7606ab31382cb1caa5ae403e77ba70c7f8e12eeda167e7040d42bfda8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"indexmap",
|
21
kinode/packages/terminal/terminal/Cargo.toml
Normal file
21
kinode/packages/terminal/terminal/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "terminal"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
rand = "0.8"
|
||||
regex = "1.10.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.component]
|
||||
package = "kinode:process"
|
@ -2,15 +2,15 @@ use anyhow::anyhow;
|
||||
use kinode_process_lib::kernel_types as kt;
|
||||
use kinode_process_lib::kinode::process::standard as wit;
|
||||
use kinode_process_lib::{
|
||||
get_blob, get_capability, get_typed_state, our_capabilities, println, set_state, vfs, Address,
|
||||
Capability, PackageId, ProcessId, Request,
|
||||
get_blob, get_typed_state, our_capabilities, print_to_terminal, println, set_state, vfs,
|
||||
Address, Capability, PackageId, ProcessId, Request,
|
||||
};
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
||||
@ -205,13 +205,6 @@ fn handle_run(
|
||||
};
|
||||
let wasm_path = format!("{}{}", drive_path, wasm_path);
|
||||
// build initial caps
|
||||
let mut initial_capabilities: HashSet<kt::Capability> = HashSet::new();
|
||||
if entry.request_networking {
|
||||
initial_capabilities.insert(kt::de_wit_capability(Capability {
|
||||
issuer: Address::new(&our.node, ("kernel", "distro", "sys")),
|
||||
params: "\"network\"".to_string(),
|
||||
}));
|
||||
}
|
||||
let process_id = format!("{}:{}", rand::random::<u64>(), package); // all scripts are given random process IDs
|
||||
let Ok(parsed_new_process_id) = process_id.parse::<ProcessId>() else {
|
||||
return Err(anyhow::anyhow!("app store: invalid process id!"));
|
||||
@ -224,19 +217,31 @@ fn handle_run(
|
||||
action: vfs::VfsAction::Read,
|
||||
})?)
|
||||
.send_and_await_response(5)??;
|
||||
Request::new()
|
||||
.target(("our", "kernel", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&kt::KernelCommand::InitializeProcess {
|
||||
id: parsed_new_process_id.clone(),
|
||||
wasm_bytes_handle: wasm_path.clone(),
|
||||
wit_version: None,
|
||||
on_exit: kt::OnExit::None, // TODO this should send a message back to runner:script:sys so that it can Drop capabilities
|
||||
initial_capabilities: HashSet::new(),
|
||||
public: entry.public,
|
||||
})?)
|
||||
.inherit(true)
|
||||
.send_and_await_response(5)??;
|
||||
let mut requested_caps: Vec<kt::Capability> = vec![];
|
||||
if let Some(to_request) = &entry.request_capabilities {
|
||||
for value in to_request {
|
||||
let mut capability = None;
|
||||
match value {
|
||||
serde_json::Value::String(process_name) => {
|
||||
if let Ok(parsed_process_id) = process_name.parse::<ProcessId>() {
|
||||
capability = get_capability(
|
||||
&Address {
|
||||
requested_caps.push(kt::Capability {
|
||||
issuer: Address {
|
||||
node: our.node.clone(),
|
||||
process: parsed_process_id.clone(),
|
||||
},
|
||||
"\"messaging\"".into(),
|
||||
);
|
||||
params: "\"messaging\"".into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
serde_json::Value::Object(map) => {
|
||||
@ -247,13 +252,13 @@ fn handle_run(
|
||||
.parse::<ProcessId>()
|
||||
{
|
||||
if let Some(params) = map.get("params") {
|
||||
capability = get_capability(
|
||||
&Address {
|
||||
requested_caps.push(kt::Capability {
|
||||
issuer: Address {
|
||||
node: our.node.clone(),
|
||||
process: parsed_process_id.clone(),
|
||||
},
|
||||
¶ms.to_string(),
|
||||
);
|
||||
params: params.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -262,36 +267,44 @@ fn handle_run(
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let Some(cap) = capability {
|
||||
initial_capabilities.insert(kt::de_wit_capability(cap));
|
||||
} else {
|
||||
println!(
|
||||
"runner: no cap: {}, for {} to request!",
|
||||
value.to_string(),
|
||||
package
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if entry.request_networking {
|
||||
requested_caps.push(kt::de_wit_capability(Capability {
|
||||
issuer: Address::new(&our.node, ("kernel", "distro", "sys")),
|
||||
params: "\"network\"".to_string(),
|
||||
}));
|
||||
}
|
||||
if entry.root {
|
||||
for cap in our_capabilities() {
|
||||
requested_caps.push(kt::de_wit_capability(cap.clone()));
|
||||
}
|
||||
}
|
||||
print_to_terminal(
|
||||
1,
|
||||
&format!(
|
||||
"{}: Process {{\n wasm_bytes_handle: {},\n wit_version: {},\n on_exit: {:?},\n public: {}\n capabilities: {}\n}}",
|
||||
parsed_new_process_id.clone(),
|
||||
wasm_path.clone(),
|
||||
"None",
|
||||
kt::OnExit::None,
|
||||
entry.public,
|
||||
{
|
||||
let mut caps_string = "[".to_string();
|
||||
for cap in requested_caps.iter() {
|
||||
caps_string += &format!("\n {}({})", cap.issuer.to_string(), cap.params);
|
||||
}
|
||||
caps_string + "\n ]"
|
||||
},
|
||||
),
|
||||
);
|
||||
Request::new()
|
||||
.target(("our", "kernel", "distro", "sys"))
|
||||
.body(serde_json::to_vec(&kt::KernelCommand::InitializeProcess {
|
||||
id: parsed_new_process_id.clone(),
|
||||
wasm_bytes_handle: wasm_path,
|
||||
wit_version: None,
|
||||
on_exit: kt::OnExit::None, // TODO this should send a message back to runner:script:sys so that it can Drop capabilities
|
||||
initial_capabilities: if entry.root {
|
||||
our_capabilities()
|
||||
.iter()
|
||||
.map(|wit: &kinode_process_lib::Capability| kt::de_wit_capability(wit.clone()))
|
||||
.collect()
|
||||
} else {
|
||||
initial_capabilities
|
||||
},
|
||||
public: entry.public,
|
||||
.body(serde_json::to_vec(&kt::KernelCommand::GrantCapabilities {
|
||||
target: parsed_new_process_id.clone(),
|
||||
capabilities: requested_caps,
|
||||
})?)
|
||||
.inherit(true)
|
||||
.send_and_await_response(5)??;
|
||||
.send()?;
|
||||
if let Some(to_grant) = &entry.grant_capabilities {
|
||||
for value in to_grant {
|
||||
match value {
|
@ -3,14 +3,10 @@ name = "top"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "329c7a8" }
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
@ -4,7 +4,7 @@ use kinode_process_lib::{
|
||||
};
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
@ -19,9 +19,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.1"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
@ -111,9 +111,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@ -128,8 +128,8 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=329c7a8#329c7a8314973c857db38c7b712318de9349eb6e"
|
||||
version = "0.5.6"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=fccb6a0#fccb6a0c07ebda3e385bff7f76e4984b741f01c7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -191,9 +191,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -251,18 +251,18 @@ checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.195"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.195"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -271,9 +271,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.111"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@ -282,9 +282,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.12.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "spdx"
|
||||
@ -306,6 +306,19 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test_runner"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"kinode_process_lib",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.56"
|
||||
@ -352,9 +365,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.14"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
|
||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
@ -383,17 +396,6 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "uninstall"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.0"
|
||||
@ -428,18 +430,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.39.0"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "111495d6204760238512f57a9af162f45086504da332af210f2f75dd80b34f1d"
|
||||
checksum = "e09bca7d6388637d27fb5edbeab11f56bfabcef8743c55ae34370e1e5030a071"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.10.15"
|
||||
version = "0.10.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "818931c85b1d197909699d36c509fa89550ccfa0d66932ba3c1726faddb4d0c7"
|
||||
checksum = "c853d3809fc9fccf3bc0ad63f4f51d8eefad0bacf88f957aa991c1d9b88b016e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
@ -447,8 +449,8 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"spdx",
|
||||
"wasm-encoder 0.39.0",
|
||||
"wasmparser 0.119.0",
|
||||
"wasm-encoder 0.41.0",
|
||||
"wasmparser 0.121.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -463,9 +465,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.119.0"
|
||||
version = "0.121.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c35daf77afb4f9b14016625144a391085ec2ca99ca9cc53ed291bb53ab5278d"
|
||||
checksum = "953cf6a7606ab31382cb1caa5ae403e77ba70c7f8e12eeda167e7040d42bfda8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"indexmap",
|
20
kinode/packages/tester/test_runner/Cargo.toml
Normal file
20
kinode/packages/tester/test_runner/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "test_runner"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.component]
|
||||
package = "kinode:process"
|
@ -1,15 +1,16 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use kinode_process_lib::{
|
||||
await_message, our_capabilities, println, spawn, vfs, Address, Message, OnExit, ProcessId,
|
||||
Request, Response, vfs::{DirEntry, FileType},
|
||||
await_message, our_capabilities, println, spawn, vfs,
|
||||
vfs::{DirEntry, FileType},
|
||||
Address, Message, OnExit, ProcessId, Request, Response,
|
||||
};
|
||||
|
||||
mod tester_types;
|
||||
use tester_types as tt;
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
||||
@ -32,7 +33,11 @@ fn handle_message(our: &Address) -> anyhow::Result<()> {
|
||||
}
|
||||
Message::Request { ref body, .. } => {
|
||||
match serde_json::from_slice(body)? {
|
||||
tt::TesterRequest::Run { ref test_names, test_timeout, .. } => {
|
||||
tt::TesterRequest::Run {
|
||||
ref test_names,
|
||||
test_timeout,
|
||||
..
|
||||
} => {
|
||||
println!("test_runner: got Run");
|
||||
|
||||
let dir_prefix = "tester:sys/tests";
|
||||
@ -75,15 +80,16 @@ fn handle_message(our: &Address) -> anyhow::Result<()> {
|
||||
|
||||
let caps_file_path = format!("{}/grant_capabilities.json", dir_prefix);
|
||||
let caps_index = children.iter().position(|i| *i.path == *caps_file_path);
|
||||
let caps_by_child: std::collections::HashMap<String, Vec<String>> = match caps_index {
|
||||
None => std::collections::HashMap::new(),
|
||||
Some(caps_index) => {
|
||||
children.remove(caps_index);
|
||||
let file = vfs::file::open_file(&caps_file_path, false)?;
|
||||
let file_contents = file.read()?;
|
||||
serde_json::from_slice(&file_contents)?
|
||||
}
|
||||
};
|
||||
let caps_by_child: std::collections::HashMap<String, Vec<String>> =
|
||||
match caps_index {
|
||||
None => std::collections::HashMap::new(),
|
||||
Some(caps_index) => {
|
||||
children.remove(caps_index);
|
||||
let file = vfs::file::open_file(&caps_file_path, false)?;
|
||||
let file_contents = file.read()?;
|
||||
serde_json::from_slice(&file_contents)?
|
||||
}
|
||||
};
|
||||
|
||||
println!("test_runner: running {:?}...", children);
|
||||
|
||||
@ -91,7 +97,13 @@ fn handle_message(our: &Address) -> anyhow::Result<()> {
|
||||
let test_path = format!("{}/{}.wasm", dir_prefix, test_name);
|
||||
let grant_caps = caps_by_child
|
||||
.get(test_name)
|
||||
.and_then(|caps| Some(caps.iter().map(|cap| ProcessId::from_str(cap).unwrap()).collect()))
|
||||
.and_then(|caps| {
|
||||
Some(
|
||||
caps.iter()
|
||||
.map(|cap| ProcessId::from_str(cap).unwrap())
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.unwrap_or(vec![]);
|
||||
let child_process_id = match spawn(
|
||||
None,
|
@ -19,9 +19,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.1"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
@ -35,17 +35,6 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "download"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kinode_process_lib",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
@ -122,9 +111,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@ -139,8 +128,8 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=329c7a8#329c7a8314973c857db38c7b712318de9349eb6e"
|
||||
version = "0.5.6"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=fccb6a0#fccb6a0c07ebda3e385bff7f76e4984b741f01c7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -202,9 +191,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -262,18 +251,18 @@ checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.195"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.195"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -282,9 +271,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.111"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@ -293,9 +282,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.12.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "spdx"
|
||||
@ -317,6 +306,20 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tester"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"indexmap",
|
||||
"kinode_process_lib",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.56"
|
||||
@ -363,9 +366,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.14"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
|
||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
@ -428,18 +431,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.39.0"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "111495d6204760238512f57a9af162f45086504da332af210f2f75dd80b34f1d"
|
||||
checksum = "e09bca7d6388637d27fb5edbeab11f56bfabcef8743c55ae34370e1e5030a071"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.10.15"
|
||||
version = "0.10.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "818931c85b1d197909699d36c509fa89550ccfa0d66932ba3c1726faddb4d0c7"
|
||||
checksum = "c853d3809fc9fccf3bc0ad63f4f51d8eefad0bacf88f957aa991c1d9b88b016e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
@ -447,8 +450,8 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"spdx",
|
||||
"wasm-encoder 0.39.0",
|
||||
"wasmparser 0.119.0",
|
||||
"wasm-encoder 0.41.0",
|
||||
"wasmparser 0.121.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -463,9 +466,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.119.0"
|
||||
version = "0.121.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c35daf77afb4f9b14016625144a391085ec2ca99ca9cc53ed291bb53ab5278d"
|
||||
checksum = "953cf6a7606ab31382cb1caa5ae403e77ba70c7f8e12eeda167e7040d42bfda8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"indexmap",
|
21
kinode/packages/tester/tester/Cargo.toml
Normal file
21
kinode/packages/tester/tester/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "tester"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3.3"
|
||||
indexmap = "2.1"
|
||||
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", tag = "v0.5.9-alpha" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "efcc759" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.component]
|
||||
package = "kinode:process"
|
@ -10,7 +10,7 @@ mod tester_types;
|
||||
use tester_types as tt;
|
||||
|
||||
wit_bindgen::generate!({
|
||||
path: "../../../wit",
|
||||
path: "wit",
|
||||
world: "process",
|
||||
exports: {
|
||||
world: Component,
|
4
kinode/src/eth/mod.rs
Normal file
4
kinode/src/eth/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
#![allow(unused)]
|
||||
pub mod provider;
|
||||
|
||||
pub use lib::types::eth as types;
|
@ -1,5 +1,3 @@
|
||||
use crate::eth::types::*;
|
||||
use crate::types::*;
|
||||
use anyhow::Result;
|
||||
use ethers::prelude::Provider;
|
||||
use ethers::types::Filter;
|
||||
@ -8,6 +6,8 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use url::Url;
|
||||
|
||||
use lib::types::{core::*, eth::*};
|
||||
|
||||
const WS_RECONNECTS: usize = 10_000; // TODO workshop this
|
||||
|
||||
/// The ETH provider runtime process is responsible for connecting to one or more ETH RPC providers
|
@ -1,6 +1,3 @@
|
||||
use crate::http::client_types::*;
|
||||
use crate::http::server_types::*;
|
||||
use crate::types::*;
|
||||
use anyhow::Result;
|
||||
use dashmap::DashMap;
|
||||
use ethers_providers::StreamExt;
|
||||
@ -13,6 +10,8 @@ use tokio_tungstenite::tungstenite::{client::IntoClientRequest, Message as Tungs
|
||||
use tokio_tungstenite::{connect_async, tungstenite};
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
|
||||
use lib::types::{core::*, http_client::*, http_server::*};
|
||||
|
||||
// Test http_client with these commands in the terminal
|
||||
// !message our http_client {"method": "GET", "url": "https://jsonplaceholder.typicode.com/posts", "headers": {}}
|
||||
// !message our http_client {"method": "POST", "url": "https://jsonplaceholder.typicode.com/posts", "headers": {"Content-Type": "application/json"}}
|
7
kinode/src/http/mod.rs
Normal file
7
kinode/src/http/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
#![allow(unused)]
|
||||
pub mod client;
|
||||
pub mod server;
|
||||
pub mod utils;
|
||||
|
||||
pub use lib::types::http_client as client_types;
|
||||
pub use lib::types::http_server as server_types;
|
@ -1,6 +1,5 @@
|
||||
use crate::http::server_types::*;
|
||||
use crate::http::utils::*;
|
||||
use crate::types::*;
|
||||
use crate::{keygen, register};
|
||||
use anyhow::Result;
|
||||
use dashmap::DashMap;
|
||||
@ -16,6 +15,8 @@ use warp::http::{header::HeaderValue, StatusCode};
|
||||
use warp::ws::{WebSocket, Ws};
|
||||
use warp::{Filter, Reply};
|
||||
|
||||
use lib::types::core::*;
|
||||
|
||||
#[cfg(not(feature = "simulation-mode"))]
|
||||
const HTTP_SELF_IMPOSED_TIMEOUT: u64 = 15;
|
||||
#[cfg(feature = "simulation-mode")]
|
||||
@ -51,6 +52,108 @@ struct BoundWsPath {
|
||||
pub secure_subdomain: Option<String>,
|
||||
pub authenticated: bool,
|
||||
pub encrypted: bool, // TODO use
|
||||
pub extension: bool,
|
||||
}
|
||||
|
||||
async fn send_push(
|
||||
id: u64,
|
||||
lazy_load_blob: Option<LazyLoadBlob>,
|
||||
source: Address,
|
||||
send_to_loop: &MessageSender,
|
||||
ws_senders: WebSocketSenders,
|
||||
channel_id: u32,
|
||||
message_type: WsMessageType,
|
||||
maybe_ext: Option<MessageType>,
|
||||
) -> bool {
|
||||
let Some(mut blob) = lazy_load_blob else {
|
||||
send_action_response(id, source, send_to_loop, Err(HttpServerError::NoBlob)).await;
|
||||
return true;
|
||||
};
|
||||
if maybe_ext.is_some() {
|
||||
let WsMessageType::Binary = message_type else {
|
||||
// TODO
|
||||
send_action_response(id, source, send_to_loop, Err(HttpServerError::NoBlob)).await;
|
||||
return true;
|
||||
};
|
||||
let action = HttpServerAction::WebSocketExtPushData {
|
||||
id,
|
||||
kinode_message_type: maybe_ext.unwrap(),
|
||||
blob: blob.bytes,
|
||||
};
|
||||
blob.bytes = rmp_serde::to_vec_named(&action).unwrap();
|
||||
}
|
||||
let ws_message = match message_type {
|
||||
WsMessageType::Text => {
|
||||
warp::ws::Message::text(String::from_utf8_lossy(&blob.bytes).to_string())
|
||||
}
|
||||
WsMessageType::Binary => warp::ws::Message::binary(blob.bytes),
|
||||
WsMessageType::Ping | WsMessageType::Pong => {
|
||||
if blob.bytes.len() > 125 {
|
||||
send_action_response(
|
||||
id,
|
||||
source,
|
||||
send_to_loop,
|
||||
Err(HttpServerError::WebSocketPushError {
|
||||
error: "Ping and Pong messages must be 125 bytes or less".to_string(),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
return true;
|
||||
}
|
||||
if message_type == WsMessageType::Ping {
|
||||
warp::ws::Message::ping(blob.bytes)
|
||||
} else {
|
||||
warp::ws::Message::pong(blob.bytes)
|
||||
}
|
||||
}
|
||||
WsMessageType::Close => {
|
||||
unreachable!();
|
||||
}
|
||||
};
|
||||
// Send to the websocket if registered
|
||||
if let Some(got) = ws_senders.get(&channel_id) {
|
||||
let owner_process = &got.value().0;
|
||||
let sender = &got.value().1;
|
||||
if owner_process != &source.process {
|
||||
send_action_response(
|
||||
id,
|
||||
source,
|
||||
send_to_loop,
|
||||
Err(HttpServerError::WebSocketPushError {
|
||||
error: "WebSocket channel not owned by this process".to_string(),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
return true;
|
||||
}
|
||||
match sender.send(ws_message).await {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
send_action_response(
|
||||
id,
|
||||
source.clone(),
|
||||
send_to_loop,
|
||||
Err(HttpServerError::WebSocketPushError {
|
||||
error: "WebSocket channel closed".to_string(),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
send_action_response(
|
||||
id,
|
||||
source.clone(),
|
||||
send_to_loop,
|
||||
Err(HttpServerError::WebSocketPushError {
|
||||
error: "WebSocket channel not found".to_string(),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// HTTP server: a runtime module that handles HTTP requests at a given port.
|
||||
@ -154,6 +257,7 @@ async fn serve(
|
||||
let cloned_jwt_secret_bytes = jwt_secret_bytes.clone();
|
||||
let cloned_print_tx = print_tx.clone();
|
||||
let ws_route = warp::ws()
|
||||
.and(warp::addr::remote())
|
||||
.and(warp::path::full())
|
||||
.and(warp::filters::host::optional())
|
||||
.and(warp::filters::header::headers_cloned())
|
||||
@ -263,6 +367,7 @@ async fn login_handler(
|
||||
|
||||
async fn ws_handler(
|
||||
ws_connection: Ws,
|
||||
socket_addr: Option<SocketAddr>,
|
||||
path: warp::path::FullPath,
|
||||
host: Option<Authority>,
|
||||
headers: warp::http::HeaderMap,
|
||||
@ -319,7 +424,16 @@ async fn ws_handler(
|
||||
}
|
||||
}
|
||||
|
||||
let is_local = socket_addr
|
||||
.map(|addr| addr.ip().is_loopback())
|
||||
.unwrap_or(false);
|
||||
|
||||
if bound_path.extension && !is_local {
|
||||
return Err(warp::reject::reject());
|
||||
}
|
||||
|
||||
let app = bound_path.app.clone();
|
||||
let extension = bound_path.extension;
|
||||
|
||||
drop(ws_path_bindings);
|
||||
|
||||
@ -339,6 +453,7 @@ async fn ws_handler(
|
||||
ws_senders.clone(),
|
||||
send_to_loop.clone(),
|
||||
print_tx.clone(),
|
||||
extension,
|
||||
)
|
||||
.await;
|
||||
}))
|
||||
@ -646,6 +761,130 @@ async fn handle_rpc_message(
|
||||
))
|
||||
}
|
||||
|
||||
fn make_websocket_message(
|
||||
our: String,
|
||||
app: ProcessId,
|
||||
channel_id: u32,
|
||||
ws_msg_type: WsMessageType,
|
||||
msg: Vec<u8>,
|
||||
) -> Option<KernelMessage> {
|
||||
Some(KernelMessage {
|
||||
id: rand::random(),
|
||||
source: Address {
|
||||
node: our.to_string(),
|
||||
process: HTTP_SERVER_PROCESS_ID.clone(),
|
||||
},
|
||||
target: Address {
|
||||
node: our.to_string(),
|
||||
process: app,
|
||||
},
|
||||
rsvp: None,
|
||||
message: Message::Request(Request {
|
||||
inherit: false,
|
||||
expects_response: None,
|
||||
body: serde_json::to_vec(&HttpServerRequest::WebSocketPush {
|
||||
channel_id,
|
||||
message_type: ws_msg_type,
|
||||
})
|
||||
.unwrap(),
|
||||
metadata: None,
|
||||
capabilities: vec![],
|
||||
}),
|
||||
lazy_load_blob: Some(LazyLoadBlob {
|
||||
mime: None,
|
||||
bytes: msg,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
fn make_ext_websocket_message(
|
||||
our: String,
|
||||
app: ProcessId,
|
||||
channel_id: u32,
|
||||
ws_msg_type: WsMessageType,
|
||||
msg: Vec<u8>,
|
||||
) -> Option<KernelMessage> {
|
||||
let option = match rmp_serde::from_slice::<HttpServerAction>(&msg) {
|
||||
Err(_) => Some((
|
||||
rand::random(),
|
||||
Message::Request(Request {
|
||||
inherit: false,
|
||||
expects_response: None,
|
||||
body: serde_json::to_vec(&HttpServerRequest::WebSocketPush {
|
||||
channel_id,
|
||||
message_type: ws_msg_type,
|
||||
})
|
||||
.unwrap(),
|
||||
metadata: None,
|
||||
capabilities: vec![],
|
||||
}),
|
||||
Some(LazyLoadBlob {
|
||||
mime: None,
|
||||
bytes: msg,
|
||||
}),
|
||||
)),
|
||||
Ok(HttpServerAction::WebSocketExtPushData {
|
||||
id,
|
||||
kinode_message_type,
|
||||
blob,
|
||||
}) => Some((
|
||||
id,
|
||||
match kinode_message_type {
|
||||
MessageType::Request => Message::Request(Request {
|
||||
inherit: false,
|
||||
expects_response: None,
|
||||
body: serde_json::to_vec(&HttpServerRequest::WebSocketPush {
|
||||
channel_id,
|
||||
message_type: ws_msg_type,
|
||||
})
|
||||
.unwrap(),
|
||||
metadata: None,
|
||||
capabilities: vec![],
|
||||
}),
|
||||
MessageType::Response => Message::Response((
|
||||
Response {
|
||||
inherit: false,
|
||||
body: serde_json::to_vec(&HttpServerRequest::WebSocketPush {
|
||||
channel_id,
|
||||
message_type: ws_msg_type,
|
||||
})
|
||||
.unwrap(),
|
||||
metadata: None,
|
||||
capabilities: vec![],
|
||||
},
|
||||
None,
|
||||
)),
|
||||
},
|
||||
Some(LazyLoadBlob {
|
||||
mime: None,
|
||||
bytes: blob,
|
||||
}),
|
||||
)),
|
||||
Ok(m) => {
|
||||
println!("http server: got unexpected message from ext websocket: {m:?}\r");
|
||||
None
|
||||
}
|
||||
};
|
||||
let Some((id, message, blob)) = option else {
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(KernelMessage {
|
||||
id,
|
||||
source: Address {
|
||||
node: our.to_string(),
|
||||
process: HTTP_SERVER_PROCESS_ID.clone(),
|
||||
},
|
||||
target: Address {
|
||||
node: our.to_string(),
|
||||
process: app,
|
||||
},
|
||||
rsvp: None,
|
||||
message,
|
||||
lazy_load_blob: blob,
|
||||
})
|
||||
}
|
||||
|
||||
async fn maintain_websocket(
|
||||
ws: WebSocket,
|
||||
our: Arc<String>,
|
||||
@ -655,6 +894,7 @@ async fn maintain_websocket(
|
||||
ws_senders: WebSocketSenders,
|
||||
send_to_loop: MessageSender,
|
||||
print_tx: PrintSender,
|
||||
extension: bool,
|
||||
) {
|
||||
let (mut write_stream, mut read_stream) = ws.split();
|
||||
|
||||
@ -693,6 +933,12 @@ async fn maintain_websocket(
|
||||
})
|
||||
.await;
|
||||
|
||||
let make_ws_message = if extension {
|
||||
make_ext_websocket_message
|
||||
} else {
|
||||
make_websocket_message
|
||||
};
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
read = read_stream.next() => {
|
||||
@ -711,32 +957,15 @@ async fn maintain_websocket(
|
||||
WsMessageType::Close
|
||||
};
|
||||
|
||||
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,
|
||||
body: serde_json::to_vec(&HttpServerRequest::WebSocketPush {
|
||||
channel_id,
|
||||
message_type: ws_msg_type,
|
||||
}).unwrap(),
|
||||
metadata: None,
|
||||
capabilities: vec![],
|
||||
}),
|
||||
lazy_load_blob: Some(LazyLoadBlob {
|
||||
mime: None,
|
||||
bytes: msg.into_bytes(),
|
||||
}),
|
||||
}).await;
|
||||
if let Some(message) = make_ws_message(
|
||||
our.to_string(),
|
||||
app.clone(),
|
||||
channel_id,
|
||||
ws_msg_type,
|
||||
msg.into_bytes(),
|
||||
) {
|
||||
let _ = send_to_loop.send(message).await;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
websocket_close(channel_id, app.clone(), &ws_senders, &send_to_loop).await;
|
||||
@ -988,6 +1217,7 @@ async fn handle_app_message(
|
||||
mut path,
|
||||
authenticated,
|
||||
encrypted,
|
||||
extension,
|
||||
} => {
|
||||
path = if path.starts_with('/') {
|
||||
format!("/{}{}", km.source.process, path)
|
||||
@ -1002,12 +1232,14 @@ async fn handle_app_message(
|
||||
secure_subdomain: None,
|
||||
authenticated,
|
||||
encrypted,
|
||||
extension,
|
||||
},
|
||||
);
|
||||
}
|
||||
HttpServerAction::WebSocketSecureBind {
|
||||
mut path,
|
||||
encrypted,
|
||||
extension,
|
||||
} => {
|
||||
path = if path.starts_with('/') {
|
||||
format!("/{}{}", km.source.process, path)
|
||||
@ -1025,6 +1257,7 @@ async fn handle_app_message(
|
||||
secure_subdomain: Some(subdomain),
|
||||
authenticated: true,
|
||||
encrypted,
|
||||
extension,
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -1044,88 +1277,52 @@ async fn handle_app_message(
|
||||
channel_id,
|
||||
message_type,
|
||||
} => {
|
||||
let Some(blob) = km.lazy_load_blob else {
|
||||
send_action_response(
|
||||
km.id,
|
||||
km.source,
|
||||
&send_to_loop,
|
||||
Err(HttpServerError::NoBlob),
|
||||
)
|
||||
.await;
|
||||
let is_return = send_push(
|
||||
km.id,
|
||||
km.lazy_load_blob,
|
||||
km.source.clone(),
|
||||
&send_to_loop,
|
||||
ws_senders,
|
||||
channel_id,
|
||||
message_type,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
if is_return {
|
||||
return;
|
||||
};
|
||||
let ws_message = match message_type {
|
||||
WsMessageType::Text => warp::ws::Message::text(
|
||||
String::from_utf8_lossy(&blob.bytes).to_string(),
|
||||
),
|
||||
WsMessageType::Binary => warp::ws::Message::binary(blob.bytes),
|
||||
WsMessageType::Ping | WsMessageType::Pong => {
|
||||
if blob.bytes.len() > 125 {
|
||||
send_action_response(
|
||||
km.id,
|
||||
km.source,
|
||||
&send_to_loop,
|
||||
Err(HttpServerError::WebSocketPushError {
|
||||
error: "Ping and Pong messages must be 125 bytes or less"
|
||||
.to_string(),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
if message_type == WsMessageType::Ping {
|
||||
warp::ws::Message::ping(blob.bytes)
|
||||
} else {
|
||||
warp::ws::Message::pong(blob.bytes)
|
||||
}
|
||||
}
|
||||
WsMessageType::Close => {
|
||||
unreachable!();
|
||||
}
|
||||
};
|
||||
// Send to the websocket if registered
|
||||
if let Some(got) = ws_senders.get(&channel_id) {
|
||||
let owner_process = &got.value().0;
|
||||
let sender = &got.value().1;
|
||||
if owner_process != &km.source.process {
|
||||
send_action_response(
|
||||
km.id,
|
||||
km.source,
|
||||
&send_to_loop,
|
||||
Err(HttpServerError::WebSocketPushError {
|
||||
error: "WebSocket channel not owned by this process"
|
||||
.to_string(),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
match sender.send(ws_message).await {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
send_action_response(
|
||||
km.id,
|
||||
km.source.clone(),
|
||||
&send_to_loop,
|
||||
Err(HttpServerError::WebSocketPushError {
|
||||
error: "WebSocket channel closed".to_string(),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
send_action_response(
|
||||
km.id,
|
||||
km.source.clone(),
|
||||
&send_to_loop,
|
||||
Err(HttpServerError::WebSocketPushError {
|
||||
error: "WebSocket channel not found".to_string(),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
HttpServerAction::WebSocketExtPushOutgoing {
|
||||
channel_id,
|
||||
message_type,
|
||||
desired_reply_type,
|
||||
} => {
|
||||
send_push(
|
||||
km.id,
|
||||
km.lazy_load_blob,
|
||||
km.source.clone(),
|
||||
&send_to_loop,
|
||||
ws_senders,
|
||||
channel_id,
|
||||
message_type,
|
||||
Some(desired_reply_type),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
HttpServerAction::WebSocketExtPushData { .. } => {
|
||||
send_action_response(
|
||||
km.id,
|
||||
km.source,
|
||||
&send_to_loop,
|
||||
Err(HttpServerError::WebSocketPushError {
|
||||
error: "Use WebSocketExtPushOutgoing, not WebSocketExtPushData"
|
||||
.to_string(),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
HttpServerAction::WebSocketClose(channel_id) => {
|
||||
if let Some(got) = ws_senders.get(&channel_id) {
|
||||
if got.value().0 != km.source.process {
|
@ -1,4 +1,3 @@
|
||||
use crate::http::server_types::*;
|
||||
use hmac::{Hmac, Mac};
|
||||
use jwt::VerifyWithKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -7,6 +6,8 @@ use std::collections::HashMap;
|
||||
use tokio::net::TcpListener;
|
||||
use warp::http::{header::HeaderName, header::HeaderValue, HeaderMap};
|
||||
|
||||
use lib::types::http_server::*;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct RpcMessage {
|
||||
pub node: Option<String>,
|
@ -1,5 +1,3 @@
|
||||
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;
|
||||
@ -10,6 +8,8 @@ use tokio::sync::mpsc;
|
||||
use tokio::task::JoinHandle;
|
||||
use wasmtime::{Config, Engine, WasmBacktraceDetails};
|
||||
|
||||
use lib::types::core::{self as t, STATE_PROCESS_ID, VFS_PROCESS_ID};
|
||||
|
||||
/// Manipulate a single process.
|
||||
pub mod process;
|
||||
/// Implement the functions served to processes by `kinode.wit`.
|
||||
@ -431,12 +431,18 @@ async fn handle_kernel_request(
|
||||
let _ = send_to_terminal
|
||||
.send(t::Printout {
|
||||
verbosity: 2,
|
||||
content: format!("kernel: no such process {:?} to kill", process_id),
|
||||
content: format!("kernel: no such process {process_id} to kill"),
|
||||
})
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let _ = send_to_terminal
|
||||
.send(t::Printout {
|
||||
verbosity: 2,
|
||||
content: format!("killing process {process_id}"),
|
||||
})
|
||||
.await;
|
||||
process_handle.abort();
|
||||
process_map.remove(&process_id);
|
||||
let _ = persist_state(&our_name, &send_to_loop, process_map).await;
|
@ -1,9 +1,8 @@
|
||||
use crate::kernel::{ProcessMessageReceiver, ProcessMessageSender};
|
||||
use crate::types as t;
|
||||
use crate::KERNEL_PROCESS_ID;
|
||||
use anyhow::Result;
|
||||
pub use kinode::process::standard as wit;
|
||||
pub use kinode::process::standard::Host as StandardHost;
|
||||
//pub use kinode::process::standard as wit;
|
||||
//pub use kinode::process::standard::Host as StandardHost;
|
||||
use ring::signature::{self, KeyPair};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::sync::Arc;
|
||||
@ -12,11 +11,16 @@ use wasmtime::component::*;
|
||||
use wasmtime::{Engine, Store};
|
||||
use wasmtime_wasi::preview2::{pipe::MemoryOutputPipe, Table, WasiCtx, WasiCtxBuilder, WasiView};
|
||||
|
||||
bindgen!({
|
||||
path: "wit",
|
||||
world: "process",
|
||||
async: true,
|
||||
});
|
||||
use lib::types::core as t;
|
||||
pub use lib::wit;
|
||||
pub use lib::wit::Host as StandardHost;
|
||||
pub use lib::Process;
|
||||
|
||||
// bindgen!({
|
||||
// path: "wit",
|
||||
// world: "process",
|
||||
// async: true,
|
||||
// });
|
||||
|
||||
pub struct ProcessState {
|
||||
pub keypair: Arc<signature::Ed25519KeyPair>,
|
@ -1,12 +1,14 @@
|
||||
use crate::kernel::process;
|
||||
use crate::kernel::process::kinode::process::standard as wit;
|
||||
use crate::kernel::process::StandardHost;
|
||||
use crate::types as t;
|
||||
use crate::types::STATE_PROCESS_ID;
|
||||
//use crate::kernel::process::kinode::process::standard as wit;
|
||||
//use crate::kernel::process::StandardHost;
|
||||
use crate::KERNEL_PROCESS_ID;
|
||||
use crate::VFS_PROCESS_ID;
|
||||
use anyhow::Result;
|
||||
|
||||
use lib::types::core::{self as t, STATE_PROCESS_ID};
|
||||
pub use lib::wit;
|
||||
pub use lib::wit::Host as StandardHost;
|
||||
|
||||
async fn print_debug(proc: &process::ProcessState, content: &str) {
|
||||
let _ = proc
|
||||
.send_to_terminal
|
@ -11,7 +11,7 @@ use ring::signature::{self, KeyPair};
|
||||
use ring::{digest as ring_digest, rand::SecureRandom};
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use crate::types::Keyfile;
|
||||
use lib::types::core::Keyfile;
|
||||
|
||||
type DiskKey = [u8; CREDENTIAL_LEN];
|
||||
|
@ -7,7 +7,7 @@ use std::sync::Arc;
|
||||
use tokio::fs;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::types::*;
|
||||
use lib::types::core::*;
|
||||
|
||||
pub async fn kv(
|
||||
our_node: String,
|
||||
@ -192,7 +192,7 @@ async fn handle_request(
|
||||
|
||||
match tx_id {
|
||||
None => {
|
||||
db.put(key, blob.bytes)?;
|
||||
db.put(key, blob.bytes).map_err(rocks_to_kv_err)?;
|
||||
}
|
||||
Some(tx_id) => {
|
||||
let mut tx = match txs.get_mut(tx_id) {
|
||||
@ -216,7 +216,7 @@ async fn handle_request(
|
||||
};
|
||||
match tx_id {
|
||||
None => {
|
||||
db.delete(key)?;
|
||||
db.delete(key).map_err(rocks_to_kv_err)?;
|
||||
}
|
||||
Some(tx_id) => {
|
||||
let mut tx = match txs.get_mut(tx_id) {
|
||||
@ -250,11 +250,11 @@ async fn handle_request(
|
||||
match action {
|
||||
KvAction::Set { key, .. } => {
|
||||
if let Some(blob) = blob {
|
||||
tx.put(&key, &blob)?;
|
||||
tx.put(&key, &blob).map_err(rocks_to_kv_err)?;
|
||||
}
|
||||
}
|
||||
KvAction::Delete { key, .. } => {
|
||||
tx.delete(&key)?;
|
||||
tx.delete(&key).map_err(rocks_to_kv_err)?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -274,7 +274,7 @@ async fn handle_request(
|
||||
// looping through open dbs and flushing their memtables
|
||||
for db_ref in open_kvs.iter() {
|
||||
let db = db_ref.value();
|
||||
db.flush()?;
|
||||
db.flush().map_err(rocks_to_kv_err)?;
|
||||
}
|
||||
(serde_json::to_vec(&KvResponse::Ok).unwrap(), None)
|
||||
}
|
||||
@ -424,7 +424,7 @@ async fn check_caps(
|
||||
let db_path = format!("{}/{}/{}", kv_path, request.package_id, request.db);
|
||||
fs::create_dir_all(&db_path).await?;
|
||||
|
||||
let db = OptimisticTransactionDB::open_default(&db_path)?;
|
||||
let db = OptimisticTransactionDB::open_default(&db_path).map_err(rocks_to_kv_err)?;
|
||||
|
||||
open_kvs.insert((request.package_id.clone(), request.db.clone()), db);
|
||||
Ok(())
|
||||
@ -497,40 +497,9 @@ fn make_error_message(our_name: String, km: &KernelMessage, error: KvError) -> K
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
fn rocks_to_kv_err(error: rocksdb::Error) -> KvError {
|
||||
KvError::RocksDBError {
|
||||
action: "".into(),
|
||||
error: error.to_string(),
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
#![feature(btree_extract_if)]
|
||||
|
||||
use crate::types::*;
|
||||
use anyhow::Result;
|
||||
use clap::{arg, value_parser, Command};
|
||||
use std::env;
|
||||
@ -8,6 +7,8 @@ use std::sync::Arc;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio::{fs, time::timeout};
|
||||
|
||||
use lib::types::core::*;
|
||||
|
||||
#[cfg(feature = "simulation-mode")]
|
||||
use ring::{rand::SystemRandom, signature, signature::KeyPair};
|
||||
|
||||
@ -22,7 +23,6 @@ mod sqlite;
|
||||
mod state;
|
||||
mod terminal;
|
||||
mod timer;
|
||||
mod types;
|
||||
mod vfs;
|
||||
|
||||
const EVENT_LOOP_CHANNEL_CAPACITY: usize = 10_000;
|
||||
@ -140,6 +140,32 @@ async fn main() {
|
||||
register::KNS_OPTIMISM_ADDRESS
|
||||
};
|
||||
|
||||
// check .testnet file for true/false in order to enforce testnet mode on subsequent boots of this node
|
||||
match fs::read(format!("{}/.testnet", home_directory_path)).await {
|
||||
Ok(contents) => {
|
||||
if contents == b"true" {
|
||||
if !on_testnet {
|
||||
println!("\x1b[38;5;196mfatal: this is a testnet node, and must be booted with the --testnet flag. exiting.\x1b[0m");
|
||||
return;
|
||||
}
|
||||
} else if contents == b"false" {
|
||||
if on_testnet {
|
||||
println!("\x1b[38;5;196mfatal: this is a mainnet node, and must be booted without the --testnet flag. exiting.\x1b[0m");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
panic!("invalid contents of .testnet file");
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
let _ = fs::write(
|
||||
format!("{}/.testnet", home_directory_path),
|
||||
format!("{}", on_testnet),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "simulation-mode"))]
|
||||
let (rpc_url, is_detached) = (matches.get_one::<String>("rpc").unwrap(), false);
|
||||
|
@ -5,7 +5,7 @@ use tokio_tungstenite::{
|
||||
tungstenite::protocol::Message::{Binary, Text},
|
||||
};
|
||||
|
||||
use crate::types;
|
||||
use lib::types::core as types;
|
||||
|
||||
type Sender = mpsc::Sender<types::KernelMessage>;
|
||||
type Receiver = mpsc::Receiver<types::KernelMessage>;
|
@ -21,7 +21,7 @@ mod utils;
|
||||
#[cfg(not(feature = "simulation-mode"))]
|
||||
use crate::net::{types::*, utils::*};
|
||||
#[cfg(not(feature = "simulation-mode"))]
|
||||
use crate::types::*;
|
||||
use lib::types::core::*;
|
||||
|
||||
// Re-export for testing.
|
||||
#[cfg(feature = "simulation-mode")]
|
@ -1,4 +1,3 @@
|
||||
use crate::types::*;
|
||||
use dashmap::DashMap;
|
||||
use futures::stream::{SplitSink, SplitStream};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -8,6 +7,8 @@ use tokio::net::TcpStream;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use tokio_tungstenite::{tungstenite, MaybeTlsStream, WebSocketStream};
|
||||
|
||||
use lib::types::core::*;
|
||||
|
||||
/// Sent to a node when you want to connect directly to them.
|
||||
/// Sent in the 'e, ee, s, es' and 's, se' phases of XX noise protocol pattern.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
@ -1,5 +1,4 @@
|
||||
use crate::net::{types::*, MESSAGE_MAX_SIZE, TIMEOUT};
|
||||
use crate::types::*;
|
||||
use anyhow::{anyhow, Result};
|
||||
use futures::stream::{SplitSink, SplitStream};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
@ -10,6 +9,8 @@ use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
|
||||
use tokio::time::timeout;
|
||||
use tokio_tungstenite::{connect_async, tungstenite, MaybeTlsStream, WebSocketStream};
|
||||
|
||||
use lib::types::core::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref PARAMS: NoiseParams = "Noise_XX_25519_ChaChaPoly_BLAKE2s"
|
||||
.parse()
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user