Merge branch 'develop' into tm/download-in-progress-message

This commit is contained in:
Tobias Merkle 2024-05-31 10:21:58 -04:00
commit a2e7aaf384
41 changed files with 997 additions and 1533 deletions

73
Cargo.lock generated
View File

@ -3044,6 +3044,29 @@ dependencies = [
"sha3-asm",
]
[[package]]
name = "kfetch"
version = "0.1.0"
dependencies = [
"anyhow",
"kinode_process_lib 0.8.0 (git+https://github.com/kinode-dao/process_lib?rev=010e175)",
"rmp-serde",
"serde",
"serde_json",
"wit-bindgen",
]
[[package]]
name = "kill"
version = "0.1.0"
dependencies = [
"anyhow",
"kinode_process_lib 0.8.0 (git+https://github.com/kinode-dao/process_lib?rev=010e175)",
"serde",
"serde_json",
"wit-bindgen",
]
[[package]]
name = "kinode"
version = "0.8.0"
@ -3129,6 +3152,28 @@ dependencies = [
"lib",
]
[[package]]
name = "kinode_process_lib"
version = "0.8.0"
source = "git+https://github.com/kinode-dao/process_lib?rev=010e175#010e175b4e66242c2ef9422088e355698728600d"
dependencies = [
"alloy-json-rpc 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=cad7935)",
"alloy-primitives 0.7.0",
"alloy-rpc-types 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=cad7935)",
"alloy-transport 0.1.0 (git+https://github.com/alloy-rs/alloy.git?rev=cad7935)",
"anyhow",
"bincode",
"http 1.1.0",
"mime_guess",
"rand 0.8.5",
"rmp-serde",
"serde",
"serde_json",
"thiserror",
"url",
"wit-bindgen",
]
[[package]]
name = "kinode_process_lib"
version = "0.8.0"
@ -4076,6 +4121,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "process_macros"
version = "0.1.0"
source = "git+https://github.com/kinode-dao/process_macros?rev=626e501#626e501d351e3365480ec6f770d474ed4ae339bf"
dependencies = [
"quote",
"syn 2.0.60",
]
[[package]]
name = "proptest"
version = "1.4.0"
@ -5282,27 +5336,14 @@ dependencies = [
"wit-bindgen",
]
[[package]]
name = "test_runner"
version = "0.1.0"
dependencies = [
"anyhow",
"bincode",
"kinode_process_lib 0.8.0 (git+https://github.com/kinode-dao/process_lib?rev=09dc9f9)",
"serde",
"serde_json",
"thiserror",
"wit-bindgen",
]
[[package]]
name = "tester"
version = "0.1.1"
dependencies = [
"anyhow",
"bincode",
"indexmap",
"kinode_process_lib 0.8.0 (git+https://github.com/kinode-dao/process_lib?rev=09dc9f9)",
"process_macros",
"serde",
"serde_json",
"thiserror",
@ -5542,10 +5583,10 @@ dependencies = [
[[package]]
name = "top"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"anyhow",
"kinode_process_lib 0.8.0 (git+https://github.com/kinode-dao/process_lib?rev=09dc9f9)",
"kinode_process_lib 0.8.0 (git+https://github.com/kinode-dao/process_lib?rev=010e175)",
"serde",
"serde_json",
"wit-bindgen",

View File

@ -22,9 +22,9 @@ members = [
"kinode/packages/kns_indexer/kns_indexer", "kinode/packages/kns_indexer/get_block", "kinode/packages/kns_indexer/state",
"kinode/packages/settings/settings",
"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/terminal/alias", "kinode/packages/terminal/cat", "kinode/packages/terminal/echo", "kinode/packages/terminal/hi", "kinode/packages/terminal/kfetch", "kinode/packages/terminal/kill", "kinode/packages/terminal/m", "kinode/packages/terminal/top",
"kinode/packages/terminal/namehash_to_name", "kinode/packages/terminal/net_diagnostics", "kinode/packages/terminal/peer", "kinode/packages/terminal/peers",
"kinode/packages/tester/tester", "kinode/packages/tester/test_runner",
"kinode/packages/tester/tester",
]
default-members = ["lib"]
resolver = "2"

View File

@ -153,14 +153,20 @@ fn get_widget() -> String {
const container = document.getElementById('latest-apps');
data.forEach(app => {
const a = document.createElement('a');
a.className = 'app p-2 grow self-stretch flex items-stretch rounded-lg shadow bg-white/10 hover:bg-white/20 font-sans cursor-pointer';
a.className = 'app p-2 grow flex items-stretch rounded-lg shadow bg-white/10 hover:bg-white/20 font-sans cursor-pointer';
a.href = `/main:app_store:sys/app-details/${app.package}:${app.publisher}`
a.target = '_blank';
a.rel = 'noopener noreferrer';
a.innerHTML = `${app.metadata.image ? `<div
const iconLetter = app.metadata_hash.replace('0x', '')[0].toUpperCase();
a.innerHTML = `<div
class="app-image rounded mr-2 grow"
style="background-image: url('${app.metadata.image}');"
></div>` : ''}
style="
background-image: url('${app.metadata.image || `/icons/${iconLetter}`}');
height: 92px;
width: 92px;
max-width: 33%;
"
></div>
<div class="app-info flex flex-col grow">
<h2 class="font-bold">${app.metadata.name}</h2>
<p>${app.metadata.description}</p>

View File

@ -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" />
<script type="module" crossorigin src="/assets/index-DBttdE6k.js"></script>
<script type="module" crossorigin src="/assets/index-Gt0JAj27.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BS5LP50I.css">
</head>

View File

@ -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" />
<script type="module" crossorigin src="/assets/index-DBttdE6k.js"></script>
<script type="module" crossorigin src="/assets/index-Gt0JAj27.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BS5LP50I.css">
</head>

View File

@ -1,6 +1,6 @@
import classNames from "classnames"
import { HomepageApp } from "../store/homepageStore"
import { FaHeart, FaRegHeart } from "react-icons/fa6"
import { FaHeart, FaRegHeart, } from "react-icons/fa6"
import { useState } from "react"
import usePersistentStore from "../store/persistentStore"
import { isMobileCheck } from "../utils/dimensions"
@ -11,7 +11,7 @@ interface AppDisplayProps {
}
const AppDisplay: React.FC<AppDisplayProps> = ({ app }) => {
const { favoriteApp } = usePersistentStore();
const { favoriteApp, favoriteApps } = usePersistentStore();
const [isHovered, setIsHovered] = useState(false)
const isMobile = isMobileCheck()
@ -46,7 +46,7 @@ const AppDisplay: React.FC<AppDisplayProps> = ({ app }) => {
favoriteApp(app.package_name)
}}
>
{app.is_favorite ? <FaHeart /> : <FaRegHeart />}
{favoriteApps[app.package_name]?.favorite ? <FaHeart /> : <FaRegHeart />}
</button>}
</a>
}

View File

@ -13,8 +13,9 @@ const AppsDock: React.FC = () => {
useEffect(() => {
let final: HomepageApp[] = []
const dockedApps = Object.keys(favoriteApps)
.map(name => ({ ...apps.find(a => a.package_name === name), order: favoriteApps[name].order }))
const dockedApps = Object.entries(favoriteApps)
.filter(([_, { favorite }]) => favorite)
.map(([name, { order }]) => ({ ...apps.find(a => a.package_name === name), order }))
.filter(a => a) as HomepageApp[]
const orderedApps = dockedApps.filter(a => a.order !== undefined && a.order !== null)
const unorderedApps = dockedApps.filter(a => a.order === undefined || a.order === null)
@ -85,12 +86,15 @@ const AppsDock: React.FC = () => {
ref={provided.innerRef}
{...provided.droppableProps}
className={classNames('flex-center flex-wrap border border-orange bg-orange/25 p-2 rounded !rounded-xl', {
'gap-8 mb-4': !isMobile,
'gap-8': !isMobile && dockedApps.length > 0,
'gap-4': !isMobile && dockedApps.length === 0,
'mb-4': !isMobile,
'gap-4 mb-2': isMobile,
'flex-col': dockedApps.length === 0
})}
>
{dockedApps.length === 0
? <div>Favorite an app to pin it to your dock.</div>
? <AppDisplay app={apps.find(app => app.package_name === 'app_store')!} />
: dockedApps.map(app => <Draggable
key={app.package_name}
draggableId={app.package_name}
@ -107,6 +111,7 @@ const AppsDock: React.FC = () => {
)}
</Draggable>)}
{provided.placeholder}
{dockedApps.length === 0 && <div>Favorite an app to pin it to your dock.</div>}
</div>
)}
</Droppable>

View File

@ -6,7 +6,6 @@ export interface HomepageApp {
path: string
label: string,
base64_icon?: string,
is_favorite: boolean,
state?: {
our_version: string
}

View File

@ -33,7 +33,7 @@ fn init(_our: Address) {
serde_json::json!({
"Add": {
"label": "KinoUpdates",
"widget": create_widget(fetch_three_most_recent_blog_posts()),
"widget": create_widget(fetch_most_recent_blog_posts(6)),
}
})
.to_string(),
@ -84,7 +84,6 @@ fn create_widget(posts: Vec<KinodeBlogPost>) -> String {
scrollbar-width: none;
"
>
<p class="m-0 self-stretch text-center">Recent Posts</p>
{}
</div>
</body>
@ -96,7 +95,7 @@ fn create_widget(posts: Vec<KinodeBlogPost>) -> String {
);
}
fn fetch_three_most_recent_blog_posts() -> Vec<KinodeBlogPost> {
fn fetch_most_recent_blog_posts(n: usize) -> Vec<KinodeBlogPost> {
let blog_posts = match http::send_request_await_response(
http::Method::GET,
url::Url::parse("https://kinode.org/api/blog/posts").unwrap(),
@ -109,13 +108,14 @@ fn fetch_three_most_recent_blog_posts() -> Vec<KinodeBlogPost> {
Err(e) => panic!("Failed to fetch blog posts: {:?}", e),
};
blog_posts.into_iter().rev().take(3).collect()
blog_posts.into_iter().rev().take(n as usize).collect()
}
/// Take first 100 chars of a blog post and append "..." to the end
fn trim_content(content: &str) -> String {
if content.len() > 100 {
format!("{}...", &content[..100])
let len = 75;
if content.len() > len {
format!("{}...", &content[..len])
} else {
content.to_string()
}
@ -123,20 +123,24 @@ fn trim_content(content: &str) -> String {
fn post_to_html_string(post: KinodeBlogPost) -> String {
format!(
r#"<div class="post p-2 grow self-stretch flex items-stretch rounded-lg shadow bg-white/10 font-sans w-full">
r#"<a
class="post p-2 grow self-stretch flex items-stretch rounded-lg shadow bg-white/10 hover:bg-white/20 font-sans w-full"
href="https://kinode.org/blog/post/{}"
target="_blank"
rel="noopener noreferrer"
>
<div
class="post-image rounded mr-2 grow"
class="post-image rounded mr-2 grow self-stretch h-full"
style="background-image: url('https://kinode.org{}');"
></div>
<div class="post-info flex flex-col grow">
<h2 class="font-bold">{}</h2>
<p>{}</p>
<a href="https://kinode.org/blog/post/{}" class="text-blue-500" target="_blank" rel="noopener noreferrer">Read more</a>
</div>
</div>"#,
</a>"#,
post.slug,
post.thumbnail_image,
post.title,
trim_content(&post.content),
post.slug,
)
}

View File

@ -1,5 +1,5 @@
[package]
name = "test_runner"
name = "kfetch"
version = "0.1.0"
edition = "2021"
@ -8,11 +8,10 @@ simulation-mode = []
[dependencies]
anyhow = "1.0"
bincode = "1.3.3"
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "09dc9f9" }
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "010e175" }
rmp-serde = "1.1.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
wit-bindgen = "0.24.0"
[lib]

View File

@ -0,0 +1,166 @@
use kinode_process_lib::kernel_types::{
KernelCommand, KernelPrint, KernelPrintResponse, KernelResponse,
};
use kinode_process_lib::{call_init, eth, net, println, Address, Message, Request};
use std::collections::HashSet;
/// Fetching OS version from main package.. LMK if there's a better way...
const CARGO_TOML: &str = include_str!("../../../../Cargo.toml");
wit_bindgen::generate!({
path: "target/wit",
world: "process-v0",
});
call_init!(init);
fn init(our: Address) {
// get identity
let Ok(Ok(Message::Response { body, .. })) = Request::to(("our", "net", "distro", "sys"))
.body(rmp_serde::to_vec(&net::NetAction::GetPeer(our.node.clone())).unwrap())
.send_and_await_response(60)
else {
println!("failed to get response from net");
return;
};
let Ok(net::NetResponse::Peer(Some(our_id))) = rmp_serde::from_slice(&body) else {
println!("got malformed response from net");
return;
};
// get eth providers
let Ok(Message::Response { body, .. }) = Request::new()
.target(("our", "eth", "distro", "sys"))
.body(serde_json::to_vec(&eth::EthConfigAction::GetProviders).unwrap())
.send_and_await_response(60)
.unwrap()
else {
println!("failed to get response from eth");
return;
};
let Ok(eth::EthConfigResponse::Providers(providers)) = serde_json::from_slice(&body) else {
println!("failed to parse eth response");
return;
};
// get eth subs
let Ok(Message::Response { body, .. }) = Request::new()
.target(("our", "eth", "distro", "sys"))
.body(serde_json::to_vec(&eth::EthConfigAction::GetState).unwrap())
.send_and_await_response(60)
.unwrap()
else {
println!("failed to get response from eth");
return;
};
let Ok(eth::EthConfigResponse::State {
active_subscriptions,
outstanding_requests,
}) = serde_json::from_slice(&body)
else {
println!("failed to parse eth response");
return;
};
// get number of processes
let Ok(Message::Response { body, .. }) = Request::new()
.target(("our", "kernel", "distro", "sys"))
.body(serde_json::to_vec(&KernelCommand::Debug(KernelPrint::ProcessMap)).unwrap())
.send_and_await_response(60)
.unwrap()
else {
println!("failed to get response from kernel");
return;
};
let Ok(KernelResponse::Debug(KernelPrintResponse::ProcessMap(map))) =
serde_json::from_slice::<KernelResponse>(&body)
else {
println!("failed to parse kernel response");
return;
};
let num_processes = map.len();
print_bird(
&our,
our_id,
providers,
// sum up all the subscriptions
active_subscriptions
.values()
.map(|v| v.len())
.sum::<usize>(),
outstanding_requests.len() as usize,
num_processes,
);
}
fn print_bird(
our: &Address,
our_id: net::Identity,
providers: HashSet<eth::ProviderConfig>,
active_subscriptions: usize,
outstanding_requests: usize,
num_processes: usize,
) {
println!(
r#"
.`
`@@,, ,* {}
`@%@@@, ,~-##`
~@@#@%#@@, ##### Kinode {}
~-%######@@@, #####
-%%#######@#####, pubkey: {}
~^^%##########@ routing: {}
>^#########@
`>#######` {} eth providers for chain IDs {}
.>######% {} active eth subscriptions
/###%^#% {} outstanding eth requests
/##%@# `
./######`
/.^`.#^#^`
` ,#`#`#, {} running processes
,/ /` `
.*`
"#,
our.node(),
version_from_cargo_toml(),
our_id.networking_key,
routing_to_string(our_id.routing),
providers.len(),
providers
.into_iter()
.map(|p| p.chain_id.to_string())
// remove duplicates
.collect::<HashSet<_>>()
.into_iter()
.collect::<Vec<_>>()
.join(", "),
active_subscriptions,
outstanding_requests,
num_processes
)
}
fn routing_to_string(routing: net::NodeRouting) -> String {
match routing {
net::NodeRouting::Direct { ip, ports } => format!(
"direct at {} with {}",
ip,
ports.into_keys().into_iter().collect::<Vec<_>>().join(", ")
),
net::NodeRouting::Routers(routers) => format!("{} routers", routers.len()),
}
}
fn version_from_cargo_toml() -> String {
let version = CARGO_TOML
.lines()
.find(|line| line.starts_with("version = "))
.expect("Failed to find version in Cargo.toml");
version
.split('=')
.last()
.expect("Failed to parse version from Cargo.toml")
.trim()
.trim_matches('"')
.to_string()
}

View File

@ -0,0 +1,20 @@
[package]
name = "kill"
version = "0.1.0"
edition = "2021"
[features]
simulation-mode = []
[dependencies]
anyhow = "1.0"
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "010e175" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
wit-bindgen = "0.24.0"
[lib]
crate-type = ["cdylib"]
[package.metadata.component]
package = "kinode:process"

View File

@ -0,0 +1,48 @@
use kinode_process_lib::kernel_types::{KernelCommand, KernelPrint, KernelResponse};
use kinode_process_lib::{
await_next_message_body, call_init, println, Address, Message, ProcessId, Request,
};
wit_bindgen::generate!({
path: "target/wit",
world: "process-v0",
});
call_init!(init);
fn init(_our: Address) {
let Ok(args) = await_next_message_body() else {
println!("failed to get args");
return;
};
let Ok(proc_id) = String::from_utf8(args) else {
println!("failed to stringify arguments");
return;
};
let body = match proc_id.parse::<ProcessId>() {
Ok(proc_id) => serde_json::to_vec(&KernelCommand::KillProcess(proc_id)).unwrap(),
Err(_) => {
println!("invalid process id");
return;
}
};
let Ok(Message::Response { body, .. }) = Request::new()
.target(("our", "kernel", "distro", "sys"))
.body(body)
.send_and_await_response(60)
.unwrap()
else {
println!("failed to get response from kernel");
return;
};
let Ok(KernelResponse::KilledProcess(proc_id)) =
serde_json::from_slice::<KernelResponse>(&body)
else {
println!("failed to parse kernel response");
return;
};
println!("killed process {}", proc_id);
}

View File

@ -9,7 +9,7 @@ call_init!(init);
fn init(_our: Address) {
let Ok(Ok(Message::Response { body, .. })) = Request::to(("our", "net", "distro", "sys"))
.body(rmp_serde::to_vec(&net::NetAction::GetDiagnostics).unwrap())
.send_and_await_response(5)
.send_and_await_response(60)
else {
println!("failed to get diagnostics from networking module");
return;
@ -18,5 +18,5 @@ fn init(_our: Address) {
println!("got malformed response from networking module");
return;
};
println!("{printout}");
println!("\r\n{printout}");
}

View File

@ -9,14 +9,6 @@
"grant_capabilities": [],
"wit_version": 0
},
"echo.wasm": {
"root": false,
"public": false,
"request_networking": false,
"request_capabilities": [],
"grant_capabilities": [],
"wit_version": 0
},
"cat.wasm": {
"root": false,
"public": false,
@ -33,6 +25,14 @@
"grant_capabilities": [],
"wit_version": 0
},
"echo.wasm": {
"root": false,
"public": false,
"request_networking": false,
"request_capabilities": [],
"grant_capabilities": [],
"wit_version": 0
},
"hi.wasm": {
"root": false,
"public": false,
@ -45,6 +45,23 @@
],
"wit_version": 0
},
"kfetch.wasm": {
"root": true,
"public": false,
"request_networking": false,
"grant_capabilities": [
"eth:distro:sys",
"kernel:distro:sys",
"net:distro:sys"
],
"wit_version": 0
},
"kill.wasm": {
"root": true,
"public": false,
"request_networking": false,
"wit_version": 0
},
"m.wasm": {
"root": true,
"public": true,
@ -100,13 +117,9 @@
"wit_version": 0
},
"top.wasm": {
"root": false,
"root": true,
"public": false,
"request_networking": false,
"request_capabilities": [
"kernel:distro:sys"
],
"grant_capabilities": [],
"wit_version": 0
}
}

View File

@ -71,6 +71,14 @@ fn init(our: Address) {
"hi".to_string(),
ProcessId::new(Some("hi"), "terminal", "sys"),
),
(
"kill".to_string(),
ProcessId::new(Some("kill"), "terminal", "sys"),
),
(
"kfetch".to_string(),
ProcessId::new(Some("kfetch"), "terminal", "sys"),
),
(
"m".to_string(),
ProcessId::new(Some("m"), "terminal", "sys"),

View File

@ -1,6 +1,6 @@
[package]
name = "top"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
[features]
@ -8,7 +8,7 @@ simulation-mode = []
[dependencies]
anyhow = "1.0"
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "09dc9f9" }
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "010e175" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
wit-bindgen = "0.24.0"

View File

@ -1,6 +1,8 @@
use kinode_process_lib::kernel_types::{KernelCommand, KernelPrint};
use kinode_process_lib::kernel_types::{
KernelCommand, KernelPrint, KernelPrintResponse, KernelResponse, PersistedProcess,
};
use kinode_process_lib::{
await_next_message_body, call_init, println, Address, ProcessId, Request,
await_next_message_body, call_init, println, Address, Message, ProcessId, Request,
};
wit_bindgen::generate!({
@ -20,25 +22,76 @@ fn init(_our: Address) {
return;
};
if proc_id.is_empty() {
let _ = Request::new()
.target(("our", "kernel", "distro", "sys"))
.body(serde_json::to_vec(&KernelCommand::Debug(KernelPrint::ProcessMap)).unwrap())
.send();
} else {
match proc_id.parse::<ProcessId>() {
Ok(proc_id) => {
let _ = Request::new()
.target(("our", "kernel", "distro", "sys"))
.body(
serde_json::to_vec(&KernelCommand::Debug(KernelPrint::Process(proc_id)))
.unwrap(),
)
.send();
let Ok(Message::Response { body, .. }) = Request::new()
.target(("our", "kernel", "distro", "sys"))
.body(if proc_id.is_empty() {
serde_json::to_vec(&KernelCommand::Debug(KernelPrint::ProcessMap)).unwrap()
} else {
match proc_id.parse::<ProcessId>() {
Ok(proc_id) => {
serde_json::to_vec(&KernelCommand::Debug(KernelPrint::Process(proc_id)))
.unwrap()
}
Err(_) => {
println!("invalid process id");
return;
}
}
Err(_) => {
println!("invalid process id");
})
.send_and_await_response(60)
.unwrap()
else {
println!("failed to get response from kernel");
return;
};
let Ok(KernelResponse::Debug(kernel_print_response)) =
serde_json::from_slice::<KernelResponse>(&body)
else {
println!("failed to parse kernel response");
return;
};
match kernel_print_response {
KernelPrintResponse::ProcessMap(process_map) => {
let len = process_map.len();
let printout = process_map
.iter()
.map(|(proc_id, process)| print_process(proc_id, process))
.collect::<Vec<_>>()
.join("\r\n");
println!("\r\n{printout}\r\n\r\ntop: {len} running processes");
}
KernelPrintResponse::Process(process) => match process {
None => {
println!("process {} not running", proc_id);
return;
}
Some(process) => {
println!("{}", print_process(&proc_id.parse().unwrap(), &process));
}
},
KernelPrintResponse::HasCap(_) => {
println!("kernel gave wrong kind of response");
}
}
}
fn print_process(id: &ProcessId, process: &PersistedProcess) -> String {
format!(
"{}:\r\n {}\r\n wit: {}\r\n on-exit: {:?}\r\n public: {}\r\n capabilities: {:?}",
id,
if process.wasm_bytes_handle.is_empty() {
"(runtime)"
} else {
&process.wasm_bytes_handle
},
process.wit_version.unwrap_or_default(),
process.on_exit,
process.public,
process
.capabilities
.iter()
.map(|c| c.to_string())
.collect::<Vec<_>>()
)
}

View File

@ -0,0 +1,27 @@
interface tester {
variant request {
run(run-request),
}
variant response {
run(result<_, fail-response>)
}
record run-request {
input-node-names: list<string>,
test-names: list<string>,
test-timeout: u64,
}
record fail-response {
test: string,
file: string,
line: u32,
column: u32,
}
}
world tester-sys-v0 {
import tester;
include process-v0;
}

View File

@ -1,556 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "getrandom"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "http"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "id-arena"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "idna"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b"
dependencies = [
"equivalent",
"hashbrown",
"serde",
]
[[package]]
name = "itoa"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "kinode_process_lib"
version = "0.5.6"
source = "git+https://github.com/kinode-dao/process_lib?rev=fccb6a0#fccb6a0c07ebda3e385bff7f76e4984b741f01c7"
dependencies = [
"anyhow",
"bincode",
"http",
"mime_guess",
"rand",
"serde",
"serde_json",
"thiserror",
"url",
"wit-bindgen",
]
[[package]]
name = "leb128"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "ryu"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "semver"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "serde"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "smallvec"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "spdx"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bde1398b09b9f93fc2fc9b9da86e362693e999d3a54a8ac47a99a5a73f638b"
dependencies = [
"smallvec",
]
[[package]]
name = "syn"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
"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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "unicase"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "url"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
version = "0.38.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-encoder"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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",
"serde",
"serde_derive",
"serde_json",
"spdx",
"wasm-encoder 0.41.0",
"wasmparser 0.121.0",
]
[[package]]
name = "wasmparser"
version = "0.118.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9"
dependencies = [
"indexmap",
"semver",
]
[[package]]
name = "wasmparser"
version = "0.121.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953cf6a7606ab31382cb1caa5ae403e77ba70c7f8e12eeda167e7040d42bfda8"
dependencies = [
"bitflags",
"indexmap",
"semver",
]
[[package]]
name = "wit-bindgen"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"bitflags",
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"wit-component",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"heck",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.16.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=efcc759#efcc7592cf3277bcb9be1034e48569c6d822b322"
dependencies = [
"anyhow",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
"wit-component",
]
[[package]]
name = "wit-component"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder 0.38.1",
"wasm-metadata",
"wasmparser 0.118.1",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df4913a2219096373fd6512adead1fb77ecdaa59d7fc517972a7d30b12f625be"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
]

View File

@ -1,172 +0,0 @@
use std::str::FromStr;
use kinode_process_lib::{
await_message, call_init, 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: "target/wit",
world: "process-v0",
});
fn make_vfs_address(our: &Address) -> anyhow::Result<Address> {
Ok(Address::new(
our.node.clone(),
ProcessId::from_str("vfs:distro:sys")?,
))
}
fn handle_message(our: &Address) -> anyhow::Result<()> {
let message = await_message()?;
match message {
Message::Response { .. } => {
return Err(tt::TesterError::UnexpectedResponse.into());
}
Message::Request { ref body, .. } => {
match serde_json::from_slice(body)? {
tt::TesterRequest::Run {
ref test_names,
test_timeout,
..
} => {
println!("test_runner: got Run");
let dir_prefix = "tester:sys/tests";
let response = Request::new()
.target(make_vfs_address(&our)?)
.body(serde_json::to_vec(&vfs::VfsRequest {
path: dir_prefix.into(),
action: vfs::VfsAction::ReadDir,
})?)
.send_and_await_response(test_timeout)??;
let Message::Response { body: vfs_body, .. } = response else {
fail!("test_runner");
};
let vfs::VfsResponse::ReadDir(mut children) =
serde_json::from_slice(&vfs_body)?
else {
println!(
"{:?}",
serde_json::from_slice::<serde_json::Value>(&vfs_body)?
);
fail!("test_runner");
};
for test_name in test_names {
let test_entry = DirEntry {
path: format!("{}/{}.wasm", dir_prefix, test_name),
file_type: FileType::File,
};
if !children.contains(&test_entry) {
return Err(anyhow::anyhow!(
"test {} not found amongst {:?}",
test_name,
children,
));
}
}
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, None)?;
let file_contents = file.read()?;
serde_json::from_slice(&file_contents)?
}
};
println!("test_runner: running {:?}...", children);
for test_name in test_names {
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(),
)
})
.unwrap_or(vec![]);
let child_process_id = match spawn(
None,
&test_path,
OnExit::None, // TODO: notify us
our_capabilities(),
grant_caps,
false, // not public
) {
Ok(child_process_id) => child_process_id,
Err(e) => {
println!("couldn't spawn {}: {}", test_path, e);
fail!("test_runner");
}
};
let response = Request::new()
.target(Address {
node: our.node.clone(),
process: child_process_id,
})
.body(body.clone())
.send_and_await_response(test_timeout)??;
let Message::Response { body, .. } = response else {
fail!("test_runner");
};
match serde_json::from_slice(&body)? {
tt::TesterResponse::Pass => {}
tt::TesterResponse::GetFullMessage(_) => {}
tt::TesterResponse::Fail {
test,
file,
line,
column,
} => {
fail!(test, file, line, column);
}
}
}
println!("test_runner: done running {:?}", children);
Response::new()
.body(serde_json::to_vec(&tt::TesterResponse::Pass)?)
.send()?;
}
tt::TesterRequest::KernelMessage(_) | tt::TesterRequest::GetFullMessage(_) => {
fail!("test_runner");
}
}
Ok(())
}
}
}
call_init!(init);
fn init(our: Address) {
println!("{}: begin", our);
loop {
match handle_message(&our) {
Ok(()) => {}
Err(e) => {
println!("test_runner: error: {:?}", e);
fail!("test_runner");
}
};
}
}

View File

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

View File

@ -9,8 +9,8 @@ simulation-mode = []
[dependencies]
anyhow = "1.0"
bincode = "1.3.3"
indexmap = "2.1"
kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "09dc9f9" }
process_macros = { git = "https://github.com/kinode-dao/process_macros", rev = "626e501" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"

View File

@ -1,21 +1,24 @@
use indexmap::map::IndexMap;
use std::collections::HashMap;
use std::str::FromStr;
use crate::kinode::process::tester::{
FailResponse, Request as TesterRequest, Response as TesterResponse, RunRequest,
};
use kinode_process_lib::kernel_types as kt;
use kinode_process_lib::{
await_message, call_init, our_capabilities, println, spawn, vfs, Address, Message, OnExit,
ProcessId, Request, Response,
};
mod tester_types;
use tester_types as tt;
mod tester_lib;
wit_bindgen::generate!({
path: "target/wit",
world: "process-v0",
world: "tester-sys-v0",
generate_unused_types: true,
additional_derives: [PartialEq, serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto],
});
type Messages = IndexMap<kt::Message, tt::KernelMessage>;
fn make_vfs_address(our: &Address) -> anyhow::Result<Address> {
Ok(Address {
node: our.node.clone(),
@ -23,88 +26,172 @@ fn make_vfs_address(our: &Address) -> anyhow::Result<Address> {
})
}
fn handle_message(
fn handle_response(message: &Message) -> anyhow::Result<()> {
let TesterResponse::Run(_) = message.body().try_into()?;
let source = message.source();
if (source.process.package_name != "tester") || (source.process.publisher_node != "sys") {
println!(
"got Response from unexpected source: {}; must be in package test:sys",
source,
);
fail!("tester");
}
Response::new().body(message.body()).send().unwrap();
Ok(())
}
fn read_caps_by_child(
dir_prefix: &str,
children: &mut Vec<vfs::DirEntry>,
) -> anyhow::Result<HashMap<String, Vec<String>>> {
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: HashMap<String, Vec<String>> = match caps_index {
None => HashMap::new(),
Some(caps_index) => {
children.remove(caps_index);
let file = vfs::file::open_file(&caps_file_path, false, None)?;
let file_contents = file.read()?;
serde_json::from_slice(&file_contents)?
}
};
Ok(caps_by_child)
}
fn handle_request(
our: &Address,
_messages: &mut Messages,
message: &Message,
node_names: &mut Vec<String>,
) -> anyhow::Result<()> {
let TesterRequest::Run(RunRequest {
input_node_names,
ref test_names,
test_timeout,
}) = message.body().try_into()?;
println!("got Run");
assert!(input_node_names.len() >= 1);
*node_names = input_node_names.clone();
if our.node != node_names[0] {
// we are not the master node
Response::new()
.body(TesterResponse::Run(Ok(())))
.send()
.unwrap();
return Ok(());
}
// we are the master node
let dir_prefix = "tester:sys/tests";
let response = Request::new()
.target(make_vfs_address(&our)?)
.body(serde_json::to_vec(&vfs::VfsRequest {
path: dir_prefix.into(),
action: vfs::VfsAction::ReadDir,
})?)
.send_and_await_response(test_timeout)??;
let Message::Response { body: vfs_body, .. } = response else {
fail!("tester");
};
let vfs::VfsResponse::ReadDir(mut children) = serde_json::from_slice(&vfs_body)? else {
println!(
"{:?}",
serde_json::from_slice::<serde_json::Value>(&vfs_body)?
);
fail!("tester");
};
for test_name in test_names {
let test_entry = vfs::DirEntry {
path: format!("{}/{}.wasm", dir_prefix, test_name),
file_type: vfs::FileType::File,
};
if !children.contains(&test_entry) {
return Err(anyhow::anyhow!(
"test {} not found amongst {:?}",
test_name,
children,
));
}
}
let caps_by_child = read_caps_by_child(dir_prefix, &mut children)?;
println!("tester: running {:?}...", children);
for test_name in test_names {
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(),
)
})
.unwrap_or(vec![]);
let child_process_id = match spawn(
None,
&test_path,
OnExit::None, // TODO: notify us
our_capabilities(),
grant_caps,
false, // not public
) {
Ok(child_process_id) => child_process_id,
Err(e) => {
println!("couldn't spawn {}: {}", test_path, e);
fail!("tester");
}
};
let response = Request::new()
.target(Address {
node: our.node.clone(),
process: child_process_id,
})
.body(message.body())
.send_and_await_response(test_timeout)??;
if response.is_request() {
fail!("tester");
};
let TesterResponse::Run(result) = response.body().try_into()?;
if let Err(FailResponse {
test,
file,
line,
column,
}) = result
{
fail!(test, file, line, column);
}
}
println!("test_runner: done running {:?}", children);
Response::new().body(TesterResponse::Run(Ok(()))).send()?;
Ok(())
}
fn handle_message(our: &Address, node_names: &mut Vec<String>) -> anyhow::Result<()> {
let Ok(message) = await_message() else {
return Ok(());
};
match message {
Message::Response { source, body, .. } => {
match serde_json::from_slice(&body)? {
tt::TesterResponse::Pass | tt::TesterResponse::Fail { .. } => {
if (source.process.package_name != "tester")
| (source.process.publisher_node != "sys")
{
return Err(tt::TesterError::UnexpectedResponse.into());
}
Response::new().body(body).send().unwrap();
}
tt::TesterResponse::GetFullMessage(_) => {
fail!("tester");
}
}
Ok(())
}
Message::Request { body, .. } => {
match serde_json::from_slice(&body)? {
tt::TesterRequest::Run {
input_node_names,
test_timeout,
..
} => {
println!("got Run");
assert!(input_node_names.len() >= 1);
*node_names = input_node_names.clone();
if our.node != node_names[0] {
Response::new()
.body(serde_json::to_vec(&tt::TesterResponse::Pass).unwrap())
.send()
.unwrap();
} else {
// we are master node
let child = "/tester:sys/pkg/test_runner.wasm";
let child_process_id = match spawn(
None,
child,
OnExit::None, // TODO: notify us
our_capabilities(),
vec!["vfs:distro:sys".parse::<ProcessId>().unwrap()],
false, // not public
) {
Ok(child_process_id) => child_process_id,
Err(e) => {
println!("couldn't spawn {}: {}", child, e);
fail!("tester");
}
};
Request::new()
.target(Address {
node: our.node.clone(),
process: child_process_id,
})
.body(body)
.expects_response(test_timeout * 2)
.send()?;
}
}
tt::TesterRequest::KernelMessage(_) | tt::TesterRequest::GetFullMessage(_) => {
fail!("tester");
}
}
Ok(())
}
if !message.is_request() {
return handle_response(&message);
}
return handle_request(our, &message, node_names);
}
call_init!(init);
fn init(our: Address) {
let mut messages: Messages = IndexMap::new();
let mut node_names: Vec<String> = Vec::new();
match Request::new()
.target(make_vfs_address(&our).unwrap())
@ -154,7 +241,7 @@ fn init(our: Address) {
}
loop {
match handle_message(&our, &mut messages, &mut node_names) {
match handle_message(&our, &mut node_names) {
Ok(()) => {}
Err(e) => {
println!("tester: error: {:?}", e,);

View File

@ -0,0 +1,29 @@
use crate::kinode::process::tester::{FailResponse, Response as TesterResponse};
#[macro_export]
macro_rules! fail {
($test:expr) => {
Response::new()
.body(TesterResponse::Run(Err(FailResponse {
test: $test.into(),
file: file!().into(),
line: line!(),
column: column!(),
})))
.send()
.unwrap();
panic!("")
};
($test:expr, $file:expr, $line:expr, $column:expr) => {
Response::new()
.body(TesterResponse::Run(Err(FailResponse {
test: $test.into(),
file: $file.into(),
line: $line,
column: $column,
})))
.send()
.unwrap();
panic!("")
};
}

View File

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

View File

@ -0,0 +1,31 @@
use crate::kinode::process::tester::{
Response as TesterResponse, FailResponse,
};
#[macro_export]
macro_rules! fail {
($test:expr) => {
Response::new()
.body(TesterResponse::Run(Err(FailResponse {
test: $test.into(),
file: file!().into(),
line: line!(),
column: column!(),
})))
.send()
.unwrap();
panic!("")
};
($test:expr, $file:expr, $line:expr, $column:expr) => {
Response::new()
.body(TesterResponse::Run(Err(FailResponse {
test: $test.into(),
file: $file.into(),
line: $line,
column: $column,
})))
.send()
.unwrap();
panic!("")
};
}

View File

@ -1,91 +0,0 @@
use serde::{Deserialize, Serialize};
use kinode_process_lib::kernel_types as kt;
use kinode_process_lib::Address;
type Rsvp = Option<Address>;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct KernelMessage {
pub id: u64,
pub source: Address,
pub target: Address,
pub rsvp: Rsvp,
pub message: kt::Message,
pub lazy_load_blob: Option<kt::LazyLoadBlob>,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum TesterRequest {
Run {
input_node_names: Vec<String>,
test_names: Vec<String>,
test_timeout: u64,
},
KernelMessage(KernelMessage),
GetFullMessage(kt::Message),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct TesterFail {
pub test: String,
pub file: String,
pub line: u32,
pub column: u32,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum TesterResponse {
Pass,
Fail {
test: String,
file: String,
line: u32,
column: u32,
},
GetFullMessage(Option<KernelMessage>),
}
#[derive(Debug, Serialize, Deserialize, thiserror::Error)]
pub enum TesterError {
#[error("RejectForeign")]
RejectForeign,
#[error("UnexpectedResponse")]
UnexpectedResponse,
#[error("FAIL {test} {message}")]
Fail { test: String, message: String },
}
#[macro_export]
macro_rules! fail {
($test:expr) => {
Response::new()
.body(
serde_json::to_vec(&tt::TesterResponse::Fail {
test: $test.into(),
file: file!().into(),
line: line!(),
column: column!(),
})
.unwrap(),
)
.send()
.unwrap();
panic!("")
};
($test:expr, $file:expr, $line:expr, $column:expr) => {
Response::new()
.body(
serde_json::to_vec(&tt::TesterResponse::Fail {
test: $test.into(),
file: $file.into(),
line: $line,
column: $column,
})
.unwrap(),
)
.send()
.unwrap();
panic!("")
};
}

View File

@ -520,52 +520,47 @@ async fn handle_kernel_request(
.await
.expect("event loop: fatal: sender died");
}
t::KernelCommand::Debug(kind) => match kind {
t::KernelPrint::ProcessMap => {
let mut process_map_string = "".to_string();
for (id, process) in &mut *process_map {
process_map_string.push_str(&format!("{}: {}\r\n", id, process));
}
let _ = send_to_terminal
.send(t::Printout {
verbosity: 0,
content: format!("kernel process map:\r\n{process_map_string}\r\nfound {} running processes", process_map.len()),
})
.await;
}
t::KernelPrint::Process(process_id) => {
let Some(proc) = process_map.get(&process_id) else {
let _ = send_to_terminal
.send(t::Printout {
verbosity: 0,
content: format!("kernel: no such running process {}", process_id),
})
.await;
return None;
};
let _ = send_to_terminal
.send(t::Printout {
verbosity: 0,
content: format!("process info for {process_id}:\r\n{proc}",),
})
.await;
}
t::KernelPrint::HasCap { on, cap } => {
let _ = send_to_terminal
.send(t::Printout {
verbosity: 0,
content: format!(
"process {} has cap:\r\n{}",
on,
process_map
.get(&on)
.map(|p| p.capabilities.contains_key(&cap))
.unwrap_or(false)
),
})
.await;
}
},
t::KernelCommand::Debug(kind) => {
let response = match kind {
t::KernelPrint::ProcessMap => t::KernelPrintResponse::ProcessMap(
process_map
.clone()
.into_iter()
.map(|(k, v)| (k, v.into()))
.collect(),
),
t::KernelPrint::Process(process_id) => t::KernelPrintResponse::Process(
process_map.get(&process_id).cloned().map(|p| p.into()),
),
t::KernelPrint::HasCap { on, cap } => t::KernelPrintResponse::HasCap(
process_map
.get(&on)
.map(|p| p.capabilities.contains_key(&cap)),
),
};
send_to_loop
.send(t::KernelMessage {
id: km.id,
source: t::Address {
node: our_name.clone(),
process: KERNEL_PROCESS_ID.clone(),
},
target: km.rsvp.unwrap_or(km.source),
rsvp: None,
message: t::Message::Response((
t::Response {
inherit: false,
body: serde_json::to_vec(&t::KernelResponse::Debug(response)).unwrap(),
metadata: None,
capabilities: vec![],
},
None,
)),
lazy_load_blob: None,
})
.await
.expect("event loop: fatal: sender died");
}
}
None
}

View File

@ -395,7 +395,7 @@ async fn main() {
match res {
Ok(_) => "graceful exit".into(),
Err(e) => format!(
"uh oh, a kernel process crashed -- this should never happen: {e:?}"
"runtime crash: {e:?}"
),
}

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,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" />
<script type="module" crossorigin src="/assets/index-DexQTHZb.js"></script>
<script type="module" crossorigin src="/assets/index-iKMNbHhl.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-B00cPdAQ.css">
</head>

View File

@ -1,11 +1,10 @@
import { FormEvent, useCallback, useEffect, useState } from "react";
import { namehash } from "ethers/lib/utils";
import { BytesLike, utils } from "ethers";
import { utils } from "ethers";
import KinodeHeader from "../components/KnsHeader";
import { NetworkingInfo, PageProps, UnencryptedIdentity } from "../lib/types";
import { PageProps, UnencryptedIdentity } from "../lib/types";
import Loader from "../components/Loader";
import { hooks } from "../connectors/metamask";
import { ipToNumber } from "../utils/ipToNumber";
import { downloadKeyfile } from "../utils/download-keyfile";
import DirectCheckbox from "../components/DirectCheckbox";
import { useNavigate } from "react-router-dom";
@ -13,6 +12,7 @@ import { Tooltip } from "../components/Tooltip";
import { KinodeTitle } from "../components/KinodeTitle";
import { isMobileCheck } from "../utils/dimensions";
import classNames from "classnames";
import { generateNetworkingKeys, getNetworkName } from "../utils/chain";
const { useProvider } = hooks;
@ -28,6 +28,10 @@ function Login({
appSizeOnLoad,
closeConnect,
routers,
setNetworkingKey,
setIpAddress,
setWsPort,
setTcpPort,
setRouters,
knsName,
setOsName,
@ -88,51 +92,18 @@ function Login({
}
// Generate keys on server that are stored temporarily
const {
networking_key,
routing: {
Both: {
ip: ip_address,
ports: {
ws: ws_port,
tcp: tcp_port
},
routers: allowed_routers
}
},
} = (await fetch("/generate-networking-info", {
method: "POST",
}).then((res) => res.json())) as NetworkingInfo;
setLoading("Please confirm the transaction in your wallet");
const ipAddress = ipToNumber(ip_address);
const data: BytesLike[] = [
direct
? (
await kns.populateTransaction.setAllIp(
namehash(knsName),
ipAddress,
ws_port || 0, // ws
0, // wt
tcp_port || 0, // tcp
0 // udp
)
).data!
: (
await kns.populateTransaction.setRouters(
namehash(knsName),
allowed_routers.map((x) => namehash(x))
)
).data!,
(
await kns.populateTransaction.setKey(
namehash(knsName),
networking_key
)
).data!,
];
const data = await generateNetworkingKeys({
direct,
kns,
nodeChainId,
chainName: getNetworkName(nodeChainId),
nameToSet: namehash(knsName),
setNetworkingKey,
setIpAddress,
setWsPort,
setTcpPort,
setRouters,
})
setLoading("Please confirm the transaction");

View File

@ -2,13 +2,12 @@ import { useState, useEffect, FormEvent, useCallback } from "react";
import { hooks } from "../connectors/metamask";
import { useNavigate } from "react-router-dom";
import { toDNSWireFormat } from "../utils/dnsWire";
import { BytesLike, utils } from "ethers";
import { utils } from "ethers";
import EnterEthName from "../components/EnterEthName";
import Loader from "../components/Loader";
import KinodeHeader from "../components/KnsHeader";
import { NetworkingInfo, PageProps } from "../lib/types";
import { ipToNumber } from "../utils/ipToNumber";
import { getNetworkName, setChain } from "../utils/chain";
import { PageProps } from "../lib/types";
import { generateNetworkingKeys, getNetworkName, setChain } from "../utils/chain";
import { hash } from "@ensdomains/eth-ens-namehash";
import DirectCheckbox from "../components/DirectCheckbox";
import { MAINNET_OPT_HEX, OPTIMISM_OPT_HEX } from "../constants/chainId";
@ -69,83 +68,36 @@ function RegisterEthName({
if (!provider) return openConnect();
setLoading("Please confirm the transaction in your wallet");
try {
setLoading("Please confirm the transaction in your wallet");
const {
networking_key,
routing: {
Both: {
ip: ip_address,
ports: {
ws: ws_port,
tcp: tcp_port
},
routers: allowed_routers
}
}
} = (await fetch("/generate-networking-info", { method: "POST" }).then(
(res) => res.json()
)) as NetworkingInfo;
const ipAddress = ipToNumber(ip_address);
setNetworkingKey(networking_key);
setIpAddress(ipAddress);
setWsPort(ws_port || 0);
setTcpPort(tcp_port || 0);
setRouters(allowed_routers);
const cleanedName = name.trim().replace(".eth", "");
const nameToSet = utils.namehash(`${cleanedName}.eth`);
const targetChainId = nodeChainId === OPTIMISM_OPT_HEX ? MAINNET_OPT_HEX : nodeChainId;
try {
await setChain(targetChainId);
} catch (error) {
window.alert(
`You must connect to the ${getNetworkName(targetChainId)} network to continue. Please connect and try again.`
);
throw new Error(`${getNetworkName(targetChainId)} not connected`);
}
const data: BytesLike[] = [
direct
? (
await kns.populateTransaction.setAllIp(
utils.namehash(`${cleanedName}.eth`),
ipAddress,
ws_port || 0, // ws
0, // wt
tcp_port || 0, // tcp
0 // udp
)
).data!
: (
await kns.populateTransaction.setRouters(
utils.namehash(`${cleanedName}.eth`),
allowed_routers.map((x) => utils.namehash(x))
)
).data!,
(
await kns.populateTransaction.setKey(
utils.namehash(`${cleanedName}.eth`),
networking_key
)
).data!,
];
const data = await generateNetworkingKeys({
direct,
kns,
nodeChainId: targetChainId,
chainName,
nameToSet,
setNetworkingKey,
setIpAddress,
setWsPort,
setTcpPort,
setRouters,
});
setLoading("Please confirm the transaction in your wallet");
// console.log("node chain id", nodeChainId);
const dnsFormat = toDNSWireFormat(`${cleanedName}.eth`);
const namehash = hash(`${cleanedName}.eth`);
const hashedName = hash(`${cleanedName}.eth`);
const tx = await knsEnsEntry.setKNSRecords(dnsFormat, data, { gasLimit: 300000 });
const onRegistered = (node: any, _name: any) => {
if (node === namehash) {
if (node === hashedName) {
kns.off("NodeRegistered", onRegistered);
setLoading("");
setOsName(`${cleanedName}.eth`);

View File

@ -2,13 +2,13 @@ import { useState, useEffect, FormEvent, useCallback } from "react";
import { hooks } from "../connectors/metamask";
import { Link, useNavigate } from "react-router-dom";
import { toDNSWireFormat } from "../utils/dnsWire";
import { BytesLike, utils } from 'ethers';
import { utils } from 'ethers';
import EnterKnsName from "../components/EnterKnsName";
import Loader from "../components/Loader";
import KinodeHeader from "../components/KnsHeader";
import { NetworkingInfo, PageProps } from "../lib/types";
import { ipToNumber } from "../utils/ipToNumber";
import { getNetworkName, setChain } from "../utils/chain";
import { PageProps } from "../lib/types";
import { generateNetworkingKeys, getNetworkName } from "../utils/chain";
import DirectCheckbox from "../components/DirectCheckbox";
import { Tooltip } from "../components/Tooltip";
@ -58,62 +58,22 @@ function RegisterKnsName({
if (!provider || !kns) return openConnect()
setLoading('Please confirm the transaction in your wallet');
try {
setLoading('Please confirm the transaction in your wallet');
let networkingInfoResponse;
try {
const response = await fetch('/generate-networking-info', { method: 'POST' });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
networkingInfoResponse = await response.json() as NetworkingInfo;
} catch (error) {
console.error('Failed to fetch networking info:', error);
throw error;
}
const nameToSet = utils.namehash(`${name}.os`);
const {
networking_key,
routing: {
Both: {
ip: ip_address,
ports: { ws: ws_port, tcp: tcp_port },
routers: allowed_routers
}
}
} = networkingInfoResponse;
const ipAddress = ipToNumber(ip_address)
setNetworkingKey(networking_key)
setIpAddress(ipAddress)
setWsPort(ws_port || 0)
setTcpPort(tcp_port || 0)
setRouters(allowed_routers)
const data: BytesLike[] = [
direct
? (await kns.populateTransaction.setAllIp(
utils.namehash(`${name}.os`),
ipAddress,
ws_port || 0, // ws
0, // wt
tcp_port || 0, // tcp
0 // udp
)).data!
: (await kns.populateTransaction.setRouters
(utils.namehash(`${name}.os`), allowed_routers.map(x => utils.namehash(x)))).data!,
(await kns.populateTransaction.setKey(utils.namehash(`${name}.os`), networking_key)).data!
]
setLoading('Please confirm the transaction in your wallet');
try {
await setChain(nodeChainId);
} catch (error) {
window.alert(`You must connect to the ${chainName} network to continue. Please connect and try again.`);
throw new Error(`${chainName} not set`)
}
const data = await generateNetworkingKeys({
direct,
kns,
nodeChainId,
chainName,
nameToSet,
setNetworkingKey,
setIpAddress,
setWsPort,
setTcpPort,
setRouters,
});
const dnsFormat = toDNSWireFormat(`${name}.os`);
const tx = await dotOs?.register(

View File

@ -7,19 +7,18 @@ import {
} from "react";
import { hooks } from "../connectors/metamask";
import { useNavigate } from "react-router-dom";
import { namehash } from "ethers/lib/utils";
import { toAscii } from "idna-uts46-hx";
import { hash } from "@ensdomains/eth-ens-namehash";
import isValidDomain from "is-valid-domain";
import Loader from "../components/Loader";
import KinodeHeader from "../components/KnsHeader";
import { NetworkingInfo, PageProps } from "../lib/types";
import { ipToNumber } from "../utils/ipToNumber";
import { getNetworkName, setChain } from "../utils/chain";
import { PageProps } from "../lib/types";
import { generateNetworkingKeys, getNetworkName } from "../utils/chain";
import { Tooltip } from "../components/Tooltip";
import DirectCheckbox from "../components/DirectCheckbox";
import EnterKnsName from "../components/EnterKnsName";
import { KinodeTitle } from "../components/KinodeTitle";
import { namehash } from "@ethersproject/hash";
const NAME_INVALID_PUNY = "Unsupported punycode character";
const NAME_NOT_OWNER = "Name does not belong to this wallet";
@ -126,64 +125,21 @@ function Reset({
if (!provider || !kns) return openConnect();
setLoading("Please confirm the transaction in your wallet");
try {
setLoading("Please confirm the transaction in your wallet");
const {
networking_key,
routing: {
Both: {
ip: ip_address,
ports: { ws: ws_port, tcp: tcp_port },
routers: allowed_routers
}
}
} = (await fetch("/generate-networking-info", { method: "POST" }).then(
(res) => res.json()
)) as NetworkingInfo;
const ipAddress = ipToNumber(ip_address);
setNetworkingKey(networking_key);
setIpAddress(ipAddress);
setWsPort(ws_port || 0);
setTcpPort(tcp_port || 0);
setRouters(allowed_routers);
const data = [
direct
? (
await kns.populateTransaction.setAllIp(
namehash(knsName),
ipAddress,
ws_port || 0, // ws
0, // wt
tcp_port || 0, // tcp
0 // udp
)
).data!
: (
await kns.populateTransaction.setRouters(
namehash(knsName),
allowed_routers.map((x) => namehash(x))
)
).data!,
(
await kns.populateTransaction.setKey(
namehash(knsName),
networking_key
)
).data!,
];
try {
await setChain(nodeChainId);
} catch (error) {
window.alert(
`You must connect to the ${chainName} network to continue. Please connect and try again.`
);
throw new Error(`${chainName} not set`);
}
const nameToSet = namehash(knsName);
const data = await generateNetworkingKeys({
direct,
kns,
nodeChainId,
chainName,
nameToSet,
setNetworkingKey,
setIpAddress,
setWsPort,
setTcpPort,
setRouters,
});
const tx = await kns.multicall(data);
@ -192,12 +148,12 @@ function Reset({
await tx.wait();
setReset(true);
setLoading("");
setDirect(direct);
navigate("/set-password");
} catch {
setLoading("");
alert("An error occurred, please try again.");
} finally {
setLoading("");
}
},
[

View File

@ -6,13 +6,12 @@ import {
} from "react";
import { hooks } from "../connectors/metamask";
import { Link, useNavigate } from "react-router-dom";
import { namehash } from "ethers/lib/utils";
import Loader from "../components/Loader";
import KinodeHeader from "../components/KnsHeader";
import { NetworkingInfo, PageProps } from "../lib/types";
import { ipToNumber } from "../utils/ipToNumber";
import { getNetworkName, setChain } from "../utils/chain";
import { PageProps } from "../lib/types";
import { generateNetworkingKeys, getNetworkName } from "../utils/chain";
import DirectCheckbox from "../components/DirectCheckbox";
import { namehash } from "@ethersproject/hash";
const { useProvider } = hooks;
@ -52,64 +51,20 @@ function ResetNode({
if (!provider) return openConnect();
setLoading("Please confirm the transaction in your wallet");
try {
setLoading("Please confirm the transaction in your wallet");
const {
networking_key,
routing: {
Both: {
ip: ip_address,
ports: { ws: ws_port, tcp: tcp_port },
routers: allowed_routers
}
}
} = (await fetch("/generate-networking-info", { method: "POST" }).then(
(res) => res.json()
)) as NetworkingInfo;
const ipAddress = ipToNumber(ip_address);
setNetworkingKey(networking_key);
setIpAddress(ipAddress);
setWsPort(ws_port || 0);
setTcpPort(tcp_port || 0);
setRouters(allowed_routers);
const data = [
direct
? (
await kns.populateTransaction.setAllIp(
namehash(knsName),
ipAddress,
ws_port || 0, // ws
0, // wt
tcp_port || 0, // tcp
0 // udp
)
).data!
: (
await kns.populateTransaction.setRouters(
namehash(knsName),
allowed_routers.map((x) => namehash(x))
)
).data!,
(
await kns.populateTransaction.setKey(
namehash(knsName),
networking_key
)
).data!,
];
try {
await setChain(nodeChainId);
} catch (error) {
window.alert(
`You must connect to the ${chainName} network to continue. Please connect and try again.`
);
throw new Error(`${chainName} not set`);
}
const data = await generateNetworkingKeys({
direct,
kns,
nodeChainId,
chainName,
nameToSet: namehash(knsName),
setNetworkingKey,
setIpAddress,
setWsPort,
setTcpPort,
setRouters,
});
const tx = await kns.multicall(data);
@ -118,12 +73,12 @@ function ResetNode({
await tx.wait();
setReset(true);
setLoading("");
setDirect(direct);
navigate("/set-password");
} catch {
setLoading("");
alert("An error occurred, please try again.");
} finally {
setLoading("");
}
},
[

View File

@ -1,4 +1,8 @@
import { SEPOLIA_OPT_HEX, OPTIMISM_OPT_HEX, MAINNET_OPT_HEX } from "../constants/chainId";
import { NetworkingInfo } from "../lib/types";
import { ipToNumber } from "./ipToNumber";
import { KNSRegistryResolver } from "../abis/types";
import { namehash } from "@ethersproject/hash";
const CHAIN_NOT_FOUND = "4902"
export interface Chain {
@ -96,3 +100,85 @@ export const setChain = async (chainId: string) => {
}
}
}
export const generateNetworkingKeys = async ({
direct,
kns,
nodeChainId,
chainName,
nameToSet,
setNetworkingKey,
setIpAddress,
setWsPort,
setTcpPort,
setRouters,
}: {
direct: boolean,
kns: KNSRegistryResolver,
nodeChainId: string,
chainName: string,
nameToSet: string,
setNetworkingKey: (networkingKey: string) => void;
setIpAddress: (ipAddress: number) => void;
setWsPort: (wsPort: number) => void;
setTcpPort: (tcpPort: number) => void;
setRouters: (routers: string[]) => void;
}) => {
const {
networking_key,
routing: {
Both: {
ip: ip_address,
ports: { ws: ws_port, tcp: tcp_port },
routers: allowed_routers
}
}
} = (await fetch("/generate-networking-info", { method: "POST" }).then(
(res) => res.json()
)) as NetworkingInfo;
const ipAddress = ipToNumber(ip_address);
setNetworkingKey(networking_key);
setIpAddress(ipAddress);
setWsPort(ws_port || 0);
setTcpPort(tcp_port || 0);
setRouters(allowed_routers);
const data = [
direct
? (
await kns.populateTransaction.setAllIp(
nameToSet,
ipAddress,
ws_port || 0, // ws
0, // wt
tcp_port || 0, // tcp
0 // udp
)
).data!
: (
await kns.populateTransaction.setRouters(
nameToSet,
allowed_routers.map((x) => namehash(x))
)
).data!,
(
await kns.populateTransaction.setKey(
nameToSet,
networking_key
)
).data!,
];
try {
await setChain(nodeChainId);
} catch (error) {
window.alert(
`You must connect to the ${chainName} network to continue. Please connect and try again.`
);
throw new Error(`${chainName} not set`);
}
return data
}

View File

@ -43,7 +43,7 @@ pub struct ProcessId {
impl Serialize for ProcessId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
S: serde::Serializer,
{
format!("{}", self).serialize(serializer)
}
@ -52,7 +52,7 @@ impl Serialize for ProcessId {
impl<'a> Deserialize<'a> for ProcessId {
fn deserialize<D>(deserializer: D) -> Result<ProcessId, D::Error>
where
D: serde::de::Deserializer<'a>,
D: serde::Deserializer<'a>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
@ -1237,6 +1237,14 @@ pub enum KernelResponse {
StartedProcess,
RunProcessError,
KilledProcess(ProcessId),
Debug(KernelPrintResponse),
}
#[derive(Debug, Serialize, Deserialize)]
pub enum KernelPrintResponse {
ProcessMap(UserspaceProcessMap),
Process(Option<UserspacePersistedProcess>),
HasCap(Option<bool>),
}
#[derive(Debug)]
@ -1282,6 +1290,7 @@ pub enum CapMessage {
pub type ReverseCapIndex = HashMap<ProcessId, HashMap<ProcessId, Vec<Capability>>>;
pub type ProcessMap = HashMap<ProcessId, PersistedProcess>;
pub type UserspaceProcessMap = HashMap<ProcessId, UserspacePersistedProcess>;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PersistedProcess {
@ -1292,29 +1301,24 @@ pub struct PersistedProcess {
pub public: bool, // marks if a process allows messages from any process
}
impl std::fmt::Display for PersistedProcess {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"Process {{\n wasm_bytes_handle: {},\n wit_version: {:?},\n on_exit: {:?},\n public: {}\n capabilities: {}\n}}",
{
if &self.wasm_bytes_handle == "" {
"(none, this is a runtime process)"
} else {
&self.wasm_bytes_handle
}
},
self.wit_version,
self.on_exit,
self.public,
{
let mut caps_string = "[".to_string();
for cap in self.capabilities.keys() {
caps_string += &format!("\n {}", cap.to_string());
}
caps_string + "\n ]"
},
)
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UserspacePersistedProcess {
pub wasm_bytes_handle: String,
pub wit_version: Option<u32>,
pub on_exit: OnExit,
pub capabilities: HashSet<Capability>,
pub public: bool,
}
impl From<PersistedProcess> for UserspacePersistedProcess {
fn from(p: PersistedProcess) -> Self {
UserspacePersistedProcess {
wasm_bytes_handle: p.wasm_bytes_handle,
wit_version: p.wit_version,
on_exit: p.on_exit,
capabilities: p.capabilities.into_keys().collect(),
public: p.public,
}
}
}