rewrite app store to use its wit API

This commit is contained in:
dr-frmr 2024-05-28 15:51:55 -06:00
parent 5dbf0745ca
commit 0dba348aca
No known key found for this signature in database
16 changed files with 269 additions and 356 deletions

30
Cargo.lock generated
View File

@ -649,7 +649,7 @@ dependencies = [
"alloy-sol-types 0.7.0", "alloy-sol-types 0.7.0",
"anyhow", "anyhow",
"bincode", "bincode",
"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=9998921)",
"rand 0.8.5", "rand 0.8.5",
"serde", "serde",
"serde_json", "serde_json",
@ -1935,7 +1935,7 @@ name = "download"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "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=9998921)",
"serde", "serde",
"serde_json", "serde_json",
"wit-bindgen", "wit-bindgen",
@ -2887,7 +2887,7 @@ name = "install"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "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=9998921)",
"serde", "serde",
"serde_json", "serde_json",
"wit-bindgen", "wit-bindgen",
@ -3217,6 +3217,28 @@ dependencies = [
"wit-bindgen", "wit-bindgen",
] ]
[[package]]
name = "kinode_process_lib"
version = "0.8.0"
source = "git+https://github.com/kinode-dao/process_lib?rev=9998921#9998921364561368fdb22f5b2839c60e1bda87a1"
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]] [[package]]
name = "kit" name = "kit"
version = "0.4.2" version = "0.4.2"
@ -5913,7 +5935,7 @@ name = "uninstall"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "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=9998921)",
"serde", "serde",
"serde_json", "serde_json",
"wit-bindgen", "wit-bindgen",

View File

@ -3,10 +3,7 @@ interface main {
// app store API as presented by main:app_store:sys-v0 // app store API as presented by main:app_store:sys-v0
// //
record package-id { use standard.{package-id};
package-name: string,
publisher-node: string,
}
record onchain-metadata { record onchain-metadata {
name: option<string>, name: option<string>,

View File

@ -11,7 +11,7 @@ alloy-primitives = "0.7.0"
alloy-sol-types = "0.7.0" alloy-sol-types = "0.7.0"
anyhow = "1.0" anyhow = "1.0"
bincode = "1.3.3" 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 = "9998921" }
rand = "0.8" rand = "0.8"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"

View File

@ -1,169 +0,0 @@
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>,
},
/// Request a package API 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.
DownloadApi {
package_id: PackageId,
desired_version_hash: Option<String>,
},
}
/// The response expected from sending a [`RemoteRequest`].
#[derive(Debug, Serialize, Deserialize)]
pub enum RemoteResponse {
DownloadApproved,
DownloadDenied(ReasonDenied),
}
#[derive(Debug, Serialize, Deserialize)]
pub enum ReasonDenied {
NoPackage,
NotMirroring,
HashMismatch { requested: String, have: String },
FileNotFound,
WorkerSpawnFailed,
}
/// 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,
metadata: kernel_types::Erc721Metadata,
/// 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,
/// List all apps we have APIs for.
ListApis,
/// Return the given API, if we have it.
GetApi(PackageId),
}
/// 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,
ListApisResponse { apis: Vec<PackageId> },
GetApiResponse(GetApiResponse), // API in blob (or None)
}
// 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,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum GetApiResponse {
Success,
Failure,
}

View File

