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",
"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",

View File

@ -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<string>,

View File

@ -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"

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::{
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(),

View File

@ -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::<State>(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::<State>(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: &eth::Provider,
requested_apis: &mut HashMap<PackageId, RequestedPackage>,
requested_packages: &mut HashMap<PackageId, RequestedPackage>,
) -> (LocalResponse, Option<LazyLoadBlob>) {
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::<Resp>(&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::<Resp>(&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::<HashMap<_, _>>()
.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"

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 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<Self> {
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(())
}
}

View File

@ -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"

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::{
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}");
}
_ => {

View File

@ -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"

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::{
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..!");

View File

@ -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"

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::{
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..!");