diff --git a/Cargo.lock b/Cargo.lock index e087760b..5ae16c48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -649,7 +649,7 @@ dependencies = [ "alloy-sol-types 0.7.0", "anyhow", "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", "serde", "serde_json", @@ -1935,7 +1935,7 @@ name = "download" version = "0.1.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=9998921)", "serde", "serde_json", "wit-bindgen", @@ -2887,7 +2887,7 @@ name = "install" version = "0.1.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=9998921)", "serde", "serde_json", "wit-bindgen", @@ -3217,6 +3217,28 @@ dependencies = [ "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]] name = "kit" version = "0.4.2" @@ -5913,7 +5935,7 @@ name = "uninstall" version = "0.1.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=9998921)", "serde", "serde_json", "wit-bindgen", diff --git a/kinode/packages/app_store/api/app_store:sys-v0.wit b/kinode/packages/app_store/api/app-store:sys-v0.wit similarity index 97% rename from kinode/packages/app_store/api/app_store:sys-v0.wit rename to kinode/packages/app_store/api/app-store:sys-v0.wit index b71d921f..0f83b66f 100644 --- a/kinode/packages/app_store/api/app_store:sys-v0.wit +++ b/kinode/packages/app_store/api/app-store:sys-v0.wit @@ -3,10 +3,7 @@ interface main { // app store API as presented by main:app_store:sys-v0 // - record package-id { - package-name: string, - publisher-node: string, - } + use standard.{package-id}; record onchain-metadata { name: option, diff --git a/kinode/packages/app_store/app_store/Cargo.toml b/kinode/packages/app_store/app_store/Cargo.toml index 44db0c4c..8c31b365 100644 --- a/kinode/packages/app_store/app_store/Cargo.toml +++ b/kinode/packages/app_store/app_store/Cargo.toml @@ -11,7 +11,7 @@ alloy-primitives = "0.7.0" alloy-sol-types = "0.7.0" 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 = "9998921" } rand = "0.8" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/kinode/packages/app_store/app_store/src/api.rs b/kinode/packages/app_store/app_store/src/api.rs deleted file mode 100644 index 01ff55db..00000000 --- a/kinode/packages/app_store/app_store/src/api.rs +++ /dev/null @@ -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, - }, - /// 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, - }, -} - -/// 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, - }, - /// 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 }, - 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, -} diff --git a/kinode/packages/app_store/app_store/src/http_api.rs b/kinode/packages/app_store/app_store/src/http_api.rs index 6b425632..2909a4ef 100644 --- a/kinode/packages/app_store/app_store/src/http_api.rs +++ b/kinode/packages/app_store/app_store/src/http_api.rs @@ -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::{ eth, http::{send_response, IncomingHttpRequest, Method, StatusCode}, @@ -223,7 +224,7 @@ fn serve_paths( None, format!("Downloading").into_bytes(), )), - DownloadResponse::Failure => Ok(( + _ => Ok(( StatusCode::SERVICE_UNAVAILABLE, None, format!("Failed to download").into_bytes(), @@ -319,7 +320,7 @@ fn serve_paths( None, format!("Downloading").into_bytes(), )), - DownloadResponse::Failure => Ok(( + _ => Ok(( StatusCode::SERVICE_UNAVAILABLE, None, format!("Failed to download").into_bytes(), diff --git a/kinode/packages/app_store/app_store/src/lib.rs b/kinode/packages/app_store/app_store/src/lib.rs index 8d3411a0..dfd152a4 100644 --- a/kinode/packages/app_store/app_store/src/lib.rs +++ b/kinode/packages/app_store/app_store/src/lib.rs @@ -1,23 +1,30 @@ -use kinode_process_lib::http::{ - bind_http_path, bind_ws_path, send_ws_push, serve_ui, HttpServerRequest, WsMessageType, +use crate::kinode::process::main::{ + 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::*; -use kinode_process_lib::{call_init, println}; +use kinode_process_lib::{ + 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 std::collections::{HashMap, HashSet}; use std::str::FromStr; wit_bindgen::generate!({ 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; -use api::*; mod types; -use types::*; +use types::{PackageState, RequestedPackage, State}; mod ft_worker_lib; use ft_worker_lib::{ spawn_receive_transfer, spawn_transfer, FTWorkerCommand, FTWorkerResult, FileTransferContext, @@ -70,7 +77,7 @@ pub enum Req { FTWorkerCommand(FTWorkerCommand), FTWorkerResult(FTWorkerResult), Eth(eth::EthSubResult), - Http(HttpServerRequest), + Http(http::HttpServerRequest), } #[derive(Debug, Serialize, Deserialize)] @@ -190,9 +197,9 @@ fn init(our: Address) { "/apps/:id/auto-update", "/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, "ui", true, @@ -201,7 +208,7 @@ fn init(our: Address) { ) .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 Request::to(("our", "homepage", "homepage", "sys")) @@ -221,20 +228,17 @@ fn init(our: Address) { .send() .unwrap(); - let mut state: State = match get_typed_state(|bytes| Ok(bincode::deserialize::(bytes)?)) - { - Some(state) => { - println!("loaded saved state"); - state - } - _ => { - println!("failed to load state, initializing"); - 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()); + let mut state: State = + match get_typed_state(|bytes| Ok(serde_json::from_slice::(bytes)?)) { + Some(state) => { + println!("loaded saved state"); + state + } + _ => { + println!("failed to load state, initializing"); + State::new(CONTRACT_ADDRESS.to_string()).unwrap() + } + }; if state.contract_address != CONTRACT_ADDRESS { println!("warning: contract address mismatch--overwriting saved state"); @@ -283,9 +287,9 @@ fn init(our: Address) { &message, ) { println!("error handling message: {:?}", e); - send_ws_push( + http::send_ws_push( channel_id, - WsMessageType::Text, + http::WsMessageType::Text, LazyLoadBlob { mime: Some("application/json".to_string()), bytes: serde_json::json!({ @@ -328,7 +332,7 @@ fn handle_message( } let (body, blob) = handle_local_request( &our, - &local_request, + local_request, state, eth_provider, requested_apis, @@ -345,7 +349,7 @@ fn handle_message( } } 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() { 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")); } - if let HttpServerRequest::Http(req) = incoming { + if let http::HttpServerRequest::Http(req) = incoming { http_api::handle_http_request( our, state, @@ -411,32 +415,24 @@ fn handle_message( fn handle_remote_request( our: &Address, source: &Address, - request: &RemoteRequest, + request: RemoteRequest, state: &mut State, ) -> Resp { match request { - RemoteRequest::Download { + RemoteRequest::Download(RemoteDownloadRequest { package_id, desired_version_hash, - } => { - let Some(package_state) = state.get_downloaded_package(package_id) else { - return Resp::RemoteResponse(RemoteResponse::DownloadDenied( - ReasonDenied::NoPackage, - )); + }) => { + let package_id = package_id.to_process_lib(); + let Some(package_state) = state.get_downloaded_package(&package_id) else { + return Resp::RemoteResponse(RemoteResponse::Denied(Reason::NoPackage)); }; if !package_state.mirroring { - return Resp::RemoteResponse(RemoteResponse::DownloadDenied( - ReasonDenied::NotMirroring, - )); + return Resp::RemoteResponse(RemoteResponse::Denied(Reason::NotMirroring)); } if let Some(hash) = desired_version_hash { - if &package_state.our_version != hash { - return Resp::RemoteResponse(RemoteResponse::DownloadDenied( - ReasonDenied::HashMismatch { - requested: hash.clone(), - have: package_state.our_version.clone(), - }, - )); + if package_state.our_version != hash { + return Resp::RemoteResponse(RemoteResponse::Denied(Reason::HashMismatch)); } } let file_name = format!("/{}.zip", package_id); @@ -452,40 +448,28 @@ fn handle_remote_request( ) .send_and_await_response(5) else { - return Resp::RemoteResponse(RemoteResponse::DownloadDenied( - ReasonDenied::FileNotFound, - )); + return Resp::RemoteResponse(RemoteResponse::Denied(Reason::NoPackage)); }; // transfer will *inherit* the blob bytes we receive from VFS match spawn_transfer(&our, &file_name, None, 60, &source) { - Ok(()) => Resp::RemoteResponse(RemoteResponse::DownloadApproved), - Err(_e) => Resp::RemoteResponse(RemoteResponse::DownloadDenied( - ReasonDenied::WorkerSpawnFailed, - )), + Ok(()) => Resp::RemoteResponse(RemoteResponse::Approved), + Err(_e) => Resp::RemoteResponse(RemoteResponse::Denied(Reason::NoPackage)), } } - RemoteRequest::DownloadApi { + RemoteRequest::DownloadApi(RemoteDownloadRequest { package_id, desired_version_hash, - } => { - let Some(package_state) = state.get_downloaded_package(package_id) else { - return Resp::RemoteResponse(RemoteResponse::DownloadDenied( - ReasonDenied::NoPackage, - )); + }) => { + let package_id = package_id.to_process_lib(); + let Some(package_state) = state.get_downloaded_package(&package_id) else { + return Resp::RemoteResponse(RemoteResponse::Denied(Reason::NoPackage)); }; if !package_state.mirroring { - return Resp::RemoteResponse(RemoteResponse::DownloadDenied( - ReasonDenied::NotMirroring, - )); + return Resp::RemoteResponse(RemoteResponse::Denied(Reason::NotMirroring)); } if let Some(desired_version_hash) = desired_version_hash { - if &package_state.our_version != desired_version_hash { - return Resp::RemoteResponse(RemoteResponse::DownloadDenied( - ReasonDenied::HashMismatch { - requested: desired_version_hash.clone(), - have: package_state.our_version.clone(), - }, - )); + if package_state.our_version != desired_version_hash { + return Resp::RemoteResponse(RemoteResponse::Denied(Reason::HashMismatch)); } } 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) else { - return Resp::RemoteResponse(RemoteResponse::DownloadDenied( - ReasonDenied::FileNotFound, - )); + return Resp::RemoteResponse(RemoteResponse::Denied(Reason::NoPackage)); }; // transfer will *inherit* the blob bytes we receive from VFS match spawn_transfer(&our, &file_name, None, 60, &source) { - Ok(()) => Resp::RemoteResponse(RemoteResponse::DownloadApproved), - Err(_e) => Resp::RemoteResponse(RemoteResponse::DownloadDenied( - ReasonDenied::WorkerSpawnFailed, - )), + Ok(()) => Resp::RemoteResponse(RemoteResponse::Approved), + Err(_e) => Resp::RemoteResponse(RemoteResponse::Denied(Reason::NoPackage)), } } } @@ -519,26 +499,27 @@ fn handle_remote_request( /// only `our.node` can call this fn handle_local_request( our: &Address, - request: &LocalRequest, + request: LocalRequest, state: &mut State, eth_provider: ð::Provider, requested_apis: &mut HashMap, requested_packages: &mut HashMap, ) -> (LocalResponse, Option) { match request { - LocalRequest::NewPackage { - package, + LocalRequest::NewPackage(NewPackageRequest { + package_id, metadata, mirror, - } => { + }) => { + let package_id = package_id.to_process_lib(); let Some(blob) = get_blob() else { return ( - LocalResponse::NewPackageResponse(NewPackageResponse::Failure), + LocalResponse::NewPackageResponse(NewPackageResponse::NoBlob), None, ); }; // 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 { 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 caps_approved: true, // TODO see if we want to auto-approve local installs manifest_hash: None, // generated in the add fn - mirroring: *mirror, + mirroring: mirror, 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 { return ( - LocalResponse::NewPackageResponse(NewPackageResponse::Failure), + LocalResponse::NewPackageResponse(NewPackageResponse::InstallFailed), None, ); }; - let drive_path = format!("/{package}/pkg"); + let drive_path = format!("/{package_id}/pkg"); let result = Request::new() .target(("our", "vfs", "distro", "sys")) .body( @@ -571,7 +552,7 @@ fn handle_local_request( ) .send_and_await_response(5); 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, ) } - LocalRequest::Download { - package: package_id, + LocalRequest::Download(DownloadRequest { + package_id, download_from, mirror, auto_update, desired_version_hash, - } => ( + }) => ( LocalResponse::DownloadResponse(start_download( our, requested_packages, - package_id, - download_from, - *mirror, - *auto_update, - desired_version_hash, + &package_id.to_process_lib(), + &download_from, + mirror, + auto_update, + &desired_version_hash, )), None, ), - LocalRequest::Install(package) => ( - match handle_install(our, state, package) { + LocalRequest::Install(package_id) => ( + match handle_install(our, state, &package_id.to_process_lib()) { Ok(()) => LocalResponse::InstallResponse(InstallResponse::Success), Err(_) => LocalResponse::InstallResponse(InstallResponse::Failure), }, None, ), - LocalRequest::Uninstall(package) => ( - match state.uninstall(package) { + LocalRequest::Uninstall(package_id) => ( + match state.uninstall(&package_id.to_process_lib()) { Ok(()) => LocalResponse::UninstallResponse(UninstallResponse::Success), Err(_) => LocalResponse::UninstallResponse(UninstallResponse::Failure), }, None, ), - LocalRequest::StartMirroring(package) => ( - match state.start_mirroring(package) { + LocalRequest::StartMirroring(package_id) => ( + match state.start_mirroring(&package_id.to_process_lib()) { true => LocalResponse::MirrorResponse(MirrorResponse::Success), false => LocalResponse::MirrorResponse(MirrorResponse::Failure), }, None, ), - LocalRequest::StopMirroring(package) => ( - match state.stop_mirroring(package) { + LocalRequest::StopMirroring(package_id) => ( + match state.stop_mirroring(&package_id.to_process_lib()) { true => LocalResponse::MirrorResponse(MirrorResponse::Success), false => LocalResponse::MirrorResponse(MirrorResponse::Failure), }, None, ), - LocalRequest::StartAutoUpdate(package) => ( - match state.start_auto_update(package) { + LocalRequest::StartAutoUpdate(package_id) => ( + match state.start_auto_update(&package_id.to_process_lib()) { true => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Success), false => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Failure), }, None, ), - LocalRequest::StopAutoUpdate(package) => ( - match state.stop_auto_update(package) { + LocalRequest::StopAutoUpdate(package_id) => ( + match state.stop_auto_update(&package_id.to_process_lib()) { true => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Success), false => LocalResponse::AutoUpdateResponse(AutoUpdateResponse::Failure), }, @@ -643,8 +624,8 @@ fn handle_local_request( rebuild_index(our, state, eth_provider, requested_apis), None, ), - LocalRequest::ListApis => (list_apis(state), None), - LocalRequest::GetApi(ref package_id) => get_api(package_id, state), + LocalRequest::Apis => (list_apis(state), None), + 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 { - LocalResponse::ListApisResponse { - apis: state.downloaded_apis.iter().cloned().collect(), - } + LocalResponse::ApisResponse(ApisResponse { + apis: state + .downloaded_apis + .clone() + .into_iter() + .map(|id| crate::kinode::process::main::PackageId::from_process_lib(id)) + .collect(), + }) } pub fn rebuild_index( @@ -707,7 +693,7 @@ pub fn rebuild_index( }; } - LocalResponse::RebuiltIndex + LocalResponse::RebuildIndexResponse(RebuildIndexResponse::Success) } pub fn start_api_download( @@ -720,16 +706,18 @@ pub fn start_api_download( match Request::to((download_from.as_str(), our.process.clone())) .inherit(true) .body( - serde_json::to_vec(&RemoteRequest::DownloadApi { - package_id: package_id.clone(), + serde_json::to_vec(&RemoteRequest::DownloadApi(RemoteDownloadRequest { + package_id: crate::kinode::process::main::PackageId::from_process_lib( + package_id.clone(), + ), desired_version_hash: desired_version_hash.map(|s| s.to_string()), - }) + })) .unwrap(), ) .send_and_await_response(5) { Ok(Ok(Message::Response { body, .. })) => match serde_json::from_slice::(&body) { - Ok(Resp::RemoteResponse(RemoteResponse::DownloadApproved)) => { + Ok(Resp::RemoteResponse(RemoteResponse::Approved)) => { requested_apis.insert( package_id, RequestedPackage { @@ -741,9 +729,9 @@ pub fn start_api_download( ); 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())) .inherit(true) .body( - serde_json::to_vec(&RemoteRequest::Download { - package_id: package_id.clone(), + serde_json::to_vec(&RemoteRequest::Download(RemoteDownloadRequest { + package_id: crate::kinode::process::main::PackageId::from_process_lib( + package_id.clone(), + ), desired_version_hash: desired_version_hash.clone(), - }) + })) .unwrap(), ) .send_and_await_response(5) { Ok(Ok(Message::Response { body, .. })) => match serde_json::from_slice::(&body) { - Ok(Resp::RemoteResponse(RemoteResponse::DownloadApproved)) => { + Ok(Resp::RemoteResponse(RemoteResponse::Approved)) => { requested_packages.insert( package_id.clone(), RequestedPackage { @@ -780,9 +770,9 @@ pub fn start_download( ); 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!! // 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; // TODO: require api_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!! // 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; match requested_package.desired_version_hash { Some(hash) => { @@ -907,13 +897,17 @@ fn handle_receive_download_package( let Some(latest_hash) = metadata .properties .code_hashes + .clone() + .into_iter() + .collect::>() .get(&metadata.properties.current_version) + .cloned() else { return Err(anyhow::anyhow!( "downloaded package has no versions in manager--rejecting download!" )); }; - if &download_hash != latest_hash { + if download_hash != latest_hash { if latest_hash.is_empty() { println!( "\x1b[33mwarning: downloaded package has no version hashes--cannot verify code integrity, proceeding anyways\x1b[0m" diff --git a/kinode/packages/app_store/app_store/src/types.rs b/kinode/packages/app_store/app_store/src/types.rs index e8637654..0a89fdd3 100644 --- a/kinode/packages/app_store/app_store/src/types.rs +++ b/kinode/packages/app_store/app_store/src/types.rs @@ -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 kinode_process_lib::eth::Log; -use kinode_process_lib::kernel_types as kt; -use kinode_process_lib::{println, *}; +use kinode_process_lib::{ + eth::Log, get_blob, http, kernel_types as kt, net, println, vfs, Address, LazyLoadBlob, + Message, NodeId, PackageId, ProcessId, Request, +}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; @@ -37,6 +38,49 @@ pub enum IndexerRequests { 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 // @@ -113,7 +157,7 @@ impl State { /// To create a new state, we populate the downloaded_packages map /// with all packages parseable from our filesystem. pub fn new(contract_address: String) -> anyhow::Result { - crate::print_to_terminal(1, "producing new state"); + kinode_process_lib::print_to_terminal(1, "producing new state"); let mut state = State { contract_address, last_saved_block: crate::CONTRACT_FIRST_BLOCK, @@ -196,7 +240,7 @@ impl State { }; } 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(()) } @@ -258,7 +302,7 @@ impl State { } self.downloaded_packages .insert(package_id.to_owned(), package_state); - crate::set_state(&bincode::serialize(self)?); + kinode_process_lib::set_state(&serde_json::to_vec(self)?); Ok(()) } @@ -276,7 +320,7 @@ impl State { true }) .unwrap_or(false); - crate::set_state(&bincode::serialize(self).unwrap()); + kinode_process_lib::set_state(&serde_json::to_vec(self).unwrap()); res } @@ -416,7 +460,7 @@ impl State { // finally, remove from downloaded packages 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}"); Ok(()) @@ -447,7 +491,7 @@ impl State { let package_hash = package_hash.to_string(); let metadata_hash = metadata_hash.to_string(); - crate::print_to_terminal( + kinode_process_lib::print_to_terminal( 1, &format!( "app registered with package_name {}, metadata_url {}, metadata_hash {}", @@ -529,7 +573,10 @@ impl State { Some(metadata) } 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 } }; @@ -545,18 +592,22 @@ impl State { if let Some(package_state) = self.downloaded_packages.get(&package_id) { if package_state.auto_update { if let Some(mirrored_from) = &package_state.mirrored_from { - crate::print_to_terminal( + kinode_process_lib::print_to_terminal( 1, &format!("auto-updating package {package_id} from {mirrored_from}"), ); Request::to(our) - .body(serde_json::to_vec(&LocalRequest::Download { - package: package_id, - download_from: mirrored_from.clone(), - mirror: package_state.mirroring, - auto_update: package_state.auto_update, - desired_version_hash: None, - })?) + .body(serde_json::to_vec(&LocalRequest::Download( + DownloadRequest { + package_id: crate::kinode::process::main::PackageId::from_process_lib( + package_id, + ), + download_from: mirrored_from.clone(), + mirror: package_state.mirroring, + auto_update: package_state.auto_update, + desired_version_hash: None, + }, + ))?) .send()?; } } @@ -595,7 +646,7 @@ impl State { _ => {} } self.last_saved_block = block_number; - crate::set_state(&bincode::serialize(self)?); + kinode_process_lib::set_state(&serde_json::to_vec(self)?); Ok(()) } } diff --git a/kinode/packages/app_store/download/Cargo.toml b/kinode/packages/app_store/download/Cargo.toml index 18cabb2e..1f1656e1 100644 --- a/kinode/packages/app_store/download/Cargo.toml +++ b/kinode/packages/app_store/download/Cargo.toml @@ -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 = "9998921" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/kinode/packages/app_store/download/src/api.rs b/kinode/packages/app_store/download/src/api.rs deleted file mode 120000 index 67bd9b8f..00000000 --- a/kinode/packages/app_store/download/src/api.rs +++ /dev/null @@ -1 +0,0 @@ -../../app_store/src/api.rs \ No newline at end of file diff --git a/kinode/packages/app_store/download/src/lib.rs b/kinode/packages/app_store/download/src/lib.rs index 20278081..3adafe93 100644 --- a/kinode/packages/app_store/download/src/lib.rs +++ b/kinode/packages/app_store/download/src/lib.rs @@ -1,13 +1,14 @@ +use crate::kinode::process::main::{DownloadResponse, LocalRequest, LocalResponse}; +use kinode::process::main::DownloadRequest; use kinode_process_lib::{ await_next_message_body, call_init, println, Address, Message, NodeId, PackageId, Request, }; -mod api; -use api::*; - wit_bindgen::generate!({ 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); @@ -36,13 +37,16 @@ fn init(our: Address) { let Ok(Ok(Message::Response { body, .. })) = Request::to((our.node(), ("main", "app_store", "sys"))) .body( - serde_json::to_vec(&LocalRequest::Download { - package: package_id.clone(), + serde_json::to_vec(&LocalRequest::Download(DownloadRequest { + 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(), mirror: true, auto_update: true, desired_version_hash: None, - }) + })) .unwrap(), ) .send_and_await_response(5) @@ -60,7 +64,7 @@ fn init(our: Address) { LocalResponse::DownloadResponse(DownloadResponse::Started) => { 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}"); } _ => { diff --git a/kinode/packages/app_store/install/Cargo.toml b/kinode/packages/app_store/install/Cargo.toml index c7da2e2d..b1a5d5b6 100644 --- a/kinode/packages/app_store/install/Cargo.toml +++ b/kinode/packages/app_store/install/Cargo.toml @@ -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 = "9998921" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/kinode/packages/app_store/install/src/api.rs b/kinode/packages/app_store/install/src/api.rs deleted file mode 120000 index 67bd9b8f..00000000 --- a/kinode/packages/app_store/install/src/api.rs +++ /dev/null @@ -1 +0,0 @@ -../../app_store/src/api.rs \ No newline at end of file diff --git a/kinode/packages/app_store/install/src/lib.rs b/kinode/packages/app_store/install/src/lib.rs index a2b1f9e8..f000a349 100644 --- a/kinode/packages/app_store/install/src/lib.rs +++ b/kinode/packages/app_store/install/src/lib.rs @@ -1,13 +1,13 @@ +use crate::kinode::process::main::{InstallResponse, LocalRequest, LocalResponse}; use kinode_process_lib::{ await_next_message_body, call_init, println, Address, Message, PackageId, Request, }; -mod api; -use api::*; - wit_bindgen::generate!({ 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); @@ -33,7 +33,15 @@ fn init(our: Address) { let Ok(Ok(Message::Response { body, .. })) = 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) else { println!("install: failed to get a response from app_store..!"); diff --git a/kinode/packages/app_store/uninstall/Cargo.toml b/kinode/packages/app_store/uninstall/Cargo.toml index 992c5742..7505154f 100644 --- a/kinode/packages/app_store/uninstall/Cargo.toml +++ b/kinode/packages/app_store/uninstall/Cargo.toml @@ -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 = "9998921" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/kinode/packages/app_store/uninstall/src/api.rs b/kinode/packages/app_store/uninstall/src/api.rs deleted file mode 120000 index 67bd9b8f..00000000 --- a/kinode/packages/app_store/uninstall/src/api.rs +++ /dev/null @@ -1 +0,0 @@ -../../app_store/src/api.rs \ No newline at end of file diff --git a/kinode/packages/app_store/uninstall/src/lib.rs b/kinode/packages/app_store/uninstall/src/lib.rs index 24e06f70..f93b610e 100644 --- a/kinode/packages/app_store/uninstall/src/lib.rs +++ b/kinode/packages/app_store/uninstall/src/lib.rs @@ -1,13 +1,13 @@ +use crate::kinode::process::main::{LocalRequest, LocalResponse, UninstallResponse}; use kinode_process_lib::{ await_next_message_body, call_init, println, Address, Message, PackageId, Request, }; -mod api; -use api::*; - wit_bindgen::generate!({ 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); @@ -33,7 +33,15 @@ fn init(our: Address) { let Ok(Ok(Message::Response { body, .. })) = 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) else { println!("uninstall: failed to get a response from app_store..!");