@ -1,4 +1,5 @@
use crate::{DownloadResponse, PackageListing, PackageState, RequestedPackage, State}; use crate::types::{PackageListing, PackageState, RequestedPackage, State};
use crate::DownloadResponse;
use kinode_process_lib::{ use kinode_process_lib::{
eth, eth,
http::{send_response, IncomingHttpRequest, Method, StatusCode}, http::{send_response, IncomingHttpRequest, Method, StatusCode},
@ -223,7 +224,7 @@ fn serve_paths(
None, None,
format!("Downloading").into_bytes(), format!("Downloading").into_bytes(),
)), )),
DownloadResponse::Failure => Ok(( _ => Ok((
StatusCode::SERVICE_UNAVAILABLE, StatusCode::SERVICE_UNAVAILABLE,
None, None,
format!("Failed to download").into_bytes(), format!("Failed to download").into_bytes(),
@ -319,7 +320,7 @@ fn serve_paths(
None, None,
format!("Downloading").into_bytes(), format!("Downloading").into_bytes(),
)), )),
DownloadResponse::Failure => Ok(( _ => Ok((
StatusCode::SERVICE_UNAVAILABLE, StatusCode::SERVICE_UNAVAILABLE,
None, None,
format!("Failed to download").into_bytes(), format!("Failed to download").into_bytes(),

View File

@ -1,23 +1,30 @@
use kinode_process_lib::http::{ use crate::kinode::process::main::{
bind_http_path, bind_ws_path, send_ws_push, serve_ui, HttpServerRequest, WsMessageType, ApisResponse, AutoUpdateResponse, DownloadRequest, DownloadResponse, GetApiResponse,
InstallResponse, LocalRequest, LocalResponse, MirrorResponse, NewPackageRequest,
NewPackageResponse, Reason, RebuildIndexResponse, RemoteDownloadRequest, RemoteRequest,
RemoteResponse, UninstallResponse,
}; };
use kinode_process_lib::kernel_types as kt; use kinode_process_lib::kernel_types as kt;
use kinode_process_lib::*; use kinode_process_lib::{
use kinode_process_lib::{call_init, println}; await_message, call_init, get_blob, get_capability, get_typed_state, println,
};
use kinode_process_lib::{
eth, http, vfs, Address, LazyLoadBlob, Message, NodeId, PackageId, ProcessId, Request, Response,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::str::FromStr; use std::str::FromStr;
wit_bindgen::generate!({ wit_bindgen::generate!({
path: "target/wit", path: "target/wit",
world: "process-v0", generate_unused_types: true,
world: "app-store-sys-v0",
additional_derives: [PartialEq, serde::Deserialize, serde::Serialize],
}); });
mod api;
mod http_api; mod http_api;
use api::*;
mod types; mod types;
use types::*; use types::{PackageState, RequestedPackage, State};
mod ft_worker_lib; mod ft_worker_lib;
use ft_worker_lib::{ use ft_worker_lib::{
spawn_receive_transfer, spawn_transfer, FTWorkerCommand, FTWorkerResult, FileTransferContext, spawn_receive_transfer, spawn_transfer, FTWorkerCommand, FTWorkerResult, FileTransferContext,
@ -70,7 +77,7 @@ pub enum Req {
FTWorkerCommand(FTWorkerCommand), FTWorkerCommand(FTWorkerCommand),
FTWorkerResult(FTWorkerResult), FTWorkerResult(FTWorkerResult),
Eth(eth::EthSubResult), Eth(eth::EthSubResult),
Http(HttpServerRequest), Http(http::HttpServerRequest),
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -190,9 +197,9 @@ fn init(our: Address) {
"/apps/:id/auto-update", "/apps/:id/auto-update",
"/apps/rebuild-index", "/apps/rebuild-index",
] { ] {
bind_http_path(path, true, false).expect("failed to bind http path"); http::bind_http_path(path, true, false).expect("failed to bind http path");
} }
serve_ui( http::serve_ui(
&our, &our,
"ui", "ui",
true, true,
@ -201,7 +208,7 @@ fn init(our: Address) {
) )
.expect("failed to serve static UI"); .expect("failed to serve static UI");
bind_ws_path("/", true, true).expect("failed to bind ws path"); http::bind_ws_path("/", true, true).expect("failed to bind ws path");
// add ourselves to the homepage // add ourselves to the homepage
Request::to(("our", "homepage", "homepage", "sys")) Request::to(("our", "homepage", "homepage", "sys"))
@ -221,20 +228,17 @@ fn init(our: Address) {
.send() .send()
.unwrap(); .unwrap();
let mut state: State = match get_typed_state(|bytes| Ok(bincode::deserialize::<State>(bytes)?)) let mut state: State =
{ match get_typed_state(|bytes| Ok(serde_json::from_slice::<State>(bytes)?)) {
Some(state) => { Some(state) => {
println!("loaded saved state"); println!("loaded saved state");
state state
} }
_ => { _ => {
println!("failed to load state, initializing"); println!("failed to load state, initializing");
State::new(CONTRACT_ADDRESS.to_string()).unwrap() State::new(CONTRACT_ADDRESS.to_string()).unwrap()
} }
}; };
// // 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 { if state.contract_address != CONTRACT_ADDRESS {
println!("warning: contract address mismatch--overwriting saved state"); println!("warning: contract address mismatch--overwriting saved state");
@ -283,9 +287,9 @@ fn init(our: Address) {
&message, &message,
) { ) {
println!("error handling message: {:?}", e); println!("error handling message: {:?}", e);
send_ws_push( http::send_ws_push(
channel_id, channel_id,
WsMessageType::Text, http::WsMessageType::Text,
LazyLoadBlob { LazyLoadBlob {
mime: Some("application/json".to_string()), mime: Some("application/json".to_string()),
bytes: serde_json::json!({ bytes: serde_json::json!({
@ -328,7 +332,7 @@ fn handle_message(
} }
let (body, blob) = handle_local_request( let (body, blob) = handle_local_request(
&our, &our,
&local_request, local_request,
state, state,
eth_provider, eth_provider,
requested_apis, requested_apis,
@ -345,7 +349,7 @@ fn handle_message(
} }
} }
Req::RemoteRequest(remote_request) => { Req::RemoteRequest(remote_request) => {
let resp = handle_remote_request(&our, &source, &remote_request, state); let resp = handle_remote_request(&our, &source, remote_request, state);
if expects_response.is_some() { if expects_response.is_some() {
Response::new().body(serde_json::to_vec(&resp)?).send()?; Response::new().body(serde_json::to_vec(&resp)?).send()?;
} }
@ -384,7 +388,7 @@ fn handle_message(
{ {
return Err(anyhow::anyhow!("http_server from non-local node")); return Err(anyhow::anyhow!("http_server from non-local node"));
} }
if let HttpServerRequest::Http(req) = incoming { if let http::HttpServerRequest::Http(req) = incoming {
http_api::handle_http_request( http_api::handle_http_request(
our, our,
state, state,
@ -411,32 +415,24 @@ fn handle_message(
fn handle_remote_request( fn handle_remote_request(
our: &Address, our: &Address,
source: &Address, source: &Address,
request: &RemoteRequest, request: RemoteRequest,
state: &mut State, state: &mut State,
) -> Resp { ) -> Resp {
match request { match request {
RemoteRequest::Download { RemoteRequest::Download(RemoteDownloadRequest {
package_id, package_id,
desired_version_hash, desired_version_hash,
} => { }) => {
let Some(package_state) = state.get_downloaded_package(package_id) else { let package_id = package_id.to_process_lib();
return Resp::RemoteResponse(RemoteResponse::DownloadDenied( let Some(package_state) = state.get_downloaded_package(&package_id) else {
ReasonDenied::NoPackage, return Resp::RemoteResponse(RemoteResponse::Denied(Reason::NoPackage));
));
}; };
if !package_state.mirroring { if !package_state.mirroring {
return Resp::RemoteResponse(RemoteResponse::DownloadDenied( return Resp::RemoteResponse(RemoteResponse::Denied(Reason::NotMirroring));
ReasonDenied::NotMirroring,
));
} }
if let Some(hash) = desired_version_hash { if let Some(hash) = desired_version_hash {
if &package_state.our_version != hash { if package_state.our_version != hash {
return Resp::RemoteResponse(RemoteResponse::DownloadDenied( return Resp::RemoteResponse(RemoteResponse::Denied(Reason::HashMismatch));
ReasonDenied::HashMismatch {
requested: hash.clone(),
have: package_state.our_version.clone(),
},
));
} }
} }
let file_name = format!("/{}.zip", package_id); let file_name = format!("/{}.zip", package_id);
@ -452,40 +448,28 @@ fn handle_remote_request(
) )
.send_and_await_response(5) .send_and_await_response(5)
else { else {
return Resp::RemoteResponse(RemoteResponse::DownloadDenied( return Resp::RemoteResponse(RemoteResponse::Denied(Reason::NoPackage));
ReasonDenied::FileNotFound,
));
}; };
// transfer will *inherit* the blob bytes we receive from VFS // transfer will *inherit* the blob bytes we receive from VFS
match spawn_transfer(&our, &file_name, None, 60, &source) { match spawn_transfer(&our, &file_name, None, 60, &source) {
Ok(()) => Resp::RemoteResponse(RemoteResponse::DownloadApproved), Ok(()) => Resp::RemoteResponse(RemoteResponse::Approved),
Err(_e) => Resp::RemoteResponse(RemoteResponse::DownloadDenied( Err(_e) => Resp::RemoteResponse(RemoteResponse::Denied(Reason::NoPackage)),
ReasonDenied::WorkerSpawnFailed,
)),
} }
} }
RemoteRequest::DownloadApi { RemoteRequest::DownloadApi(RemoteDownloadRequest {
package_id, package_id,
desired_version_hash, desired_version_hash,
} => { }) => {
let Some(package_state) = state.get_downloaded_package(package_id) else { let package_id = package_id.to_process_lib();
return Resp::RemoteResponse(RemoteResponse::DownloadDenied( let Some(package_state) = state.get_downloaded_package(&package_id) else {
ReasonDenied::NoPackage, return Resp::RemoteResponse(RemoteResponse::Denied(Reason::NoPackage));
));
}; };
if !package_state.mirroring { if !package_state.mirroring {
return Resp::RemoteResponse(RemoteResponse::DownloadDenied( return Resp::RemoteResponse(RemoteResponse::Denied(Reason::NotMirroring));
ReasonDenied::NotMirroring,
));
} }
if let Some(desired_version_hash) = desired_version_hash { if let Some(desired_version_hash) = desired_version_hash {
if &package_state.our_version != desired_version_hash { if package_state.our_version != desired_version_hash {
return Resp::RemoteResponse(RemoteResponse::DownloadDenied( return Resp::RemoteResponse(RemoteResponse::Denied(Reason::HashMismatch));
ReasonDenied::HashMismatch {
requested: desired_version_hash.clone(),
have: package_state.our_version.clone(),
},
));
} }
} }
let file_name = format!("/{}-api-v0.zip", package_id); // TODO: actual version let file_name = format!("/{}-api-v0.zip", package_id); // TODO: actual version
@ -501,16 +485,12 @@ fn handle_remote_request(
) )
.send_and_await_response(5) .send_and_await_response(5)
else { else {
return Resp::RemoteResponse(RemoteResponse::DownloadDenied( return Resp::RemoteResponse(RemoteResponse::Denied(Reason::NoPackage));
ReasonDenied::FileNotFound,
));
}; };
// transfer will *inherit* the blob bytes we receive from VFS // transfer will *inherit* the blob bytes we receive from VFS
match spawn_transfer(&our, &file_name, None, 60, &source) { match spawn_transfer(&our, &file_name, None, 60, &source) {
Ok(()) => Resp::RemoteResponse(RemoteResponse::DownloadApproved), Ok(()) => Resp::RemoteResponse(RemoteResponse::Approved),
Err(_e) => Resp::RemoteResponse(RemoteResponse::DownloadDenied( Err(_e) => Resp::RemoteResponse(RemoteResponse::Denied(Reason::NoPackage)),
ReasonDenied::WorkerSpawnFailed,
)),
} }
} }
} }
@ -519,26 +499,27 @@ fn handle_remote_request(
/// only `our.node` can call this /// only `our.node` can call this
fn handle_local_request( fn handle_local_request(
our: &Address, our: &Address,
request: &LocalRequest, request: LocalRequest,
state: &mut State, state: &mut State,
eth_provider: &eth::Provider, eth_provider: &eth::Provider,
requested_apis: &mut HashMap<PackageId, RequestedPackage>, requested_apis: &mut HashMap<PackageId, RequestedPackage>,
requested_packages: &mut HashMap<PackageId, RequestedPackage>, requested_packages: &mut HashMap<PackageId, RequestedPackage>,
) -> (LocalResponse, Option<LazyLoadBlob>) { ) -> (LocalResponse, Option<LazyLoadBlob>) {
match request { match request {
LocalRequest::NewPackage { LocalRequest::NewPackage(NewPackageRequest {
package, package_id,
metadata, metadata,
mirror, mirror,
} => { }) => {
let package_id = package_id.to_process_lib();
let Some(blob) = get_blob() else { let Some(blob) = get_blob() else {
return ( return (
LocalResponse::NewPackageResponse(NewPackageResponse::Failure), LocalResponse::NewPackageResponse(NewPackageResponse::NoBlob),
None, None,
); );
}; };
// set the version hash for this new local package // set the version hash for this new local package
let our_version = generate_version_hash(&blob.bytes); let our_version = types::generate_version_hash(&blob.bytes);
let package_state = PackageState { let package_state = PackageState {
mirrored_from: Some(our.node.clone()), mirrored_from: Some(our.node.clone()),
@ -547,19 +528,19 @@ fn handle_local_request(
verified: true, // side loaded apps are implicitly verified because there is no "source" to verify against verified: true, // side loaded apps are implicitly verified because there is no "source" to verify against
caps_approved: true, // TODO see if we want to auto-approve local installs caps_approved: true, // TODO see if we want to auto-approve local installs
manifest_hash: None, // generated in the add fn manifest_hash: None, // generated in the add fn
mirroring: *mirror, mirroring: mirror,
auto_update: false, // can't auto-update a local package auto_update: false, // can't auto-update a local package
metadata: Some(metadata.clone()), metadata: Some(metadata.to_erc721_metadata()),
}; };
let Ok(()) = state.add_downloaded_package(package, package_state, Some(blob.bytes)) let Ok(()) = state.add_downloaded_package(&package_id, package_state, Some(blob.bytes))
else { else {
return ( return (
LocalResponse::NewPackageResponse(NewPackageResponse::Failure), LocalResponse::NewPackageResponse(NewPackageResponse::InstallFailed),
None, None,
); );
}; };
let drive_path = format!("/{package}/pkg"); let drive_path = format!("/{package_id}/pkg");
let result = Request::new() let result = Request::new()
.target(("our", "vfs", "distro", "sys")) .target(("our", "vfs", "distro", "sys"))
.body( .body(
@ -571,7 +552,7 @@ fn handle_local_request(
) )
.send_and_await_response(5); .send_and_await_response(5);
if let Ok(Ok(_)) = result { if let Ok(Ok(_)) = result {
state.downloaded_apis.insert(package.to_owned()); state.downloaded_apis.insert(package_id.to_owned());
}; };
( (
@ -579,61 +560,61 @@ fn handle_local_request(
None, None,
) )
} }
LocalRequest::Download { LocalRequest::Download(DownloadRequest {
package: package_id, package_id,
download_from, download_from,
mirror, mirror,
auto_update, auto_update,
desired_version_hash, desired_version_hash,
} => ( }) => (
LocalResponse::DownloadResponse(start_download( LocalResponse::DownloadResponse(start_download(
our, our,
requested_packages, requested_packages,
package_id, &package_id.to_process_lib(),
download_from, &download_from,
*mirror, mirror,
*auto_update, auto_update,
desired_version_hash, &desired_version_hash,
)), )),
None, None,
), ),
LocalRequest::Install(package) => ( LocalRequest::Install(package_id) => (
match handle_install(our, state, package) { match handle_install(our, state, &package_id.to_process_lib()) {
Ok(()) => LocalResponse::InstallResponse(InstallResponse::Success), Ok(()) => LocalResponse::InstallResponse(InstallResponse::Success),
Err(_) => LocalResponse::InstallResponse(InstallResponse::Failure), Err(_) => LocalResponse::InstallResponse(InstallResponse::Failure),
}, },
None, None,
), ),
LocalRequest::Uninstall(package) => ( LocalRequest::Uninstall(package_id) => (
match state.uninstall(package) { match state.uninstall(&package_id.to_process_lib()) {
Ok(()) => LocalResponse::UninstallResponse(UninstallResponse::Success), Ok(()) => LocalResponse::UninstallResponse(UninstallResponse::Success),
Err(_) => LocalResponse::UninstallResponse(UninstallResponse::Failure), Err(_) => LocalResponse::UninstallResponse(UninstallResponse::Failure),
}, },
None, None,
), ),
LocalRequest::StartMirroring(package) => ( LocalRequest::StartMirroring(package_id) => (
match state.start_mirroring(package) { match state.start_mirroring(&package_id.to_process_lib()) {
true => LocalResponse::MirrorResponse(MirrorResponse::Success), true => LocalResponse::MirrorResponse(MirrorResponse::Success),
false => LocalResponse::MirrorResponse(MirrorResponse::Failure), false => LocalResponse::MirrorResponse(MirrorResponse::Failure),
}, },
None, None,
), ),
LocalRequest::StopMirroring(package) => ( LocalRequest::StopMirroring(package_id) => (
match state.stop_mirroring(package) { match state.stop_mirroring(&package_id.to_process_lib()) {
true => LocalResponse::MirrorResponse(MirrorResponse::Success), true => LocalResponse::MirrorResponse(MirrorResponse::Success),
false => LocalResponse::MirrorResponse(MirrorResponse::Failure), false => LocalResponse::MirrorResponse(MirrorResponse::Failure),
}, },
None, None,
), ),
LocalRequest::StartAutoUpdate(package) => ( LocalRequest::StartAutoUpdate(package_id) => (
match state.start_auto_update(package) { match state.start_auto_update(&package_id.to_process_lib()) {
true => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Success), true => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Success),
false => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Failure), false => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Failure),
}, },
None, None,
), ),
LocalRequest::StopAutoUpdate(package) => ( LocalRequest::StopAutoUpdate(package_id) => (
match state.stop_auto_update(package) { match state.stop_auto_update(&package_id.to_process_lib()) {
true => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Success), true => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Success),
false => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Failure), false => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Failure),
}, },
@ -643,8 +624,8 @@ fn handle_local_request(
rebuild_index(our, state, eth_provider, requested_apis), rebuild_index(our, state, eth_provider, requested_apis),
None, None,
), ),
LocalRequest::ListApis => (list_apis(state), None), LocalRequest::Apis => (list_apis(state), None),
LocalRequest::GetApi(ref package_id) => get_api(package_id, state), LocalRequest::GetApi(package_id) => get_api(&package_id.to_process_lib(), state),
} }
} }
@ -679,9 +660,14 @@ pub fn get_api(package_id: &PackageId, state: &mut State) -> (LocalResponse, Opt
} }
pub fn list_apis(state: &mut State) -> LocalResponse { pub fn list_apis(state: &mut State) -> LocalResponse {
LocalResponse::ListApisResponse { LocalResponse::ApisResponse(ApisResponse {
apis: state.downloaded_apis.iter().cloned().collect(), apis: state
} .downloaded_apis
.clone()
.into_iter()
.map(|id| crate::kinode::process::main::PackageId::from_process_lib(id))
.collect(),
})
} }
pub fn rebuild_index( pub fn rebuild_index(
@ -707,7 +693,7 @@ pub fn rebuild_index(
}; };
} }
LocalResponse::RebuiltIndex LocalResponse::RebuildIndexResponse(RebuildIndexResponse::Success)
} }
pub fn start_api_download( pub fn start_api_download(
@ -720,16 +706,18 @@ pub fn start_api_download(
match Request::to((download_from.as_str(), our.process.clone())) match Request::to((download_from.as_str(), our.process.clone()))
.inherit(true) .inherit(true)
.body( .body(
serde_json::to_vec(&RemoteRequest::DownloadApi { serde_json::to_vec(&RemoteRequest::DownloadApi(RemoteDownloadRequest {
package_id: package_id.clone(), package_id: crate::kinode::process::main::PackageId::from_process_lib(
package_id.clone(),
),
desired_version_hash: desired_version_hash.map(|s| s.to_string()), desired_version_hash: desired_version_hash.map(|s| s.to_string()),
}) }))
.unwrap(), .unwrap(),
) )
.send_and_await_response(5) .send_and_await_response(5)
{ {
Ok(Ok(Message::Response { body, .. })) => match serde_json::from_slice::<Resp>(&body) { Ok(Ok(Message::Response { body, .. })) => match serde_json::from_slice::<Resp>(&body) {
Ok(Resp::RemoteResponse(RemoteResponse::DownloadApproved)) => { Ok(Resp::RemoteResponse(RemoteResponse::Approved)) => {
requested_apis.insert( requested_apis.insert(
package_id, package_id,
RequestedPackage { RequestedPackage {
@ -741,9 +729,9 @@ pub fn start_api_download(
); );
DownloadResponse::Started DownloadResponse::Started
} }
_ => DownloadResponse::Failure, _ => DownloadResponse::BadResponse,
}, },
_ => DownloadResponse::Failure, _ => DownloadResponse::BadResponse,
} }
} }
@ -759,16 +747,18 @@ pub fn start_download(
match Request::to((download_from.as_str(), our.process.clone())) match Request::to((download_from.as_str(), our.process.clone()))
.inherit(true) .inherit(true)
.body( .body(
serde_json::to_vec(&RemoteRequest::Download { serde_json::to_vec(&RemoteRequest::Download(RemoteDownloadRequest {
package_id: package_id.clone(), package_id: crate::kinode::process::main::PackageId::from_process_lib(
package_id.clone(),
),
desired_version_hash: desired_version_hash.clone(), desired_version_hash: desired_version_hash.clone(),
}) }))
.unwrap(), .unwrap(),
) )
.send_and_await_response(5) .send_and_await_response(5)
{ {
Ok(Ok(Message::Response { body, .. })) => match serde_json::from_slice::<Resp>(&body) { Ok(Ok(Message::Response { body, .. })) => match serde_json::from_slice::<Resp>(&body) {
Ok(Resp::RemoteResponse(RemoteResponse::DownloadApproved)) => { Ok(Resp::RemoteResponse(RemoteResponse::Approved)) => {
requested_packages.insert( requested_packages.insert(
package_id.clone(), package_id.clone(),
RequestedPackage { RequestedPackage {
@ -780,9 +770,9 @@ pub fn start_download(
); );
DownloadResponse::Started DownloadResponse::Started
} }
_ => DownloadResponse::Failure, _ => DownloadResponse::BadResponse,
}, },
_ => DownloadResponse::Failure, _ => DownloadResponse::BadResponse,
} }
} }
@ -834,7 +824,7 @@ fn handle_receive_download_api(
}; };
// check the version hash for this download against requested!! // check the version hash for this download against requested!!
// for now we can reject if it's not latest. // for now we can reject if it's not latest.
let download_hash = generate_version_hash(&blob.bytes); let download_hash = types::generate_version_hash(&blob.bytes);
let mut verified = false; let mut verified = false;
// TODO: require api_hash // TODO: require api_hash
if let Some(hash) = requested_package.desired_version_hash { if let Some(hash) = requested_package.desired_version_hash {
@ -874,7 +864,7 @@ fn handle_receive_download_package(
}; };
// check the version hash for this download against requested!! // check the version hash for this download against requested!!
// for now we can reject if it's not latest. // for now we can reject if it's not latest.
let download_hash = generate_version_hash(&blob.bytes); let download_hash = types::generate_version_hash(&blob.bytes);
let mut verified = false; let mut verified = false;
match requested_package.desired_version_hash { match requested_package.desired_version_hash {
Some(hash) => { Some(hash) => {
@ -907,13 +897,17 @@ fn handle_receive_download_package(
let Some(latest_hash) = metadata let Some(latest_hash) = metadata
.properties .properties
.code_hashes .code_hashes
.clone()
.into_iter()
.collect::<HashMap<_, _>>()
.get(&metadata.properties.current_version) .get(&metadata.properties.current_version)
.cloned()
else { else {
return Err(anyhow::anyhow!( return Err(anyhow::anyhow!(
"downloaded package has no versions in manager--rejecting download!" "downloaded package has no versions in manager--rejecting download!"
)); ));
}; };
if &download_hash != latest_hash { if download_hash != latest_hash {
if latest_hash.is_empty() { if latest_hash.is_empty() {
println!( println!(
"\x1b[33mwarning: downloaded package has no version hashes--cannot verify code integrity, proceeding anyways\x1b[0m" "\x1b[33mwarning: downloaded package has no version hashes--cannot verify code integrity, proceeding anyways\x1b[0m"

View File

@ -1,8 +1,9 @@
use crate::{start_api_download, DownloadResponse, LocalRequest}; use crate::kinode::process::main::{DownloadRequest, LocalRequest, OnchainMetadata};
use alloy_sol_types::{sol, SolEvent}; use alloy_sol_types::{sol, SolEvent};
use kinode_process_lib::eth::Log; use kinode_process_lib::{
use kinode_process_lib::kernel_types as kt; eth::Log, get_blob, http, kernel_types as kt, net, println, vfs, Address, LazyLoadBlob,
use kinode_process_lib::{println, *}; Message, NodeId, PackageId, ProcessId, Request,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -37,6 +38,49 @@ pub enum IndexerRequests {
NodeInfo { name: String, block: u64 }, NodeInfo { name: String, block: u64 },
} }
// quite annoyingly, we must convert from our gen'd version of PackageId
// to the process_lib's gen'd version. this is in order to access custom
// Impls that we want to use
impl crate::kinode::process::main::PackageId {
pub fn to_process_lib(self) -> PackageId {
PackageId {
package_name: self.package_name,
publisher_node: self.publisher_node,
}
}
pub fn from_process_lib(package_id: PackageId) -> Self {
Self {
package_name: package_id.package_name,
publisher_node: package_id.publisher_node,
}
}
}
// less annoying but still bad
impl OnchainMetadata {
pub fn to_erc721_metadata(self) -> kt::Erc721Metadata {
use kt::Erc721Properties;
kt::Erc721Metadata {
name: self.name,
description: self.description,
image: self.image,
external_url: self.external_url,
animation_url: self.animation_url,
properties: Erc721Properties {
package_name: self.properties.package_name,
publisher: self.properties.publisher,
current_version: self.properties.current_version,
mirrors: self.properties.mirrors,
code_hashes: self.properties.code_hashes.into_iter().collect(),
license: self.properties.license,
screenshots: self.properties.screenshots,
wit_version: self.properties.wit_version,
dependencies: self.properties.dependencies,
},
}
}
}
// //
// app store types // app store types
// //
@ -113,7 +157,7 @@ impl State {
/// To create a new state, we populate the downloaded_packages map /// To create a new state, we populate the downloaded_packages map
/// with all packages parseable from our filesystem. /// with all packages parseable from our filesystem.
pub fn new(contract_address: String) -> anyhow::Result<Self> { pub fn new(contract_address: String) -> anyhow::Result<Self> {
crate::print_to_terminal(1, "producing new state"); kinode_process_lib::print_to_terminal(1, "producing new state");
let mut state = State { let mut state = State {
contract_address, contract_address,
last_saved_block: crate::CONTRACT_FIRST_BLOCK, last_saved_block: crate::CONTRACT_FIRST_BLOCK,
@ -196,7 +240,7 @@ impl State {
}; };
} }
self.downloaded_apis.insert(package_id.to_owned()); self.downloaded_apis.insert(package_id.to_owned());
crate::set_state(&bincode::serialize(self)?); kinode_process_lib::set_state(&serde_json::to_vec(self)?);
Ok(()) Ok(())
} }
@ -258,7 +302,7 @@ impl State {
} }
self.downloaded_packages self.downloaded_packages
.insert(package_id.to_owned(), package_state); .insert(package_id.to_owned(), package_state);
crate::set_state(&bincode::serialize(self)?); kinode_process_lib::set_state(&serde_json::to_vec(self)?);
Ok(()) Ok(())
} }
@ -276,7 +320,7 @@ impl State {
true true
}) })
.unwrap_or(false); .unwrap_or(false);
crate::set_state(&bincode::serialize(self).unwrap()); kinode_process_lib::set_state(&serde_json::to_vec(self).unwrap());
res res
} }
@ -416,7 +460,7 @@ impl State {
// finally, remove from downloaded packages // finally, remove from downloaded packages
self.downloaded_packages.remove(package_id); self.downloaded_packages.remove(package_id);
crate::set_state(&bincode::serialize(self)?); kinode_process_lib::set_state(&serde_json::to_vec(self)?);
println!("uninstalled {package_id}"); println!("uninstalled {package_id}");
Ok(()) Ok(())
@ -447,7 +491,7 @@ impl State {
let package_hash = package_hash.to_string(); let package_hash = package_hash.to_string();
let metadata_hash = metadata_hash.to_string(); let metadata_hash = metadata_hash.to_string();
crate::print_to_terminal( kinode_process_lib::print_to_terminal(
1, 1,
&format!( &format!(
"app registered with package_name {}, metadata_url {}, metadata_hash {}", "app registered with package_name {}, metadata_url {}, metadata_hash {}",
@ -529,7 +573,10 @@ impl State {
Some(metadata) Some(metadata)
} }
Err(e) => { Err(e) => {
crate::print_to_terminal(1, &format!("failed to fetch metadata: {e:?}")); kinode_process_lib::print_to_terminal(
1,
&format!("failed to fetch metadata: {e:?}"),
);
None None
} }
}; };
@ -545,18 +592,22 @@ impl State {
if let Some(package_state) = self.downloaded_packages.get(&package_id) { if let Some(package_state) = self.downloaded_packages.get(&package_id) {
if package_state.auto_update { if package_state.auto_update {
if let Some(mirrored_from) = &package_state.mirrored_from { if let Some(mirrored_from) = &package_state.mirrored_from {
crate::print_to_terminal( kinode_process_lib::print_to_terminal(
1, 1,
&format!("auto-updating package {package_id} from {mirrored_from}"), &format!("auto-updating package {package_id} from {mirrored_from}"),
); );
Request::to(our) Request::to(our)
.body(serde_json::to_vec(&LocalRequest::Download { .body(serde_json::to_vec(&LocalRequest::Download(
package: package_id, DownloadRequest {
download_from: mirrored_from.clone(), package_id: crate::kinode::process::main::PackageId::from_process_lib(
mirror: package_state.mirroring, package_id,
auto_update: package_state.auto_update, ),
desired_version_hash: None, download_from: mirrored_from.clone(),
})?) mirror: package_state.mirroring,
auto_update: package_state.auto_update,
desired_version_hash: None,
},
))?)
.send()?; .send()?;
} }
} }
@ -595,7 +646,7 @@ impl State {
_ => {} _ => {}
} }
self.last_saved_block = block_number; self.last_saved_block = block_number;
crate::set_state(&bincode::serialize(self)?); kinode_process_lib::set_state(&serde_json::to_vec(self)?);
Ok(()) Ok(())
} }
} }

View File

@ -8,7 +8,7 @@ simulation-mode = []
[dependencies] [dependencies]
anyhow = "1.0" 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 = "9998921" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
wit-bindgen = "0.24.0" wit-bindgen = "0.24.0"

View File

@ -1 +0,0 @@
../../app_store/src/api.rs

View File

@ -1,13 +1,14 @@
use crate::kinode::process::main::{DownloadResponse, LocalRequest, LocalResponse};
use kinode::process::main::DownloadRequest;
use kinode_process_lib::{ use kinode_process_lib::{
await_next_message_body, call_init, println, Address, Message, NodeId, PackageId, Request, await_next_message_body, call_init, println, Address, Message, NodeId, PackageId, Request,
}; };
mod api;
use api::*;
wit_bindgen::generate!({ wit_bindgen::generate!({
path: "target/wit", path: "target/wit",
world: "process-v0", generate_unused_types: true,
world: "app-store-sys-v0",
additional_derives: [PartialEq, serde::Deserialize, serde::Serialize],
}); });
call_init!(init); call_init!(init);
@ -36,13 +37,16 @@ fn init(our: Address) {
let Ok(Ok(Message::Response { body, .. })) = let Ok(Ok(Message::Response { body, .. })) =
Request::to((our.node(), ("main", "app_store", "sys"))) Request::to((our.node(), ("main", "app_store", "sys")))
.body( .body(
serde_json::to_vec(&LocalRequest::Download { serde_json::to_vec(&LocalRequest::Download(DownloadRequest {
package: package_id.clone(), package_id: crate::kinode::process::main::PackageId {
package_name: package_id.package_name.clone(),
publisher_node: package_id.publisher_node.clone(),
},
download_from: download_from.clone(), download_from: download_from.clone(),
mirror: true, mirror: true,
auto_update: true, auto_update: true,
desired_version_hash: None, desired_version_hash: None,
}) }))
.unwrap(), .unwrap(),
) )
.send_and_await_response(5) .send_and_await_response(5)
@ -60,7 +64,7 @@ fn init(our: Address) {
LocalResponse::DownloadResponse(DownloadResponse::Started) => { LocalResponse::DownloadResponse(DownloadResponse::Started) => {
println!("started downloading package {package_id} from {download_from}"); println!("started downloading package {package_id} from {download_from}");
} }
LocalResponse::DownloadResponse(DownloadResponse::Failure) => { LocalResponse::DownloadResponse(_) => {
println!("failed to download package {package_id} from {download_from}"); println!("failed to download package {package_id} from {download_from}");
} }
_ => { _ => {

View File

@ -8,7 +8,7 @@ simulation-mode = []
[dependencies] [dependencies]
anyhow = "1.0" 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 = "9998921" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
wit-bindgen = "0.24.0" wit-bindgen = "0.24.0"

View File

@ -1 +0,0 @@
../../app_store/src/api.rs

View File

@ -1,13 +1,13 @@
use crate::kinode::process::main::{InstallResponse, LocalRequest, LocalResponse};
use kinode_process_lib::{ use kinode_process_lib::{
await_next_message_body, call_init, println, Address, Message, PackageId, Request, await_next_message_body, call_init, println, Address, Message, PackageId, Request,
}; };
mod api;
use api::*;
wit_bindgen::generate!({ wit_bindgen::generate!({
path: "target/wit", path: "target/wit",
world: "process-v0", generate_unused_types: true,
world: "app-store-sys-v0",
additional_derives: [PartialEq, serde::Deserialize, serde::Serialize],
}); });
call_init!(init); call_init!(init);
@ -33,7 +33,15 @@ fn init(our: Address) {
let Ok(Ok(Message::Response { body, .. })) = let Ok(Ok(Message::Response { body, .. })) =
Request::to((our.node(), ("main", "app_store", "sys"))) Request::to((our.node(), ("main", "app_store", "sys")))
.body(serde_json::to_vec(&LocalRequest::Install(package_id.clone())).unwrap()) .body(
serde_json::to_vec(&LocalRequest::Install(
crate::kinode::process::main::PackageId {
package_name: package_id.package_name.clone(),
publisher_node: package_id.publisher_node.clone(),
},
))
.unwrap(),
)
.send_and_await_response(5) .send_and_await_response(5)
else { else {
println!("install: failed to get a response from app_store..!"); println!("install: failed to get a response from app_store..!");

View File

@ -8,7 +8,7 @@ simulation-mode = []
[dependencies] [dependencies]
anyhow = "1.0" 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 = "9998921" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
wit-bindgen = "0.24.0" wit-bindgen = "0.24.0"

View File

@ -1 +0,0 @@
../../app_store/src/api.rs

View File

@ -1,13 +1,13 @@
use crate::kinode::process::main::{LocalRequest, LocalResponse, UninstallResponse};
use kinode_process_lib::{ use kinode_process_lib::{
await_next_message_body, call_init, println, Address, Message, PackageId, Request, await_next_message_body, call_init, println, Address, Message, PackageId, Request,
}; };
mod api;
use api::*;
wit_bindgen::generate!({ wit_bindgen::generate!({
path: "target/wit", path: "target/wit",
world: "process-v0", generate_unused_types: true,
world: "app-store-sys-v0",
additional_derives: [PartialEq, serde::Deserialize, serde::Serialize],
}); });
call_init!(init); call_init!(init);
@ -33,7 +33,15 @@ fn init(our: Address) {
let Ok(Ok(Message::Response { body, .. })) = let Ok(Ok(Message::Response { body, .. })) =
Request::to((our.node(), ("main", "app_store", "sys"))) Request::to((our.node(), ("main", "app_store", "sys")))
.body(serde_json::to_vec(&LocalRequest::Uninstall(package_id.clone())).unwrap()) .body(
serde_json::to_vec(&LocalRequest::Uninstall(
crate::kinode::process::main::PackageId {
package_name: package_id.package_name.clone(),
publisher_node: package_id.publisher_node.clone(),
},
))
.unwrap(),
)
.send_and_await_response(5) .send_and_await_response(5)
else { else {
println!("uninstall: failed to get a response from app_store..!"); println!("uninstall: failed to get a response from app_store..!");