From daae14c68406da81e2cc6a8b1fc75fe14a8cb5a7 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Mon, 9 Dec 2024 17:23:02 +0200 Subject: [PATCH 01/30] kns_indexer: add kv as state backend --- Cargo.lock | 68 ++-- .../kns_indexer/kns_indexer/Cargo.toml | 2 +- .../kns_indexer/kns_indexer/src/lib.rs | 319 +++++++++++++----- kinode/packages/kns_indexer/pkg/manifest.json | 6 +- 4 files changed, 268 insertions(+), 127 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86152e01..0e8c1d91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,7 +78,7 @@ name = "alias" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "serde", "serde_json", "wit-bindgen", @@ -1012,7 +1012,7 @@ dependencies = [ "alloy-sol-types", "anyhow", "bincode", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "process_macros", "rand 0.8.5", "serde", @@ -1383,7 +1383,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "serde", "serde_json", "url", @@ -1592,7 +1592,7 @@ name = "cat" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "serde", "serde_json", "wit-bindgen", @@ -1656,7 +1656,7 @@ dependencies = [ "alloy-sol-types", "anyhow", "bincode", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "process_macros", "rand 0.8.5", "serde", @@ -1675,7 +1675,7 @@ version = "0.2.1" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "pleco", "serde", "serde_json", @@ -1867,7 +1867,7 @@ checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" name = "contacts" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.9.4 (git+https://github.com/kinode-dao/process_lib?rev=088a549)", + "kinode_process_lib 0.9.4", "process_macros", "serde", "serde_json", @@ -2444,7 +2444,7 @@ name = "download" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "process_macros", "serde", "serde_json", @@ -2456,7 +2456,7 @@ name = "downloads" version = "0.5.0" dependencies = [ "anyhow", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "process_macros", "rand 0.8.5", "serde", @@ -2493,7 +2493,7 @@ dependencies = [ name = "echo" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "wit-bindgen", ] @@ -2732,7 +2732,7 @@ version = "0.2.0" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "process_macros", "rand 0.8.5", "serde", @@ -2886,7 +2886,7 @@ dependencies = [ name = "get_block" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "serde", "serde_json", "wit-bindgen", @@ -2951,7 +2951,7 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" name = "globe" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "serde", "serde_json", "url", @@ -3078,7 +3078,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" name = "help" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "wit-bindgen", ] @@ -3107,7 +3107,7 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" name = "hi" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "serde", "serde_json", "wit-bindgen", @@ -3140,7 +3140,7 @@ version = "0.1.2" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "serde", "serde_json", "wit-bindgen", @@ -3455,7 +3455,7 @@ name = "install" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "process_macros", "serde", "serde_json", @@ -3632,7 +3632,7 @@ name = "kfetch" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "rmp-serde", "serde", "serde_json", @@ -3644,7 +3644,7 @@ name = "kill" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "serde", "serde_json", "wit-bindgen", @@ -3740,8 +3740,7 @@ dependencies = [ [[package]] name = "kinode_process_lib" version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c257733fdc158b8223e43d92baeac02fe3d6a06b62953dbaea36e989f861b138" +source = "git+https://github.com/kinode-dao/process_lib?rev=088a549#088a5497257eada697e0869d6a8d7e9ef5e620f6" dependencies = [ "alloy 0.1.4", "alloy-primitives", @@ -3762,8 +3761,9 @@ dependencies = [ [[package]] name = "kinode_process_lib" -version = "0.9.4" -source = "git+https://github.com/kinode-dao/process_lib?rev=088a549#088a5497257eada697e0869d6a8d7e9ef5e620f6" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613dee198b9f19c72b55324ceb70a55da0e63c0b17ee5d528c6bfb6705267f7f" dependencies = [ "alloy 0.1.4", "alloy-primitives", @@ -3827,7 +3827,7 @@ dependencies = [ "alloy-sol-types", "anyhow", "hex", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "rmp-serde", "serde", "serde_json", @@ -4056,7 +4056,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "regex", "serde", "serde_json", @@ -4226,7 +4226,7 @@ dependencies = [ name = "net_diagnostics" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "rmp-serde", "serde", "wit-bindgen", @@ -4552,7 +4552,7 @@ dependencies = [ name = "peer" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "rmp-serde", "serde", "wit-bindgen", @@ -4562,7 +4562,7 @@ dependencies = [ name = "peers" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "rmp-serde", "serde", "wit-bindgen", @@ -5603,7 +5603,7 @@ dependencies = [ "anyhow", "base64 0.22.1", "bincode", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "rmp-serde", "serde", "serde_json", @@ -5821,7 +5821,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" name = "state" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "serde", "serde_json", "wit-bindgen", @@ -5998,7 +5998,7 @@ version = "0.1.1" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "rand 0.8.5", "regex", "serde", @@ -6012,7 +6012,7 @@ version = "0.1.1" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "process_macros", "serde", "serde_json", @@ -6269,7 +6269,7 @@ version = "0.2.0" dependencies = [ "anyhow", "clap", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "serde", "serde_json", "wit-bindgen", @@ -6600,7 +6600,7 @@ name = "uninstall" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kinode_process_lib 0.9.7", "process_macros", "serde", "serde_json", diff --git a/kinode/packages/kns_indexer/kns_indexer/Cargo.toml b/kinode/packages/kns_indexer/kns_indexer/Cargo.toml index 4a6b3cbe..84b4963f 100644 --- a/kinode/packages/kns_indexer/kns_indexer/Cargo.toml +++ b/kinode/packages/kns_indexer/kns_indexer/Cargo.toml @@ -11,7 +11,7 @@ anyhow = "1.0" alloy-primitives = "0.7.0" alloy-sol-types = "0.7.0" hex = "0.4.3" -kinode_process_lib = "0.9.4" +kinode_process_lib = "0.9.6" rmp-serde = "1.1.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/kinode/packages/kns_indexer/kns_indexer/src/lib.rs b/kinode/packages/kns_indexer/kns_indexer/src/lib.rs index 765e36e1..ae799758 100644 --- a/kinode/packages/kns_indexer/kns_indexer/src/lib.rs +++ b/kinode/packages/kns_indexer/kns_indexer/src/lib.rs @@ -4,13 +4,15 @@ use crate::kinode::process::kns_indexer::{ use alloy_primitives::keccak256; use alloy_sol_types::SolEvent; use kinode_process_lib::{ - await_message, call_init, eth, kimap, net, print_to_terminal, println, timer, Address, Message, - Request, Response, + await_message, call_init, eth, kimap, + kv::{self, Kv}, + net, print_to_terminal, println, timer, Address, Message, Request, Response, }; use serde::{Deserialize, Serialize}; use std::{ - collections::{hash_map::HashMap, BTreeMap}, + collections::BTreeMap, net::{IpAddr, Ipv4Addr, Ipv6Addr}, + str::FromStr, }; wit_bindgen::generate!({ @@ -41,15 +43,172 @@ const DELAY_MS: u64 = 1_000; // 1s #[derive(Clone, Debug, Serialize, Deserialize)] struct State { - chain_id: u64, - // what contract this state pertains to - contract_address: eth::Address, - // namehash to human readable name - names: HashMap, - // human readable name to most recent on-chain routing information as json - nodes: HashMap, + // TODO: maybe these shouldn't even be stored in-mem. + // version of the state in kv + version: u32, // last block we have an update from last_block: u64, + // kv handle + // includes keys and values for: + // "chain_id", "version", "last_block", "contract_address", + // "{namehash}" -> "{name}", "{name}" -> "{node_info}" + kv: Kv>, // todo: maybe serialize directly into known enum of possible types? +} + +impl State { + fn load(our: &Address) -> Self { + let kv: Kv> = match kv::open(our.package_id(), "kns_indexer", Some(10)) { + Ok(kv) => kv, + Err(e) => panic!("fatal: error opening kns_indexer key_value database: {e:?}"), + }; + + let mut state = Self { + kv, + version: 0, + last_block: 0, + }; + + // Load or initialize chain_id + let chain_id = state.get_chain_id(); + if chain_id == 0 { + state.set_chain_id(CHAIN_ID); + } + + // Load or initialize contract_address + let contract_address = state.get_contract_address(); + if contract_address + == eth::Address::from_str(KIMAP_ADDRESS) + .expect("Failed to parse KIMAP_ADDRESS constant") + { + state.set_contract_address(contract_address); + } + + // Load or initialize last_block + let last_block = state.get_last_block(); + if last_block == 0 { + state.set_last_block(KIMAP_FIRST_BLOCK); + } + + // Load or initialize version + let version = state.get_version(); + if version == 0 { + state.set_version(1); // Start at version 1 + } + + // Update state struct with final values + state.version = state.get_version(); + state.last_block = state.get_last_block(); + + println!( + "kns_indexer: loaded state: version: {}, last_block: {}, chain_id: {}, kimap_address: {}", + state.version, + state.last_block, + state.get_chain_id(), + state.get_contract_address() + ); + + state + } + + fn get_last_block(&self) -> u64 { + self.kv + .get(&"last_block".to_string()) + .ok() + .and_then(|bytes| serde_json::from_slice(&bytes).ok()) + .unwrap_or(0) + } + + fn set_last_block(&mut self, block: u64) { + self.kv + .set( + &"last_block".to_string(), + &serde_json::to_vec(&block).unwrap(), + None, + ) + .unwrap(); + } + + fn get_version(&self) -> u32 { + self.kv + .get(&"version".to_string()) + .ok() + .and_then(|bytes| serde_json::from_slice(&bytes).ok()) + .unwrap_or(0) + } + + fn set_version(&mut self, version: u32) { + self.kv + .set( + &"version".to_string(), + &serde_json::to_vec(&version).unwrap(), + None, + ) + .unwrap(); + } + + fn get_name(&self, namehash: &str) -> Option { + self.kv + .get(&namehash.to_string()) + .ok() + .and_then(|bytes| String::from_utf8(bytes).ok()) + } + + fn set_name(&mut self, namehash: &str, name: &str) { + self.kv + .set(&namehash.to_string(), &name.as_bytes().to_vec(), None) + .unwrap(); + } + + fn get_node(&self, name: &str) -> Option { + self.kv + .get(&name.to_string()) + .ok() + .and_then(|bytes| serde_json::from_slice(&bytes).ok()) + } + + fn set_node(&mut self, name: &str, node: &net::KnsUpdate) { + self.kv + .set(&name.to_string(), &serde_json::to_vec(&node).unwrap(), None) + .unwrap(); + } + + fn get_chain_id(&self) -> u64 { + self.kv + .get(&"chain_id".to_string()) + .ok() + .and_then(|bytes| serde_json::from_slice(&bytes).ok()) + .unwrap_or(CHAIN_ID) + } + + fn set_chain_id(&mut self, chain_id: u64) { + self.kv + .set( + &"chain_id".to_string(), + &serde_json::to_vec(&chain_id).unwrap(), + None, + ) + .unwrap(); + } + + fn get_contract_address(&self) -> eth::Address { + match self.kv.get(&"contract_address".to_string()) { + Ok(bytes) => match serde_json::from_slice(&bytes) { + Ok(addr) => addr, + Err(_) => eth::Address::from_str(KIMAP_ADDRESS) + .expect("Failed to parse KIMAP_ADDRESS constant"), + }, + Err(_) => eth::Address::from_str(KIMAP_ADDRESS) + .expect("Failed to parse KIMAP_ADDRESS constant"), + } + } + + fn set_contract_address(&mut self, contract_address: eth::Address) { + if let Ok(bytes) = serde_json::to_vec(&contract_address) { + self.kv + .set(&"contract_address".to_string(), &bytes, None) + .expect("Failed to set contract address"); + } + } } // note: not defined in wit api right now like IndexerRequests. @@ -70,18 +229,8 @@ call_init!(init); fn init(our: Address) { println!("indexing on contract address {KIMAP_ADDRESS}"); - // we **can** persist PKI state between boots but with current size, it's - // more robust just to reload the whole thing. the new contracts will allow - // us to quickly verify we have the updated mapping with root hash, but right - // now it's tricky to recover from missed events. - - let state = State { - chain_id: CHAIN_ID, - contract_address: KIMAP_ADDRESS.parse::().unwrap(), - nodes: HashMap::new(), - names: HashMap::new(), - last_block: KIMAP_FIRST_BLOCK, - }; + // state is loaded from kv, and updated with the current block number and version. + let state = State::load(&our); if let Err(e) = main(our, state) { println!("fatal error: {e}"); @@ -94,7 +243,8 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { // sub_id: 1 let mints_filter = eth::Filter::new() - .address(state.contract_address) + .address(state.get_contract_address()) + .from_block(state.get_last_block()) .to_block(eth::BlockNumberOrTag::Latest) .event("Mint(bytes32,bytes32,bytes,bytes)"); @@ -108,21 +258,23 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { // sub_id: 2 let notes_filter = eth::Filter::new() - .address(state.contract_address) + .address(state.get_contract_address()) + .from_block(state.get_last_block()) .to_block(eth::BlockNumberOrTag::Latest) .event("Note(bytes32,bytes32,bytes,bytes,bytes)") .topic3(notes); // 60s timeout -- these calls can take a long time // if they do time out, we try them again - let eth_provider: eth::Provider = eth::Provider::new(state.chain_id, SUBSCRIPTION_TIMEOUT); + let eth_provider: eth::Provider = + eth::Provider::new(state.get_chain_id(), SUBSCRIPTION_TIMEOUT); print_to_terminal( 1, &format!( "subscribing, state.block: {}, chain_id: {}", - state.last_block - 1, - state.chain_id + state.get_last_block() - 1, + state.get_chain_id() ), ); @@ -197,7 +349,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { // sending a response to the proper place. Response::new() .body(serde_json::to_vec(&IndexerResponses::Name( - state.names.get(hash).cloned(), + state.get_name(hash), ))?) .send()?; } @@ -205,10 +357,12 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { IndexerRequests::NodeInfo(NodeInfoRequest { ref name, .. }) => { Response::new() .body(serde_json::to_vec(&IndexerResponses::NodeInfo( - state.nodes.get(name).cloned(), + state.get_node(name), ))?) .send()?; } + // note no longer relevant. + // TODO: redo with iterator once available. IndexerRequests::GetState(GetStateRequest { .. }) => { Response::new().body(serde_json::to_vec(&state)?).send()?; } @@ -322,68 +476,53 @@ fn handle_note(state: &mut State, note: &kimap::contract::Note) -> anyhow::Resul return Err(anyhow::anyhow!("skipping invalid note: {note_label}")); } - let Some(node_name) = get_parent_name(&state.names, &node_hash) else { + let Some(node_name) = get_parent_name(&state, &node_hash) else { return Err(KnsError::NoParentError.into()); }; - match note_label.as_str() { - "~ws-port" => { - let ws = bytes_to_port(¬e.data)?; - if let Some(node) = state.nodes.get_mut(&node_name) { + if let Some(mut node) = state.get_node(&node_name) { + match note_label.as_str() { + "~ws-port" => { + let ws = bytes_to_port(¬e.data)?; node.ports.insert("ws".to_string(), ws); - // port defined, -> direct - node.routers = vec![]; + node.routers = vec![]; // port defined, -> direct } - } - "~tcp-port" => { - let tcp = bytes_to_port(¬e.data)?; - if let Some(node) = state.nodes.get_mut(&node_name) { + "~tcp-port" => { + let tcp = bytes_to_port(¬e.data)?; node.ports.insert("tcp".to_string(), tcp); - // port defined, -> direct - node.routers = vec![]; + node.routers = vec![]; // port defined, -> direct } - } - "~net-key" => { - if note.data.len() != 32 { - return Err(anyhow::anyhow!("invalid net-key length")); - } - if let Some(node) = state.nodes.get_mut(&node_name) { + "~net-key" => { + if note.data.len() != 32 { + return Err(anyhow::anyhow!("invalid net-key length")); + } node.public_key = hex::encode(¬e.data); } - } - "~routers" => { - let routers = decode_routers(¬e.data, state); - if let Some(node) = state.nodes.get_mut(&node_name) { + "~routers" => { + let routers = decode_routers(¬e.data, state); node.routers = routers; - // -> indirect - node.ports = BTreeMap::new(); + node.ports = BTreeMap::new(); // -> indirect node.ips = vec![]; } - } - "~ip" => { - let ip = bytes_to_ip(¬e.data)?; - if let Some(node) = state.nodes.get_mut(&node_name) { + "~ip" => { + let ip = bytes_to_ip(¬e.data)?; node.ips = vec![ip.to_string()]; - // -> direct - node.routers = vec![]; + node.routers = vec![]; // -> direct + } + _other => { + // Ignore unknown notes } } - _other => { - // Ignore unknown notes - } - } - // only send an update if we have a *full* set of data for networking: - // a node name, plus either or - if let Some(node_info) = state.nodes.get(&node_name) { - if !node_info.public_key.is_empty() - && ((!node_info.ips.is_empty() && !node_info.ports.is_empty()) - || node_info.routers.len() > 0) + // Update the node in the state + state.set_node(&node_name, &node); + + // Only send an update if we have a *full* set of data for networking + if !node.public_key.is_empty() + && ((!node.ips.is_empty() && !node.ports.is_empty()) || !node.routers.is_empty()) { Request::to(("our", "net", "distro", "sys")) - .body(rmp_serde::to_vec(&net::NetAction::KnsUpdate( - node_info.clone(), - ))?) + .body(rmp_serde::to_vec(&net::NetAction::KnsUpdate(node))?) .send()?; } } @@ -411,15 +550,15 @@ fn handle_log( return Err(anyhow::anyhow!("skipping invalid name: {name}")); } - let full_name = match get_parent_name(&state.names, &parent_hash) { + let full_name = match get_parent_name(&state, &parent_hash) { Some(parent_name) => format!("{name}.{parent_name}"), None => name, }; - state.names.insert(child_hash.clone(), full_name.clone()); - state.nodes.insert( - full_name.clone(), - net::KnsUpdate { + state.set_name(&child_hash.clone(), &full_name.clone()); + state.set_node( + &full_name.clone(), + &net::KnsUpdate { name: full_name.clone(), public_key: String::new(), ips: Vec::new(), @@ -486,13 +625,13 @@ fn fetch_and_process_logs( } } -fn get_parent_name(names: &HashMap, parent_hash: &str) -> Option { - let mut current_hash = parent_hash; +fn get_parent_name(state: &State, parent_hash: &str) -> Option { + let mut current_hash = parent_hash.to_string(); let mut components = Vec::new(); // Collect components in a vector let mut visited_hashes = std::collections::HashSet::new(); - while let Some(parent_name) = names.get(current_hash) { - if !visited_hashes.insert(current_hash) { + while let Some(parent_name) = state.get_name(¤t_hash) { + if !visited_hashes.insert(current_hash.clone()) { break; } @@ -501,7 +640,7 @@ fn get_parent_name(names: &HashMap, parent_hash: &str) -> Option } // Update current_hash to the parent's hash for the next iteration - if let Some(new_parent_hash) = names.get(parent_name) { + if let Some(new_parent_hash) = state.get_name(&parent_name) { current_hash = new_parent_hash; } else { break; @@ -521,13 +660,13 @@ fn get_parent_name(names: &HashMap, parent_hash: &str) -> Option #[cfg(feature = "simulation-mode")] fn add_temp_hardcoded_tlzs(state: &mut State) { // add some hardcoded top level zones - state.names.insert( - "0xdeeac81ae11b64e7cab86d089c306e5d223552a630f02633ce170d2786ff1bbd".to_string(), - "os".to_string(), + state.set_name( + &"0xdeeac81ae11b64e7cab86d089c306e5d223552a630f02633ce170d2786ff1bbd".to_string(), + &"os".to_string(), ); - state.names.insert( - "0x137d9e4cc0479164d40577620cb3b41b083c6e8dbf58f8523be76d207d6fd8ea".to_string(), - "dev".to_string(), + state.set_name( + &"0x137d9e4cc0479164d40577620cb3b41b083c6e8dbf58f8523be76d207d6fd8ea".to_string(), + &"dev".to_string(), ); } @@ -545,7 +684,7 @@ fn decode_routers(data: &[u8], state: &State) -> Vec { for chunk in data.chunks(32) { let hash_str = format!("0x{}", hex::encode(chunk)); - match state.names.get(&hash_str) { + match state.get_name(&hash_str) { Some(full_name) => routers.push(full_name.clone()), None => print_to_terminal( 1, diff --git a/kinode/packages/kns_indexer/pkg/manifest.json b/kinode/packages/kns_indexer/pkg/manifest.json index 86365326..e791e57a 100644 --- a/kinode/packages/kns_indexer/pkg/manifest.json +++ b/kinode/packages/kns_indexer/pkg/manifest.json @@ -8,12 +8,14 @@ "eth:distro:sys", "http_server:distro:sys", "net:distro:sys", - "timer:distro:sys" + "timer:distro:sys", + "kv:distro:sys" ], "grant_capabilities": [ "eth:distro:sys", "http_server:distro:sys", - "timer:distro:sys" + "timer:distro:sys", + "kv:distro:sys" ], "public": false } From f633b078b7c3bea69bd2730df06cf340a17baeb2 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Mon, 9 Dec 2024 17:30:12 +0200 Subject: [PATCH 02/30] kns: add meta keys --- kinode/packages/kns_indexer/Cargo.lock | 6 +- .../packages/kns_indexer/get_block/Cargo.toml | 2 +- .../kns_indexer/kns_indexer/src/lib.rs | 82 ++++++++++++------- kinode/packages/kns_indexer/state/Cargo.toml | 2 +- 4 files changed, 56 insertions(+), 36 deletions(-) diff --git a/kinode/packages/kns_indexer/Cargo.lock b/kinode/packages/kns_indexer/Cargo.lock index ac88fe79..721ea944 100644 --- a/kinode/packages/kns_indexer/Cargo.lock +++ b/kinode/packages/kns_indexer/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -1462,9 +1462,9 @@ dependencies = [ [[package]] name = "kinode_process_lib" -version = "0.9.4" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c257733fdc158b8223e43d92baeac02fe3d6a06b62953dbaea36e989f861b138" +checksum = "613dee198b9f19c72b55324ceb70a55da0e63c0b17ee5d528c6bfb6705267f7f" dependencies = [ "alloy", "alloy-primitives", diff --git a/kinode/packages/kns_indexer/get_block/Cargo.toml b/kinode/packages/kns_indexer/get_block/Cargo.toml index 351d48f6..33658dd1 100644 --- a/kinode/packages/kns_indexer/get_block/Cargo.toml +++ b/kinode/packages/kns_indexer/get_block/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" simulation-mode = [] [dependencies] -kinode_process_lib = "0.9.4" +kinode_process_lib = "0.9.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" diff --git a/kinode/packages/kns_indexer/kns_indexer/src/lib.rs b/kinode/packages/kns_indexer/kns_indexer/src/lib.rs index ae799758..78a6606c 100644 --- a/kinode/packages/kns_indexer/kns_indexer/src/lib.rs +++ b/kinode/packages/kns_indexer/kns_indexer/src/lib.rs @@ -43,15 +43,14 @@ const DELAY_MS: u64 = 1_000; // 1s #[derive(Clone, Debug, Serialize, Deserialize)] struct State { - // TODO: maybe these shouldn't even be stored in-mem. // version of the state in kv version: u32, // last block we have an update from last_block: u64, // kv handle // includes keys and values for: - // "chain_id", "version", "last_block", "contract_address", - // "{namehash}" -> "{name}", "{name}" -> "{node_info}" + // "meta:chain_id", "meta:version", "meta:last_block", "meta:contract_address", + // "names:{namehash}" -> "{name}", "nodes:{name}" -> "{node_info}" kv: Kv>, // todo: maybe serialize directly into known enum of possible types? } @@ -68,13 +67,13 @@ impl State { last_block: 0, }; - // Load or initialize chain_id + // load or initialize chain_id let chain_id = state.get_chain_id(); if chain_id == 0 { state.set_chain_id(CHAIN_ID); } - // Load or initialize contract_address + // load or initialize contract_address let contract_address = state.get_contract_address(); if contract_address == eth::Address::from_str(KIMAP_ADDRESS) @@ -83,19 +82,19 @@ impl State { state.set_contract_address(contract_address); } - // Load or initialize last_block + // load or initialize last_block let last_block = state.get_last_block(); if last_block == 0 { state.set_last_block(KIMAP_FIRST_BLOCK); } - // Load or initialize version + // load or initialize version let version = state.get_version(); if version == 0 { state.set_version(1); // Start at version 1 } - // Update state struct with final values + // update state struct with final values state.version = state.get_version(); state.last_block = state.get_last_block(); @@ -110,9 +109,30 @@ impl State { state } + fn meta_version_key() -> &'static str { + "meta:version" + } + fn meta_last_block_key() -> &'static str { + "meta:last_block" + } + fn meta_chain_id_key() -> &'static str { + "meta:chain_id" + } + fn meta_contract_address_key() -> &'static str { + "meta:contract_address" + } + + fn name_key(namehash: &str) -> String { + format!("names:{namehash}") + } + + fn node_key(name: &str) -> String { + format!("nodes:{name}") + } + fn get_last_block(&self) -> u64 { self.kv - .get(&"last_block".to_string()) + .get(&Self::meta_last_block_key().to_string()) .ok() .and_then(|bytes| serde_json::from_slice(&bytes).ok()) .unwrap_or(0) @@ -121,16 +141,17 @@ impl State { fn set_last_block(&mut self, block: u64) { self.kv .set( - &"last_block".to_string(), + &Self::meta_last_block_key().to_string(), &serde_json::to_vec(&block).unwrap(), None, ) .unwrap(); + self.last_block = block; } fn get_version(&self) -> u32 { self.kv - .get(&"version".to_string()) + .get(&Self::meta_version_key().to_string()) .ok() .and_then(|bytes| serde_json::from_slice(&bytes).ok()) .unwrap_or(0) @@ -139,42 +160,47 @@ impl State { fn set_version(&mut self, version: u32) { self.kv .set( - &"version".to_string(), + &Self::meta_version_key().to_string(), &serde_json::to_vec(&version).unwrap(), None, ) .unwrap(); + self.version = version; } fn get_name(&self, namehash: &str) -> Option { self.kv - .get(&namehash.to_string()) + .get(&Self::name_key(namehash)) .ok() .and_then(|bytes| String::from_utf8(bytes).ok()) } fn set_name(&mut self, namehash: &str, name: &str) { self.kv - .set(&namehash.to_string(), &name.as_bytes().to_vec(), None) + .set(&Self::name_key(namehash), &name.as_bytes().to_vec(), None) .unwrap(); } fn get_node(&self, name: &str) -> Option { self.kv - .get(&name.to_string()) + .get(&Self::node_key(name)) .ok() .and_then(|bytes| serde_json::from_slice(&bytes).ok()) } fn set_node(&mut self, name: &str, node: &net::KnsUpdate) { self.kv - .set(&name.to_string(), &serde_json::to_vec(&node).unwrap(), None) + .set( + &Self::node_key(name), + &serde_json::to_vec(&node).unwrap(), + None, + ) .unwrap(); } fn get_chain_id(&self) -> u64 { self.kv - .get(&"chain_id".to_string()) + .get(&Self::meta_chain_id_key().to_string()) .ok() .and_then(|bytes| serde_json::from_slice(&bytes).ok()) .unwrap_or(CHAIN_ID) @@ -183,7 +209,7 @@ impl State { fn set_chain_id(&mut self, chain_id: u64) { self.kv .set( - &"chain_id".to_string(), + &Self::meta_chain_id_key().to_string(), &serde_json::to_vec(&chain_id).unwrap(), None, ) @@ -191,7 +217,7 @@ impl State { } fn get_contract_address(&self) -> eth::Address { - match self.kv.get(&"contract_address".to_string()) { + match self.kv.get(&Self::meta_contract_address_key().to_string()) { Ok(bytes) => match serde_json::from_slice(&bytes) { Ok(addr) => addr, Err(_) => eth::Address::from_str(KIMAP_ADDRESS) @@ -205,7 +231,7 @@ impl State { fn set_contract_address(&mut self, contract_address: eth::Address) { if let Ok(bytes) = serde_json::to_vec(&contract_address) { self.kv - .set(&"contract_address".to_string(), &bytes, None) + .set(&Self::meta_contract_address_key().to_string(), &bytes, None) .expect("Failed to set contract address"); } } @@ -402,7 +428,7 @@ fn handle_eth_message( let block_number = eth_provider.get_block_number(); if let Ok(block_number) = block_number { print_to_terminal(2, &format!("new block: {}", block_number)); - state.last_block = block_number; + state.set_last_block(block_number); } } handle_pending_notes(state, pending_notes)?; @@ -440,15 +466,9 @@ fn handle_pending_notes( None => { print_to_terminal(1, &format!("pending note handling error: {e:?}")) } - Some(ee) => match ee { - KnsError::NoParentError => { - // print_to_terminal( - // 1, - // &format!("note still awaiting mint; attempt {attempt}"), - // ); - keep_notes.push((note, attempt + 1)); - } - }, + Some(KnsError::NoParentError) => { + keep_notes.push((note, attempt + 1)); + } } } } @@ -536,7 +556,7 @@ fn handle_log( log: ð::Log, ) -> anyhow::Result<()> { if let Some(block) = log.block_number { - state.last_block = block; + state.set_last_block(block); } match log.topics()[0] { diff --git a/kinode/packages/kns_indexer/state/Cargo.toml b/kinode/packages/kns_indexer/state/Cargo.toml index 3299252d..7308b80f 100644 --- a/kinode/packages/kns_indexer/state/Cargo.toml +++ b/kinode/packages/kns_indexer/state/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" simulation-mode = [] [dependencies] -kinode_process_lib = "0.9.4" +kinode_process_lib = "0.9.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.24.0" From 8a5ac51e7ba1ea0b9e92a3eae7f3cc5f6e761d55 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Tue, 10 Dec 2024 21:54:14 +0200 Subject: [PATCH 03/30] kns: block number hotfix --- .../kns_indexer/kns_indexer/src/lib.rs | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/kinode/packages/kns_indexer/kns_indexer/src/lib.rs b/kinode/packages/kns_indexer/kns_indexer/src/lib.rs index 78a6606c..74cc3226 100644 --- a/kinode/packages/kns_indexer/kns_indexer/src/lib.rs +++ b/kinode/packages/kns_indexer/kns_indexer/src/lib.rs @@ -176,26 +176,30 @@ impl State { } fn set_name(&mut self, namehash: &str, name: &str) { + println!("set_name({namehash}, {name})"); self.kv .set(&Self::name_key(namehash), &name.as_bytes().to_vec(), None) .unwrap(); } fn get_node(&self, name: &str) -> Option { - self.kv + let x = self.kv .get(&Self::node_key(name)) .ok() - .and_then(|bytes| serde_json::from_slice(&bytes).ok()) + .and_then(|bytes| serde_json::from_slice(&bytes).ok()); + println!("get_node({name}) -> {x:?}"); + x } fn set_node(&mut self, name: &str, node: &net::KnsUpdate) { - self.kv + let x = self.kv .set( &Self::node_key(name), &serde_json::to_vec(&node).unwrap(), None, - ) - .unwrap(); + ); + println!("set_node({name}, {:?})", node); + x.unwrap(); } fn get_chain_id(&self) -> u64 { @@ -258,6 +262,8 @@ fn init(our: Address) { // state is loaded from kv, and updated with the current block number and version. let state = State::load(&our); + println!("got last block: {}", state.get_last_block()); + if let Err(e) = main(our, state) { println!("fatal error: {e}"); } @@ -308,7 +314,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { println!("subscribing to new logs..."); eth_provider.subscribe_loop(1, mints_filter.clone()); eth_provider.subscribe_loop(2, notes_filter.clone()); - + println!("done subscribing to new logs."); // if subscription results come back in the wrong order, we store them here // until the right block is reached. @@ -626,7 +632,7 @@ fn fetch_and_process_logs( filter: eth::Filter, pending_notes: &mut BTreeMap>, ) { - let filter = filter.from_block(KIMAP_FIRST_BLOCK); + let filter = filter.from_block(state.last_block); loop { match eth_provider.get_logs(&filter) { Ok(logs) => { From 3259f279ea1f2a0342b53240d66739ad361014d4 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Tue, 10 Dec 2024 21:54:33 +0200 Subject: [PATCH 04/30] sqlite: fix faulty db_path --- kinode/src/sqlite.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/kinode/src/sqlite.rs b/kinode/src/sqlite.rs index ecc08be3..84e778b1 100644 --- a/kinode/src/sqlite.rs +++ b/kinode/src/sqlite.rs @@ -83,10 +83,14 @@ impl SqliteState { fs::create_dir_all(&db_path).await?; - let db_file_path = format!("{}.db", db); + let db_file_path = db_path.join(format!("{}.db", db)); let db_conn = Connection::open(db_file_path)?; - let _ = db_conn.execute("PRAGMA journal_mode=WAL", []); + let _: String = db_conn.query_row( + "PRAGMA journal_mode=WAL", + [], + |row| row.get(0) + )?; self.open_dbs.insert(key, Mutex::new(db_conn)); From 9fddb2cedd9796ebb87ca34770fed5cae4c0e952 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Tue, 10 Dec 2024 21:54:52 +0200 Subject: [PATCH 05/30] app_store: sqlite storage backend --- kinode/packages/app_store/chain/src/lib.rs | 616 ++++++++++++++------ kinode/packages/app_store/pkg/manifest.json | 2 + 2 files changed, 433 insertions(+), 185 deletions(-) diff --git a/kinode/packages/app_store/chain/src/lib.rs b/kinode/packages/app_store/chain/src/lib.rs index 66db5995..44c29e9b 100644 --- a/kinode/packages/app_store/chain/src/lib.rs +++ b/kinode/packages/app_store/chain/src/lib.rs @@ -33,14 +33,14 @@ use alloy_primitives::keccak256; use alloy_sol_types::SolEvent; use kinode::process::chain::ChainResponses; use kinode_process_lib::{ - await_message, call_init, eth, get_blob, get_state, http, kernel_types as kt, kimap, + await_message, call_init, eth, http, kimap, get_blob, print_to_terminal, println, timer, Address, Message, PackageId, Request, Response, + sqlite::{self, Sqlite}, + kernel_types as kt, }; use serde::{Deserialize, Serialize}; -use std::{ - collections::{HashMap, HashSet}, - str::FromStr, -}; +use std::str::FromStr; +use std::collections::HashMap; wit_bindgen::generate!({ path: "target/wit", @@ -63,7 +63,6 @@ const KIMAP_ADDRESS: &str = "0x9CE8cCD2932DC727c70f9ae4f8C2b68E6Abed58C"; const DELAY_MS: u64 = 1_000; // 1s -#[derive(Debug, Serialize, Deserialize)] pub struct State { /// the kimap helper we are using pub kimap: kimap::Kimap, @@ -71,10 +70,8 @@ pub struct State { /// when we boot, we can read logs starting from this block and /// rebuild latest state. pub last_saved_block: u64, - /// onchain listings - pub listings: HashMap, - /// set of packages that we have published - pub published: HashSet, + /// tables: listings: , published: vec + pub db: DB, } /// listing information derived from metadata hash in listing event @@ -83,10 +80,9 @@ pub struct PackageListing { pub tba: eth::Address, pub metadata_uri: String, pub metadata_hash: String, - // should this even be optional? - // relegate to only valid apps maybe? pub metadata: Option, pub auto_update: bool, + pub block: u64 } #[derive(Debug, Serialize, Deserialize, process_macros::SerdeJsonInto)] @@ -96,18 +92,270 @@ pub enum Req { Request(ChainRequests), } +pub struct DB { + inner: Sqlite, +} + +impl DB { + pub fn connect(our: &Address) -> anyhow::Result { + let inner = sqlite::open(our.package_id(), "app_store_chain.sqlite", Some(10))?; + // create tables + inner.write(CREATE_META_TABLE.into(), vec![], None)?; + inner.write(CREATE_LISTINGS_TABLE.into(), vec![], None)?; + inner.write(CREATE_PUBLISHED_TABLE.into(), vec![], None)?; + + Ok(Self { inner }) + } + + pub fn get_last_saved_block(&self) -> anyhow::Result { + let query = "SELECT value FROM meta WHERE key = 'last_saved_block'"; + let rows = self.inner.read(query.into(), vec![])?; + if let Some(row) = rows.get(0) { + if let Some(val_str) = row.get("value").and_then(|v| v.as_str()) { + if let Ok(block) = val_str.parse::() { + return Ok(block); + } + } + } + Ok(0) + } + + pub fn set_last_saved_block(&self, block: u64) -> anyhow::Result<()> { + let query = "INSERT INTO meta (key, value) VALUES ('last_saved_block', ?) + ON CONFLICT(key) DO UPDATE SET value=excluded.value"; + let params = vec![block.to_string().into()]; + self.inner.write(query.into(), params, None)?; + Ok(()) + } + + pub fn insert_or_update_listing(&self, package_id: &PackageId, listing: &PackageListing) -> anyhow::Result<()> { + let metadata_json = if let Some(m) = &listing.metadata { + serde_json::to_string(m)? + } else { + "".to_string() + }; + + let query = "INSERT INTO listings (package_name, publisher_node, tba, metadata_uri, metadata_hash, metadata_json, auto_update, block) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(package_name, publisher_node) + DO UPDATE SET + tba=excluded.tba, + metadata_uri=excluded.metadata_uri, + metadata_hash=excluded.metadata_hash, + metadata_json=excluded.metadata_json, + auto_update=excluded.auto_update, + block=excluded.block"; + let params = vec![ + package_id.package_name.clone().into(), + package_id.publisher_node.clone().into(), + listing.tba.to_string().into(), + listing.metadata_uri.clone().into(), + listing.metadata_hash.clone().into(), + metadata_json.into(), + (if listing.auto_update {1} else {0}).into(), + listing.block.into() + ]; + + self.inner.write(query.into(), params, None)?; + Ok(()) + } + + + pub fn delete_listing(&self, package_id: &PackageId) -> anyhow::Result<()> { + let query = "DELETE FROM listings WHERE package_name = ? AND publisher_node = ?"; + let params = vec![ + package_id.package_name.clone().into(), + package_id.publisher_node.clone().into(), + ]; + self.inner.write(query.into(), params, None)?; + Ok(()) + } + + pub fn get_listing(&self, package_id: &PackageId) -> anyhow::Result> { + let query = "SELECT tba, metadata_uri, metadata_hash, metadata_json, auto_update, block FROM listings WHERE package_name = ? AND publisher_node = ?"; + let params = vec![ + package_id.package_name.clone().into(), + package_id.publisher_node.clone().into(), + ]; + let rows = self.inner.read(query.into(), params)?; + if let Some(row) = rows.get(0) { + Ok(Some(self.row_to_listing(row)?)) + } else { + Ok(None) + } + } + + pub fn get_all_listings(&self) -> anyhow::Result> { + let query = "SELECT package_name, publisher_node, tba, metadata_uri, metadata_hash, metadata_json, auto_update, block FROM listings"; + let rows = self.inner.read(query.into(), vec![])?; + let mut listings = Vec::new(); + for row in rows { + let pid = PackageId { + package_name: row["package_name"].as_str().unwrap_or("").to_string(), + publisher_node: row["publisher_node"].as_str().unwrap_or("").to_string(), + }; + let listing = self.row_to_listing(&row)?; + listings.push((pid, listing)); + } + Ok(listings) + } + + pub fn get_listings_batch(&self, limit: u64, offset: u64) -> anyhow::Result> { + let query = format!( + "SELECT package_name, publisher_node, tba, metadata_uri, metadata_hash, metadata_json, auto_update, block + FROM listings + ORDER BY package_name, publisher_node + LIMIT {} OFFSET {}", + limit, offset + ); + + let rows = self.inner.read(query, vec![])?; + let mut listings = Vec::new(); + for row in rows { + let pid = PackageId { + package_name: row["package_name"].as_str().unwrap_or("").to_string(), + publisher_node: row["publisher_node"].as_str().unwrap_or("").to_string(), + }; + let listing = self.row_to_listing(&row)?; + listings.push((pid, listing)); + } + Ok(listings) + } + + pub fn get_listings_since_block(&self, block_number: u64) -> anyhow::Result> { + let query = "SELECT package_name, publisher_node, tba, metadata_uri, metadata_hash, metadata_json, auto_update, block + FROM listings + WHERE block > ?"; + let params = vec![block_number.into()]; + let rows = self.inner.read(query.into(), params)?; + let mut listings = Vec::new(); + for row in rows { + let pid = PackageId { + package_name: row["package_name"].as_str().unwrap_or("").to_string(), + publisher_node: row["publisher_node"].as_str().unwrap_or("").to_string(), + }; + let listing = self.row_to_listing(&row)?; + listings.push((pid, listing)); + } + Ok(listings) + } + + + pub fn row_to_listing(&self, row: &HashMap) -> anyhow::Result { + let tba_str = row["tba"].as_str().ok_or_else(|| anyhow::anyhow!("Invalid tba"))?; + let tba = tba_str.parse::()?; + let metadata_uri = row["metadata_uri"].as_str().unwrap_or("").to_string(); + let metadata_hash = row["metadata_hash"].as_str().unwrap_or("").to_string(); + let metadata_json = row["metadata_json"].as_str().unwrap_or(""); + let metadata: Option = + if metadata_json.is_empty() { + None + } else { + serde_json::from_str(metadata_json)? + }; + let auto_update = row["auto_update"].as_i64().unwrap_or(0) == 1; + let block = row["block"].as_i64().unwrap_or(0) as u64; + + Ok(PackageListing { + tba, + metadata_uri, + metadata_hash, + metadata, + auto_update, + block, + }) + } + + pub fn get_published(&self, package_id: &PackageId) -> anyhow::Result { + let query = "SELECT 1 FROM published WHERE package_name = ? AND publisher_node = ?"; + let params = vec![ + package_id.package_name.clone().into(), + package_id.publisher_node.clone().into(), + ]; + let rows = self.inner.read(query.into(), params)?; + Ok(!rows.is_empty()) + } + + pub fn insert_published(&self, package_id: &PackageId) -> anyhow::Result<()> { + let query = "INSERT INTO published (package_name, publisher_node) VALUES (?, ?) ON CONFLICT DO NOTHING"; + let params = vec![ + package_id.package_name.clone().into(), + package_id.publisher_node.clone().into(), + ]; + self.inner.write(query.into(), params, None)?; + Ok(()) + } + + pub fn delete_published(&self, package_id: &PackageId) -> anyhow::Result<()> { + let query = "DELETE FROM published WHERE package_name = ? AND publisher_node = ?"; + let params = vec![ + package_id.package_name.clone().into(), + package_id.publisher_node.clone().into(), + ]; + self.inner.write(query.into(), params, None)?; + Ok(()) + } + + pub fn get_all_published(&self) -> anyhow::Result> { + let query = "SELECT package_name, publisher_node FROM published"; + let rows = self.inner.read(query.into(), vec![])?; + let mut result = Vec::new(); + for row in rows { + let pid = PackageId { + package_name: row["package_name"].as_str().unwrap_or("").to_string(), + publisher_node: row["publisher_node"].as_str().unwrap_or("").to_string(), + }; + result.push(pid); + } + Ok(result) + } +} + +const CREATE_META_TABLE: &str = " +CREATE TABLE IF NOT EXISTS meta ( + key TEXT PRIMARY KEY, + value TEXT +);"; + +const CREATE_LISTINGS_TABLE: &str = " +CREATE TABLE IF NOT EXISTS listings ( + package_name TEXT NOT NULL, + publisher_node TEXT NOT NULL, + tba TEXT NOT NULL, + metadata_uri TEXT, + metadata_hash TEXT, + metadata_json TEXT, + auto_update INTEGER NOT NULL DEFAULT 0, + block INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (package_name, publisher_node) +);"; + +const CREATE_PUBLISHED_TABLE: &str = " +CREATE TABLE IF NOT EXISTS published ( + package_name TEXT NOT NULL, + publisher_node TEXT NOT NULL, + PRIMARY KEY (package_name, publisher_node) +);"; + call_init!(init); fn init(our: Address) { - println!( - "chain started, indexing on contract address {}", - KIMAP_ADDRESS - ); - // create new provider with request-timeout of 60s - // can change, log requests can take quite a long time. let eth_provider: eth::Provider = eth::Provider::new(CHAIN_ID, CHAIN_TIMEOUT); - let mut state = fetch_state(eth_provider); - fetch_and_subscribe_logs(&our, &mut state); + let db = DB::connect(&our).expect("failed to open DB"); + let kimap_helper = kimap::Kimap::new(eth_provider, eth::Address::from_str(KIMAP_ADDRESS).unwrap()); + let last_saved_block = db.get_last_saved_block().unwrap_or(0); + + println!( + "chain started, indexing on kimap address {} at block {}", + KIMAP_ADDRESS, last_saved_block + ); + let mut state = State { + kimap: kimap_helper, + last_saved_block, + db, + }; + + fetch_and_subscribe_logs(&our, &mut state, last_saved_block); loop { match await_message() { @@ -126,17 +374,15 @@ fn init(our: Address) { fn handle_message(our: &Address, state: &mut State, message: &Message) -> anyhow::Result<()> { if !message.is_request() { if message.is_local(&our) && message.source().process == "timer:distro:sys" { - // handling of ETH RPC subscriptions delayed by DELAY_MS - // to allow kns to have a chance to process block: handle now let Some(context) = message.context() else { - return Err(anyhow::anyhow!("foo")); + return Err(anyhow::anyhow!("No context in timer message")); }; let log = serde_json::from_slice(context)?; handle_eth_log(our, state, log, false)?; return Ok(()); } } else { - match message.body().try_into()? { + match serde_json::from_slice::(message.body())? { Req::Eth(eth_result) => { if !message.is_local(our) || message.source().process != "eth:distro:sys" { return Err(anyhow::anyhow!( @@ -147,16 +393,11 @@ fn handle_message(our: &Address, state: &mut State, message: &Message) -> anyhow if let Ok(eth::EthSub { result, .. }) = eth_result { if let eth::SubscriptionResult::Log(ref log) = result { - // delay handling of ETH RPC subscriptions by DELAY_MS - // to allow kns to have a chance to process block timer::set_timer(DELAY_MS, Some(serde_json::to_vec(log)?)); } } else { - // attempt to resubscribe - state - .kimap - .provider - .subscribe_loop(1, app_store_filter(state)); + // re-subscribe if error + state.kimap.provider.subscribe_loop(1, app_store_filter(state)); } } Req::Request(chains) => { @@ -171,48 +412,37 @@ fn handle_message(our: &Address, state: &mut State, message: &Message) -> anyhow fn handle_local_request(state: &mut State, req: ChainRequests) -> anyhow::Result<()> { match req { ChainRequests::GetApp(package_id) => { - let onchain_app = state - .listings - .get(&package_id.clone().to_process_lib()) - .map(|app| OnchainApp { - package_id: package_id, - tba: app.tba.to_string(), - metadata_uri: app.metadata_uri.clone(), - metadata_hash: app.metadata_hash.clone(), - metadata: app.metadata.as_ref().map(|m| m.clone().into()), - auto_update: app.auto_update, - }); + let pid = package_id.clone().to_process_lib(); + let listing = state.db.get_listing(&pid)?; + let onchain_app = listing.map(|app| app.to_onchain_app(&pid)); let response = ChainResponses::GetApp(onchain_app); Response::new().body(&response).send()?; } ChainRequests::GetApps => { - let apps: Vec = state - .listings - .iter() - .map(|(id, listing)| listing.to_onchain_app(id)) + let listings = state.db.get_all_listings()?; + let apps: Vec = listings + .into_iter() + .map(|(pid, listing)| listing.to_onchain_app(&pid)) .collect(); - let response = ChainResponses::GetApps(apps); Response::new().body(&response).send()?; } ChainRequests::GetOurApps => { - let apps: Vec = state - .published - .iter() - .filter_map(|id| { - state - .listings - .get(id) - .map(|listing| listing.to_onchain_app(id)) - }) - .collect(); - + let published_list = state.db.get_all_published()?; + let mut apps = Vec::new(); + for pid in published_list { + if let Some(listing) = state.db.get_listing(&pid)? { + apps.push(listing.to_onchain_app(&pid)); + } + } let response = ChainResponses::GetOurApps(apps); Response::new().body(&response).send()?; } ChainRequests::StartAutoUpdate(package_id) => { - if let Some(listing) = state.listings.get_mut(&package_id.to_process_lib()) { + let pid = package_id.to_process_lib(); + if let Some(mut listing) = state.db.get_listing(&pid)? { listing.auto_update = true; + state.db.insert_or_update_listing(&pid, &listing)?; let response = ChainResponses::AutoUpdateStarted; Response::new().body(&response).send()?; } else { @@ -221,8 +451,10 @@ fn handle_local_request(state: &mut State, req: ChainRequests) -> anyhow::Result } } ChainRequests::StopAutoUpdate(package_id) => { - if let Some(listing) = state.listings.get_mut(&package_id.to_process_lib()) { + let pid = package_id.to_process_lib(); + if let Some(mut listing) = state.db.get_listing(&pid)? { listing.auto_update = false; + state.db.insert_or_update_listing(&pid, &listing)?; let response = ChainResponses::AutoUpdateStopped; Response::new().body(&response).send()?; } else { @@ -256,7 +488,7 @@ fn handle_eth_log( if package.is_empty() || publisher.is_empty() { Err(anyhow::anyhow!("invalid publisher name")) } else { - Ok(PackageId::new(&package, &publisher)) + Ok(PackageId::new(package, publisher)) } })?; @@ -265,8 +497,8 @@ fn handle_eth_log( // at the URI. let metadata_uri = String::from_utf8_lossy(¬e.data).to_string(); - let is_our_package = &package_id.publisher() == &our.node(); - + let is_our_package = package_id.publisher() == our.node(); + println!("we got a log: {block_number} {package_id} {metadata_uri}"); let (tba, metadata_hash) = if !startup { // generate ~metadata-hash full-path let hash_note = format!("~metadata-hash.{}", note.parent_path); @@ -290,10 +522,12 @@ fn handle_eth_log( match data { None => { - // if ~metadata-uri is also empty, this is an unpublish action! + // unpublish if metadata_uri empty if metadata_uri.is_empty() { - state.published.remove(&package_id); - state.listings.remove(&package_id); + state.db.delete_published(&package_id)?; + state.db.delete_listing(&package_id)?; + state.last_saved_block = block_number; + state.db.set_last_saved_block(block_number)?; return Ok(()); } return Err(anyhow::anyhow!( @@ -307,7 +541,7 @@ fn handle_eth_log( }; if is_our_package { - state.published.insert(package_id.clone()); + state.db.insert_published(&package_id)?; } // if this is a startup event, we don't need to fetch metadata from the URI -- @@ -320,109 +554,143 @@ fn handle_eth_log( None }; - match state.listings.entry(package_id.clone()) { - std::collections::hash_map::Entry::Occupied(mut listing) => { - let listing = listing.get_mut(); - listing.metadata_uri = metadata_uri; - listing.tba = tba; - listing.metadata_hash = metadata_hash; - listing.metadata = metadata.clone(); - } - std::collections::hash_map::Entry::Vacant(listing) => { - listing.insert(PackageListing { - tba, - metadata_uri, - metadata_hash, - metadata: metadata.clone(), - auto_update: false, - }); - } + let mut listing = state + .db + .get_listing(&package_id)? + .unwrap_or(PackageListing { + tba, + metadata_uri: metadata_uri.clone(), + metadata_hash: metadata_hash.clone(), + metadata: metadata.clone(), + auto_update: false, + block: block_number, + }); + // update fields + listing.tba = tba; + listing.metadata_uri = metadata_uri; + listing.metadata_hash = metadata_hash; + listing.metadata = metadata.clone(); + + state.db.insert_or_update_listing(&package_id, &listing)?; + + if !startup && listing.auto_update { + println!("kicking off auto-update for: {}", package_id); + Request::to(("our", "downloads", "app_store", "sys")) + .body(&DownloadRequests::AutoUpdate(AutoUpdateRequest { + package_id: crate::kinode::process::main::PackageId::from_process_lib( + package_id.clone(), + ), + metadata: metadata.unwrap().into(), + })) + .send() + .unwrap(); } if !startup { - // if auto_update is enabled, send a message to downloads to kick off the update. - if let Some(listing) = state.listings.get(&package_id) { - if listing.auto_update { - print_to_terminal(0, &format!("kicking off auto-update for: {}", package_id)); - Request::to(("our", "downloads", "app_store", "sys")) - .body(&DownloadRequests::AutoUpdate(AutoUpdateRequest { - package_id: crate::kinode::process::main::PackageId::from_process_lib( - package_id, - ), - metadata: metadata.unwrap().into(), - })) - .send() - .unwrap(); - } - } + state.last_saved_block = block_number; + state.db.set_last_saved_block(block_number)?; } - state.last_saved_block = block_number; - Ok(()) } /// after startup, fetch metadata for all listings /// we do this as a separate step to not repeatedly fetch outdated metadata /// as we process logs. -fn update_all_metadata(state: &mut State) { - state.listings.retain(|package_id, listing| { - let (tba, metadata_hash) = { - // generate ~metadata-hash full-path - let hash_note = format!( - "~metadata-hash.{}.{}", - package_id.package(), - package_id.publisher() - ); +fn update_all_metadata(state: &mut State, last_saved_block: u64) { + let updated_listings = match state.db.get_listings_since_block(last_saved_block) { + Ok(listings) => listings, + Err(e) => { + print_to_terminal(1, &format!("error fetching updated listings since block {last_saved_block}: {e}")); + return; + } + }; - // owner can change which we don't track (yet?) so don't save, need to get when desired - let Ok((tba, _owner, data)) = (match state.kimap.get(&hash_note) { - Ok(gr) => Ok(gr), - Err(e) => match e { - eth::EthError::RpcError(_) => { - // retry on RpcError after DELAY_MS sleep - // sleep here rather than with, e.g., a message to - // `timer:distro:sys` so that events are processed in - // order of receipt - std::thread::sleep(std::time::Duration::from_millis(DELAY_MS)); - state.kimap.get(&hash_note) + for (pid, mut listing) in updated_listings { + let hash_note = format!("~metadata-hash.{}.{}", pid.package(), pid.publisher()); + let (tba, metadata_hash) = match state.kimap.get(&hash_note) { + Ok((t, _o, data)) => { + match data { + None => { + // If metadata_uri empty, unpublish + if listing.metadata_uri.is_empty() { + if let Err(e) = state.db.delete_published(&pid) { + print_to_terminal(1, &format!("error deleting published: {e}")); + } + } + if let Err(e) = state.db.delete_listing(&pid) { + print_to_terminal(1, &format!("error deleting listing: {e}")); + } + continue; } - _ => Err(e), - }, - }) else { - return false; - }; - - match data { - None => { - // if ~metadata-uri is also empty, this is an unpublish action! - if listing.metadata_uri.is_empty() { - state.published.remove(package_id); - } - return false; + Some(hash_note) => (t, String::from_utf8_lossy(&hash_note).to_string()), + } + } + Err(e) => { + // If RpcError, retry once after delay + if let eth::EthError::RpcError(_) = e { + std::thread::sleep(std::time::Duration::from_millis(DELAY_MS)); + match state.kimap.get(&hash_note) { + Ok((t, _o, data)) => { + if let Some(hash_note) = data { + (t, String::from_utf8_lossy(&hash_note).to_string()) + } else { + // no data again after retry + if listing.metadata_uri.is_empty() { + if let Err(e) = state.db.delete_published(&pid) { + print_to_terminal(1, &format!("error deleting published: {e}")); + } + } + if let Err(e) = state.db.delete_listing(&pid) { + print_to_terminal(1, &format!("error deleting listing: {e}")); + } + continue; + } + } + Err(e2) => { + print_to_terminal(1, &format!("error retrieving metadata-hash after retry: {e2:?}")); + continue; + } + } + } else { + print_to_terminal(1, &format!("error retrieving metadata-hash: {e:?} for {pid}")); + continue; } - Some(hash_note) => (tba, String::from_utf8_lossy(&hash_note).to_string()), } }; + + // Update listing fields listing.tba = tba; listing.metadata_hash = metadata_hash; - let metadata = - fetch_metadata_from_url(&listing.metadata_uri, &listing.metadata_hash, 30).ok(); + + let metadata = match fetch_metadata_from_url(&listing.metadata_uri, &listing.metadata_hash, 30) { + Ok(md) => Some(md), + Err(err) => { + print_to_terminal(1, &format!("error fetching metadata for {}: {err}", pid)); + None + } + }; listing.metadata = metadata.clone(); - if listing.auto_update { - print_to_terminal(0, &format!("kicking off auto-update for: {}", package_id)); - Request::to(("our", "downloads", "app_store", "sys")) - .body(&DownloadRequests::AutoUpdate(AutoUpdateRequest { - package_id: crate::kinode::process::main::PackageId::from_process_lib( - package_id.clone(), - ), - metadata: metadata.unwrap().into(), - })) - .send() - .unwrap(); + + if let Err(e) = state.db.insert_or_update_listing(&pid, &listing) { + print_to_terminal(1, &format!("error updating listing {}: {e}", pid)); } - true - }); + + if listing.auto_update { + if let Some(md) = metadata { + print_to_terminal(0, &format!("kicking off auto-update for: {}", pid)); + if let Err(e) = Request::to(("our", "downloads", "app_store", "sys")) + .body(&DownloadRequests::AutoUpdate(AutoUpdateRequest { + package_id: crate::kinode::process::main::PackageId::from_process_lib(pid.clone()), + metadata: md.into(), + })) + .send() + { + print_to_terminal(1, &format!("error sending auto-update request: {e}")); + } + } + } + } } /// create the filter used for app store getLogs and subscription. @@ -441,21 +709,25 @@ pub fn app_store_filter(state: &State) -> eth::Filter { } /// create a filter to fetch app store event logs from chain and subscribe to new events -pub fn fetch_and_subscribe_logs(our: &Address, state: &mut State) { +pub fn fetch_and_subscribe_logs(our: &Address, state: &mut State, last_saved_block: u64) { let filter = app_store_filter(state); // get past logs, subscribe to new ones. // subscribe first so we don't miss any logs - println!("subscribing..."); - state.kimap.provider.subscribe_loop(1, filter.clone()); - for log in fetch_logs( - &state.kimap.provider, - &filter.from_block(state.last_saved_block), - ) { + state.kimap.provider.subscribe_loop(1, filter.clone()); + println!("fetching old logs from block {last_saved_block}"); + for log in fetch_logs(&state.kimap.provider, &filter.from_block(last_saved_block)) { if let Err(e) = handle_eth_log(our, state, log, true) { print_to_terminal(1, &format!("error ingesting log: {e}")); }; } - update_all_metadata(state); + + update_all_metadata(state, last_saved_block); + // save updated last_saved_block + if let Ok(block_number) = state.kimap.provider.get_block_number() { + state.last_saved_block = block_number; + state.db.set_last_saved_block(block_number).unwrap(); + } + println!("up to date to block {}", state.last_saved_block); } /// fetch logs from the chain with a given filter @@ -504,32 +776,6 @@ pub fn keccak_256_hash(bytes: &[u8]) -> String { format!("0x{:x}", hasher.finalize()) } -/// fetch state from disk or create a new one if that fails -pub fn fetch_state(provider: eth::Provider) -> State { - if let Some(state_bytes) = get_state() { - match serde_json::from_slice::(&state_bytes) { - Ok(state) => { - if state.kimap.address().to_string() == KIMAP_ADDRESS { - return state; - } else { - println!( - "state contract address mismatch. rebuilding state! expected {}, got {}", - KIMAP_ADDRESS, - state.kimap.address().to_string() - ); - } - } - Err(e) => println!("failed to deserialize saved state, rebuilding: {e}"), - } - } - State { - kimap: kimap::Kimap::new(provider, eth::Address::from_str(KIMAP_ADDRESS).unwrap()), - last_saved_block: 0, - listings: HashMap::new(), - published: HashSet::new(), - } -} - // 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 @@ -584,4 +830,4 @@ impl From for OnchainMetadata { }, } } -} +} \ No newline at end of file diff --git a/kinode/packages/app_store/pkg/manifest.json b/kinode/packages/app_store/pkg/manifest.json index d409e265..b01c2aa1 100644 --- a/kinode/packages/app_store/pkg/manifest.json +++ b/kinode/packages/app_store/pkg/manifest.json @@ -39,6 +39,7 @@ "eth:distro:sys", "http_server:distro:sys", "http_client:distro:sys", + "sqlite:distro:sys", { "process": "vfs:distro:sys", "params": { @@ -52,6 +53,7 @@ "vfs:distro:sys", "http_client:distro:sys", "eth:distro:sys", + "sqlite:distro:sys", "timer:distro:sys" ], "public": false From cb320416d93eb8623c2b23e2f5614ffe3aea3291 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:55:22 +0000 Subject: [PATCH 06/30] Format Rust code using rustfmt --- kinode/packages/app_store/chain/src/lib.rs | 99 ++++++++++++------- .../kns_indexer/kns_indexer/src/lib.rs | 14 +-- kinode/src/sqlite.rs | 6 +- 3 files changed, 74 insertions(+), 45 deletions(-) diff --git a/kinode/packages/app_store/chain/src/lib.rs b/kinode/packages/app_store/chain/src/lib.rs index 44c29e9b..29bc1c48 100644 --- a/kinode/packages/app_store/chain/src/lib.rs +++ b/kinode/packages/app_store/chain/src/lib.rs @@ -33,14 +33,14 @@ use alloy_primitives::keccak256; use alloy_sol_types::SolEvent; use kinode::process::chain::ChainResponses; use kinode_process_lib::{ - await_message, call_init, eth, http, kimap, get_blob, - print_to_terminal, println, timer, Address, Message, PackageId, Request, Response, + await_message, call_init, eth, get_blob, http, kernel_types as kt, kimap, print_to_terminal, + println, sqlite::{self, Sqlite}, - kernel_types as kt, + timer, Address, Message, PackageId, Request, Response, }; use serde::{Deserialize, Serialize}; -use std::str::FromStr; use std::collections::HashMap; +use std::str::FromStr; wit_bindgen::generate!({ path: "target/wit", @@ -82,7 +82,7 @@ pub struct PackageListing { pub metadata_hash: String, pub metadata: Option, pub auto_update: bool, - pub block: u64 + pub block: u64, } #[derive(Debug, Serialize, Deserialize, process_macros::SerdeJsonInto)] @@ -128,13 +128,17 @@ impl DB { Ok(()) } - pub fn insert_or_update_listing(&self, package_id: &PackageId, listing: &PackageListing) -> anyhow::Result<()> { + pub fn insert_or_update_listing( + &self, + package_id: &PackageId, + listing: &PackageListing, + ) -> anyhow::Result<()> { let metadata_json = if let Some(m) = &listing.metadata { serde_json::to_string(m)? } else { "".to_string() }; - + let query = "INSERT INTO listings (package_name, publisher_node, tba, metadata_uri, metadata_hash, metadata_json, auto_update, block) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(package_name, publisher_node) @@ -152,14 +156,13 @@ impl DB { listing.metadata_uri.clone().into(), listing.metadata_hash.clone().into(), metadata_json.into(), - (if listing.auto_update {1} else {0}).into(), - listing.block.into() + (if listing.auto_update { 1 } else { 0 }).into(), + listing.block.into(), ]; self.inner.write(query.into(), params, None)?; Ok(()) } - pub fn delete_listing(&self, package_id: &PackageId) -> anyhow::Result<()> { let query = "DELETE FROM listings WHERE package_name = ? AND publisher_node = ?"; @@ -200,7 +203,11 @@ impl DB { Ok(listings) } - pub fn get_listings_batch(&self, limit: u64, offset: u64) -> anyhow::Result> { + pub fn get_listings_batch( + &self, + limit: u64, + offset: u64, + ) -> anyhow::Result> { let query = format!( "SELECT package_name, publisher_node, tba, metadata_uri, metadata_hash, metadata_json, auto_update, block FROM listings @@ -222,7 +229,10 @@ impl DB { Ok(listings) } - pub fn get_listings_since_block(&self, block_number: u64) -> anyhow::Result> { + pub fn get_listings_since_block( + &self, + block_number: u64, + ) -> anyhow::Result> { let query = "SELECT package_name, publisher_node, tba, metadata_uri, metadata_hash, metadata_json, auto_update, block FROM listings WHERE block > ?"; @@ -240,14 +250,18 @@ impl DB { Ok(listings) } - - pub fn row_to_listing(&self, row: &HashMap) -> anyhow::Result { - let tba_str = row["tba"].as_str().ok_or_else(|| anyhow::anyhow!("Invalid tba"))?; + pub fn row_to_listing( + &self, + row: &HashMap, + ) -> anyhow::Result { + let tba_str = row["tba"] + .as_str() + .ok_or_else(|| anyhow::anyhow!("Invalid tba"))?; let tba = tba_str.parse::()?; let metadata_uri = row["metadata_uri"].as_str().unwrap_or("").to_string(); let metadata_hash = row["metadata_hash"].as_str().unwrap_or("").to_string(); let metadata_json = row["metadata_json"].as_str().unwrap_or(""); - let metadata: Option = + let metadata: Option = if metadata_json.is_empty() { None } else { @@ -342,7 +356,8 @@ fn init(our: Address) { let eth_provider: eth::Provider = eth::Provider::new(CHAIN_ID, CHAIN_TIMEOUT); let db = DB::connect(&our).expect("failed to open DB"); - let kimap_helper = kimap::Kimap::new(eth_provider, eth::Address::from_str(KIMAP_ADDRESS).unwrap()); + let kimap_helper = + kimap::Kimap::new(eth_provider, eth::Address::from_str(KIMAP_ADDRESS).unwrap()); let last_saved_block = db.get_last_saved_block().unwrap_or(0); println!( @@ -397,7 +412,10 @@ fn handle_message(our: &Address, state: &mut State, message: &Message) -> anyhow } } else { // re-subscribe if error - state.kimap.provider.subscribe_loop(1, app_store_filter(state)); + state + .kimap + .provider + .subscribe_loop(1, app_store_filter(state)); } } Req::Request(chains) => { @@ -601,7 +619,10 @@ fn update_all_metadata(state: &mut State, last_saved_block: u64) { let updated_listings = match state.db.get_listings_since_block(last_saved_block) { Ok(listings) => listings, Err(e) => { - print_to_terminal(1, &format!("error fetching updated listings since block {last_saved_block}: {e}")); + print_to_terminal( + 1, + &format!("error fetching updated listings since block {last_saved_block}: {e}"), + ); return; } }; @@ -638,7 +659,10 @@ fn update_all_metadata(state: &mut State, last_saved_block: u64) { // no data again after retry if listing.metadata_uri.is_empty() { if let Err(e) = state.db.delete_published(&pid) { - print_to_terminal(1, &format!("error deleting published: {e}")); + print_to_terminal( + 1, + &format!("error deleting published: {e}"), + ); } } if let Err(e) = state.db.delete_listing(&pid) { @@ -648,12 +672,18 @@ fn update_all_metadata(state: &mut State, last_saved_block: u64) { } } Err(e2) => { - print_to_terminal(1, &format!("error retrieving metadata-hash after retry: {e2:?}")); + print_to_terminal( + 1, + &format!("error retrieving metadata-hash after retry: {e2:?}"), + ); continue; } } } else { - print_to_terminal(1, &format!("error retrieving metadata-hash: {e:?} for {pid}")); + print_to_terminal( + 1, + &format!("error retrieving metadata-hash: {e:?} for {pid}"), + ); continue; } } @@ -663,15 +693,16 @@ fn update_all_metadata(state: &mut State, last_saved_block: u64) { listing.tba = tba; listing.metadata_hash = metadata_hash; - let metadata = match fetch_metadata_from_url(&listing.metadata_uri, &listing.metadata_hash, 30) { - Ok(md) => Some(md), - Err(err) => { - print_to_terminal(1, &format!("error fetching metadata for {}: {err}", pid)); - None - } - }; + let metadata = + match fetch_metadata_from_url(&listing.metadata_uri, &listing.metadata_hash, 30) { + Ok(md) => Some(md), + Err(err) => { + print_to_terminal(1, &format!("error fetching metadata for {}: {err}", pid)); + None + } + }; listing.metadata = metadata.clone(); - + if let Err(e) = state.db.insert_or_update_listing(&pid, &listing) { print_to_terminal(1, &format!("error updating listing {}: {e}", pid)); } @@ -681,7 +712,9 @@ fn update_all_metadata(state: &mut State, last_saved_block: u64) { print_to_terminal(0, &format!("kicking off auto-update for: {}", pid)); if let Err(e) = Request::to(("our", "downloads", "app_store", "sys")) .body(&DownloadRequests::AutoUpdate(AutoUpdateRequest { - package_id: crate::kinode::process::main::PackageId::from_process_lib(pid.clone()), + package_id: crate::kinode::process::main::PackageId::from_process_lib( + pid.clone(), + ), metadata: md.into(), })) .send() @@ -713,7 +746,7 @@ pub fn fetch_and_subscribe_logs(our: &Address, state: &mut State, last_saved_blo let filter = app_store_filter(state); // get past logs, subscribe to new ones. // subscribe first so we don't miss any logs - state.kimap.provider.subscribe_loop(1, filter.clone()); + state.kimap.provider.subscribe_loop(1, filter.clone()); println!("fetching old logs from block {last_saved_block}"); for log in fetch_logs(&state.kimap.provider, &filter.from_block(last_saved_block)) { if let Err(e) = handle_eth_log(our, state, log, true) { @@ -830,4 +863,4 @@ impl From for OnchainMetadata { }, } } -} \ No newline at end of file +} diff --git a/kinode/packages/kns_indexer/kns_indexer/src/lib.rs b/kinode/packages/kns_indexer/kns_indexer/src/lib.rs index 74cc3226..01dcf526 100644 --- a/kinode/packages/kns_indexer/kns_indexer/src/lib.rs +++ b/kinode/packages/kns_indexer/kns_indexer/src/lib.rs @@ -183,7 +183,8 @@ impl State { } fn get_node(&self, name: &str) -> Option { - let x = self.kv + let x = self + .kv .get(&Self::node_key(name)) .ok() .and_then(|bytes| serde_json::from_slice(&bytes).ok()); @@ -192,12 +193,11 @@ impl State { } fn set_node(&mut self, name: &str, node: &net::KnsUpdate) { - let x = self.kv - .set( - &Self::node_key(name), - &serde_json::to_vec(&node).unwrap(), - None, - ); + let x = self.kv.set( + &Self::node_key(name), + &serde_json::to_vec(&node).unwrap(), + None, + ); println!("set_node({name}, {:?})", node); x.unwrap(); } diff --git a/kinode/src/sqlite.rs b/kinode/src/sqlite.rs index 84e778b1..d8646be7 100644 --- a/kinode/src/sqlite.rs +++ b/kinode/src/sqlite.rs @@ -86,11 +86,7 @@ impl SqliteState { let db_file_path = db_path.join(format!("{}.db", db)); let db_conn = Connection::open(db_file_path)?; - let _: String = db_conn.query_row( - "PRAGMA journal_mode=WAL", - [], - |row| row.get(0) - )?; + let _: String = db_conn.query_row("PRAGMA journal_mode=WAL", [], |row| row.get(0))?; self.open_dbs.insert(key, Mutex::new(db_conn)); From cdd04f12f9c91d80571d4b8396c18a517b861898 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Wed, 11 Dec 2024 16:15:09 +0200 Subject: [PATCH 07/30] app_store UI: fix false missing local downloads message on publish --- .../app_store/ui/src/pages/PublishPage.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/kinode/packages/app_store/ui/src/pages/PublishPage.tsx b/kinode/packages/app_store/ui/src/pages/PublishPage.tsx index e9c16c44..285aa537 100644 --- a/kinode/packages/app_store/ui/src/pages/PublishPage.tsx +++ b/kinode/packages/app_store/ui/src/pages/PublishPage.tsx @@ -12,7 +12,7 @@ const NAME_INVALID = "Package name must contain only valid characters (a-z, 0-9, export default function PublishPage() { const { openConnectModal } = useConnectModal(); - const { ourApps, fetchOurApps, downloads } = useAppsStore(); + const { ourApps, fetchOurApps, downloads, fetchDownloadsForApp } = useAppsStore(); const publicClient = usePublicClient(); const { address, isConnected, isConnecting } = useAccount(); @@ -34,6 +34,13 @@ export default function PublishPage() { fetchOurApps(); }, [fetchOurApps]); + useEffect(() => { + if (packageName && publisherId) { + const id = `${packageName}:${publisherId}`; + fetchDownloadsForApp(id); + } + }, [packageName, publisherId, fetchDownloadsForApp]); + const validatePackageName = useCallback((name: string) => { // Allow lowercase letters, numbers, hyphens, and dots const validNameRegex = /^[a-z0-9.-]+$/; @@ -69,9 +76,12 @@ export default function PublishPage() { // Check if code_hashes exist in metadata and is an object if (metadata.properties && metadata.properties.code_hashes && typeof metadata.properties.code_hashes === 'object') { const codeHashes = metadata.properties.code_hashes; - const missingHashes = Object.entries(codeHashes).filter(([version, hash]) => - !downloads[`${packageName}:${publisherId}`]?.some(d => d.File?.name === `${hash}.zip`) - ); + console.log('Available downloads:', downloads[`${packageName}:${publisherId}`]); + + const missingHashes = Object.entries(codeHashes).filter(([version, hash]) => { + const hasDownload = downloads[`${packageName}:${publisherId}`]?.some(d => d.File?.name === `${hash}.zip`); + return !hasDownload; + }); if (missingHashes.length > 0) { setMetadataError(`Missing local downloads for mirroring versions: ${missingHashes.map(([version]) => version).join(', ')}`); From 825e04ea2ba0ec9011374ad78a5f17f28a66bd53 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Wed, 11 Dec 2024 16:35:36 +0200 Subject: [PATCH 08/30] app_store UI: reload our_apps after publishing a new one --- .../app_store/ui/src/pages/PublishPage.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/kinode/packages/app_store/ui/src/pages/PublishPage.tsx b/kinode/packages/app_store/ui/src/pages/PublishPage.tsx index 285aa537..91213d3e 100644 --- a/kinode/packages/app_store/ui/src/pages/PublishPage.tsx +++ b/kinode/packages/app_store/ui/src/pages/PublishPage.tsx @@ -41,6 +41,18 @@ export default function PublishPage() { } }, [packageName, publisherId, fetchDownloadsForApp]); + useEffect(() => { + if (isConfirmed) { + // Fetch our apps again after successful publish + fetchOurApps(); + // Reset form fields + setPackageName(""); + setPublisherId(window.our?.node || ""); + setMetadataUrl(""); + setMetadataHash(""); + } + }, [isConfirmed, fetchOurApps]); + const validatePackageName = useCallback((name: string) => { // Allow lowercase letters, numbers, hyphens, and dots const validNameRegex = /^[a-z0-9.-]+$/; @@ -173,12 +185,6 @@ export default function PublishPage() { gas: BigInt(1000000), }); - // Reset form fields - setPackageName(""); - setPublisherId(window.our?.node || ""); - setMetadataUrl(""); - setMetadataHash(""); - } catch (error) { console.error(error); } From b532d15fbd96a91457a3a7a8e89f6f4812b691b1 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Thu, 12 Dec 2024 17:10:59 +0200 Subject: [PATCH 09/30] app_store UI: fixes --- kinode/packages/app_store/ui/src/index.css | 294 +++++++++++++++--- .../app_store/ui/src/pages/AppPage.tsx | 6 + .../app_store/ui/src/pages/PublishPage.tsx | 36 ++- .../app_store/ui/src/pages/StorePage.tsx | 16 +- 4 files changed, 288 insertions(+), 64 deletions(-) diff --git a/kinode/packages/app_store/ui/src/index.css b/kinode/packages/app_store/ui/src/index.css index e83e6591..53b8ecfc 100644 --- a/kinode/packages/app_store/ui/src/index.css +++ b/kinode/packages/app_store/ui/src/index.css @@ -1,9 +1,36 @@ +:root { + /* Core colors */ + --orange: #ff7e33; + --dark-orange: #e56a24; + --red: #e53e3e; + --blue: #4299e1; + --green: #48bb78; + --gray: #718096; + + /* Sophisticated neutrals */ + --bg-light: #fdf6e3; + /* Solarized inspired beige */ + --bg-dark: #1f1d24; + /* Deep slate with hint of purple */ + --surface-light: #f5efd9; + /* Slightly deeper complementary beige */ + --surface-dark: #2a2832; + /* Rich eggplant-tinged dark */ + --text-light: #2d2a2e; + /* Warm charcoal */ + --text-dark: #e8e6f0; + /* Cool moonlight white */ + + /* Border radius */ + --border-radius: 8px; +} + /* Base styles */ body { font-family: var(--font-family-main); line-height: 1.6; - color: light-dark(var(--off-black), var(--off-white)); - background-color: light-dark(var(--tan), var(--tasteful-dark)); + color: light-dark(var(--text-light), var(--text-dark)); + background-color: light-dark(var(--bg-light), var(--bg-dark)); } /* Layout */ @@ -35,7 +62,7 @@ a:hover { /* Header */ .app-header { - background-color: light-dark(var(--off-white), var(--off-black)); + background-color: light-dark(var(--surface-light), var(--surface-dark)); padding: 1rem; margin-bottom: 1rem; display: flex; @@ -76,7 +103,7 @@ a:hover { .header-left nav a:hover, .header-left nav a.active { background-color: var(--orange); - color: var(--white); + color: var(--text-light); } /* Forms */ @@ -91,6 +118,9 @@ form { display: flex; flex-direction: column; margin-bottom: 1rem; + background: light-dark(var(--surface-light), var(--surface-dark)); + padding: 0.75rem; + border-radius: var(--border-radius); } label { @@ -102,15 +132,21 @@ select { padding: 0.5rem; border: 1px solid var(--gray); border-radius: var(--border-radius); - background-color: light-dark(var(--white), var(--tasteful-dark)); - color: light-dark(var(--off-black), var(--off-white)); + background-color: light-dark(var(--surface-light), var(--surface-dark)); + color: light-dark(var(--text-light), var(--text-dark)); } /* Buttons */ button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + height: 40px; + font-weight: 500; padding: 0.5rem 1rem; background-color: var(--orange); - color: var(--white); + color: var(--text-light); border: none; border-radius: var(--border-radius); cursor: pointer; @@ -125,6 +161,14 @@ button:disabled { cursor: not-allowed; } +button.danger { + background-color: var(--red); +} + +button.danger:hover { + background-color: color-mix(in srgb, var(--red) 85%, black); +} + /* Tables */ table { width: 100%; @@ -151,6 +195,9 @@ td { /* Messages */ .message { + display: flex; + align-items: center; + font-weight: 500; padding: 1rem; border-radius: var(--border-radius); margin-bottom: 1rem; @@ -158,17 +205,18 @@ td { .message.error { background-color: var(--red); - color: var(--white); + color: var(--text-light); } .message.success { - background-color: var(--green); - color: var(--white); + background: light-dark(var(--surface-light), var(--surface-dark)); + color: light-dark(var(--text-light), var(--text-dark)); + border: 1px solid var(--green); } .message.info { background-color: var(--blue); - color: var(--white); + color: var(--text-light); } /* Publisher Info */ @@ -242,17 +290,23 @@ td { align-items: center; gap: 0.5rem; color: var(--red); + margin-top: 0.5rem; font-size: 0.9rem; - margin-top: 0.25rem; } -/* App Page and Download Page shared styles */ +/* Shared page styles */ +.store-page, .app-page, -.downloads-page { - background-color: light-dark(var(--white), var(--maroon)); - border-radius: var(--border-radius); - padding: 2rem; - width: 100%; +.my-apps-page, +.downloads-page, +.publish-page { + padding: 1rem; + background: light-dark(var(--bg-light), var(--bg-dark)); + margin: 0 1vw; +} + +.app-info { + max-width: 20rem; } .app-header { @@ -268,12 +322,26 @@ td { } .app-info { - background-color: light-dark(var(--tan), var(--tasteful-dark)); + background: light-dark(var(--surface-light), var(--surface-dark)); border-radius: var(--border-radius); padding: 1.5rem; margin-bottom: 2rem; } +/* Components with secondary backgrounds */ +.app-header, +.app-info, +.app-description, +.form-group, +.search-bar input, +.version-selector, +.mirror-selector select, +.secondary, +.message.success { + background: light-dark(var(--surface-light), var(--surface-dark)) !important; + color: light-dark(var(--text-light), var(--text-dark)); +} + /* Download Page specific styles */ .download-section { display: flex; @@ -289,8 +357,8 @@ td { padding: 0.5em; border: 1px solid var(--gray); border-radius: var(--border-radius); - background-color: light-dark(var(--white), var(--tasteful-dark)); - color: light-dark(var(--off-black), var(--off-white)); + background-color: light-dark(var(--surface-light), var(--surface-dark)); + color: light-dark(var(--text-light), var(--text-dark)); } /* Action Buttons */ @@ -311,23 +379,23 @@ td { .primary { background-color: var(--orange); - color: var(--white); + color: var(--text-light); } .primary:hover:not(:disabled) { background-color: var(--dark-orange); - color: var(--white); + color: var(--text-light); } .secondary { - background-color: light-dark(var(--off-white), var(--off-black)); + background-color: light-dark(var(--surface-light), var(--surface-dark)); color: var(--orange); border: 2px solid var(--orange); } .secondary:hover:not(:disabled) { background-color: var(--orange); - color: var(--white); + color: var(--text-light); } .action-button:disabled, @@ -385,8 +453,8 @@ td { } .cap-approval-content { - background-color: light-dark(var(--white), var(--tasteful-dark)); - color: light-dark(var(--off-black), var(--off-white)); + background-color: light-dark(var(--surface-light), var(--surface-dark)); + color: light-dark(var(--text-light), var(--text-dark)); padding: 2rem; border-radius: 8px; max-width: 80%; @@ -395,8 +463,8 @@ td { } .json-display { - background-color: light-dark(var(--tan), var(--off-black)); - color: light-dark(var(--off-black), var(--off-white)); + background-color: light-dark(var(--surface-light), var(--surface-dark)); + color: light-dark(var(--text-light), var(--text-dark)); padding: 1rem; border-radius: 4px; white-space: pre-wrap; @@ -410,6 +478,44 @@ td { margin-top: 1rem; } +/* Search bar */ +.search-bar { + width: 100%; + margin: 1rem auto 2rem; + position: relative; +} + +.search-bar input { + width: 100%; + padding: 0.75rem 1rem 0.75rem 2.5rem; + border: 2px solid transparent; + border-radius: 2rem; + background: light-dark(var(--surface-light), var(--surface-dark)); + color: light-dark(var(--text-light), var(--text-dark)); + font-size: 1rem; + transition: all 0.2s ease; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.search-bar input:focus { + outline: none; + border-color: var(--orange); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.search-bar svg { + position: absolute; + left: 0.75rem; + top: 50%; + transform: translateY(-50%); + color: var(--gray); + pointer-events: none; +} + +.search-bar input::placeholder { + color: var(--gray); +} + /* Responsive adjustments */ @media (max-width: 48em) { @@ -442,7 +548,7 @@ td { } .manifest-display { - background: light-dark(var(--white), var(--tasteful-dark)); + background: light-dark(var(--surface-light), var(--surface-dark)); border-radius: var(--border-radius); padding: 1rem; max-width: 600px; @@ -450,7 +556,7 @@ td { .process-manifest { margin-bottom: 0.5rem; - border: 1px solid light-dark(var(--gray), var(--off-black)); + border: 1px solid light-dark(var(--gray), var(--surface-dark)); border-radius: var(--border-radius); overflow: hidden; } @@ -464,12 +570,12 @@ td { background: none; border: none; cursor: pointer; - color: light-dark(var(--off-black), var(--off-white)); + color: light-dark(var(--text-light), var(--text-dark)); transition: background-color 0.2s; } .process-header:hover { - background: light-dark(var(--tan), var(--off-black)); + background: light-dark(var(--surface-light), var(--surface-dark)); } .process-name { @@ -481,7 +587,7 @@ td { .process-indicators { display: flex; gap: 0.5rem; - color: light-dark(var(--gray), var(--off-white)); + color: light-dark(var(--gray), var(--text-dark)); } .network-icon { @@ -498,8 +604,8 @@ td { .process-details { padding: 1rem; - background: light-dark(var(--tan), var(--off-black)); - border-top: 1px solid light-dark(var(--gray), var(--off-black)); + background: light-dark(var(--surface-light), var(--surface-dark)); + border-top: 1px solid light-dark(var(--gray), var(--surface-dark)); } .capability-section { @@ -512,13 +618,13 @@ td { .capability-section h4 { margin: 0 0 0.5rem 0; - color: light-dark(var(--off-black), var(--off-white)); + color: light-dark(var(--text-light), var(--text-dark)); } .capability-section ul { margin: 0; padding-left: 1.5rem; - color: light-dark(var(--gray), var(--off-white)); + color: light-dark(var(--gray), var(--text-dark)); } .capability-section li { @@ -538,7 +644,7 @@ td { align-items: center; gap: 0.5rem; padding: 0.5rem; - color: light-dark(var(--off-black), var(--off-white)); + color: light-dark(var(--text-light), var(--text-dark)); } .notification-details { @@ -548,7 +654,7 @@ td { width: 320px; max-height: 400px; overflow-y: auto; - background-color: light-dark(var(--white), var(--tasteful-dark)); + background-color: light-dark(var(--surface-light), var(--surface-dark)); border-radius: var(--border-radius); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); z-index: 1000; @@ -557,7 +663,7 @@ td { .badge { background-color: var(--orange); - color: var(--white); + color: var(--text-light); border-radius: 50%; padding: 0.25rem 0.5rem; font-size: 0.75rem; @@ -571,8 +677,8 @@ td { padding: 1rem; margin: 0.5rem 0; border-radius: var(--border-radius); - background-color: light-dark(var(--tan), var(--off-black)); - color: light-dark(var(--off-black), var(--off-white)); + background-color: light-dark(var(--surface-light), var(--surface-dark)); + color: light-dark(var(--text-light), var(--text-dark)); } .notification-item.error { @@ -606,7 +712,7 @@ td { background: none; border: none; cursor: pointer; - color: light-dark(var(--gray), var(--off-white)); + color: light-dark(var(--gray), var(--text-dark)); padding: 0.25rem; } @@ -617,7 +723,7 @@ td { .progress-bar { margin-top: 0.5rem; height: 4px; - background-color: light-dark(var(--white), var(--off-black)); + background-color: light-dark(var(--surface-light), var(--surface-dark)); border-radius: 2px; overflow: hidden; } @@ -643,8 +749,8 @@ td { } .modal-content { - background-color: light-dark(var(--white), var(--tasteful-dark)); - color: light-dark(var(--off-black), var(--off-white)); + background-color: light-dark(var(--surface-light), var(--surface-dark)); + color: light-dark(var(--text-light), var(--text-dark)); padding: 1.5rem; border-radius: var(--border-radius); position: relative; @@ -660,7 +766,7 @@ td { background: none; border: none; cursor: pointer; - color: light-dark(var(--gray), var(--off-white)); + color: light-dark(var(--gray), var(--text-dark)); padding: 0.25rem; } @@ -713,4 +819,96 @@ td { 100% { opacity: 1; } +} + +/* Loading Spinner */ +.loading-spinner { + display: inline-block; + width: 20px; + height: 20px; + margin-right: 8px; + border: 2px solid var(--text-light); + border-radius: 50%; + border-top-color: transparent; + animation: spin 1s linear infinite; +} + +.loading-spinner.small { + width: 14px; + height: 14px; + margin-right: 6px; + border-width: 1.5px; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +/* Publish Page */ +.publish-page { + padding: 1rem; +} + +.publish-page h1 { + margin-bottom: 2rem; +} + +.connect-wallet { + text-align: center; + padding: 2rem; + background: light-dark(var(--surface-light), var(--surface-dark)); + border-radius: var(--border-radius); + margin-bottom: 2rem; +} + +.publish-form { + background: light-dark(var(--surface-light), var(--surface-dark)); + padding: 2rem; + border-radius: var(--border-radius); + margin-bottom: 2rem; +} + +.package-list { + list-style: none; + padding: 0; + margin: 0; + display: grid; + gap: 1rem; +} + +.package-list li { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem; + background: light-dark(var(--surface-light), var(--surface-dark)); + border-radius: var(--border-radius); +} + +.package-list .app-name { + display: flex; + align-items: center; + gap: 1rem; + color: inherit; + text-decoration: none; +} + +.package-list .app-name:hover { + color: var(--orange); +} + +.package-icon { + width: 32px; + height: 32px; + border-radius: var(--border-radius); +} + +.no-packages { + text-align: center; + padding: 2rem; + background: light-dark(var(--surface-light), var(--surface-dark)); + border-radius: var(--border-radius); + color: var(--gray); } \ No newline at end of file diff --git a/kinode/packages/app_store/ui/src/pages/AppPage.tsx b/kinode/packages/app_store/ui/src/pages/AppPage.tsx index b37fa0cb..bb6c97aa 100644 --- a/kinode/packages/app_store/ui/src/pages/AppPage.tsx +++ b/kinode/packages/app_store/ui/src/pages/AppPage.tsx @@ -148,6 +148,12 @@ export default function AppPage() { {latestVersion && (
  • Latest Version: {latestVersion}
  • )} + {installedApp?.pending_update_hash && ( +
  • + Failed Auto-Update: + Update to version with hash {installedApp.pending_update_hash.slice(0, 8)}... failed, approve newly requested capabilities and install it here: +
  • + )}
  • Publisher: {app.package_id.publisher_node}
  • License: {app.metadata?.properties?.license || "Not specified"}
  • diff --git a/kinode/packages/app_store/ui/src/pages/PublishPage.tsx b/kinode/packages/app_store/ui/src/pages/PublishPage.tsx index 91213d3e..71dcf5b8 100644 --- a/kinode/packages/app_store/ui/src/pages/PublishPage.tsx +++ b/kinode/packages/app_store/ui/src/pages/PublishPage.tsx @@ -247,14 +247,20 @@ export default function PublishPage() { )} {isConfirming ? ( -
    Publishing package...
    +
    +
    + Publishing package... +
    ) : !address || !isConnected ? ( - <> +

    Please connect your wallet to publish a package

    - +
    ) : isConnecting ? ( -
    Approve connection in your wallet
    +
    +
    + Approve connection in your wallet +
    ) : (
    @@ -289,8 +295,15 @@ export default function PublishPage() { placeholder="Calculated automatically from metadata URL" />
    -
    )} @@ -309,21 +322,24 @@ export default function PublishPage() {

    Packages You Own

    {Object.keys(ourApps).length > 0 ? ( -
      +
        {Object.values(ourApps).map((app) => (
      • - {app.metadata?.name || app.package_id.package_name} + {app.metadata?.image && ( + + )} + {app.metadata?.name || app.package_id.package_name} -
      • ))}
      ) : ( -

      No packages published

      +

      No packages published

      )}
    diff --git a/kinode/packages/app_store/ui/src/pages/StorePage.tsx b/kinode/packages/app_store/ui/src/pages/StorePage.tsx index aa3fca00..aa657212 100644 --- a/kinode/packages/app_store/ui/src/pages/StorePage.tsx +++ b/kinode/packages/app_store/ui/src/pages/StorePage.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react"; import useAppsStore from "../store"; import { AppListing } from "../types/Apps"; import { Link } from "react-router-dom"; +import { FaSearch } from "react-icons/fa"; export default function StorePage() { const { listings, fetchListings } = useAppsStore(); @@ -25,12 +26,15 @@ export default function StorePage() { return (
    - setSearchQuery(e.target.value)} - /> +
    + setSearchQuery(e.target.value)} + /> + +
    {!listings ? ( From d0a440709cbd0994ce81d56872b33c0dae6b6b1f Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Fri, 13 Dec 2024 17:55:07 +0200 Subject: [PATCH 10/30] app_store api: add more download error variants --- kinode/packages/app_store/api/app_store:sys-v1.wit | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kinode/packages/app_store/api/app_store:sys-v1.wit b/kinode/packages/app_store/api/app_store:sys-v1.wit index 360d7cfe..a3d198cc 100644 --- a/kinode/packages/app_store/api/app_store:sys-v1.wit +++ b/kinode/packages/app_store/api/app_store:sys-v1.wit @@ -236,6 +236,9 @@ interface downloads { blob-not-found, vfs-error, handling-error(string), + timeout, + invalid-manifest, + offline, } /// Notification that a download is complete From 8e3ef6f83547d7a4688ecb6f5ed3a9a8cecf49d1 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Fri, 13 Dec 2024 17:55:47 +0200 Subject: [PATCH 11/30] ft_worker: forward error to parent if killed by timer --- .../packages/app_store/ft_worker/src/lib.rs | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/kinode/packages/app_store/ft_worker/src/lib.rs b/kinode/packages/app_store/ft_worker/src/lib.rs index 9c80ae2c..fbb0f0de 100644 --- a/kinode/packages/app_store/ft_worker/src/lib.rs +++ b/kinode/packages/app_store/ft_worker/src/lib.rs @@ -29,6 +29,7 @@ //! //! - Hash mismatches between the received file and the expected hash are detected and reported. //! - Various I/O errors are caught and propagated. +//! - A 120 second killswitch is implemented to clean up dangling transfers. //! //! ## Integration with App Store: //! @@ -61,6 +62,7 @@ wit_bindgen::generate!({ }); const CHUNK_SIZE: u64 = 262144; // 256KB +const KILL_SWITCH_MS: u64 = 120000; // 2 minutes call_init!(init); fn init(our: Address) { @@ -78,8 +80,7 @@ fn init(our: Address) { } // killswitch timer, 2 minutes. sender or receiver gets killed/cleaned up. - // TODO: killswitch update bubbles up to downloads process? - timer::set_timer(120000, None); + timer::set_timer(KILL_SWITCH_MS, None); let start = std::time::Instant::now(); @@ -105,7 +106,21 @@ fn init(our: Address) { start.elapsed().as_millis() ), ), - Err(e) => print_to_terminal(1, &format!("ft_worker: receive error: {}", e)), + Err(e) => { + print_to_terminal(1, &format!("ft_worker: receive error: {}", e)); + // bubble up to parent. + Request::new() + .body(DownloadRequests::DownloadComplete( + DownloadCompleteRequest { + package_id: package_id.clone().into(), + version_hash: desired_version_hash.to_string(), + err: Some(DownloadError::HandlingError(e.to_string())), + }, + )) + .target(parent_process) + .send() + .unwrap(); + } } } DownloadRequests::RemoteDownload(remote_request) => { @@ -187,6 +202,17 @@ fn handle_receiver( loop { let message = await_message()?; if *message.source() == timer_address { + // send error message to downloads process + Request::new() + .body(DownloadRequests::DownloadComplete( + DownloadCompleteRequest { + package_id: package_id.clone().into(), + version_hash: version_hash.to_string(), + err: Some(DownloadError::Timeout), + }, + )) + .target(parent_process.clone()) + .send()?; return Ok(()); } if !message.is_request() { From 08c9374ba47ecfa8408dd9e99f2b202163094b1e Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Fri, 13 Dec 2024 17:56:41 +0200 Subject: [PATCH 12/30] app_store:downloads: wip try all mirrors for auto_update --- .../packages/app_store/downloads/src/lib.rs | 318 ++++++++++++++---- 1 file changed, 250 insertions(+), 68 deletions(-) diff --git a/kinode/packages/app_store/downloads/src/lib.rs b/kinode/packages/app_store/downloads/src/lib.rs index 9755956a..d418baa2 100644 --- a/kinode/packages/app_store/downloads/src/lib.rs +++ b/kinode/packages/app_store/downloads/src/lib.rs @@ -46,7 +46,11 @@ use crate::kinode::process::downloads::{ DownloadError, DownloadRequests, DownloadResponses, Entry, FileEntry, HashMismatch, LocalDownloadRequest, RemoteDownloadRequest, RemoveFileRequest, }; -use std::{collections::HashSet, io::Read, str::FromStr}; +use std::{ + collections::{HashMap, HashSet}, + io::Read, + str::FromStr, +}; use ft_worker_lib::{spawn_receive_transfer, spawn_send_transfer}; use kinode_process_lib::{ @@ -78,6 +82,16 @@ pub enum Resp { HttpClient(Result), } +// todo: save? +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct AutoUpdateStatus { + mirrors_left: HashSet, // set(node/url) + mirrors_failed: Vec<(String, DownloadError)>, // vec(node/url, error) + active_mirror: String, // (node/url) +} + +type AutoUpdates = HashMap<(PackageId, String), AutoUpdateStatus>; + #[derive(Debug, Serialize, Deserialize)] pub struct State { // persisted metadata about which packages we are mirroring @@ -117,13 +131,11 @@ fn init(our: Address) { let mut tmp = vfs::open_dir("/app_store:sys/downloads/tmp", true, None).expect("could not open tmp"); - let mut auto_updates: HashSet<(PackageId, String)> = HashSet::new(); + // metadata for in-flight auto-updates + let mut auto_updates: AutoUpdates = HashMap::new(); loop { match await_message() { - Err(send_error) => { - print_to_terminal(1, &format!("downloads: got network error: {send_error}")); - } Ok(message) => { if let Err(e) = handle_message( &our, @@ -143,6 +155,31 @@ fn init(our: Address) { .unwrap(); } } + Err(send_error) => { + print_to_terminal(1, &format!("downloads: got network error: {send_error}")); + if let Some(context) = &send_error.context { + if let Ok(download_request) = serde_json::from_slice::(&context) { + let key = ( + download_request.package_id.to_process_lib(), + download_request.desired_version_hash.clone(), + ); + + // Get the error first + let error = if send_error.kind.is_timeout() { + DownloadError::Timeout + } else if send_error.kind.is_offline() { + DownloadError::Offline + } else { + DownloadError::HandlingError(send_error.to_string()) + }; + + // Then remove and get metadata + if let Some(metadata) = auto_updates.remove(&key) { + try_next_mirror(metadata, key, &mut auto_updates, error); + } + } + } + } } } } @@ -157,7 +194,7 @@ fn handle_message( message: &Message, downloads: &mut Directory, _tmp: &mut Directory, - auto_updates: &mut HashSet<(PackageId, String)>, + auto_updates: &mut AutoUpdates, ) -> anyhow::Result<()> { if message.is_request() { match message.body().try_into()? { @@ -175,6 +212,10 @@ fn handle_message( if download_from.starts_with("http") { // use http_client to GET it + print_to_terminal( + 1, + "kicking off http download for {package_id:?} and {version_hash:?}", + ); Request::to(("our", "http_client", "distro", "sys")) .body( serde_json::to_vec(&client::HttpClientAction::Http( @@ -257,50 +298,48 @@ fn handle_message( if !message.is_local(our) { return Err(anyhow::anyhow!("got non local download complete")); } - // if we have a pending auto_install, forward that context to the main process. - // it will check if the caps_hashes match (no change in capabilities), and auto_install if it does. - let manifest_hash = if auto_updates.remove(&( - req.package_id.clone().to_process_lib(), - req.version_hash.clone(), - )) { - match get_manifest_hash( - req.package_id.clone().to_process_lib(), - req.version_hash.clone(), - ) { - Ok(manifest_hash) => Some(manifest_hash), - Err(e) => { - print_to_terminal( - 1, - &format!("auto_update: error getting manifest hash: {:?}", e), - ); - None - } - } - } else { - None - }; - - // pushed to UI via websockets + // forward to main:app_store:sys, pushed to UI via websockets Request::to(("our", "main", "app_store", "sys")) .body(serde_json::to_vec(&req)?) .send()?; - // trigger auto-update install trigger to main:app_store:sys - if let Some(manifest_hash) = manifest_hash { - let auto_download_complete_req = AutoDownloadCompleteRequest { - download_info: req.clone(), - manifest_hash, - }; - print_to_terminal( - 1, - &format!( - "auto_update download complete: triggering install on main:app_store:sys" - ), - ); - Request::to(("our", "main", "app_store", "sys")) - .body(serde_json::to_vec(&auto_download_complete_req)?) - .send()?; + // Check if this is an auto-update download + let key = ( + req.package_id.clone().to_process_lib(), + req.version_hash.clone(), + ); + if let Some(metadata) = auto_updates.remove(&key) { + match (&req.err, get_manifest_hash(key.0.clone(), key.1.clone())) { + // Success case - no download error and valid manifest + (None, Ok(manifest_hash)) => { + print_to_terminal(1, "auto_update download complete: triggering install on main:app_store:sys"); + let auto_download_complete_req = AutoDownloadCompleteRequest { + download_info: req.clone(), + manifest_hash, + }; + Request::to(("our", "main", "app_store", "sys")) + .body(serde_json::to_vec(&auto_download_complete_req)?) + .send()?; + } + // Download succeeded but manifest invalid + (None, Err(e)) => { + print_to_terminal( + 1, + &format!("auto_update: error getting manifest hash: {:?}", e), + ); + try_next_mirror( + metadata, + key, + auto_updates, + DownloadError::InvalidManifest, + ); + } + // Download failed + (Some(err), _) => { + try_next_mirror(metadata, key, auto_updates, err.clone()); + } + } } } DownloadRequests::GetFiles(maybe_id) => { @@ -414,29 +453,53 @@ fn handle_message( } = auto_update_request.clone(); let process_lib_package_id = package_id.clone().to_process_lib(); - // default auto_update to publisher. TODO: more config here. - let download_from = metadata.properties.publisher; + // default auto_update to publisher + let download_from = metadata.properties.publisher.clone(); let current_version = metadata.properties.current_version; let code_hashes = metadata.properties.code_hashes; + // Create a HashSet of mirrors including the publisher + let mut mirrors = HashSet::new(); + mirrors.extend(metadata.properties.mirrors.into_iter()); + mirrors.insert(metadata.properties.publisher.clone()); + let version_hash = code_hashes - .iter() - .find(|(version, _)| version == ¤t_version) - .map(|(_, hash)| hash.clone()) - .ok_or_else(|| anyhow::anyhow!("auto_update: error for package_id: {}, current_version: {}, no matching hash found", process_lib_package_id.to_string(), current_version))?; + .iter() + .find(|(version, _)| version == ¤t_version) + .map(|(_, hash)| hash.clone()) + // note, if this errors, full on failure I thnk no? + // and bubble this up. + .ok_or_else(|| anyhow::anyhow!("auto_update: error for package_id: {}, current_version: {}, no matching hash found", process_lib_package_id.to_string(), current_version))?; + + print_to_terminal( + 1, + &format!( + "auto_update: initializing download for {:?} from {} with version {}", + package_id, download_from, version_hash + ), + ); let download_request = LocalDownloadRequest { package_id, - download_from, + download_from: download_from.clone(), desired_version_hash: version_hash.clone(), }; - // kick off local download to ourselves. + // Initialize auto-update status with mirrors + let key = (process_lib_package_id.clone(), version_hash.clone()); + auto_updates.insert( + key, + AutoUpdateStatus { + mirrors_left: mirrors, + mirrors_failed: Vec::new(), + active_mirror: download_from.clone(), + }, + ); + + // kick off local download to ourselves Request::to(("our", "downloads", "app_store", "sys")) .body(DownloadRequests::LocalDownload(download_request)) .send()?; - - auto_updates.insert((process_lib_package_id, version_hash)); } _ => {} } @@ -445,18 +508,30 @@ fn handle_message( Resp::Download(download_response) => { // get context of the response. // handled are errors or ok responses from a remote node. + // check state, do action based on that! if let Some(context) = message.context() { let download_request = serde_json::from_slice::(context)?; match download_response { DownloadResponses::Err(e) => { - Request::to(("our", "main", "app_store", "sys")) - .body(DownloadCompleteRequest { - package_id: download_request.package_id.clone(), - version_hash: download_request.desired_version_hash.clone(), - err: Some(e), - }) - .send()?; + print_to_terminal(1, &format!("downloads: got error response: {e:?}")); + let key = ( + download_request.package_id.clone().to_process_lib(), + download_request.desired_version_hash.clone(), + ); + + if let Some(metadata) = auto_updates.remove(&key) { + try_next_mirror(metadata, key, auto_updates, e); + } else { + // If not an auto-update, forward error as before + Request::to(("our", "main", "app_store", "sys")) + .body(DownloadCompleteRequest { + package_id: download_request.package_id, + version_hash: download_request.desired_version_hash, + err: Some(e), + }) + .send()?; + } } DownloadResponses::Success => { // todo: maybe we do something here. @@ -477,8 +552,14 @@ fn handle_message( return Err(anyhow::anyhow!("http_client response without context")); }; let download_request = serde_json::from_slice::(context)?; + let key = ( + download_request.package_id.clone().to_process_lib(), + download_request.desired_version_hash.clone(), + ); + if let Ok(client::HttpClientResponse::Http(client::HttpResponse { - status, .. + status, + .. })) = resp { if status == 200 { @@ -487,26 +568,127 @@ fn handle_message( 1, &format!("error handling http_client response: {:?}", e), ); + + if let Some(metadata) = auto_updates.remove(&key) { + try_next_mirror(metadata, key, auto_updates, e); + } else { + Request::to(("our", "main", "app_store", "sys")) + .body(DownloadRequests::DownloadComplete( + DownloadCompleteRequest { + package_id: download_request.package_id.clone(), + version_hash: download_request.desired_version_hash.clone(), + err: Some(e), + }, + )) + .send()?; + } + } else { + // Success case - remove from auto_updates if it was an auto-update + if auto_updates.remove(&key).is_some() { + print_to_terminal( + 1, + &format!( + "auto_update: successfully downloaded package {:?} version {}", + download_request.package_id, + download_request.desired_version_hash + ), + ); + } + } + } else { + print_to_terminal( + 1, + &format!("http_client error status: {status}"), + ); + if let Some(metadata) = auto_updates.remove(&key) { + try_next_mirror( + metadata, + key, + auto_updates, + DownloadError::HandlingError(format!("HTTP status {status}")), + ); + } else { Request::to(("our", "main", "app_store", "sys")) .body(DownloadRequests::DownloadComplete( DownloadCompleteRequest { - package_id: download_request.package_id.clone(), - version_hash: download_request.desired_version_hash.clone(), - err: Some(e), + package_id: download_request.package_id, + version_hash: download_request.desired_version_hash, + err: Some(DownloadError::HandlingError(format!("HTTP status {status}"))), }, )) .send()?; } } } else { - println!("got http_client error: {resp:?}"); + print_to_terminal(1, &format!("got http_client error: {resp:?}")); + if let Some(metadata) = auto_updates.remove(&key) { + try_next_mirror( + metadata, + key, + auto_updates, + DownloadError::HandlingError(format!("HTTP client error: {resp:?}")), + ); + } } - } + } } } Ok(()) } +/// Try the next available mirror for a download, recording the current mirror's failure +fn try_next_mirror( + mut metadata: AutoUpdateStatus, + key: (PackageId, String), + auto_updates: &mut AutoUpdates, + error: DownloadError, +) { + print_to_terminal( + 1, + &format!( + "auto_update: got error from mirror {mirror:?} {error:?}, trying next mirror: {next_mirror:?}", + next_mirror = metadata.mirrors_left.iter().next().cloned(), + mirror = metadata.active_mirror, + error = error + ), + ); + // Record failure and remove from available mirrors + metadata + .mirrors_failed + .push((metadata.active_mirror.clone(), error)); + metadata.mirrors_left.remove(&metadata.active_mirror); + + let (package_id, version_hash) = key.clone(); + + match metadata.mirrors_left.iter().next().cloned() { + Some(next_mirror) => { + metadata.active_mirror = next_mirror.clone(); + auto_updates.insert(key, metadata); + Request::to(("our", "downloads", "app_store", "sys")) + .body( + serde_json::to_vec(&LocalDownloadRequest { + package_id: crate::kinode::process::main::PackageId::from_process_lib( + package_id, + ), + download_from: next_mirror, + desired_version_hash: version_hash.clone(), + }) + .unwrap(), + ) + .send() + .unwrap(); + } + None => { + print_to_terminal( + 1, + "auto_update: no more mirrors to try for package_id {package_id:?}", + ); + // Clean up and send comprehensive error to main + auto_updates.remove(&key); + } + } +} + fn handle_receive_http_download( download_request: &LocalDownloadRequest, ) -> anyhow::Result<(), DownloadError> { From b0cc4162feda51b302f991268d914990bad82652 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Mon, 16 Dec 2024 14:56:00 +0200 Subject: [PATCH 13/30] app_store: robustify auto_update, add http endpoints --- .../app_store/api/app_store:sys-v1.wit | 20 +- .../app_store/app_store/src/http_api.rs | 43 ++- .../packages/app_store/app_store/src/lib.rs | 109 +++++-- .../packages/app_store/app_store/src/state.rs | 81 ++++- .../packages/app_store/app_store/src/utils.rs | 1 - .../packages/app_store/downloads/src/lib.rs | 282 ++++++++++-------- .../app_store/ft_worker/src/ft_worker_lib.rs | 22 +- .../packages/app_store/ft_worker/src/lib.rs | 2 + 8 files changed, 376 insertions(+), 184 deletions(-) diff --git a/kinode/packages/app_store/api/app_store:sys-v1.wit b/kinode/packages/app_store/api/app_store:sys-v1.wit index a3d198cc..2ca7d205 100644 --- a/kinode/packages/app_store/api/app_store:sys-v1.wit +++ b/kinode/packages/app_store/api/app_store:sys-v1.wit @@ -248,12 +248,26 @@ interface downloads { err: option, } - /// Request for an auto-download complete - record auto-download-complete-request { - download-info: download-complete-request, + /// Variant for an auto-download complete + variant auto-download-complete-request { + success(auto-download-success), + err(auto-download-error), + } + + /// Auto-download success + record auto-download-success { + package-id: package-id, + version-hash: string, manifest-hash: string, } + /// Auto-download error + record auto-download-error { + package-id: package-id, + version-hash: string, + tries: list>, // (mirror, error) + } + /// Represents a hash mismatch error record hash-mismatch { desired: string, 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 47da69ea..8079ede1 100644 --- a/kinode/packages/app_store/app_store/src/http_api.rs +++ b/kinode/packages/app_store/app_store/src/http_api.rs @@ -3,11 +3,13 @@ //! and sends back http_responses. //! use crate::{ - kinode::process::chain::{ChainRequests, ChainResponses}, - kinode::process::downloads::{ - DownloadRequests, DownloadResponses, Entry, LocalDownloadRequest, RemoveFileRequest, + kinode::process::{ + chain::{ChainRequests, ChainResponses}, + downloads::{ + DownloadRequests, DownloadResponses, Entry, LocalDownloadRequest, RemoveFileRequest, + }, }, - state::{MirrorCheck, PackageState, State}, + state::{MirrorCheck, PackageState, State, Updates}, }; use kinode_process_lib::{ http::{self, server, Method, StatusCode}, @@ -28,6 +30,7 @@ pub fn init_frontend(our: &Address, http_server: &mut server::HttpServer) { "/downloads", // all downloads "/installed", // all installed apps "/ourapps", // all apps we've published + "/updates", // all auto_updates "/apps/:id", // detail about an on-chain app "/downloads/:id", // local downloads for an app "/installed/:id", // detail about an installed app @@ -38,6 +41,7 @@ pub fn init_frontend(our: &Address, http_server: &mut server::HttpServer) { "/downloads/:id/mirror", // start mirroring a version of a downloaded app "/downloads/:id/remove", // remove a downloaded app "/apps/:id/auto-update", // set auto-updating a version of a downloaded app + "/updates/:id/clear", // clear update info for an app. "/mirrorcheck/:node", // check if a node/mirror is online/offline ] { http_server @@ -207,9 +211,10 @@ fn make_widget() -> String { pub fn handle_http_request( our: &Address, state: &mut State, + updates: &mut Updates, req: &server::IncomingHttpRequest, ) -> (server::HttpResponse, Option) { - match serve_paths(our, state, req) { + match serve_paths(our, state, updates, req) { Ok((status_code, _headers, body)) => ( server::HttpResponse::new(status_code).header("Content-Type", "application/json"), Some(LazyLoadBlob { @@ -248,13 +253,13 @@ fn gen_package_info(id: &PackageId, state: &PackageState) -> serde_json::Value { "our_version_hash": state.our_version_hash, "verified": state.verified, "caps_approved": state.caps_approved, - "pending_update_hash": state.pending_update_hash, }) } fn serve_paths( our: &Address, state: &mut State, + updates: &mut Updates, req: &server::IncomingHttpRequest, ) -> anyhow::Result<(http::StatusCode, Option>, Vec)> { let method = req.method()?; @@ -533,7 +538,6 @@ fn serve_paths( .ok_or(anyhow::anyhow!("missing blob"))? .bytes; let body_json: serde_json::Value = serde_json::from_slice(&body).unwrap_or_default(); - let version_hash = body_json .get("version_hash") .and_then(|v| v.as_str()) @@ -697,6 +701,31 @@ fn serve_paths( )), } } + // GET all failed/pending auto_updates + "/updates" => { + let serialized = serde_json::to_vec(&updates).unwrap_or_default(); + return Ok((StatusCode::OK, None, serialized)); + } + // POST clear all failed/pending auto_updates for a package_id + "/updates/:id/clear" => { + let Ok(package_id) = get_package_id(url_params) else { + return Ok(( + StatusCode::BAD_REQUEST, + None, + format!("Missing package_id").into_bytes(), + )); + }; + if method != Method::POST { + return Ok(( + StatusCode::METHOD_NOT_ALLOWED, + None, + format!("Invalid method {method} for {bound_path}").into_bytes(), + )); + } + let _ = updates.package_updates.remove(&package_id); + updates.save(); + Ok((StatusCode::OK, None, vec![])) + } // GET online/offline mirrors for a listed app "/mirrorcheck/:node" => { if method != Method::GET { diff --git a/kinode/packages/app_store/app_store/src/lib.rs b/kinode/packages/app_store/app_store/src/lib.rs index 07f7fe4e..d63a152b 100644 --- a/kinode/packages/app_store/app_store/src/lib.rs +++ b/kinode/packages/app_store/app_store/src/lib.rs @@ -42,7 +42,7 @@ use kinode_process_lib::{ LazyLoadBlob, Message, PackageId, Response, }; use serde::{Deserialize, Serialize}; -use state::State; +use state::{State, UpdateInfo, Updates}; wit_bindgen::generate!({ path: "target/wit", @@ -83,15 +83,19 @@ fn init(our: Address) { let mut http_server = http::server::HttpServer::new(5); http_api::init_frontend(&our, &mut http_server); + // state = state built from the filesystem, installed packages + // updates = state saved with get/set_state(), auto_update metadata. let mut state = State::load().expect("state loading failed"); - + let mut updates = Updates::load(); loop { match await_message() { Err(send_error) => { print_to_terminal(1, &format!("main: got network error: {send_error}")); } Ok(message) => { - if let Err(e) = handle_message(&our, &mut state, &mut http_server, &message) { + if let Err(e) = + handle_message(&our, &mut state, &mut updates, &mut http_server, &message) + { let error_message = format!("error handling message: {e:?}"); print_to_terminal(1, &error_message); Response::new() @@ -111,6 +115,7 @@ fn init(our: Address) { fn handle_message( our: &Address, state: &mut State, + updates: &mut Updates, http_server: &mut http::server::HttpServer, message: &Message, ) -> anyhow::Result<()> { @@ -134,7 +139,7 @@ fn handle_message( } http_server.handle_request( server_request, - |incoming| http_api::handle_http_request(our, state, &incoming), + |incoming| http_api::handle_http_request(our, state, updates, &incoming), |_channel_id, _message_type, _blob| { // not expecting any websocket messages from FE currently }, @@ -168,40 +173,80 @@ fn handle_message( "auto download complete from non-local node" )); } - // auto_install case: - // the downloads process has given us the new package manifest's - // capability hashes, and the old package's capability hashes. - // we can use these to determine if the new package has the same - // capabilities as the old one, and if so, auto-install it. - let manifest_hash = req.manifest_hash; - let package_id = req.download_info.package_id; - let version_hash = req.download_info.version_hash; + match req { + AutoDownloadCompleteRequest::Success(succ) => { + // auto_install case: + // the downloads process has given us the new package manifest's + // capability hashes, and the old package's capability hashes. + // we can use these to determine if the new package has the same + // capabilities as the old one, and if so, auto-install it. + let manifest_hash = succ.manifest_hash; + let package_id = succ.package_id; + let version_hash = succ.version_hash; - let process_lib_package_id = package_id.clone().to_process_lib(); + let process_lib_package_id = package_id.clone().to_process_lib(); - // first, check if we have the package and get its manifest hash - let should_auto_install = state - .packages - .get(&process_lib_package_id) - .map(|package| package.manifest_hash == Some(manifest_hash.clone())) - .unwrap_or(false); + // first, check if we have the package and get its manifest hash + let should_auto_install = state + .packages + .get(&process_lib_package_id) + .map(|package| package.manifest_hash == Some(manifest_hash.clone())) + .unwrap_or(false); - if should_auto_install { - if let Err(e) = - utils::install(&package_id, None, &version_hash, state, &our.node) - { - if let Some(package) = state.packages.get_mut(&process_lib_package_id) { - package.pending_update_hash = Some(version_hash); + if should_auto_install { + if let Err(e) = + utils::install(&package_id, None, &version_hash, state, &our.node) + { + println!("error auto-installing package: {e}"); + // Get or create the outer map for this package + updates + .package_updates + .entry(package_id.to_process_lib()) + .or_default() + .insert( + version_hash.clone(), + UpdateInfo { + errors: vec![], + pending_manifest_hash: Some(manifest_hash.clone()), + }, + ); + updates.save(); + } else { + println!( + "auto-installed update for package: {process_lib_package_id}" + ); + } + } else { + // TODO. + updates + .package_updates + .entry(package_id.to_process_lib()) + .or_default() + .insert( + version_hash.clone(), + UpdateInfo { + errors: vec![], + pending_manifest_hash: Some(manifest_hash.clone()), + }, + ); + updates.save(); } - println!("error auto-installing package: {e}"); - } else { - println!("auto-installed update for package: {process_lib_package_id}"); } - } else { - if let Some(package) = state.packages.get_mut(&process_lib_package_id) { - package.pending_update_hash = Some(version_hash); - println!("error auto-installing package: manifest hash mismatch"); + AutoDownloadCompleteRequest::Err(err) => { + println!("error auto-downloading package: {err:?}"); + updates + .package_updates + .entry(err.package_id.to_process_lib()) + .or_default() + .insert( + err.version_hash.clone(), + UpdateInfo { + errors: err.tries, + pending_manifest_hash: None, + }, + ); + updates.save(); } } } diff --git a/kinode/packages/app_store/app_store/src/state.rs b/kinode/packages/app_store/app_store/src/state.rs index 811f1605..7882d080 100644 --- a/kinode/packages/app_store/app_store/src/state.rs +++ b/kinode/packages/app_store/app_store/src/state.rs @@ -1,5 +1,5 @@ -use crate::{utils, VFS_TIMEOUT}; -use kinode_process_lib::{kimap, vfs, PackageId}; +use crate::{kinode::process::downloads::DownloadError, utils, VFS_TIMEOUT}; +use kinode_process_lib::{get_state, kimap, set_state, vfs, PackageId}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; @@ -54,9 +54,6 @@ pub struct PackageState { /// capabilities have changed. if they have changed, auto-install must fail /// and the user must approve the new capabilities. pub manifest_hash: Option, - /// stores the version hash of a failed auto-install attempt, which can be - /// later installed by the user by approving new caps. - pub pending_update_hash: Option, } // this seems cleaner to me right now with pending_update_hash, but given how we serialize @@ -133,7 +130,6 @@ impl State { verified: true, // implicitly verified (TODO re-evaluate) caps_approved: false, // must re-approve if you want to do something ?? manifest_hash: Some(manifest_hash), - pending_update_hash: None, // ... this could be a separate state saved. don't want to reflect this info on-disk as a file. }, ); @@ -147,3 +143,76 @@ impl State { Ok(()) } } + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(transparent)] +pub struct Updates { + #[serde(with = "package_id_map")] + pub package_updates: HashMap>, // package id -> version_hash -> update info +} + +impl Default for Updates { + fn default() -> Self { + Self { + package_updates: HashMap::new(), + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct UpdateInfo { + pub errors: Vec<(String, DownloadError)>, // errors collected by downloads process + pub pending_manifest_hash: Option, // pending manifest hash that differed from the installed one +} + +impl Updates { + pub fn load() -> Self { + let bytes = get_state(); + + if let Some(bytes) = bytes { + serde_json::from_slice(&bytes).unwrap_or_default() + } else { + Self::default() + } + } + + pub fn save(&self) { + let bytes = serde_json::to_vec(self).unwrap_or_default(); + set_state(&bytes); + } +} + +// note: serde_json doesn't support non-string keys when serializing maps, so +// we have to use a custom simple serializer. +mod package_id_map { + use super::*; + use std::{collections::HashMap, str::FromStr}; + + pub fn serialize( + map: &HashMap>, + s: S, + ) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeMap; + let mut map_ser = s.serialize_map(Some(map.len()))?; + for (k, v) in map { + map_ser.serialize_entry(&k.to_string(), v)?; + } + map_ser.end() + } + + pub fn deserialize<'de, D>( + d: D, + ) -> Result>, D::Error> + where + D: serde::Deserializer<'de>, + { + let string_map = HashMap::>::deserialize(d)?; + Ok(string_map + .into_iter() + .filter_map(|(k, v)| PackageId::from_str(&k).ok().map(|pid| (pid, v))) + .collect()) + } +} diff --git a/kinode/packages/app_store/app_store/src/utils.rs b/kinode/packages/app_store/app_store/src/utils.rs index acaa1384..18fccac2 100644 --- a/kinode/packages/app_store/app_store/src/utils.rs +++ b/kinode/packages/app_store/app_store/src/utils.rs @@ -225,7 +225,6 @@ pub fn install( verified: true, // sideloaded 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: Some(manifest_hash), - pending_update_hash: None, // TODO: doublecheck if problematically overwrites auto_update state. }; if let Ok(extracted) = extract_api(&process_package_id) { diff --git a/kinode/packages/app_store/downloads/src/lib.rs b/kinode/packages/app_store/downloads/src/lib.rs index d418baa2..6467b2c8 100644 --- a/kinode/packages/app_store/downloads/src/lib.rs +++ b/kinode/packages/app_store/downloads/src/lib.rs @@ -42,9 +42,9 @@ //! mechanism is implemented in the FT worker for improved modularity and performance. //! use crate::kinode::process::downloads::{ - AutoDownloadCompleteRequest, AutoUpdateRequest, DirEntry, DownloadCompleteRequest, - DownloadError, DownloadRequests, DownloadResponses, Entry, FileEntry, HashMismatch, - LocalDownloadRequest, RemoteDownloadRequest, RemoveFileRequest, + AutoDownloadCompleteRequest, AutoDownloadError, AutoUpdateRequest, DirEntry, + DownloadCompleteRequest, DownloadError, DownloadRequests, DownloadResponses, Entry, FileEntry, + HashMismatch, LocalDownloadRequest, RemoteDownloadRequest, RemoveFileRequest, }; use std::{ collections::{HashMap, HashSet}, @@ -53,6 +53,7 @@ use std::{ }; use ft_worker_lib::{spawn_receive_transfer, spawn_send_transfer}; +use kinode::process::downloads::AutoDownloadSuccess; use kinode_process_lib::{ await_message, call_init, get_blob, get_state, http::client, @@ -73,7 +74,6 @@ wit_bindgen::generate!({ mod ft_worker_lib; pub const VFS_TIMEOUT: u64 = 5; // 5s -pub const APP_SHARE_TIMEOUT: u64 = 120; // 120s #[derive(Debug, Serialize, Deserialize, process_macros::SerdeJsonInto)] #[serde(untagged)] // untagged as a meta-type for all incoming responses @@ -82,7 +82,6 @@ pub enum Resp { HttpClient(Result), } -// todo: save? #[derive(Debug, Serialize, Deserialize, Clone)] pub struct AutoUpdateStatus { mirrors_left: HashSet, // set(node/url) @@ -158,12 +157,14 @@ fn init(our: Address) { Err(send_error) => { print_to_terminal(1, &format!("downloads: got network error: {send_error}")); if let Some(context) = &send_error.context { - if let Ok(download_request) = serde_json::from_slice::(&context) { + if let Ok(download_request) = + serde_json::from_slice::(&context) + { let key = ( download_request.package_id.to_process_lib(), download_request.desired_version_hash.clone(), ); - + // Get the error first let error = if send_error.kind.is_timeout() { DownloadError::Timeout @@ -172,7 +173,7 @@ fn init(our: Address) { } else { DownloadError::HandlingError(send_error.to_string()) }; - + // Then remove and get metadata if let Some(metadata) = auto_updates.remove(&key) { try_next_mirror(metadata, key, &mut auto_updates, error); @@ -241,7 +242,6 @@ fn handle_message( &package_id, &desired_version_hash, &download_from, - APP_SHARE_TIMEOUT, )?; Request::to((&download_from, "downloads", "app_store", "sys")) @@ -277,13 +277,8 @@ fn handle_message( } let target_worker = Address::from_str(&worker_address)?; - let _ = spawn_send_transfer( - our, - &package_id, - &desired_version_hash, - APP_SHARE_TIMEOUT, - &target_worker, - )?; + let _ = + spawn_send_transfer(our, &package_id, &desired_version_hash, &target_worker)?; let resp = DownloadResponses::Success; Response::new().body(&resp).send()?; } @@ -309,36 +304,18 @@ fn handle_message( req.package_id.clone().to_process_lib(), req.version_hash.clone(), ); + if let Some(metadata) = auto_updates.remove(&key) { - match (&req.err, get_manifest_hash(key.0.clone(), key.1.clone())) { - // Success case - no download error and valid manifest - (None, Ok(manifest_hash)) => { - print_to_terminal(1, "auto_update download complete: triggering install on main:app_store:sys"); - let auto_download_complete_req = AutoDownloadCompleteRequest { - download_info: req.clone(), - manifest_hash, - }; - Request::to(("our", "main", "app_store", "sys")) - .body(serde_json::to_vec(&auto_download_complete_req)?) - .send()?; - } - // Download succeeded but manifest invalid - (None, Err(e)) => { - print_to_terminal( - 1, - &format!("auto_update: error getting manifest hash: {:?}", e), - ); - try_next_mirror( - metadata, - key, - auto_updates, - DownloadError::InvalidManifest, - ); - } - // Download failed - (Some(err), _) => { - try_next_mirror(metadata, key, auto_updates, err.clone()); - } + if let Some(err) = req.err { + try_next_mirror(metadata, key, auto_updates, err); + } else if let Err(_e) = handle_auto_update_success(key.0.clone(), key.1.clone()) + { + try_next_mirror( + metadata, + key, + auto_updates, + DownloadError::InvalidManifest, + ); } } } @@ -454,12 +431,20 @@ fn handle_message( let process_lib_package_id = package_id.clone().to_process_lib(); // default auto_update to publisher - let download_from = metadata.properties.publisher.clone(); + // let download_from = metadata.properties.publisher.clone(); let current_version = metadata.properties.current_version; let code_hashes = metadata.properties.code_hashes; // Create a HashSet of mirrors including the publisher let mut mirrors = HashSet::new(); + + let download_from = if let Some(first_mirror) = metadata.properties.mirrors.first() + { + first_mirror.clone() + } else { + "randomnode111.os".to_string() + }; + println!("first_download_from: {download_from}"); mirrors.extend(metadata.properties.mirrors.into_iter()); mirrors.insert(metadata.properties.publisher.clone()); @@ -474,8 +459,8 @@ fn handle_message( print_to_terminal( 1, &format!( - "auto_update: initializing download for {:?} from {} with version {}", - package_id, download_from, version_hash + "auto_update: kicking off download for {:?} from {} with version {} from mirror {}", + package_id, download_from, version_hash, download_from ), ); @@ -523,7 +508,7 @@ fn handle_message( if let Some(metadata) = auto_updates.remove(&key) { try_next_mirror(metadata, key, auto_updates, e); } else { - // If not an auto-update, forward error as before + // If not an auto-update, forward error normally Request::to(("our", "main", "app_store", "sys")) .body(DownloadCompleteRequest { package_id: download_request.package_id, @@ -556,81 +541,83 @@ fn handle_message( download_request.package_id.clone().to_process_lib(), download_request.desired_version_hash.clone(), ); - - if let Ok(client::HttpClientResponse::Http(client::HttpResponse { - status, - .. - })) = resp - { - if status == 200 { - if let Err(e) = handle_receive_http_download(&download_request) { - print_to_terminal( - 1, - &format!("error handling http_client response: {:?}", e), - ); - - if let Some(metadata) = auto_updates.remove(&key) { - try_next_mirror(metadata, key, auto_updates, e); - } else { - Request::to(("our", "main", "app_store", "sys")) - .body(DownloadRequests::DownloadComplete( - DownloadCompleteRequest { - package_id: download_request.package_id.clone(), - version_hash: download_request.desired_version_hash.clone(), - err: Some(e), - }, - )) - .send()?; - } + + // Check if this is an auto-update request + let is_auto_update = auto_updates.contains_key(&key); + let metadata = if is_auto_update { + auto_updates.remove(&key) + } else { + None + }; + + // Handle any non-200 response or client error + let Ok(client::HttpClientResponse::Http(resp)) = resp else { + if let Some(meta) = metadata { + let error = if let Err(e) = resp { + format!("HTTP client error: {e:?}") } else { - // Success case - remove from auto_updates if it was an auto-update - if auto_updates.remove(&key).is_some() { - print_to_terminal( - 1, - &format!( - "auto_update: successfully downloaded package {:?} version {}", - download_request.package_id, - download_request.desired_version_hash + "unexpected response type".to_string() + }; + try_next_mirror( + meta, + key, + auto_updates, + DownloadError::HandlingError(error), + ); + } + return Ok(()); + }; + + if resp.status != 200 { + let error = + DownloadError::HandlingError(format!("HTTP status {}", resp.status)); + handle_download_error( + is_auto_update, + metadata, + key, + auto_updates, + error, + &download_request, + )?; + return Ok(()); + } + + // Handle successful download + if let Err(e) = handle_receive_http_download(&download_request) { + print_to_terminal(1, &format!("error handling http_client response: {:?}", e)); + handle_download_error( + is_auto_update, + metadata, + key, + auto_updates, + e, + &download_request, + )?; + } else if is_auto_update { + match handle_auto_update_success(key.0.clone(), key.1.clone()) { + Ok(_) => print_to_terminal( + 1, + &format!( + "auto_update: successfully downloaded package {:?} version {}", + &download_request.package_id, + &download_request.desired_version_hash + ), + ), + Err(_) => { + if let Some(meta) = metadata { + try_next_mirror( + meta, + key, + auto_updates, + DownloadError::HandlingError( + "could not get manifest hash".to_string(), ), ); } - } - } else { - print_to_terminal( - 1, - &format!("http_client error status: {status}"), - ); - if let Some(metadata) = auto_updates.remove(&key) { - try_next_mirror( - metadata, - key, - auto_updates, - DownloadError::HandlingError(format!("HTTP status {status}")), - ); - } else { - Request::to(("our", "main", "app_store", "sys")) - .body(DownloadRequests::DownloadComplete( - DownloadCompleteRequest { - package_id: download_request.package_id, - version_hash: download_request.desired_version_hash, - err: Some(DownloadError::HandlingError(format!("HTTP status {status}"))), - }, - )) - .send()?; } } - } else { - print_to_terminal(1, &format!("got http_client error: {resp:?}")); - if let Some(metadata) = auto_updates.remove(&key) { - try_next_mirror( - metadata, - key, - auto_updates, - DownloadError::HandlingError(format!("HTTP client error: {resp:?}")), - ); - } } - } + } } } Ok(()) @@ -647,7 +634,7 @@ fn try_next_mirror( 1, &format!( "auto_update: got error from mirror {mirror:?} {error:?}, trying next mirror: {next_mirror:?}", - next_mirror = metadata.mirrors_left.iter().next().cloned(), + next_mirror = metadata.mirrors_left.iter().next().cloned(), mirror = metadata.active_mirror, error = error ), @@ -666,13 +653,13 @@ fn try_next_mirror( auto_updates.insert(key, metadata); Request::to(("our", "downloads", "app_store", "sys")) .body( - serde_json::to_vec(&LocalDownloadRequest { + serde_json::to_vec(&DownloadRequests::LocalDownload(LocalDownloadRequest { package_id: crate::kinode::process::main::PackageId::from_process_lib( package_id, ), download_from: next_mirror, desired_version_hash: version_hash.clone(), - }) + })) .unwrap(), ) .send() @@ -683,7 +670,18 @@ fn try_next_mirror( 1, "auto_update: no more mirrors to try for package_id {package_id:?}", ); - // Clean up and send comprehensive error to main + // gather, and send error to main. + let node_tries = metadata.mirrors_failed; + let auto_download_error = AutoDownloadCompleteRequest::Err(AutoDownloadError { + package_id: crate::kinode::process::main::PackageId::from_process_lib(package_id), + version_hash, + tries: node_tries, + }); + + Request::to(("our", "main", "app_store", "sys")) + .body(auto_download_error) + .send() + .unwrap(); auto_updates.remove(&key); } } @@ -740,6 +738,48 @@ fn handle_receive_http_download( Ok(()) } +fn handle_download_error( + is_auto_update: bool, + metadata: Option, + key: (PackageId, String), + auto_updates: &mut AutoUpdates, + error: impl Into, + download_request: &LocalDownloadRequest, +) -> anyhow::Result<()> { + let error = error.into(); + if is_auto_update { + if let Some(meta) = metadata { + try_next_mirror(meta, key, auto_updates, error); + } + } else { + Request::to(("our", "main", "app_store", "sys")) + .body(DownloadRequests::DownloadComplete( + DownloadCompleteRequest { + package_id: download_request.package_id.clone(), + version_hash: download_request.desired_version_hash.clone(), + err: Some(error), + }, + )) + .send()?; + } + Ok(()) +} + +/// Handle auto-update success case by getting manifest hash and sending completion message +fn handle_auto_update_success(package_id: PackageId, version_hash: String) -> anyhow::Result<()> { + let manifest_hash = get_manifest_hash(package_id.clone(), version_hash.clone())?; + + Request::to(("our", "main", "app_store", "sys")) + .body(AutoDownloadCompleteRequest::Success(AutoDownloadSuccess { + package_id: crate::kinode::process::main::PackageId::from_process_lib(package_id), + version_hash, + manifest_hash, + })) + .send() + .unwrap(); + Ok(()) +} + fn format_entries(entries: Vec, state: &State) -> Vec { entries .into_iter() diff --git a/kinode/packages/app_store/ft_worker/src/ft_worker_lib.rs b/kinode/packages/app_store/ft_worker/src/ft_worker_lib.rs index 4ee9cf45..200c2e5f 100644 --- a/kinode/packages/app_store/ft_worker/src/ft_worker_lib.rs +++ b/kinode/packages/app_store/ft_worker/src/ft_worker_lib.rs @@ -17,7 +17,6 @@ pub fn spawn_send_transfer( our: &Address, package_id: &PackageId, version_hash: &str, - timeout: u64, to_addr: &Address, ) -> anyhow::Result<()> { let transfer_id: u64 = rand::random(); @@ -33,17 +32,14 @@ pub fn spawn_send_transfer( return Err(anyhow::anyhow!("failed to spawn ft_worker!")); }; - let req = Request::new() - .target((&our.node, worker_process_id)) - .expects_response(timeout + 1) - .body( - serde_json::to_vec(&DownloadRequests::RemoteDownload(RemoteDownloadRequest { - package_id: package_id.clone(), - desired_version_hash: version_hash.to_string(), - worker_address: to_addr.to_string(), - })) - .unwrap(), - ); + let req = Request::new().target((&our.node, worker_process_id)).body( + serde_json::to_vec(&DownloadRequests::RemoteDownload(RemoteDownloadRequest { + package_id: package_id.clone(), + desired_version_hash: version_hash.to_string(), + worker_address: to_addr.to_string(), + })) + .unwrap(), + ); req.send()?; Ok(()) } @@ -58,7 +54,6 @@ pub fn spawn_receive_transfer( package_id: &PackageId, version_hash: &str, from_node: &str, - timeout: u64, ) -> anyhow::Result
    { let transfer_id: u64 = rand::random(); let timer_id = ProcessId::new(Some("timer"), "distro", "sys"); @@ -75,7 +70,6 @@ pub fn spawn_receive_transfer( let req = Request::new() .target((&our.node, worker_process_id.clone())) - .expects_response(timeout + 1) .body( serde_json::to_vec(&DownloadRequests::LocalDownload(LocalDownloadRequest { package_id: package_id.clone(), diff --git a/kinode/packages/app_store/ft_worker/src/lib.rs b/kinode/packages/app_store/ft_worker/src/lib.rs index fbb0f0de..428971c6 100644 --- a/kinode/packages/app_store/ft_worker/src/lib.rs +++ b/kinode/packages/app_store/ft_worker/src/lib.rs @@ -109,6 +109,8 @@ fn init(our: Address) { Err(e) => { print_to_terminal(1, &format!("ft_worker: receive error: {}", e)); // bubble up to parent. + // TODO: doublecheck this. + // if this fires on a basic timeout, that's bad. Request::new() .body(DownloadRequests::DownloadComplete( DownloadCompleteRequest { From 4a6f57e7a1908268c8d18d4012fcc1493b34f150 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Mon, 16 Dec 2024 16:22:02 +0200 Subject: [PATCH 14/30] app_store UI: show failed auto-updates --- .../packages/app_store/downloads/src/lib.rs | 12 +- .../app_store/ui/src/components/Header.tsx | 13 +- kinode/packages/app_store/ui/src/index.css | 551 ++++++++++++++++++ .../app_store/ui/src/pages/MyAppsPage.tsx | 399 ++++++++----- .../app_store/ui/src/pages/PublishPage.tsx | 2 + .../app_store/ui/src/pages/StorePage.tsx | 3 +- .../packages/app_store/ui/src/store/index.ts | 52 +- .../packages/app_store/ui/src/types/Apps.ts | 29 + 8 files changed, 918 insertions(+), 143 deletions(-) diff --git a/kinode/packages/app_store/downloads/src/lib.rs b/kinode/packages/app_store/downloads/src/lib.rs index 6467b2c8..fba082d6 100644 --- a/kinode/packages/app_store/downloads/src/lib.rs +++ b/kinode/packages/app_store/downloads/src/lib.rs @@ -753,13 +753,11 @@ fn handle_download_error( } } else { Request::to(("our", "main", "app_store", "sys")) - .body(DownloadRequests::DownloadComplete( - DownloadCompleteRequest { - package_id: download_request.package_id.clone(), - version_hash: download_request.desired_version_hash.clone(), - err: Some(error), - }, - )) + .body(DownloadCompleteRequest { + package_id: download_request.package_id.clone(), + version_hash: download_request.desired_version_hash.clone(), + err: Some(error), + }) .send()?; } Ok(()) diff --git a/kinode/packages/app_store/ui/src/components/Header.tsx b/kinode/packages/app_store/ui/src/components/Header.tsx index 4afdc4d7..0fe826c7 100644 --- a/kinode/packages/app_store/ui/src/components/Header.tsx +++ b/kinode/packages/app_store/ui/src/components/Header.tsx @@ -1,11 +1,16 @@ import React from 'react'; -import { Link } from 'react-router-dom'; +import { Link, useLocation } from 'react-router-dom'; import { STORE_PATH, PUBLISH_PATH, MY_APPS_PATH } from '../constants/path'; import { ConnectButton } from '@rainbow-me/rainbowkit'; import { FaHome } from "react-icons/fa"; import NotificationBay from './NotificationBay'; +import useAppsStore from '../store'; const Header: React.FC = () => { + const location = useLocation(); + const { updates } = useAppsStore(); + const updateCount = Object.keys(updates || {}).length; + return (
    @@ -15,7 +20,10 @@ const Header: React.FC = () => { Apps Publish - My Apps + + My Apps + {updateCount > 0 && {updateCount}} +
    @@ -25,4 +33,5 @@ const Header: React.FC = () => {
    ); }; + export default Header; \ No newline at end of file diff --git a/kinode/packages/app_store/ui/src/index.css b/kinode/packages/app_store/ui/src/index.css index 53b8ecfc..affa625f 100644 --- a/kinode/packages/app_store/ui/src/index.css +++ b/kinode/packages/app_store/ui/src/index.css @@ -2,6 +2,7 @@ /* Core colors */ --orange: #ff7e33; --dark-orange: #e56a24; + --orange-hover: #ff9900; --red: #e53e3e; --blue: #4299e1; --green: #48bb78; @@ -98,6 +99,9 @@ a:hover { text-decoration: none; padding: 0.5rem; border-radius: var(--border-radius); + position: relative; + display: inline-flex; + align-items: center; } .header-left nav a:hover, @@ -169,6 +173,28 @@ button.danger:hover { background-color: color-mix(in srgb, var(--red) 85%, black); } +/*Download Button */ +.download-btn { + background: var(--orange); + color: var(--text-light); + border: none; +} + +.download-btn:hover { + background: var(--dark-orange); +} + +/* Notification Button */ +/* .notification-btn { + background: var(--surface-dark); + color: var(--text); + border: 1px solid var(--gray); +} + +.notification-btn:hover { + background: var(--surface-hover); +} */ + /* Tables */ table { width: 100%; @@ -405,6 +431,21 @@ td { cursor: not-allowed; } +.action-button.download-button { + background: var(--orange); + color: var(--text-light); + border: none; +} + +.action-button.download-button:hover:not(:disabled) { + background: var(--dark-orange); +} + +.action-button.download-button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + /* App actions */ .app-actions { display: flex; @@ -911,4 +952,514 @@ td { background: light-dark(var(--surface-light), var(--surface-dark)); border-radius: var(--border-radius); color: var(--gray); +} + +/* Update badge */ +.update-badge { + background: var(--red); + color: var(--text-light); + border-radius: 50%; + padding: 0.15rem 0.4rem; + font-size: 0.75rem; + position: absolute; + top: -5px; + right: -5px; + min-width: 18px; + height: 18px; + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; +} + +/* Updates section */ +.updates-section { + margin-bottom: 2rem; +} + +.section-title { + color: var(--orange); + font-size: 1.25rem; + margin-bottom: 1rem; +} + +.updates-list { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.update-item { + background-color: light-dark(var(--surface-light), var(--surface-dark)); + border-radius: var(--border-radius); + overflow: hidden; + border: 1px solid transparent; +} + +.update-item.error { + border-color: var(--red); +} + +.update-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.75rem 1rem; + cursor: pointer; + transition: background-color 0.2s; +} + +.update-header:hover { + background-color: rgba(255, 255, 255, 0.05); +} + +.update-title { + display: flex; + align-items: center; + gap: 0.75rem; + font-weight: 500; +} + +.error-badge { + color: var(--red); +} + +.update-summary { + display: flex; + align-items: center; + gap: 0.75rem; + margin-left: 0.75rem; + font-size: 0.9rem; +} + +.error-count { + color: var(--red); + font-weight: 500; +} + +.manifest-badge { + color: var(--orange); + font-weight: 500; +} + +.update-actions { + display: flex; + gap: 0.5rem; +} + +.action-button { + display: flex; + align-items: center; + gap: 0.5rem; + background: none; + border: none; + padding: 0.5rem 0.75rem; + border-radius: var(--border-radius); + cursor: pointer; + color: var(--gray); + transition: all 0.2s ease; + font-size: 0.9rem; +} + +.action-button.retry { + border: 1px solid var(--blue); + color: var(--blue); +} + +.action-button.retry:hover { + background-color: var(--blue); + color: white; +} + +.action-button.clear { + color: var(--red); +} + +.action-button.clear:hover { + background-color: rgba(229, 62, 62, 0.1); +} + +.update-details { + padding: 1rem 1rem 1rem 2.75rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); + background-color: rgba(0, 0, 0, 0.1); +} + +.version-info { + color: var(--gray); + font-size: 0.9rem; + margin-bottom: 0.75rem; +} + +.manifest-info { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--orange); + font-size: 0.9rem; + margin-bottom: 0.75rem; +} + +.error-list { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.error-item { + display: flex; + align-items: flex-start; + gap: 0.5rem; + color: var(--red); + font-size: 0.9rem; + padding: 0.5rem; + background-color: rgba(229, 62, 62, 0.1); + border-radius: var(--border-radius); +} + +.error-icon { + flex-shrink: 0; + margin-top: 0.2rem; +} + +/* Updates Required Section */ +.updates-required-section { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--border-radius); + padding: 1.5rem; + margin-bottom: 2rem; +} + +.updates-required-section h2 { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--red); + margin-bottom: 1rem; +} + +.updates-required-section .warning-icon { + color: var(--red); +} + +.update-items { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.update-item { + background: var(--background); + border: 1px solid var(--border); + border-radius: var(--border-radius); + padding: 1rem; +} + +.update-item.failed { + border-left: 4px solid var(--red); +} + +.update-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; +} + +.package-name { + font-weight: 600; + font-size: 1.1rem; +} + +.version { + color: var(--text-secondary); + font-size: 0.9rem; +} + +.error-message { + background: var(--surface); + padding: 0.75rem; + border-radius: var(--border-radius); + margin: 0.5rem 0; + color: var(--text-secondary); + display: flex; + align-items: center; + gap: 1rem; +} + +.action-buttons { + display: flex; + gap: 0.5rem; + margin-top: 1rem; +} + +.retry-btn, +.review-caps-btn, +.details-btn { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + border-radius: var(--border-radius); + font-size: 0.9rem; + cursor: pointer; + transition: all 0.2s; +} + +.retry-btn, +.review-caps-btn { + background: var(--orange); + color: var(--text-light); +} + +.details-btn { + background: transparent; + border: 1px solid var(--border); + color: var(--text); +} + +.retry-btn:hover, +.review-caps-btn:hover { + background: var(--orange-dark); +} + +.details-btn:hover { + background: var(--surface); +} + +.timestamp { + margin-top: 0.5rem; + font-size: 0.8rem; + color: var(--text-secondary); +} + +/* Updates section */ +.updates-section { + margin-bottom: 2rem; +} + +.section-title { + color: var(--orange); + font-size: 1.25rem; + margin-bottom: 1rem; +} + +.updates-list { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.update-item { + background-color: light-dark(var(--surface-light), var(--surface-dark)); + border-radius: var(--border-radius); + overflow: hidden; +} + +.update-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.75rem 1rem; + cursor: pointer; + transition: background-color 0.2s; +} + +.update-header:hover { + background-color: rgba(255, 255, 255, 0.05); +} + +.update-title { + display: flex; + align-items: center; + gap: 0.5rem; + font-weight: 500; +} + +.update-actions { + display: flex; + gap: 0.5rem; +} + +.action-button { + background: none; + border: none; + cursor: pointer; + color: var(--gray); + transition: color 0.2s; + display: flex; + align-items: center; +} + +.action-button.retry:hover { + color: var(--blue); +} + +.action-button.clear:hover { + color: var(--red); +} + +.update-details { + padding: 0.75rem 1rem 1rem 2.25rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + +.version-info { + color: var(--gray); + font-size: 0.9rem; + margin-bottom: 0.5rem; +} + +.manifest-info { + color: var(--orange); + font-size: 0.9rem; + margin-bottom: 0.5rem; +} + +.error-list { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.error-item { + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--red); + font-size: 0.9rem; +} + +.error-icon { + flex-shrink: 0; +} + +/* App Page Layout */ +.app-page { + max-width: 80rem; + margin: 0 auto; + padding: 2rem 1rem; +} + +/* Updates Section */ +.updates-section { + margin-bottom: 8; +} + +.update-item { + border: 1px solid transparent; + border-radius: 0.5rem; + padding: 1rem; + margin-bottom: 1rem; + background-color: light-dark(var(--surface-light), var(--surface-dark)); + border: 1px solid light-dark(var(--gray), var(--surface-dark)); +} + +.update-header { + display: flex; + align-items: center; + justify-content: space-between; + cursor: pointer; +} + +.update-summary { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.update-details { + margin-top: 1rem; + color: light-dark(var(--text-secondary), var(--text)); +} + +.retry-button { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1rem; + border-radius: 0.375rem; + font-size: 1rem; + background-color: light-dark(var(--surface-light), var(--surface-dark)); + color: var(--orange); + border: 1px solid var(--orange); + transition: background-color 0.2s, color 0.2s; +} + +.error-count { + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + font-size: 0.75rem; + background-color: light-dark(var(--red-100), var(--red-900)); + color: light-dark(var(--red-700), var(--red-200)); +} + +/* Navigation */ +.navigation { + display: flex; + align-items: center; + gap: 4; + margin-bottom: 1.5rem; +} + +.nav-button { + display: flex; + align-items: center; + gap: 2; + padding: 0.75rem 1rem; + border-radius: 0.375rem; + font-size: 1rem; + background-color: light-dark(var(--surface-light), var(--surface-dark)); + color: var(--orange); + border: 1px solid var(--orange); + transition: background-color 0.2s, color 0.2s; +} + +.current-path { + font-size: 1rem; + color: light-dark(var(--text-secondary), var(--text)); +} + + + +.file-explorer { + border: 1px solid light-dark(var(--gray), var(--surface-dark)); + border-radius: 0.5rem; + overflow: hidden; + background-color: light-dark(var(--surface-light), var(--surface-dark)); +} + +.file-explorer h3 { + padding: 0.75rem 1rem; + font-size: 1.125rem; + font-weight: 500; + background-color: light-dark(var(--surface-light), var(--surface-dark)); + border-bottom: 1px solid light-dark(var(--gray), var(--surface-dark)); +} + +.downloads-table { + width: 100%; +} + +.downloads-table th { + padding: 0.75rem 1rem; + font-size: 1rem; + font-weight: 500; + text-align: left; + color: light-dark(var(--text-secondary), var(--text)); + background-color: light-dark(var(--surface-light), var(--surface-dark)); + border-bottom: 1px solid light-dark(var(--gray), var(--surface-dark)); +} + +.downloads-table td { + padding: 0.75rem 1rem; + font-size: 1rem; + border-bottom: 1px solid light-dark(var(--gray), var(--surface-dark)); +} + +.downloads-table tr.file:hover, +.downloads-table tr.directory:hover { + background-color: light-dark(var(--surface-light), var(--surface-dark)); + cursor: pointer; } \ No newline at end of file diff --git a/kinode/packages/app_store/ui/src/pages/MyAppsPage.tsx b/kinode/packages/app_store/ui/src/pages/MyAppsPage.tsx index fa2af6f7..2913460e 100644 --- a/kinode/packages/app_store/ui/src/pages/MyAppsPage.tsx +++ b/kinode/packages/app_store/ui/src/pages/MyAppsPage.tsx @@ -1,7 +1,8 @@ import React, { useState, useEffect } from "react"; -import { FaFolder, FaFile, FaChevronLeft, FaSync, FaRocket, FaSpinner, FaCheck, FaTrash } from "react-icons/fa"; +import { FaFolder, FaFile, FaChevronLeft, FaSync, FaRocket, FaSpinner, FaCheck, FaTrash, FaExclamationTriangle, FaTimesCircle, FaChevronDown, FaChevronRight } from "react-icons/fa"; +import { useNavigate } from "react-router-dom"; import useAppsStore from "../store"; -import { DownloadItem, PackageManifest, PackageState } from "../types/Apps"; +import { DownloadItem, PackageManifestEntry, PackageState, Updates, DownloadError, UpdateInfo } from "../types/Apps"; // Core packages that cannot be uninstalled const CORE_PACKAGES = [ @@ -16,6 +17,7 @@ const CORE_PACKAGES = [ ]; export default function MyAppsPage() { + const navigate = useNavigate(); const { fetchDownloads, fetchDownloadsForApp, @@ -25,16 +27,20 @@ export default function MyAppsPage() { removeDownload, fetchInstalled, installed, - uninstallApp + uninstallApp, + fetchUpdates, + clearUpdates, + updates } = useAppsStore(); const [currentPath, setCurrentPath] = useState([]); const [items, setItems] = useState([]); + const [expandedUpdates, setExpandedUpdates] = useState>(new Set()); const [isInstalling, setIsInstalling] = useState(false); const [isUninstalling, setIsUninstalling] = useState(false); const [error, setError] = useState(null); const [showCapApproval, setShowCapApproval] = useState(false); - const [manifest, setManifest] = useState(null); + const [manifest, setManifest] = useState(null); const [selectedItem, setSelectedItem] = useState(null); const [showUninstallConfirm, setShowUninstallConfirm] = useState(false); const [appToUninstall, setAppToUninstall] = useState(null); @@ -42,6 +48,7 @@ export default function MyAppsPage() { useEffect(() => { loadItems(); fetchInstalled(); + fetchUpdates(); }, [currentPath]); const loadItems = async () => { @@ -59,34 +66,132 @@ export default function MyAppsPage() { } }; - const initiateUninstall = (app: any) => { - const packageId = `${app.package_id.package_name}:${app.package_id.publisher_node}`; - if (CORE_PACKAGES.includes(packageId)) { - setError("Cannot uninstall core system packages"); - return; - } - setAppToUninstall(app); - setShowUninstallConfirm(true); + const handleClearUpdates = async (packageId: string) => { + await clearUpdates(packageId); + fetchUpdates(); // Refresh updates after clearing }; - const handleUninstall = async () => { - if (!appToUninstall) return; - setIsUninstalling(true); - const packageId = `${appToUninstall.package_id.package_name}:${appToUninstall.package_id.publisher_node}`; - try { - await uninstallApp(packageId); - await fetchInstalled(); - await loadItems(); - setShowUninstallConfirm(false); - setAppToUninstall(null); - } catch (error) { - console.error('Uninstallation failed:', error); - setError(`Uninstallation failed: ${error instanceof Error ? error.message : String(error)}`); - } finally { - setIsUninstalling(false); - } + const toggleUpdateExpansion = (packageId: string) => { + setExpandedUpdates(prev => { + const newSet = new Set(prev); + if (newSet.has(packageId)) { + newSet.delete(packageId); + } else { + newSet.add(packageId); + } + return newSet; + }); }; + const formatError = (error: DownloadError): string => { + if (typeof error === 'string') { + return error; + } else if ('HashMismatch' in error) { + return `Hash mismatch (expected ${error.HashMismatch.desired.slice(0, 8)}, got ${error.HashMismatch.actual.slice(0, 8)})`; + } else if ('HandlingError' in error) { + return error.HandlingError; + } else if ('Timeout' in error) { + return 'Connection timed out'; + } + return 'Unknown error'; + }; + + const renderUpdates = () => { + if (!updates || Object.keys(updates).length === 0) { + return ( +
    +

    Failed Auto Updates (0)

    +

    None found, all clear!

    +
    + ); + } + + return ( +
    +

    Failed Auto Updates ({Object.keys(updates).length})

    + {Object.keys(updates).length > 0 ? ( +
    + {Object.entries(updates).map(([packageId, versionMap]) => { + const totalErrors = Object.values(versionMap).reduce((sum, info) => + sum + (info.errors?.length || 0), 0); + const hasManifestChanges = Object.values(versionMap).some(info => + info.pending_manifest_hash); + + return ( +
    +
    toggleUpdateExpansion(packageId)}> +
    + {expandedUpdates.has(packageId) ? : } + + {packageId} +
    + {totalErrors > 0 && ( + {totalErrors} error{totalErrors !== 1 ? 's' : ''} + )} + {hasManifestChanges && ( + Manifest changes pending + )} +
    +
    +
    + + +
    +
    + {expandedUpdates.has(packageId) && Object.entries(versionMap).map(([versionHash, info]) => ( +
    +
    + Version: {versionHash.slice(0, 8)}... +
    + {info.pending_manifest_hash && ( +
    + + Pending manifest: {info.pending_manifest_hash.slice(0, 8)}... +
    + )} + {info.errors && info.errors.length > 0 && ( +
    + {info.errors.map(([source, error], idx) => ( +
    + + {source}: {formatError(error)} +
    + ))} +
    + )} +
    + ))} +
    + ); + })} +
    + ) : ( +
    + No failed auto updates found. +
    + )} +
    + ); + }; const navigateToItem = (item: DownloadItem) => { if (item.Dir) { @@ -173,113 +278,149 @@ export default function MyAppsPage() { return Object.values(installed).some(app => app.package_id.package_name === packageName); }; + const initiateUninstall = (app: any) => { + const packageId = `${app.package_id.package_name}:${app.package_id.publisher_node}`; + if (CORE_PACKAGES.includes(packageId)) { + setError("Cannot uninstall core system packages"); + return; + } + setAppToUninstall(app); + setShowUninstallConfirm(true); + }; + + const handleUninstall = async () => { + if (!appToUninstall) return; + setIsUninstalling(true); + const packageId = `${appToUninstall.package_id.package_name}:${appToUninstall.package_id.publisher_node}`; + try { + await uninstallApp(packageId); + await fetchInstalled(); + await loadItems(); + setShowUninstallConfirm(false); + setAppToUninstall(null); + } catch (error) { + console.error('Uninstallation failed:', error); + setError(`Uninstallation failed: ${error instanceof Error ? error.message : String(error)}`); + } finally { + setIsUninstalling(false); + } + }; + return ( -
    -

    My Apps

    +
    + {error &&
    {error}
    } + {renderUpdates()} - {/* Installed Apps Section */} -
    -

    Installed Apps

    - - - - - - - - - {Object.values(installed).map((app) => { - const packageId = `${app.package_id.package_name}:${app.package_id.publisher_node}`; - const isCore = CORE_PACKAGES.includes(packageId); - return ( - - - - - ); - })} - -
    Package IDActions
    {packageId} - {isCore ? ( - Core Package - ) : ( - - )} -
    + {/* Navigation */} +
    + {currentPath.length > 0 && ( + + )} +
    + {currentPath.length === 0 ? 'Downloads' : currentPath.join('/')} +
    - {/* Downloads Section */} -
    -

    Downloads

    -
    - {currentPath.length > 0 && ( - - )} - /{currentPath.join('/')} + {/* Items Table */} +
    +
    +

    Installed Apps

    + + + + + + + + + {Object.values(installed).map((app) => { + const packageId = `${app.package_id.package_name}:${app.package_id.publisher_node}`; + const isCore = CORE_PACKAGES.includes(packageId); + return ( + + + + + ); + })} + +
    Package IDActions
    {packageId} + {isCore ? ( + Core Package + ) : ( + + )} +
    - - - - - - - - - - - - {items.map((item, index) => { - const isFile = !!item.File; - const name = isFile ? item.File!.name : item.Dir!.name; - const isInstalled = isFile && isAppInstalled(name); - return ( - navigateToItem(item)} className={isFile ? 'file' : 'directory'}> - - - - - - - ); - })} - -
    NameTypeSizeMirroringActions
    - {isFile ? : } {name} - {isFile ? 'File' : 'Directory'}{isFile ? `${(item.File!.size / 1024).toFixed(2)} KB` : '-'}{!isFile && (item.Dir!.mirroring ? 'Yes' : 'No')} - {!isFile && ( - - )} - {isFile && !isInstalled && ( - <> - - - - )} - {isFile && isInstalled && ( - - )} -
    -
    - {error && ( -
    - {error} +
    +

    Downloads

    +
    + {currentPath.length > 0 && ( + + )} + /{currentPath.join('/')} +
    + + + + + + + + + + + + {items.map((item, index) => { + const isFile = !!item.File; + const name = isFile ? item.File!.name : item.Dir!.name; + const isInstalled = isFile && isAppInstalled(name); + return ( + navigateToItem(item)} className={isFile ? 'file' : 'directory'}> + + + + + + + ); + })} + +
    NameTypeSizeMirroringActions
    + {isFile ? : } {name} + {isFile ? 'File' : 'Directory'}{isFile ? `${(item.File!.size / 1024).toFixed(2)} KB` : '-'}{!isFile && (item.Dir!.mirroring ? 'Yes' : 'No')} + {!isFile && ( + + )} + {isFile && !isInstalled && ( + <> + + + + )} + {isFile && isInstalled && ( + + )} +
    - )} +
    {/* Uninstall Confirmation Modal */} {showUninstallConfirm && appToUninstall && ( @@ -318,8 +459,6 @@ export default function MyAppsPage() {
    )} - - {showCapApproval && manifest && (
    diff --git a/kinode/packages/app_store/ui/src/pages/PublishPage.tsx b/kinode/packages/app_store/ui/src/pages/PublishPage.tsx index 71dcf5b8..7e8f3ec2 100644 --- a/kinode/packages/app_store/ui/src/pages/PublishPage.tsx +++ b/kinode/packages/app_store/ui/src/pages/PublishPage.tsx @@ -23,6 +23,7 @@ export default function PublishPage() { }); const [packageName, setPackageName] = useState(""); + // @ts-ignore const [publisherId, setPublisherId] = useState(window.our?.node || ""); const [metadataUrl, setMetadataUrl] = useState(""); const [metadataHash, setMetadataHash] = useState(""); @@ -47,6 +48,7 @@ export default function PublishPage() { fetchOurApps(); // Reset form fields setPackageName(""); + // @ts-ignore setPublisherId(window.our?.node || ""); setMetadataUrl(""); setMetadataHash(""); diff --git a/kinode/packages/app_store/ui/src/pages/StorePage.tsx b/kinode/packages/app_store/ui/src/pages/StorePage.tsx index aa657212..6e9e1af5 100644 --- a/kinode/packages/app_store/ui/src/pages/StorePage.tsx +++ b/kinode/packages/app_store/ui/src/pages/StorePage.tsx @@ -5,11 +5,12 @@ import { Link } from "react-router-dom"; import { FaSearch } from "react-icons/fa"; export default function StorePage() { - const { listings, fetchListings } = useAppsStore(); + const { listings, fetchListings, fetchUpdates } = useAppsStore(); const [searchQuery, setSearchQuery] = useState(""); useEffect(() => { fetchListings(); + fetchUpdates(); }, [fetchListings]); // extensive temp null handling due to weird prod bug diff --git a/kinode/packages/app_store/ui/src/store/index.ts b/kinode/packages/app_store/ui/src/store/index.ts index 9a17f7b8..cc7c8c0b 100644 --- a/kinode/packages/app_store/ui/src/store/index.ts +++ b/kinode/packages/app_store/ui/src/store/index.ts @@ -1,6 +1,6 @@ import { create } from 'zustand' import { persist } from 'zustand/middleware' -import { PackageState, AppListing, MirrorCheckFile, DownloadItem, HomepageApp, ManifestResponse, Notification } from '../types/Apps' +import { PackageState, AppListing, MirrorCheckFile, DownloadItem, HomepageApp, ManifestResponse, Notification, UpdateInfo } from '../types/Apps' import { HTTP_STATUS } from '../constants/http' import KinodeClientApi from "@kinode/client-api" import { WEBSOCKET_URL } from '../utils/ws' @@ -16,6 +16,7 @@ interface AppsStore { notifications: Notification[] homepageApps: HomepageApp[] activeDownloads: Record + updates: Record fetchData: (id: string) => Promise fetchListings: () => Promise @@ -48,6 +49,8 @@ interface AppsStore { clearActiveDownload: (appId: string) => void clearAllActiveDownloads: () => void; + fetchUpdates: () => Promise + clearUpdates: (packageId: string) => Promise } const useAppsStore = create()((set, get) => ({ @@ -58,7 +61,7 @@ const useAppsStore = create()((set, get) => ({ activeDownloads: {}, homepageApps: [], notifications: [], - + updates: {}, fetchData: async (id: string) => { if (!id) return; @@ -380,6 +383,33 @@ const useAppsStore = create()((set, get) => ({ }); }, + fetchUpdates: async () => { + try { + const res = await fetch(`${BASE_URL}/updates`); + if (res.status === HTTP_STATUS.OK) { + const updates = await res.json(); + set({ updates }); + } + } catch (error) { + console.error("Error fetching updates:", error); + } + }, + + clearUpdates: async (packageId: string) => { + try { + await fetch(`${BASE_URL}/updates/${packageId}/clear`, { + method: 'POST', + }); + set((state) => { + const newUpdates = { ...state.updates }; + delete newUpdates[packageId]; + return { updates: newUpdates }; + }); + } catch (error) { + console.error("Error clearing updates:", error); + } + }, + ws: new KinodeClientApi({ uri: WEBSOCKET_URL, nodeId: (window as any).our?.node, @@ -419,10 +449,26 @@ const useAppsStore = create()((set, get) => ({ get().removeNotification(`download-${appId}`); if (error) { + const formatDownloadError = (error: any): string => { + if (typeof error === 'object' && error !== null) { + if ('HashMismatch' in error) { + const { actual, desired } = error.HashMismatch; + return `Hash mismatch: expected ${desired.slice(0, 8)}..., got ${actual.slice(0, 8)}...`; + } + // Try to serialize the error object if it's not a HashMismatch + try { + return JSON.stringify(error); + } catch { + return String(error); + } + } + return String(error); + }; + get().addNotification({ id: `error-${appId}`, type: 'error', - message: `Download failed for ${package_id.package_name}: ${error}`, + message: `Download failed for ${package_id.package_name}: ${formatDownloadError(error)}`, timestamp: Date.now(), }); } else { diff --git a/kinode/packages/app_store/ui/src/types/Apps.ts b/kinode/packages/app_store/ui/src/types/Apps.ts index 3da17270..c1051d7d 100644 --- a/kinode/packages/app_store/ui/src/types/Apps.ts +++ b/kinode/packages/app_store/ui/src/types/Apps.ts @@ -94,6 +94,35 @@ export interface HomepageApp { favorite: boolean; } +export interface HashMismatch { + desired: string; + actual: string; +} + +export type DownloadError = + | "NoPackage" + | "NotMirroring" + | { HashMismatch: HashMismatch } + | "FileNotFound" + | "WorkerSpawnFailed" + | "HttpClientError" + | "BlobNotFound" + | "VfsError" + | { HandlingError: string } + | "Timeout" + | "InvalidManifest" + | "Offline"; + +export interface UpdateInfo { + errors: [string, DownloadError][]; // [url/node, error] + pending_manifest_hash: string | null; +} + +export type Updates = { + [key: string]: { // package_id + [key: string]: UpdateInfo; // version_hash -> update info + }; +}; export type NotificationActionType = 'click' | 'modal' | 'popup' | 'redirect'; From 38d29345690a44e93a119ebb2beec5dc430eaa3b Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Mon, 16 Dec 2024 16:42:44 +0200 Subject: [PATCH 15/30] app_store UI: css cleanup --- kinode/packages/app_store/ui/src/index.css | 285 ++------------------- 1 file changed, 16 insertions(+), 269 deletions(-) diff --git a/kinode/packages/app_store/ui/src/index.css b/kinode/packages/app_store/ui/src/index.css index affa625f..0ba6cc81 100644 --- a/kinode/packages/app_store/ui/src/index.css +++ b/kinode/packages/app_store/ui/src/index.css @@ -329,6 +329,7 @@ td { padding: 1rem; background: light-dark(var(--bg-light), var(--bg-dark)); margin: 0 1vw; + border-radius: var(--border-radius); } .app-info { @@ -996,10 +997,6 @@ td { border: 1px solid transparent; } -.update-item.error { - border-color: var(--red); -} - .update-header { display: flex; justify-content: space-between; @@ -1020,269 +1017,12 @@ td { font-weight: 500; } -.error-badge { - color: var(--red); -} - -.update-summary { - display: flex; - align-items: center; - gap: 0.75rem; - margin-left: 0.75rem; - font-size: 0.9rem; -} - -.error-count { - color: var(--red); - font-weight: 500; -} - -.manifest-badge { - color: var(--orange); - font-weight: 500; -} - .update-actions { display: flex; gap: 0.5rem; } -.action-button { - display: flex; - align-items: center; - gap: 0.5rem; - background: none; - border: none; - padding: 0.5rem 0.75rem; - border-radius: var(--border-radius); - cursor: pointer; - color: var(--gray); - transition: all 0.2s ease; - font-size: 0.9rem; -} - -.action-button.retry { - border: 1px solid var(--blue); - color: var(--blue); -} - -.action-button.retry:hover { - background-color: var(--blue); - color: white; -} - -.action-button.clear { - color: var(--red); -} - -.action-button.clear:hover { - background-color: rgba(229, 62, 62, 0.1); -} - -.update-details { - padding: 1rem 1rem 1rem 2.75rem; - border-top: 1px solid rgba(255, 255, 255, 0.1); - background-color: rgba(0, 0, 0, 0.1); -} - -.version-info { - color: var(--gray); - font-size: 0.9rem; - margin-bottom: 0.75rem; -} - -.manifest-info { - display: flex; - align-items: center; - gap: 0.5rem; - color: var(--orange); - font-size: 0.9rem; - margin-bottom: 0.75rem; -} - -.error-list { - display: flex; - flex-direction: column; - gap: 0.75rem; -} - -.error-item { - display: flex; - align-items: flex-start; - gap: 0.5rem; - color: var(--red); - font-size: 0.9rem; - padding: 0.5rem; - background-color: rgba(229, 62, 62, 0.1); - border-radius: var(--border-radius); -} - -.error-icon { - flex-shrink: 0; - margin-top: 0.2rem; -} - -/* Updates Required Section */ -.updates-required-section { - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--border-radius); - padding: 1.5rem; - margin-bottom: 2rem; -} - -.updates-required-section h2 { - display: flex; - align-items: center; - gap: 0.5rem; - color: var(--red); - margin-bottom: 1rem; -} - -.updates-required-section .warning-icon { - color: var(--red); -} - -.update-items { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.update-item { - background: var(--background); - border: 1px solid var(--border); - border-radius: var(--border-radius); - padding: 1rem; -} - -.update-item.failed { - border-left: 4px solid var(--red); -} - -.update-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 0.5rem; -} - -.package-name { - font-weight: 600; - font-size: 1.1rem; -} - -.version { - color: var(--text-secondary); - font-size: 0.9rem; -} - -.error-message { - background: var(--surface); - padding: 0.75rem; - border-radius: var(--border-radius); - margin: 0.5rem 0; - color: var(--text-secondary); - display: flex; - align-items: center; - gap: 1rem; -} - -.action-buttons { - display: flex; - gap: 0.5rem; - margin-top: 1rem; -} - -.retry-btn, -.review-caps-btn, -.details-btn { - display: inline-flex; - align-items: center; - gap: 0.5rem; - padding: 0.5rem 1rem; - border-radius: var(--border-radius); - font-size: 0.9rem; - cursor: pointer; - transition: all 0.2s; -} - -.retry-btn, -.review-caps-btn { - background: var(--orange); - color: var(--text-light); -} - -.details-btn { - background: transparent; - border: 1px solid var(--border); - color: var(--text); -} - -.retry-btn:hover, -.review-caps-btn:hover { - background: var(--orange-dark); -} - -.details-btn:hover { - background: var(--surface); -} - -.timestamp { - margin-top: 0.5rem; - font-size: 0.8rem; - color: var(--text-secondary); -} - -/* Updates section */ -.updates-section { - margin-bottom: 2rem; -} - -.section-title { - color: var(--orange); - font-size: 1.25rem; - margin-bottom: 1rem; -} - -.updates-list { - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.update-item { - background-color: light-dark(var(--surface-light), var(--surface-dark)); - border-radius: var(--border-radius); - overflow: hidden; -} - -.update-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0.75rem 1rem; - cursor: pointer; - transition: background-color 0.2s; -} - -.update-header:hover { - background-color: rgba(255, 255, 255, 0.05); -} - -.update-title { - display: flex; - align-items: center; - gap: 0.5rem; - font-weight: 500; -} - -.update-actions { - display: flex; - gap: 0.5rem; -} - -.action-button { +.update-actions .action-button { background: none; border: none; cursor: pointer; @@ -1292,11 +1032,11 @@ td { align-items: center; } -.action-button.retry:hover { +.update-actions .action-button.retry:hover { color: var(--blue); } -.action-button.clear:hover { +.update-actions .action-button.clear:hover { color: var(--red); } @@ -1421,13 +1161,11 @@ td { color: light-dark(var(--text-secondary), var(--text)); } - - .file-explorer { border: 1px solid light-dark(var(--gray), var(--surface-dark)); - border-radius: 0.5rem; - overflow: hidden; - background-color: light-dark(var(--surface-light), var(--surface-dark)); + padding: 1rem; + border-radius: var(--border-radius); + background: light-dark(var(--surface-light), var(--surface-dark)); } .file-explorer h3 { @@ -1440,6 +1178,8 @@ td { .downloads-table { width: 100%; + border-radius: var(--border-radius); + overflow: hidden; } .downloads-table th { @@ -1462,4 +1202,11 @@ td { .downloads-table tr.directory:hover { background-color: light-dark(var(--surface-light), var(--surface-dark)); cursor: pointer; +} + +.updates-section { + background: light-dark(var(--surface-light), var(--surface-dark)); + padding: 1rem; + margin-bottom: 1rem; + border-radius: var(--border-radius); } \ No newline at end of file From f150d52dd9da0a598802ba7256d63e1648fe06ac Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Tue, 17 Dec 2024 01:18:58 +0200 Subject: [PATCH 16/30] publish ui fixes --- .../app_store/ui/src/components/Tooltip.tsx | 16 ++++ kinode/packages/app_store/ui/src/index.css | 80 ++++++++++++++++++- .../app_store/ui/src/pages/PublishPage.tsx | 28 +++---- 3 files changed, 109 insertions(+), 15 deletions(-) create mode 100644 kinode/packages/app_store/ui/src/components/Tooltip.tsx diff --git a/kinode/packages/app_store/ui/src/components/Tooltip.tsx b/kinode/packages/app_store/ui/src/components/Tooltip.tsx new file mode 100644 index 00000000..f5d62694 --- /dev/null +++ b/kinode/packages/app_store/ui/src/components/Tooltip.tsx @@ -0,0 +1,16 @@ +import React from 'react'; + +interface TooltipProps { + content: React.ReactNode; + children?: React.ReactNode; +} + +export function Tooltip({ content, children }: TooltipProps) { + return ( +
    + {children} + +
    {content}
    +
    + ); +} diff --git a/kinode/packages/app_store/ui/src/index.css b/kinode/packages/app_store/ui/src/index.css index 0ba6cc81..1e33865d 100644 --- a/kinode/packages/app_store/ui/src/index.css +++ b/kinode/packages/app_store/ui/src/index.css @@ -617,7 +617,7 @@ td { } .process-header:hover { - background: light-dark(var(--surface-light), var(--surface-dark)); + background-color: light-dark(var(--surface-light), var(--surface-dark)); } .process-name { @@ -1209,4 +1209,82 @@ td { padding: 1rem; margin-bottom: 1rem; border-radius: var(--border-radius); +} + +.tooltip-container { + position: relative; + display: inline-flex; + align-items: center; + gap: 4px; +} + +.tooltip-icon { + cursor: help; + color: #666; + font-size: 14px; + position: relative; +} + +.tooltip-content { + position: absolute; + left: 24px; + top: -4px; + background: #333; + color: white; + padding: 8px 12px; + border-radius: 4px; + font-size: 14px; + white-space: nowrap; + z-index: 1000; + opacity: 0; + visibility: hidden; + transition: opacity 0.3s ease, visibility 0.3s ease; + min-width: max-content; +} + +/* Create an invisible bridge between icon and content */ +.tooltip-content::after { + content: ''; + position: absolute; + left: -20px; /* Cover the gap between icon and content */ + top: 0; + width: 20px; + height: 100%; + background: transparent; +} + +.tooltip-container:hover .tooltip-content { + opacity: 1; + visibility: visible; + transition-delay: 0.2s; +} + +.tooltip-content:hover { + opacity: 1 !important; + visibility: visible !important; +} + +.tooltip-content::before { + content: ''; + position: absolute; + left: -4px; + top: 8px; + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + border-right: 4px solid #333; +} + +.tooltip-content a { + color: #fff; + text-decoration: underline; +} + +.tooltip-content a:hover { + text-decoration: none; +} + +.wallet-status { + display: flex; + align-items: center; + gap: 4px; } \ No newline at end of file diff --git a/kinode/packages/app_store/ui/src/pages/PublishPage.tsx b/kinode/packages/app_store/ui/src/pages/PublishPage.tsx index 7e8f3ec2..294faf76 100644 --- a/kinode/packages/app_store/ui/src/pages/PublishPage.tsx +++ b/kinode/packages/app_store/ui/src/pages/PublishPage.tsx @@ -7,6 +7,7 @@ import { mechAbi, KIMAP, encodeIntoMintCall, encodeMulticalls, kimapAbi, MULTICA import { kinohash } from '../utils/kinohash'; import useAppsStore from "../store"; import { PackageSelector } from "../components"; +import { Tooltip } from '../components/Tooltip'; const NAME_INVALID = "Package name must contain only valid characters (a-z, 0-9, -, and .)"; @@ -241,13 +242,16 @@ export default function PublishPage() { return (

    Publish Package

    - {Boolean(address) && ( -
    - Publishing as: - {address?.slice(0, 4)}...{address?.slice(-4)} + {!address ? ( +
    + +
    + ) : ( +
    + Connected: {address.slice(0, 6)}...{address.slice(-4)} +
    )} - {isConfirming ? (
    @@ -272,26 +276,22 @@ export default function PublishPage() {
    - +
    + + add a link to metadata.json here (example link)} /> +
    setMetadataUrl(e.target.value)} onBlur={calculateMetadataHash} - placeholder="https://github/my-org/my-repo/metadata.json" /> -

    - Metadata is a JSON file that describes your package. -

    {metadataError &&

    {metadataError}

    }
    - + Date: Tue, 17 Dec 2024 17:23:57 +0200 Subject: [PATCH 17/30] kns & app_store: review fixes --- .../packages/app-store/app-store/src/lib.rs | 2 - kinode/packages/app-store/chain/src/lib.rs | 10 ++--- kinode/packages/app-store/pkg/manifest.json | 4 +- .../kns-indexer/kns-indexer/src/lib.rs | 42 ++++++++----------- kinode/packages/kns-indexer/pkg/manifest.json | 2 +- 5 files changed, 23 insertions(+), 37 deletions(-) diff --git a/kinode/packages/app-store/app-store/src/lib.rs b/kinode/packages/app-store/app-store/src/lib.rs index ba385d66..15eff4bf 100644 --- a/kinode/packages/app-store/app-store/src/lib.rs +++ b/kinode/packages/app-store/app-store/src/lib.rs @@ -78,8 +78,6 @@ pub enum Resp { call_init!(init); fn init(our: Address) { - println!("started"); - let mut http_server = http::server::HttpServer::new(5); http_api::init_frontend(&our, &mut http_server); diff --git a/kinode/packages/app-store/chain/src/lib.rs b/kinode/packages/app-store/chain/src/lib.rs index 1e641ccd..7e262b2b 100644 --- a/kinode/packages/app-store/chain/src/lib.rs +++ b/kinode/packages/app-store/chain/src/lib.rs @@ -360,10 +360,6 @@ fn init(our: Address) { kimap::Kimap::new(eth_provider, eth::Address::from_str(KIMAP_ADDRESS).unwrap()); let last_saved_block = db.get_last_saved_block().unwrap_or(0); - println!( - "chain started, indexing on kimap address {} at block {}", - KIMAP_ADDRESS, last_saved_block - ); let mut state = State { kimap: kimap_helper, last_saved_block, @@ -520,7 +516,7 @@ fn handle_eth_log( let metadata_uri = String::from_utf8_lossy(¬e.data).to_string(); let is_our_package = package_id.publisher() == our.node(); - println!("we got a log: {block_number} {package_id} {metadata_uri}"); + let (tba, metadata_hash) = if !startup { // generate ~metadata-hash full-path let hash_note = format!("~metadata-hash.{}", note.parent_path); @@ -751,7 +747,7 @@ pub fn fetch_and_subscribe_logs(our: &Address, state: &mut State, last_saved_blo // get past logs, subscribe to new ones. // subscribe first so we don't miss any logs state.kimap.provider.subscribe_loop(1, filter.clone()); - println!("fetching old logs from block {last_saved_block}"); + // println!("fetching old logs from block {last_saved_block}"); for log in fetch_logs(&state.kimap.provider, &filter.from_block(last_saved_block)) { if let Err(e) = handle_eth_log(our, state, log, true) { print_to_terminal(1, &format!("error ingesting log: {e}")); @@ -764,7 +760,7 @@ pub fn fetch_and_subscribe_logs(our: &Address, state: &mut State, last_saved_blo state.last_saved_block = block_number; state.db.set_last_saved_block(block_number).unwrap(); } - println!("up to date to block {}", state.last_saved_block); + // println!("up to date to block {}", state.last_saved_block); } /// fetch logs from the chain with a given filter diff --git a/kinode/packages/app-store/pkg/manifest.json b/kinode/packages/app-store/pkg/manifest.json index ea3d3a43..4a7e951e 100644 --- a/kinode/packages/app-store/pkg/manifest.json +++ b/kinode/packages/app-store/pkg/manifest.json @@ -37,8 +37,8 @@ "vfs:distro:sys", "kns-indexer:kns-indexer:sys", "eth:distro:sys", - "http_server:distro:sys", - "http_client:distro:sys", + "http-server:distro:sys", + "http-client:distro:sys", "sqlite:distro:sys", { "process": "vfs:distro:sys", diff --git a/kinode/packages/kns-indexer/kns-indexer/src/lib.rs b/kinode/packages/kns-indexer/kns-indexer/src/lib.rs index 2d450d94..c7dcc2ac 100644 --- a/kinode/packages/kns-indexer/kns-indexer/src/lib.rs +++ b/kinode/packages/kns-indexer/kns-indexer/src/lib.rs @@ -100,11 +100,17 @@ impl State { state.last_block = state.get_last_block(); println!( - "kns_indexer: loaded state: version: {}, last_block: {}, chain_id: {}, kimap_address: {}", + "\n 🐦‍⬛ KNS Indexer State\n\ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n\ + Version {:>6}\n\ + Last Block {:>6}\n\ + Chain ID {:>6}\n\ + KIMAP {}\n\ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\n", state.version, state.last_block, state.get_chain_id(), - state.get_contract_address() + state.get_contract_address().to_string() ); state @@ -177,7 +183,6 @@ impl State { } fn set_name(&mut self, namehash: &str, name: &str) { - println!("set_name({namehash}, {name})"); self.kv .set(&Self::name_key(namehash), &name.as_bytes().to_vec(), None) .unwrap(); @@ -189,7 +194,6 @@ impl State { .get(&Self::node_key(name)) .ok() .and_then(|bytes| serde_json::from_slice(&bytes).ok()); - println!("get_node({name}) -> {x:?}"); x } @@ -199,7 +203,6 @@ impl State { &serde_json::to_vec(&node).unwrap(), None, ); - println!("set_node({name}, {:?})", node); x.unwrap(); } @@ -299,13 +302,9 @@ enum KnsError { call_init!(init); fn init(our: Address) { - println!("indexing on contract address {KIMAP_ADDRESS}"); - // state is loaded from kv, and updated with the current block number and version. let state = State::load(&our); - println!("got last block: {}", state.get_last_block()); - if let Err(e) = main(our, state) { println!("fatal error: {e}"); } @@ -315,10 +314,14 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { #[cfg(feature = "simulation-mode")] add_temp_hardcoded_tlzs(&mut state); + let chain_id = state.get_chain_id(); + let kimap_address = state.get_contract_address(); + let last_block = state.get_last_block(); + // sub_id: 1 let mints_filter = eth::Filter::new() - .address(state.get_contract_address()) - .from_block(state.get_last_block()) + .address(kimap_address) + .from_block(last_block) .to_block(eth::BlockNumberOrTag::Latest) .event("Mint(bytes32,bytes32,bytes,bytes)"); @@ -332,28 +335,17 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { // sub_id: 2 let notes_filter = eth::Filter::new() - .address(state.get_contract_address()) - .from_block(state.get_last_block()) + .address(kimap_address) + .from_block(last_block) .to_block(eth::BlockNumberOrTag::Latest) .event("Note(bytes32,bytes32,bytes,bytes,bytes)") .topic3(notes); // 60s timeout -- these calls can take a long time // if they do time out, we try them again - let eth_provider: eth::Provider = - eth::Provider::new(state.get_chain_id(), SUBSCRIPTION_TIMEOUT); - - print_to_terminal( - 1, - &format!( - "subscribing, state.block: {}, chain_id: {}", - state.get_last_block() - 1, - state.get_chain_id() - ), - ); + let eth_provider: eth::Provider = eth::Provider::new(chain_id, SUBSCRIPTION_TIMEOUT); // subscribe to logs first, so no logs are missed - println!("subscribing to new logs..."); eth_provider.subscribe_loop(1, mints_filter.clone()); eth_provider.subscribe_loop(2, notes_filter.clone()); println!("done subscribing to new logs."); diff --git a/kinode/packages/kns-indexer/pkg/manifest.json b/kinode/packages/kns-indexer/pkg/manifest.json index ca86d702..fe7b4fd1 100644 --- a/kinode/packages/kns-indexer/pkg/manifest.json +++ b/kinode/packages/kns-indexer/pkg/manifest.json @@ -13,7 +13,7 @@ ], "grant_capabilities": [ "eth:distro:sys", - "http_server:distro:sys", + "http-server:distro:sys", "timer:distro:sys", "kv:distro:sys" ], From d448966ef66775de28fb91a64185f8a2b352cff4 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Tue, 17 Dec 2024 22:00:51 +0200 Subject: [PATCH 18/30] kns: cleanup --- Cargo.lock | 87 +++++---- Cargo.toml | 3 +- kinode/packages/kns-indexer/Cargo.lock | 13 +- kinode/packages/kns-indexer/Cargo.toml | 3 +- .../kns-indexer/api/kns-indexer:sys-v0.wit | 18 +- .../packages/kns-indexer/get-block/Cargo.toml | 2 +- .../kns-indexer/kns-indexer/Cargo.toml | 2 +- .../kns-indexer/kns-indexer/src/lib.rs | 176 ++++++------------ kinode/packages/kns-indexer/pkg/scripts.json | 12 -- kinode/packages/kns-indexer/state/Cargo.toml | 20 -- kinode/packages/kns-indexer/state/src/lib.rs | 46 ----- 11 files changed, 114 insertions(+), 268 deletions(-) delete mode 100644 kinode/packages/kns-indexer/state/Cargo.toml delete mode 100644 kinode/packages/kns-indexer/state/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c10e7773..5ceaadea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,7 +93,7 @@ name = "alias" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -1045,7 +1045,7 @@ dependencies = [ "alloy-sol-types", "anyhow", "bincode", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "process_macros", "rand 0.8.5", "serde", @@ -1625,7 +1625,7 @@ name = "cat" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -1689,7 +1689,7 @@ dependencies = [ "alloy-sol-types", "anyhow", "bincode", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "process_macros", "rand 0.8.5", "serde", @@ -1708,7 +1708,7 @@ version = "0.2.1" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "pleco", "serde", "serde_json", @@ -1906,7 +1906,7 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" name = "contacts" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "process_macros", "serde", "serde_json", @@ -2502,7 +2502,7 @@ name = "download" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "process_macros", "serde", "serde_json", @@ -2514,7 +2514,7 @@ name = "downloads" version = "0.5.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "process_macros", "rand 0.8.5", "serde", @@ -2551,7 +2551,7 @@ dependencies = [ name = "echo" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "wit-bindgen 0.36.0", ] @@ -2808,7 +2808,7 @@ version = "0.2.0" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "process_macros", "rand 0.8.5", "serde", @@ -2962,7 +2962,7 @@ dependencies = [ name = "get_block" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0209da1)", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -3151,7 +3151,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" name = "help" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "wit-bindgen 0.36.0", ] @@ -3180,7 +3180,7 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" name = "hi" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -3213,7 +3213,7 @@ version = "0.1.2" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -3656,7 +3656,7 @@ name = "install" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "process_macros", "serde", "serde_json", @@ -3834,7 +3834,7 @@ name = "kfetch" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "rmp-serde", "serde", "serde_json", @@ -3846,7 +3846,7 @@ name = "kill" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -3939,6 +3939,28 @@ dependencies = [ "wit-bindgen 0.24.0", ] +[[package]] +name = "kinode_process_lib" +version = "0.10.0" +source = "git+https://github.com/kinode-dao/process_lib?rev=0209da1#0209da15340d9a2b92152916a8349d8f248775e5" +dependencies = [ + "alloy 0.1.4", + "alloy-primitives 0.7.7", + "alloy-sol-macro", + "alloy-sol-types", + "anyhow", + "bincode", + "http 1.2.0", + "mime_guess", + "rand 0.8.5", + "rmp-serde", + "serde", + "serde_json", + "thiserror 1.0.69", + "url", + "wit-bindgen 0.36.0", +] + [[package]] name = "kinode_process_lib" version = "0.10.0" @@ -4007,7 +4029,7 @@ dependencies = [ "alloy-sol-types", "anyhow", "hex", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0209da1)", "process_macros", "rmp-serde", "serde", @@ -4244,7 +4266,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "regex", "serde", "serde_json", @@ -4413,7 +4435,7 @@ dependencies = [ name = "net-diagnostics" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "rmp-serde", "serde", "wit-bindgen 0.36.0", @@ -4759,7 +4781,7 @@ dependencies = [ name = "peer" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "rmp-serde", "serde", "wit-bindgen 0.36.0", @@ -4769,7 +4791,7 @@ dependencies = [ name = "peers" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "rmp-serde", "serde", "wit-bindgen 0.36.0", @@ -5849,7 +5871,7 @@ dependencies = [ "anyhow", "base64 0.22.1", "bincode", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "rmp-serde", "serde", "serde_json", @@ -6066,17 +6088,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "state" -version = "0.1.0" -dependencies = [ - "kinode_process_lib 0.10.0", - "process_macros", - "serde", - "serde_json", - "wit-bindgen 0.36.0", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -6271,7 +6282,7 @@ version = "0.1.1" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "rand 0.8.5", "regex", "serde", @@ -6285,7 +6296,7 @@ version = "0.1.1" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "process_macros", "serde", "serde_json", @@ -6560,7 +6571,7 @@ version = "0.2.0" dependencies = [ "anyhow", "clap", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -6893,7 +6904,7 @@ name = "uninstall" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0", + "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=9c441fe)", "process_macros", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 7ca952cd..65a72d73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,7 @@ members = [ "kinode/packages/chess/chess", "kinode/packages/contacts/contacts", "kinode/packages/homepage/homepage", - "kinode/packages/kns-indexer/kns-indexer", "kinode/packages/kns-indexer/get-block", "kinode/packages/kns-indexer/state", - "kinode/packages/settings/settings", + "kinode/packages/kns-indexer/kns-indexer", "kinode/packages/kns-indexer/get-block", "kinode/packages/settings/settings", "kinode/packages/terminal/terminal", "kinode/packages/terminal/alias", "kinode/packages/terminal/cat", "kinode/packages/terminal/echo", "kinode/packages/terminal/help", "kinode/packages/terminal/hi", "kinode/packages/terminal/kfetch", diff --git a/kinode/packages/kns-indexer/Cargo.lock b/kinode/packages/kns-indexer/Cargo.lock index e42a487c..f90ab4f9 100644 --- a/kinode/packages/kns-indexer/Cargo.lock +++ b/kinode/packages/kns-indexer/Cargo.lock @@ -1508,7 +1508,7 @@ dependencies = [ [[package]] name = "kinode_process_lib" version = "0.10.0" -source = "git+https://github.com/kinode-dao/process_lib?rev=9c441fe#9c441fe19d534d640980b0495ef8dc00fcbabc03" +source = "git+https://github.com/kinode-dao/process_lib?rev=0209da1#0209da15340d9a2b92152916a8349d8f248775e5" dependencies = [ "alloy", "alloy-primitives 0.7.7", @@ -2458,17 +2458,6 @@ dependencies = [ "der", ] -[[package]] -name = "state" -version = "0.1.0" -dependencies = [ - "kinode_process_lib", - "process_macros", - "serde", - "serde_json", - "wit-bindgen", -] - [[package]] name = "static_assertions" version = "1.1.0" diff --git a/kinode/packages/kns-indexer/Cargo.toml b/kinode/packages/kns-indexer/Cargo.toml index 662ed7a6..8e902472 100644 --- a/kinode/packages/kns-indexer/Cargo.toml +++ b/kinode/packages/kns-indexer/Cargo.toml @@ -3,8 +3,7 @@ resolver = "2" members = [ "get-block", "kns-indexer", - "state", -] + ] [profile.release] panic = "abort" diff --git a/kinode/packages/kns-indexer/api/kns-indexer:sys-v0.wit b/kinode/packages/kns-indexer/api/kns-indexer:sys-v0.wit index fd3fb52f..f49ba4ac 100644 --- a/kinode/packages/kns-indexer/api/kns-indexer:sys-v0.wit +++ b/kinode/packages/kns-indexer/api/kns-indexer:sys-v0.wit @@ -14,16 +14,12 @@ interface kns-indexer { /// returns an Option /// set block to 0 if you just want to get the current state of the indexer node-info(node-info-request), - /// return the entire state of the indexer at the given block - /// set block to 0 if you just want to get the current state of the indexer - get-state(get-state-request), } variant indexer-response { name(option), node-info(option), - get-state(wit-state), - } + } record namehash-to-name-request { hash: string, @@ -35,10 +31,6 @@ interface kns-indexer { block: u64, } - record get-state-request { - block: u64, - } - record wit-kns-update { name: string, public-key: string, @@ -46,14 +38,6 @@ interface kns-indexer { ports: list>, // map, but wit doesn't support maps routers: list, } - - record wit-state { - chain-id: u64, - contract-address: list, // 20-byte ETH address - names: list>, // map, but wit doesn't support maps - nodes: list>, // map, but wit doesn't support maps - last-block: u64, - } } world kns-indexer-sys-v0 { diff --git a/kinode/packages/kns-indexer/get-block/Cargo.toml b/kinode/packages/kns-indexer/get-block/Cargo.toml index c90afe6c..f818b82e 100644 --- a/kinode/packages/kns-indexer/get-block/Cargo.toml +++ b/kinode/packages/kns-indexer/get-block/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" simulation-mode = [] [dependencies] -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "9c441fe" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0209da1" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.36.0" diff --git a/kinode/packages/kns-indexer/kns-indexer/Cargo.toml b/kinode/packages/kns-indexer/kns-indexer/Cargo.toml index bdd7ce1e..35dc6549 100644 --- a/kinode/packages/kns-indexer/kns-indexer/Cargo.toml +++ b/kinode/packages/kns-indexer/kns-indexer/Cargo.toml @@ -11,7 +11,7 @@ anyhow = "1.0" alloy-primitives = "0.7.0" alloy-sol-types = "0.7.0" hex = "0.4.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "9c441fe" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0209da1" } process_macros = "0.1" rmp-serde = "1.1.2" serde = { version = "1.0", features = ["derive"] } diff --git a/kinode/packages/kns-indexer/kns-indexer/src/lib.rs b/kinode/packages/kns-indexer/kns-indexer/src/lib.rs index c7dcc2ac..aa4b4869 100644 --- a/kinode/packages/kns-indexer/kns-indexer/src/lib.rs +++ b/kinode/packages/kns-indexer/kns-indexer/src/lib.rs @@ -1,6 +1,5 @@ use crate::kinode::process::kns_indexer::{ - GetStateRequest, IndexerRequest, IndexerResponse, NamehashToNameRequest, NodeInfoRequest, - WitKnsUpdate, WitState, + IndexerRequest, IndexerResponse, NamehashToNameRequest, NodeInfoRequest, WitKnsUpdate, }; use alloy_primitives::keccak256; use alloy_sol_types::SolEvent; @@ -38,6 +37,8 @@ const KIMAP_FIRST_BLOCK: u64 = kimap::KIMAP_FIRST_BLOCK; // optimism #[cfg(feature = "simulation-mode")] const KIMAP_FIRST_BLOCK: u64 = 1; // local +const CURRENT_VERSION: u32 = 1; + const MAX_PENDING_ATTEMPTS: u8 = 3; const SUBSCRIPTION_TIMEOUT: u64 = 60; const DELAY_MS: u64 = 1_000; // 1s @@ -52,7 +53,7 @@ struct State { // includes keys and values for: // "meta:chain_id", "meta:version", "meta:last_block", "meta:contract_address", // "names:{namehash}" -> "{name}", "nodes:{name}" -> "{node_info}" - kv: Kv>, // todo: maybe serialize directly into known enum of possible types? + kv: Kv>, } impl State { @@ -68,109 +69,91 @@ impl State { last_block: 0, }; - // load or initialize chain_id - let chain_id = state.get_chain_id(); - if chain_id == 0 { - state.set_chain_id(CHAIN_ID); - } - - // load or initialize contract_address - let contract_address = state.get_contract_address(); - if contract_address - == eth::Address::from_str(KIMAP_ADDRESS) - .expect("Failed to parse KIMAP_ADDRESS constant") - { - state.set_contract_address(contract_address); - } - - // load or initialize last_block - let last_block = state.get_last_block(); - if last_block == 0 { - state.set_last_block(KIMAP_FIRST_BLOCK); - } - - // load or initialize version let version = state.get_version(); - if version == 0 { - state.set_version(1); // Start at version 1 + let chain_id = state.get_chain_id(); + let contract_address = state.get_contract_address(); + let last_block = state.get_last_block(); + + if version != CURRENT_VERSION + || chain_id != CHAIN_ID + || contract_address != eth::Address::from_str(KIMAP_ADDRESS).unwrap() + { + // if version/contract/chain_id are new, run migrations here. } + state.set_chain_id(chain_id); + state.set_contract_address(contract_address); + state.set_version(CURRENT_VERSION); + // update state struct with final values - state.version = state.get_version(); - state.last_block = state.get_last_block(); + state.version = version; + state.last_block = last_block; println!( "\n 🐦‍⬛ KNS Indexer State\n\ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n\ - Version {:>6}\n\ - Last Block {:>6}\n\ - Chain ID {:>6}\n\ - KIMAP {}\n\ + Version {}\n\ + Last Block {}\n\ + Chain ID {}\n\ + KIMAP {}\n\ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\n", state.version, state.last_block, - state.get_chain_id(), - state.get_contract_address().to_string() + chain_id, + contract_address.to_string(), ); state } - fn meta_version_key() -> &'static str { - "meta:version" + fn meta_version_key() -> String { + "meta:version".to_string() } - fn meta_last_block_key() -> &'static str { - "meta:last_block" + + fn meta_last_block_key() -> String { + "meta:last_block".to_string() } - fn meta_chain_id_key() -> &'static str { - "meta:chain_id" + + fn meta_chain_id_key() -> String { + "meta:chain_id".to_string() } - fn meta_contract_address_key() -> &'static str { - "meta:contract_address" + + fn meta_contract_address_key() -> String { + "meta:contract_address".to_string() } fn name_key(namehash: &str) -> String { - format!("names:{namehash}") + format!("name:{}", namehash) } fn node_key(name: &str) -> String { - format!("nodes:{name}") + format!("node:{}", name) } fn get_last_block(&self) -> u64 { self.kv - .get(&Self::meta_last_block_key().to_string()) + .get_as::(&Self::meta_last_block_key()) .ok() - .and_then(|bytes| serde_json::from_slice(&bytes).ok()) - .unwrap_or(0) + .unwrap_or(KIMAP_FIRST_BLOCK) } fn set_last_block(&mut self, block: u64) { self.kv - .set( - &Self::meta_last_block_key().to_string(), - &serde_json::to_vec(&block).unwrap(), - None, - ) + .set_as::(&Self::meta_last_block_key(), &block, None) .unwrap(); self.last_block = block; } fn get_version(&self) -> u32 { self.kv - .get(&Self::meta_version_key().to_string()) + .get_as::(&Self::meta_version_key()) .ok() - .and_then(|bytes| serde_json::from_slice(&bytes).ok()) - .unwrap_or(0) + .unwrap_or(CURRENT_VERSION) } fn set_version(&mut self, version: u32) { self.kv - .set( - &Self::meta_version_key().to_string(), - &serde_json::to_vec(&version).unwrap(), - None, - ) + .set_as::(&Self::meta_version_key(), &version, None) .unwrap(); self.version = version; } @@ -189,83 +172,46 @@ impl State { } fn get_node(&self, name: &str) -> Option { - let x = self - .kv - .get(&Self::node_key(name)) - .ok() - .and_then(|bytes| serde_json::from_slice(&bytes).ok()); - x + self.kv.get_as::(&Self::node_key(name)).ok() } fn set_node(&mut self, name: &str, node: &net::KnsUpdate) { - let x = self.kv.set( - &Self::node_key(name), - &serde_json::to_vec(&node).unwrap(), - None, - ); - x.unwrap(); + self.kv + .set_as::(&Self::node_key(name), &node, None) + .unwrap(); } fn get_chain_id(&self) -> u64 { self.kv - .get(&Self::meta_chain_id_key().to_string()) + .get_as::(&Self::meta_chain_id_key()) .ok() - .and_then(|bytes| serde_json::from_slice(&bytes).ok()) .unwrap_or(CHAIN_ID) } fn set_chain_id(&mut self, chain_id: u64) { self.kv - .set( - &Self::meta_chain_id_key().to_string(), - &serde_json::to_vec(&chain_id).unwrap(), - None, - ) + .set_as::(&Self::meta_chain_id_key(), &chain_id, None) .unwrap(); } fn get_contract_address(&self) -> eth::Address { - match self.kv.get(&Self::meta_contract_address_key().to_string()) { - Ok(bytes) => match serde_json::from_slice(&bytes) { - Ok(addr) => addr, - Err(_) => eth::Address::from_str(KIMAP_ADDRESS) - .expect("Failed to parse KIMAP_ADDRESS constant"), - }, + match self + .kv + .get_as::(&Self::meta_contract_address_key()) + { + Ok(addr) => addr, Err(_) => eth::Address::from_str(KIMAP_ADDRESS) .expect("Failed to parse KIMAP_ADDRESS constant"), } } fn set_contract_address(&mut self, contract_address: eth::Address) { - if let Ok(bytes) = serde_json::to_vec(&contract_address) { - self.kv - .set(&Self::meta_contract_address_key().to_string(), &bytes, None) - .expect("Failed to set contract address"); - } + self.kv + .set_as::(&Self::meta_contract_address_key(), &contract_address, None) + .expect("Failed to set contract address"); } } -// impl From for WitState { -// fn from(s: State) -> Self { -// let contract_address: [u8; 20] = s.contract_address.into(); -// WitState { -// chain_id: s.chain_id.clone(), -// contract_address: contract_address.to_vec(), -// names: s -// .names -// .iter() -// .map(|(k, v)| (k.clone(), v.clone())) -// .collect::>(), -// nodes: s -// .nodes -// .iter() -// .map(|(k, v)| (k.clone(), v.clone().into())) -// .collect::>(), -// last_block: s.last_block.clone(), -// } -// } -// } - impl From for WitKnsUpdate { fn from(k: net::KnsUpdate) -> Self { WitKnsUpdate { @@ -344,6 +290,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { // 60s timeout -- these calls can take a long time // if they do time out, we try them again let eth_provider: eth::Provider = eth::Provider::new(chain_id, SUBSCRIPTION_TIMEOUT); + let _kimap_helper = kimap::Kimap::new(eth_provider.clone(), kimap_address); // subscribe to logs first, so no logs are missed eth_provider.subscribe_loop(1, mints_filter.clone()); @@ -427,11 +374,6 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { )) .send()?; } - // note no longer relevant. - // TODO: redo with iterator once available. - IndexerRequest::GetState(GetStateRequest { .. }) => { - Response::new().body(serde_json::to_vec(&state)?).send()?; - } } } } diff --git a/kinode/packages/kns-indexer/pkg/scripts.json b/kinode/packages/kns-indexer/pkg/scripts.json index bee99cd1..25798ef7 100644 --- a/kinode/packages/kns-indexer/pkg/scripts.json +++ b/kinode/packages/kns-indexer/pkg/scripts.json @@ -10,17 +10,5 @@ "eth:distro:sys" ], "wit_version": 1 - }, - "state.wasm": { - "root": false, - "public": false, - "request_networking": false, - "request_capabilities": [ - "kns-indexer:kns-indexer:sys" - ], - "grant_capabilities": [ - "kns-indexer:kns-indexer:sys" - ], - "wit_version": 1 } } diff --git a/kinode/packages/kns-indexer/state/Cargo.toml b/kinode/packages/kns-indexer/state/Cargo.toml deleted file mode 100644 index 4bd0387d..00000000 --- a/kinode/packages/kns-indexer/state/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "state" -version = "0.1.0" -edition = "2021" - -[features] -simulation-mode = [] - -[dependencies] -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "9c441fe" } -process_macros = "0.1" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -wit-bindgen = "0.36.0" - -[lib] -crate-type = ["cdylib"] - -[package.metadata.component] -package = "kinode:process" diff --git a/kinode/packages/kns-indexer/state/src/lib.rs b/kinode/packages/kns-indexer/state/src/lib.rs deleted file mode 100644 index 27df8a95..00000000 --- a/kinode/packages/kns-indexer/state/src/lib.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::kinode::process::kns_indexer::{GetStateRequest, IndexerRequest, IndexerResponse}; -use kinode_process_lib::{eth, script, Address, Message, Request}; - -wit_bindgen::generate!({ - path: "target/wit", - world: "kns-indexer-sys-v0", - generate_unused_types: true, - additional_derives: [serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto], -}); - -script!(init); -fn init(_our: Address, _args: String) -> String { - // we don't take any args - - let Ok(Message::Response { body, .. }) = - Request::to(("our", "kns-indexer", "kns-indexer", "sys")) - .body(IndexerRequest::GetState(GetStateRequest { block: 0 })) - .send_and_await_response(10) - .unwrap() - else { - return "failed to get state from kns-indexer".to_string(); - }; - let Ok(IndexerResponse::GetState(state)) = body.try_into() else { - return "failed to deserialize state".to_string(); - }; - // can change later, but for now, just print every known node name - let mut names = state - .names - .iter() - .map(|(_k, v)| v.clone()) - .collect::>(); - names.sort(); - let contract_address: [u8; 20] = state - .contract_address - .try_into() - .expect("invalid contract addess: doesn't have 20 bytes"); - let contract_address: eth::Address = contract_address.into(); - format!( - "\nrunning on chain id {}\nCA: {}\n{} known nodes as of block {}\n {}", - state.chain_id, - contract_address, - names.len(), - state.last_block, - names.join("\n ") - ) -} From a6bb05173ce39708de6267293fd5410f4f656590 Mon Sep 17 00:00:00 2001 From: hosted-fornet Date: Tue, 17 Dec 2024 17:30:15 -0800 Subject: [PATCH 19/30] use process_lib with matching alloy version --- Cargo.lock | 78 +-- kinode/packages/app-store/Cargo.lock | 2 +- .../packages/app-store/app-store/Cargo.toml | 2 +- kinode/packages/app-store/chain/Cargo.toml | 2 +- kinode/packages/app-store/download/Cargo.toml | 2 +- .../packages/app-store/downloads/Cargo.toml | 2 +- .../packages/app-store/ft-worker/Cargo.toml | 2 +- kinode/packages/app-store/install/Cargo.toml | 2 +- .../packages/app-store/uninstall/Cargo.toml | 2 +- kinode/packages/chess/Cargo.lock | 2 +- kinode/packages/chess/chess/Cargo.toml | 2 +- kinode/packages/contacts/Cargo.lock | 2 +- kinode/packages/contacts/contacts/Cargo.toml | 2 +- kinode/packages/contacts/get-names/Cargo.toml | 2 +- kinode/packages/homepage/Cargo.lock | 2 +- kinode/packages/homepage/homepage/Cargo.toml | 2 +- kinode/packages/kns-indexer/Cargo.lock | 477 +++++++++--------- .../packages/kns-indexer/get-block/Cargo.toml | 2 +- .../kns-indexer/kns-indexer/Cargo.toml | 2 +- kinode/packages/settings/Cargo.lock | 2 +- kinode/packages/settings/settings/Cargo.toml | 2 +- kinode/packages/terminal/Cargo.lock | 2 +- kinode/packages/terminal/alias/Cargo.toml | 2 +- kinode/packages/terminal/cat/Cargo.toml | 2 +- kinode/packages/terminal/echo/Cargo.toml | 2 +- kinode/packages/terminal/help/Cargo.toml | 2 +- kinode/packages/terminal/hi/Cargo.toml | 2 +- kinode/packages/terminal/kfetch/Cargo.toml | 2 +- kinode/packages/terminal/kill/Cargo.toml | 2 +- kinode/packages/terminal/m/Cargo.toml | 2 +- .../terminal/net-diagnostics/Cargo.toml | 2 +- kinode/packages/terminal/peer/Cargo.toml | 2 +- kinode/packages/terminal/peers/Cargo.toml | 2 +- kinode/packages/terminal/terminal/Cargo.toml | 2 +- kinode/packages/terminal/top/Cargo.toml | 2 +- kinode/packages/tester/Cargo.lock | 2 +- kinode/packages/tester/tester/Cargo.toml | 2 +- 37 files changed, 307 insertions(+), 318 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8421261a..63707f15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,7 +94,7 @@ name = "alias" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -1275,7 +1275,7 @@ dependencies = [ "alloy-sol-types 0.8.15", "anyhow", "bincode", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "process_macros", "rand 0.8.5", "serde", @@ -1858,7 +1858,7 @@ name = "cat" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -1922,7 +1922,7 @@ dependencies = [ "alloy-sol-types 0.8.15", "anyhow", "bincode", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "process_macros", "rand 0.8.5", "serde", @@ -1941,7 +1941,7 @@ version = "0.2.1" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "pleco", "serde", "serde_json", @@ -2139,7 +2139,7 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" name = "contacts" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "process_macros", "serde", "serde_json", @@ -2735,7 +2735,7 @@ name = "download" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "process_macros", "serde", "serde_json", @@ -2747,7 +2747,7 @@ name = "downloads" version = "0.5.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "process_macros", "rand 0.8.5", "serde", @@ -2784,7 +2784,7 @@ dependencies = [ name = "echo" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "wit-bindgen 0.36.0", ] @@ -3052,7 +3052,7 @@ version = "0.2.0" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "process_macros", "rand 0.8.5", "serde", @@ -3206,7 +3206,7 @@ dependencies = [ name = "get_block" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0209da1)", + "kinode_process_lib 0.10.0", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -3402,7 +3402,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" name = "help" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "wit-bindgen 0.36.0", ] @@ -3431,7 +3431,7 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" name = "hi" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -3464,7 +3464,7 @@ version = "0.1.2" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -3907,7 +3907,7 @@ name = "install" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "process_macros", "serde", "serde_json", @@ -4085,7 +4085,7 @@ name = "kfetch" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "rmp-serde", "serde", "serde_json", @@ -4097,7 +4097,7 @@ name = "kill" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -4191,29 +4191,7 @@ dependencies = [ [[package]] name = "kinode_process_lib" version = "0.10.0" -source = "git+https://github.com/kinode-dao/process_lib?rev=0209da1#0209da15340d9a2b92152916a8349d8f248775e5" -dependencies = [ - "alloy 0.1.4", - "alloy-primitives 0.7.7", - "alloy-sol-macro 0.7.7", - "alloy-sol-types 0.7.7", - "anyhow", - "bincode", - "http 1.2.0", - "mime_guess", - "rand 0.8.5", - "rmp-serde", - "serde", - "serde_json", - "thiserror 1.0.69", - "url", - "wit-bindgen 0.36.0", -] - -[[package]] -name = "kinode_process_lib" -version = "0.10.0" -source = "git+https://github.com/kinode-dao/process_lib?rev=0443ece#0443ece2a5dfdbdc1b40db454a1535e1b1c1a1b3" +source = "git+https://github.com/kinode-dao/process_lib?rev=d97e012#d97e012842dd4cc0e036d5de5048064e770302ab" dependencies = [ "alloy 0.8.1", "alloy-primitives 0.8.15", @@ -4278,7 +4256,7 @@ dependencies = [ "alloy-sol-types 0.8.15", "anyhow", "hex", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0209da1)", + "kinode_process_lib 0.10.0", "process_macros", "rmp-serde", "serde", @@ -4515,7 +4493,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "regex", "serde", "serde_json", @@ -4684,7 +4662,7 @@ dependencies = [ name = "net-diagnostics" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "rmp-serde", "serde", "wit-bindgen 0.36.0", @@ -5043,7 +5021,7 @@ dependencies = [ name = "peer" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "rmp-serde", "serde", "wit-bindgen 0.36.0", @@ -5053,7 +5031,7 @@ dependencies = [ name = "peers" version = "0.1.0" dependencies = [ - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "rmp-serde", "serde", "wit-bindgen 0.36.0", @@ -6169,7 +6147,7 @@ dependencies = [ "anyhow", "base64 0.22.1", "bincode", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "rmp-serde", "serde", "serde_json", @@ -6592,7 +6570,7 @@ version = "0.1.1" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "rand 0.8.5", "regex", "serde", @@ -6606,7 +6584,7 @@ version = "0.1.1" dependencies = [ "anyhow", "bincode", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "process_macros", "serde", "serde_json", @@ -6897,7 +6875,7 @@ version = "0.2.0" dependencies = [ "anyhow", "clap", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "serde", "serde_json", "wit-bindgen 0.36.0", @@ -7264,7 +7242,7 @@ name = "uninstall" version = "0.1.0" dependencies = [ "anyhow", - "kinode_process_lib 0.10.0 (git+https://github.com/kinode-dao/process_lib?rev=0443ece)", + "kinode_process_lib 0.10.0", "process_macros", "serde", "serde_json", diff --git a/kinode/packages/app-store/Cargo.lock b/kinode/packages/app-store/Cargo.lock index 2e4cf025..9ca53fa8 100644 --- a/kinode/packages/app-store/Cargo.lock +++ b/kinode/packages/app-store/Cargo.lock @@ -1890,7 +1890,7 @@ dependencies = [ [[package]] name = "kinode_process_lib" version = "0.10.0" -source = "git+https://github.com/kinode-dao/process_lib?rev=0443ece#0443ece2a5dfdbdc1b40db454a1535e1b1c1a1b3" +source = "git+https://github.com/kinode-dao/process_lib?rev=d97e012#d97e012842dd4cc0e036d5de5048064e770302ab" dependencies = [ "alloy", "alloy-primitives", diff --git a/kinode/packages/app-store/app-store/Cargo.toml b/kinode/packages/app-store/app-store/Cargo.toml index b916657a..b0848e88 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.8.15" alloy-sol-types = "0.8.15" anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } process_macros = "0.1" rand = "0.8" serde = { version = "1.0", features = ["derive"] } diff --git a/kinode/packages/app-store/chain/Cargo.toml b/kinode/packages/app-store/chain/Cargo.toml index 9ca0aa70..d3766d88 100644 --- a/kinode/packages/app-store/chain/Cargo.toml +++ b/kinode/packages/app-store/chain/Cargo.toml @@ -11,7 +11,7 @@ alloy-primitives = "0.8.15" alloy-sol-types = "0.8.15" anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } process_macros = "0.1" rand = "0.8" serde = { version = "1.0", features = ["derive"] } diff --git a/kinode/packages/app-store/download/Cargo.toml b/kinode/packages/app-store/download/Cargo.toml index dd746604..1f338f2c 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 = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } process_macros = "0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/kinode/packages/app-store/downloads/Cargo.toml b/kinode/packages/app-store/downloads/Cargo.toml index 432ccb6a..a4dcce47 100644 --- a/kinode/packages/app-store/downloads/Cargo.toml +++ b/kinode/packages/app-store/downloads/Cargo.toml @@ -8,7 +8,7 @@ simulation-mode = [] [dependencies] anyhow = "1.0" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } process_macros = "0.1" rand = "0.8" serde = { version = "1.0", features = ["derive"] } diff --git a/kinode/packages/app-store/ft-worker/Cargo.toml b/kinode/packages/app-store/ft-worker/Cargo.toml index d9f8c64f..d06fd11f 100644 --- a/kinode/packages/app-store/ft-worker/Cargo.toml +++ b/kinode/packages/app-store/ft-worker/Cargo.toml @@ -9,7 +9,7 @@ simulation-mode = [] [dependencies] anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } process_macros = "0.1" rand = "0.8" serde = { version = "1.0", features = ["derive"] } diff --git a/kinode/packages/app-store/install/Cargo.toml b/kinode/packages/app-store/install/Cargo.toml index 2b2b471f..4bcced59 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 = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } process_macros = "0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/kinode/packages/app-store/uninstall/Cargo.toml b/kinode/packages/app-store/uninstall/Cargo.toml index d963dfad..ebc3f81a 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 = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } process_macros = "0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/kinode/packages/chess/Cargo.lock b/kinode/packages/chess/Cargo.lock index efb859c3..8bba0aeb 100644 --- a/kinode/packages/chess/Cargo.lock +++ b/kinode/packages/chess/Cargo.lock @@ -1813,7 +1813,7 @@ dependencies = [ [[package]] name = "kinode_process_lib" version = "0.10.0" -source = "git+https://github.com/kinode-dao/process_lib?rev=0443ece#0443ece2a5dfdbdc1b40db454a1535e1b1c1a1b3" +source = "git+https://github.com/kinode-dao/process_lib?rev=d97e012#d97e012842dd4cc0e036d5de5048064e770302ab" dependencies = [ "alloy", "alloy-primitives", diff --git a/kinode/packages/chess/chess/Cargo.toml b/kinode/packages/chess/chess/Cargo.toml index e980d875..46e84273 100644 --- a/kinode/packages/chess/chess/Cargo.toml +++ b/kinode/packages/chess/chess/Cargo.toml @@ -9,7 +9,7 @@ simulation-mode = [] [dependencies] anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } pleco = "0.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/kinode/packages/contacts/Cargo.lock b/kinode/packages/contacts/Cargo.lock index ca76c4d3..d7bd2664 100644 --- a/kinode/packages/contacts/Cargo.lock +++ b/kinode/packages/contacts/Cargo.lock @@ -1774,7 +1774,7 @@ dependencies = [ [[package]] name = "kinode_process_lib" version = "0.10.0" -source = "git+https://github.com/kinode-dao/process_lib?rev=0443ece#0443ece2a5dfdbdc1b40db454a1535e1b1c1a1b3" +source = "git+https://github.com/kinode-dao/process_lib?rev=d97e012#d97e012842dd4cc0e036d5de5048064e770302ab" dependencies = [ "alloy", "alloy-primitives", diff --git a/kinode/packages/contacts/contacts/Cargo.toml b/kinode/packages/contacts/contacts/Cargo.toml index 2cf504ef..78ca9b09 100644 --- a/kinode/packages/contacts/contacts/Cargo.toml +++ b/kinode/packages/contacts/contacts/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" simulation-mode = [] [dependencies] -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } process_macros = "0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/kinode/packages/contacts/get-names/Cargo.toml b/kinode/packages/contacts/get-names/Cargo.toml index 17b2981d..b47c55cb 100644 --- a/kinode/packages/contacts/get-names/Cargo.toml +++ b/kinode/packages/contacts/get-names/Cargo.toml @@ -6,7 +6,7 @@ publish = false [dependencies] anyhow = "1.0" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } process_macros = "0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/kinode/packages/homepage/Cargo.lock b/kinode/packages/homepage/Cargo.lock index 49f4d3e7..93d39986 100644 --- a/kinode/packages/homepage/Cargo.lock +++ b/kinode/packages/homepage/Cargo.lock @@ -1763,7 +1763,7 @@ dependencies = [ [[package]] name = "kinode_process_lib" version = "0.10.0" -source = "git+https://github.com/kinode-dao/process_lib?rev=0443ece#0443ece2a5dfdbdc1b40db454a1535e1b1c1a1b3" +source = "git+https://github.com/kinode-dao/process_lib?rev=d97e012#d97e012842dd4cc0e036d5de5048064e770302ab" dependencies = [ "alloy", "alloy-primitives", diff --git a/kinode/packages/homepage/homepage/Cargo.toml b/kinode/packages/homepage/homepage/Cargo.toml index afe74908..6dd29c63 100644 --- a/kinode/packages/homepage/homepage/Cargo.toml +++ b/kinode/packages/homepage/homepage/Cargo.toml @@ -9,7 +9,7 @@ simulation-mode = [] [dependencies] anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.36.0" diff --git a/kinode/packages/kns-indexer/Cargo.lock b/kinode/packages/kns-indexer/Cargo.lock index b2ee67be..8d5c6fad 100644 --- a/kinode/packages/kns-indexer/Cargo.lock +++ b/kinode/packages/kns-indexer/Cargo.lock @@ -24,6 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -37,9 +38,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba1c79677c9ce51c8d45e20845b05e6fb070ea2c863fba03ad6af2c778474bd" +checksum = "e7e1758e5d759c0114140152ae72032eafcfdd7b599e995ebbc8eeafa2b4c977" dependencies = [ "alloy-consensus", "alloy-core", @@ -59,47 +60,65 @@ version = "0.1.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0161082e0edd9013d23083465cc04b20e44b7a15646d36ba7b0cdb7cd6fe18f" dependencies = [ - "alloy-primitives 0.8.15", + "alloy-primitives", "num_enum", "strum", ] [[package]] name = "alloy-consensus" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da374e868f54c7f4ad2ad56829827badca388efd645f8cf5fccc61c2b5343504" +checksum = "a205d0cbb7bfdf9f4fd4b0ec842bc4c5f926e8c14ec3072d3fd75dd363baf1e0" dependencies = [ "alloy-eips", - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-rlp", "alloy-serde", + "alloy-trie", + "auto_impl", "c-kzg", + "derive_more", + "serde", +] + +[[package]] +name = "alloy-consensus-any" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993c34090a3f281cb746fd1604520cf21f8407ffbeb006aaa34c0556bffa718e" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", "serde", ] [[package]] name = "alloy-core" -version = "0.7.7" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529fc6310dc1126c8de51c376cbc59c79c7f662bd742be7dc67055d5421a81b4" +checksum = "c618bd382f0bc2ac26a7e4bfae01c9b015ca8f21b37ca40059ae35a7e62b3dc6" dependencies = [ "alloy-dyn-abi", - "alloy-json-abi 0.7.7", - "alloy-primitives 0.7.7", - "alloy-sol-types 0.7.7", + "alloy-json-abi", + "alloy-primitives", + "alloy-rlp", + "alloy-sol-types", ] [[package]] name = "alloy-dyn-abi" -version = "0.7.7" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413902aa18a97569e60f679c23f46a18db1656d87ab4d4e49d0e1e52042f66df" +checksum = "41056bde53ae10ffbbf11618efbe1e0290859e5eab0fe9ef82ebdb62f12a866f" dependencies = [ - "alloy-json-abi 0.7.7", - "alloy-primitives 0.7.7", - "alloy-sol-type-parser 0.7.7", - "alloy-sol-types 0.7.7", + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", "const-hex", "itoa", "serde", @@ -108,15 +127,41 @@ dependencies = [ ] [[package]] -name = "alloy-eips" -version = "0.1.4" +name = "alloy-eip2930" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f76ecab54890cdea1e4808fc0891c7e6cfcf71fe1a9fe26810c7280ef768f4ed" +checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c986539255fb839d1533c128e190e557e52ff652c9ef62939e233a81dd93f7e" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more", + "serde", +] + +[[package]] +name = "alloy-eips" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d9907c29ce622946759bf4fd3418166bfeae76c1c544b8081c7be3acd9b4be" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", "alloy-rlp", "alloy-serde", "c-kzg", + "derive_more", "once_cell", "serde", "sha2", @@ -124,92 +169,78 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca15afde1b6d15e3fc1c97421262b1bbb37aee45752e3c8b6d6f13f776554ff" +checksum = "68f13f7405a8eb8021258994ed1beab490c3e509ebbe2c18e1c24ae10749d56b" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives", "alloy-serde", + "alloy-trie", "serde", ] -[[package]] -name = "alloy-json-abi" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc05b04ac331a9f07e3a4036ef7926e49a8bf84a99a1ccfc7e2ab55a5fcbb372" -dependencies = [ - "alloy-primitives 0.7.7", - "alloy-sol-type-parser 0.7.7", - "serde", - "serde_json", -] - [[package]] name = "alloy-json-abi" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c357da577dfb56998d01f574d81ad7a1958d248740a7981b205d69d65a7da404" dependencies = [ - "alloy-primitives 0.8.15", - "alloy-sol-type-parser 0.8.15", + "alloy-primitives", + "alloy-sol-type-parser", "serde", "serde_json", ] [[package]] name = "alloy-json-rpc" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d6f34930b7e3e2744bcc79056c217f00cb2abb33bc5d4ff88da7623c5bb078b" +checksum = "39a786ce6bc7539dc30cabac6b7875644247c9e7d780e71a9f254d42ebdc013c" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives", + "alloy-sol-types", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.7", "tracing", ] [[package]] name = "alloy-network" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f6895fc31b48fa12306ef9b4f78b7764f8bd6d7d91cdb0a40e233704a0f23f" +checksum = "99051f82f77159d5bee06108f33cffee02849e2861fc500bf74213aa2ae8a26e" dependencies = [ "alloy-consensus", + "alloy-consensus-any", "alloy-eips", "alloy-json-rpc", - "alloy-primitives 0.7.7", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-any", "alloy-rpc-types-eth", "alloy-serde", "alloy-signer", - "alloy-sol-types 0.7.7", + "alloy-sol-types", "async-trait", "auto_impl", "futures-utils-wasm", - "thiserror 1.0.69", + "serde", + "serde_json", + "thiserror 2.0.7", ] [[package]] -name = "alloy-primitives" -version = "0.7.7" +name = "alloy-network-primitives" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" +checksum = "d2aff127863f8279921397be8af0ac3f05a8757d5c4c972b491c278518fa07c7" dependencies = [ - "alloy-rlp", - "bytes", - "cfg-if", - "const-hex", - "derive_more 0.99.18", - "hex-literal", - "itoa", - "k256", - "keccak-asm", - "proptest", - "rand", - "ruint", + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-serde", "serde", - "tiny-keccak", ] [[package]] @@ -222,7 +253,7 @@ dependencies = [ "bytes", "cfg-if", "const-hex", - "derive_more 1.0.0", + "derive_more", "foldhash", "hashbrown 0.15.2", "hex-literal", @@ -242,16 +273,17 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c538bfa893d07e27cb4f3c1ab5f451592b7c526d511d62b576a2ce59e146e4a" +checksum = "0280a4f68e0cefde9449ee989a248230efbe3f95255299d2a7a92009e154629d" dependencies = [ "alloy-chains", "alloy-consensus", "alloy-eips", "alloy-json-rpc", "alloy-network", - "alloy-primitives 0.7.7", + "alloy-network-primitives", + "alloy-primitives", "alloy-rpc-client", "alloy-rpc-types-eth", "alloy-transport", @@ -263,13 +295,17 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", + "parking_lot", "pin-project", "reqwest", + "schnellru", "serde", "serde_json", + "thiserror 2.0.7", "tokio", "tracing", "url", + "wasmtimer", ] [[package]] @@ -296,11 +332,12 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba31bae67773fd5a60020bea900231f8396202b7feca4d0c70c6b59308ab4a8" +checksum = "b6fc8b0f68619cfab3a2e15dca7b80ab266f78430bb4353dec546528e04b7449" dependencies = [ "alloy-json-rpc", + "alloy-primitives", "alloy-transport", "alloy-transport-http", "futures", @@ -313,73 +350,75 @@ dependencies = [ "tower", "tracing", "url", + "wasmtimer", ] [[package]] name = "alloy-rpc-types" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "184a7a42c7ba9141cc9e76368356168c282c3bc3d9e5d78f3556bdfe39343447" +checksum = "986f23fe42ac95832901a24b93c20f7ed2b9644394c02b86222801230da60041" dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-any" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e3aa433d3657b42e98e257ee6fa201f5c853245648a33da8fbb7497a5008bf" +dependencies = [ + "alloy-consensus-any", "alloy-rpc-types-eth", "alloy-serde", ] [[package]] name = "alloy-rpc-types-eth" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4123ee21f99ba4bd31bfa36ba89112a18a500f8b452f02b35708b1b951e2b9" +checksum = "0643cc497a71941f526454fe4fecb47e9307d3a7b6c05f70718a0341643bcc79" dependencies = [ "alloy-consensus", + "alloy-consensus-any", "alloy-eips", - "alloy-primitives 0.7.7", + "alloy-network-primitives", + "alloy-primitives", "alloy-rlp", "alloy-serde", - "alloy-sol-types 0.7.7", + "alloy-sol-types", + "derive_more", "itertools 0.13.0", "serde", "serde_json", - "thiserror 1.0.69", ] [[package]] name = "alloy-serde" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9416c52959e66ead795a11f4a86c248410e9e368a0765710e57055b8a1774dd6" +checksum = "ea61b049d7ecc66a29f107970dae493d0908e366048f7484a1ca9b02c85f9b2b" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives", "serde", "serde_json", ] [[package]] name = "alloy-signer" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b33753c09fa1ad85e5b092b8dc2372f1e337a42e84b9b4cff9fede75ba4adb32" +checksum = "93461b0e79c2ddd791fec5f369ab5c2686a33bbb03530144972edf5248f8a2c7" dependencies = [ - "alloy-primitives 0.7.7", + "alloy-primitives", "async-trait", "auto_impl", "elliptic-curve", "k256", - "thiserror 1.0.69", -] - -[[package]] -name = "alloy-sol-macro" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" -dependencies = [ - "alloy-sol-macro-expander 0.7.7", - "alloy-sol-macro-input 0.7.7", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.90", + "thiserror 2.0.7", ] [[package]] @@ -388,39 +427,21 @@ version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9d64f851d95619233f74b310f12bcf16e0cbc27ee3762b6115c14a84809280a" dependencies = [ - "alloy-sol-macro-expander 0.8.15", - "alloy-sol-macro-input 0.8.15", + "alloy-sol-macro-expander", + "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", "syn 2.0.90", ] -[[package]] -name = "alloy-sol-macro-expander" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" -dependencies = [ - "alloy-sol-macro-input 0.7.7", - "const-hex", - "heck", - "indexmap", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.90", - "syn-solidity 0.7.7", - "tiny-keccak", -] - [[package]] name = "alloy-sol-macro-expander" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bf7ed1574b699f48bf17caab4e6e54c6d12bc3c006ab33d58b1e227c1c3559f" dependencies = [ - "alloy-sol-macro-input 0.8.15", + "alloy-sol-macro-input", "const-hex", "heck", "indexmap", @@ -428,25 +449,10 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.90", - "syn-solidity 0.8.15", + "syn-solidity", "tiny-keccak", ] -[[package]] -name = "alloy-sol-macro-input" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" -dependencies = [ - "const-hex", - "dunce", - "heck", - "proc-macro2", - "quote", - "syn 2.0.90", - "syn-solidity 0.7.7", -] - [[package]] name = "alloy-sol-macro-input" version = "0.8.15" @@ -459,17 +465,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.90", - "syn-solidity 0.8.15", -] - -[[package]] -name = "alloy-sol-type-parser" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" -dependencies = [ - "serde", - "winnow", + "syn-solidity", ] [[package]] @@ -482,36 +478,24 @@ dependencies = [ "winnow", ] -[[package]] -name = "alloy-sol-types" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" -dependencies = [ - "alloy-primitives 0.7.7", - "alloy-sol-macro 0.7.7", - "const-hex", - "serde", -] - [[package]] name = "alloy-sol-types" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1174cafd6c6d810711b4e00383037bdb458efc4fe3dbafafa16567e0320c54d8" dependencies = [ - "alloy-json-abi 0.8.15", - "alloy-primitives 0.8.15", - "alloy-sol-macro 0.8.15", + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", "const-hex", "serde", ] [[package]] name = "alloy-transport" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b51a291f949f755e6165c3ed562883175c97423703703355f4faa4b7d0a57c" +checksum = "baf656f983e14812df65b5aee37e7b37535f68a848295e6ed736b2054a405cb7" dependencies = [ "alloy-json-rpc", "base64", @@ -519,18 +503,19 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.7", "tokio", "tower", "tracing", "url", + "wasmtimer", ] [[package]] name = "alloy-transport-http" -version = "0.1.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d65871f9f1cafe1ed25cde2f1303be83e6473e995a2d56c275ae4fcce6119c" +checksum = "ec938d51a47b7953b1c0fd8ddeb89a29eb113cd4908dfc4e01c7893b252d669f" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -541,6 +526,22 @@ dependencies = [ "url", ] +[[package]] +name = "alloy-trie" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a5fd8fea044cc9a8c8a50bb6f28e31f0385d820f116c5b98f6f4e55d6e5590b" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arrayvec", + "derive_more", + "nybbles", + "serde", + "smallvec", + "tracing", +] + [[package]] name = "anyhow" version = "1.0.94" @@ -676,6 +677,9 @@ name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] [[package]] name = "async-stream" @@ -899,12 +903,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core-foundation" version = "0.9.4" @@ -930,6 +928,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crunchy" version = "0.2.2" @@ -960,11 +964,12 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.5.3" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", + "crossbeam-utils", "hashbrown 0.14.5", "lock_api", "once_cell", @@ -992,19 +997,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "derive_more" -version = "0.99.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version 0.4.1", - "syn 2.0.90", -] - [[package]] name = "derive_more" version = "1.0.0" @@ -1361,6 +1353,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + [[package]] name = "hashbrown" version = "0.14.5" @@ -1763,12 +1761,12 @@ dependencies = [ [[package]] name = "kinode_process_lib" version = "0.10.0" -source = "git+https://github.com/kinode-dao/process_lib?rev=0209da1#0209da15340d9a2b92152916a8349d8f248775e5" +source = "git+https://github.com/kinode-dao/process_lib?rev=d97e012#d97e012842dd4cc0e036d5de5048064e770302ab" dependencies = [ "alloy", - "alloy-primitives 0.7.7", - "alloy-sol-macro 0.7.7", - "alloy-sol-types 0.7.7", + "alloy-primitives", + "alloy-sol-macro", + "alloy-sol-types", "anyhow", "bincode", "http", @@ -1786,8 +1784,8 @@ dependencies = [ name = "kns-indexer" version = "0.2.0" dependencies = [ - "alloy-primitives 0.8.15", - "alloy-sol-types 0.8.15", + "alloy-primitives", + "alloy-sol-types", "anyhow", "hex", "kinode_process_lib", @@ -1978,6 +1976,19 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "nybbles" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95f06be0417d97f81fe4e5c86d7d01b392655a9cac9c19a848aa033e18937b23" +dependencies = [ + "alloy-rlp", + "const-hex", + "proptest", + "serde", + "smallvec", +] + [[package]] name = "object" version = "0.36.5" @@ -2063,6 +2074,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + [[package]] name = "parking_lot_core" version = "0.9.10" @@ -2186,30 +2207,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -2557,6 +2554,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schnellru" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" +dependencies = [ + "ahash", + "cfg-if", + "hashbrown 0.13.2", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2729,6 +2737,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -2821,18 +2832,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "syn-solidity" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" -dependencies = [ - "paste", - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "syn-solidity" version = "0.8.15" @@ -3033,17 +3032,16 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.13" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -3064,7 +3062,6 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3315,6 +3312,20 @@ dependencies = [ "semver 1.0.24", ] +[[package]] +name = "wasmtimer" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0048ad49a55b9deb3953841fa1fc5858f0efbcb7a18868c899a360269fac1b23" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + [[package]] name = "web-sys" version = "0.3.76" diff --git a/kinode/packages/kns-indexer/get-block/Cargo.toml b/kinode/packages/kns-indexer/get-block/Cargo.toml index f818b82e..3f04d0a4 100644 --- a/kinode/packages/kns-indexer/get-block/Cargo.toml +++ b/kinode/packages/kns-indexer/get-block/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" simulation-mode = [] [dependencies] -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0209da1" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.36.0" diff --git a/kinode/packages/kns-indexer/kns-indexer/Cargo.toml b/kinode/packages/kns-indexer/kns-indexer/Cargo.toml index 2ba73d91..42c65d83 100644 --- a/kinode/packages/kns-indexer/kns-indexer/Cargo.toml +++ b/kinode/packages/kns-indexer/kns-indexer/Cargo.toml @@ -11,7 +11,7 @@ anyhow = "1.0" alloy-primitives = "0.8.15" alloy-sol-types = "0.8.15" hex = "0.4.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0209da1" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } process_macros = "0.1" rmp-serde = "1.1.2" serde = { version = "1.0", features = ["derive"] } diff --git a/kinode/packages/settings/Cargo.lock b/kinode/packages/settings/Cargo.lock index 94eb2004..fd83dedd 100644 --- a/kinode/packages/settings/Cargo.lock +++ b/kinode/packages/settings/Cargo.lock @@ -1751,7 +1751,7 @@ dependencies = [ [[package]] name = "kinode_process_lib" version = "0.10.0" -source = "git+https://github.com/kinode-dao/process_lib?rev=0443ece#0443ece2a5dfdbdc1b40db454a1535e1b1c1a1b3" +source = "git+https://github.com/kinode-dao/process_lib?rev=d97e012#d97e012842dd4cc0e036d5de5048064e770302ab" dependencies = [ "alloy", "alloy-primitives", diff --git a/kinode/packages/settings/settings/Cargo.toml b/kinode/packages/settings/settings/Cargo.toml index 6c376492..ad4fd1be 100644 --- a/kinode/packages/settings/settings/Cargo.toml +++ b/kinode/packages/settings/settings/Cargo.toml @@ -10,7 +10,7 @@ simulation-mode = [] anyhow = "1.0" base64 = "0.22.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } rmp-serde = "1.2.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/kinode/packages/terminal/Cargo.lock b/kinode/packages/terminal/Cargo.lock index 676f47ce..e2f86087 100644 --- a/kinode/packages/terminal/Cargo.lock +++ b/kinode/packages/terminal/Cargo.lock @@ -1919,7 +1919,7 @@ dependencies = [ [[package]] name = "kinode_process_lib" version = "0.10.0" -source = "git+https://github.com/kinode-dao/process_lib?rev=0443ece#0443ece2a5dfdbdc1b40db454a1535e1b1c1a1b3" +source = "git+https://github.com/kinode-dao/process_lib?rev=d97e012#d97e012842dd4cc0e036d5de5048064e770302ab" dependencies = [ "alloy", "alloy-primitives", diff --git a/kinode/packages/terminal/alias/Cargo.toml b/kinode/packages/terminal/alias/Cargo.toml index 6c6f628b..35fc1537 100644 --- a/kinode/packages/terminal/alias/Cargo.toml +++ b/kinode/packages/terminal/alias/Cargo.toml @@ -8,7 +8,7 @@ simulation-mode = [] [dependencies] anyhow = "1.0" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.36.0" diff --git a/kinode/packages/terminal/cat/Cargo.toml b/kinode/packages/terminal/cat/Cargo.toml index 168eba03..11e4c4f7 100644 --- a/kinode/packages/terminal/cat/Cargo.toml +++ b/kinode/packages/terminal/cat/Cargo.toml @@ -8,7 +8,7 @@ simulation-mode = [] [dependencies] anyhow = "1.0" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.36.0" diff --git a/kinode/packages/terminal/echo/Cargo.toml b/kinode/packages/terminal/echo/Cargo.toml index 4bec4e28..935588e4 100644 --- a/kinode/packages/terminal/echo/Cargo.toml +++ b/kinode/packages/terminal/echo/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" simulation-mode = [] [dependencies] -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } wit-bindgen = "0.36.0" [lib] diff --git a/kinode/packages/terminal/help/Cargo.toml b/kinode/packages/terminal/help/Cargo.toml index 2c8dd55e..44b87425 100644 --- a/kinode/packages/terminal/help/Cargo.toml +++ b/kinode/packages/terminal/help/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" simulation-mode = [] [dependencies] -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } wit-bindgen = "0.36.0" [lib] diff --git a/kinode/packages/terminal/hi/Cargo.toml b/kinode/packages/terminal/hi/Cargo.toml index 8dc8850b..9a96f8ac 100644 --- a/kinode/packages/terminal/hi/Cargo.toml +++ b/kinode/packages/terminal/hi/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" simulation-mode = [] [dependencies] -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.36.0" diff --git a/kinode/packages/terminal/kfetch/Cargo.toml b/kinode/packages/terminal/kfetch/Cargo.toml index 27dd5595..33329a1a 100644 --- a/kinode/packages/terminal/kfetch/Cargo.toml +++ b/kinode/packages/terminal/kfetch/Cargo.toml @@ -8,7 +8,7 @@ simulation-mode = [] [dependencies] anyhow = "1.0" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } rmp-serde = "1.1.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/kinode/packages/terminal/kill/Cargo.toml b/kinode/packages/terminal/kill/Cargo.toml index 1dc11cd8..4a3d9804 100644 --- a/kinode/packages/terminal/kill/Cargo.toml +++ b/kinode/packages/terminal/kill/Cargo.toml @@ -8,7 +8,7 @@ simulation-mode = [] [dependencies] anyhow = "1.0" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.36.0" diff --git a/kinode/packages/terminal/m/Cargo.toml b/kinode/packages/terminal/m/Cargo.toml index 8c3f7fb7..5da95c31 100644 --- a/kinode/packages/terminal/m/Cargo.toml +++ b/kinode/packages/terminal/m/Cargo.toml @@ -9,7 +9,7 @@ simulation-mode = [] [dependencies] anyhow = "1.0" clap = "4.4" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } regex = "1.10.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/kinode/packages/terminal/net-diagnostics/Cargo.toml b/kinode/packages/terminal/net-diagnostics/Cargo.toml index e21a9667..74b8852f 100644 --- a/kinode/packages/terminal/net-diagnostics/Cargo.toml +++ b/kinode/packages/terminal/net-diagnostics/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" simulation-mode = [] [dependencies] -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } rmp-serde = "1.1.2" serde = { version = "1.0", features = ["derive"] } wit-bindgen = "0.36.0" diff --git a/kinode/packages/terminal/peer/Cargo.toml b/kinode/packages/terminal/peer/Cargo.toml index b3184d7f..1836b22d 100644 --- a/kinode/packages/terminal/peer/Cargo.toml +++ b/kinode/packages/terminal/peer/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" simulation-mode = [] [dependencies] -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } rmp-serde = "1.1.2" serde = { version = "1.0", features = ["derive"] } wit-bindgen = "0.36.0" diff --git a/kinode/packages/terminal/peers/Cargo.toml b/kinode/packages/terminal/peers/Cargo.toml index ed5c5ca6..f8d72ccb 100644 --- a/kinode/packages/terminal/peers/Cargo.toml +++ b/kinode/packages/terminal/peers/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" simulation-mode = [] [dependencies] -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } rmp-serde = "1.1.2" serde = { version = "1.0", features = ["derive"] } wit-bindgen = "0.36.0" diff --git a/kinode/packages/terminal/terminal/Cargo.toml b/kinode/packages/terminal/terminal/Cargo.toml index 03370442..8fd22861 100644 --- a/kinode/packages/terminal/terminal/Cargo.toml +++ b/kinode/packages/terminal/terminal/Cargo.toml @@ -9,7 +9,7 @@ simulation-mode = [] [dependencies] anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } rand = "0.8" regex = "1.10.3" serde = { version = "1.0", features = ["derive"] } diff --git a/kinode/packages/terminal/top/Cargo.toml b/kinode/packages/terminal/top/Cargo.toml index 7c4bcf31..89733404 100644 --- a/kinode/packages/terminal/top/Cargo.toml +++ b/kinode/packages/terminal/top/Cargo.toml @@ -9,7 +9,7 @@ simulation-mode = [] [dependencies] anyhow = "1.0" clap = "4.4" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" wit-bindgen = "0.36.0" diff --git a/kinode/packages/tester/Cargo.lock b/kinode/packages/tester/Cargo.lock index ea6d0019..66a4cb5b 100644 --- a/kinode/packages/tester/Cargo.lock +++ b/kinode/packages/tester/Cargo.lock @@ -1751,7 +1751,7 @@ dependencies = [ [[package]] name = "kinode_process_lib" version = "0.10.0" -source = "git+https://github.com/kinode-dao/process_lib?rev=0443ece#0443ece2a5dfdbdc1b40db454a1535e1b1c1a1b3" +source = "git+https://github.com/kinode-dao/process_lib?rev=d97e012#d97e012842dd4cc0e036d5de5048064e770302ab" dependencies = [ "alloy", "alloy-primitives", diff --git a/kinode/packages/tester/tester/Cargo.toml b/kinode/packages/tester/tester/Cargo.toml index c79540fc..0ef09ec7 100644 --- a/kinode/packages/tester/tester/Cargo.toml +++ b/kinode/packages/tester/tester/Cargo.toml @@ -9,7 +9,7 @@ simulation-mode = [] [dependencies] anyhow = "1.0" bincode = "1.3.3" -kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "0443ece" } +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } process_macros = "0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" From 7c18d9a00fa28257521f0333b779d398fe3c8543 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Wed, 18 Dec 2024 14:51:15 -0500 Subject: [PATCH 20/30] add fallback to old insecure passwords for 0.10 only --- kinode/src/http/login.html | 25 +++++++++++++++++++ kinode/src/main.rs | 18 +++++++++++-- .../register-ui/src/pages/ImportKeyfile.tsx | 21 ++++++++++++++++ kinode/src/register-ui/src/pages/Login.tsx | 20 +++++++++++++++ package-lock.json | 6 +++++ 5 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 package-lock.json diff --git a/kinode/src/http/login.html b/kinode/src/http/login.html index 59f22746..e33d3971 100644 --- a/kinode/src/http/login.html +++ b/kinode/src/http/login.html @@ -156,6 +156,9 @@ + + + + diff --git a/kinode/packages/settings/settings/src/lib.rs b/kinode/packages/settings/settings/src/lib.rs index 2c46c103..d0c56fd3 100644 --- a/kinode/packages/settings/settings/src/lib.rs +++ b/kinode/packages/settings/settings/src/lib.rs @@ -1,10 +1,10 @@ use kinode_process_lib::{ - await_message, call_init, eth, get_blob, homepage, http, kernel_types, kimap, net, println, - Address, Capability, LazyLoadBlob, Message, NodeId, ProcessId, Request, Response, SendError, - SendErrorKind, + await_message, call_init, eth, get_blob, get_capability, homepage, http, kernel_types, kimap, + net, println, Address, Capability, LazyLoadBlob, Message, NodeId, ProcessId, Request, Response, + SendError, SendErrorKind, }; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::{collections::HashMap, vec}; const ICON: &str = include_str!("icon"); @@ -18,6 +18,7 @@ enum SettingsRequest { PeerId(NodeId), EthConfig(eth::EthConfigAction), Shutdown, + Reset, KillProcess(ProcessId), SetStylesheet(String), } @@ -464,6 +465,19 @@ fn handle_settings_request( .send() .unwrap(); } + SettingsRequest::Reset => { + // reset KNS + let kns_address = Address::new(&state.our.node, ("kns-indexer", "kns-indexer", "sys")); + let root_cap = get_capability(&kns_address, "{\"root\":true}"); + + if let Some(cap) = root_cap { + Request::to(("our", "kns-indexer", "kns-indexer", "sys")) + .body(serde_json::to_vec(&SettingsRequest::Reset).unwrap()) + .capabilities(vec![cap]) + .send() + .unwrap(); + } + } SettingsRequest::KillProcess(pid) => { // kill a process if let Err(_) = Request::to(("our", "kernel", "distro", "sys")) diff --git a/kinode/packages/settings/ui/src/App.tsx b/kinode/packages/settings/ui/src/App.tsx index 6d4078c6..06514c94 100644 --- a/kinode/packages/settings/ui/src/App.tsx +++ b/kinode/packages/settings/ui/src/App.tsx @@ -80,6 +80,11 @@ function App() { setTimeout(() => window.location.reload(), 1000); }; + const handleReset = () => { + apiCall("Reset"); + setTimeout(() => window.location.reload(), 1000); + }; + const handleSaveStylesheet = () => { const stylesheet = (document.getElementById('stylesheet-editor') as HTMLTextAreaElement).value; apiCall({ "SetStylesheet": stylesheet }); @@ -141,7 +146,20 @@ function App() {

    {appState.identity?.networking_key}

    {appState.identity?.ws_routing &&

    {appState.identity.ws_routing}

    } {appState.identity?.routers &&

    {appState.identity.routers}

    } - +
    + + +
    From 4e68a9f319b0ba66a05f4548d96940a08c70447d Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Thu, 19 Dec 2024 19:09:30 +0200 Subject: [PATCH 25/30] kns: add node-info debug script --- Cargo.lock | 11 ++++++ Cargo.toml | 1 + kinode/packages/kns-indexer/Cargo.lock | 11 ++++++ kinode/packages/kns-indexer/Cargo.toml | 1 + .../packages/kns-indexer/node-info/Cargo.toml | 20 +++++++++++ .../packages/kns-indexer/node-info/src/lib.rs | 35 +++++++++++++++++++ kinode/packages/kns-indexer/pkg/scripts.json | 12 +++++++ 7 files changed, 91 insertions(+) create mode 100644 kinode/packages/kns-indexer/node-info/Cargo.toml create mode 100644 kinode/packages/kns-indexer/node-info/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d80242b6..f9a3df95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4701,6 +4701,17 @@ dependencies = [ "libc", ] +[[package]] +name = "node_info" +version = "0.1.0" +dependencies = [ + "kinode_process_lib 0.10.0", + "process_macros", + "serde", + "serde_json", + "wit-bindgen 0.36.0", +] + [[package]] name = "nohash-hasher" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 44701286..ca2b7bd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ members = [ "kinode/packages/contacts/contacts", "kinode/packages/homepage/homepage", "kinode/packages/kns-indexer/kns-indexer", "kinode/packages/kns-indexer/get-block", "kinode/packages/settings/settings", "kinode/packages/kns-indexer/reset", + "kinode/packages/kns-indexer/node-info", "kinode/packages/terminal/terminal", "kinode/packages/terminal/alias", "kinode/packages/terminal/cat", "kinode/packages/terminal/echo", "kinode/packages/terminal/help", "kinode/packages/terminal/hi", "kinode/packages/terminal/kfetch", diff --git a/kinode/packages/kns-indexer/Cargo.lock b/kinode/packages/kns-indexer/Cargo.lock index 1c2bb637..5f8724d9 100644 --- a/kinode/packages/kns-indexer/Cargo.lock +++ b/kinode/packages/kns-indexer/Cargo.lock @@ -1917,6 +1917,17 @@ dependencies = [ "tempfile", ] +[[package]] +name = "node_info" +version = "0.1.0" +dependencies = [ + "kinode_process_lib", + "process_macros", + "serde", + "serde_json", + "wit-bindgen", +] + [[package]] name = "num-bigint" version = "0.4.6" diff --git a/kinode/packages/kns-indexer/Cargo.toml b/kinode/packages/kns-indexer/Cargo.toml index 35d4c4f3..a7be9519 100644 --- a/kinode/packages/kns-indexer/Cargo.toml +++ b/kinode/packages/kns-indexer/Cargo.toml @@ -4,6 +4,7 @@ members = [ "get-block", "kns-indexer", "reset", + "node-info", ] [profile.release] diff --git a/kinode/packages/kns-indexer/node-info/Cargo.toml b/kinode/packages/kns-indexer/node-info/Cargo.toml new file mode 100644 index 00000000..a3122167 --- /dev/null +++ b/kinode/packages/kns-indexer/node-info/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "node_info" +version = "0.1.0" +edition = "2021" + +[features] +simulation-mode = [] + +[dependencies] +kinode_process_lib = { git = "https://github.com/kinode-dao/process_lib", rev = "d97e012" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +process_macros = "0.1" +wit-bindgen = "0.36.0" + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "kinode:process" diff --git a/kinode/packages/kns-indexer/node-info/src/lib.rs b/kinode/packages/kns-indexer/node-info/src/lib.rs new file mode 100644 index 00000000..36277475 --- /dev/null +++ b/kinode/packages/kns-indexer/node-info/src/lib.rs @@ -0,0 +1,35 @@ +use kinode::process::kns_indexer::{IndexerRequest, IndexerResponse, NodeInfoRequest}; +use kinode_process_lib::{println, script, Address, Request}; +use std::str::FromStr; + +wit_bindgen::generate!({ + path: "target/wit", + world: "kns-indexer-sys-v0", + generate_unused_types: true, + additional_derives: [serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto], +}); + +script!(init); +fn init(_our: Address, args: String) -> String { + let node_name = args.split_whitespace().next().unwrap_or("").to_string(); + + let kns = Address::from_str("our@kns-indexer:kns-indexer:sys").unwrap(); + + let resp = Request::to(kns) + .body(IndexerRequest::NodeInfo(NodeInfoRequest { + name: node_name, + block: 0, + })) + .send_and_await_response(5) + .unwrap() + .unwrap(); + + let resp = serde_json::from_slice::(&resp.body()).unwrap(); + + match resp { + IndexerResponse::NodeInfo(node_info) => { + format!("node info: {node_info:#?}") + } + _ => "node info: name not found".to_string(), + } +} diff --git a/kinode/packages/kns-indexer/pkg/scripts.json b/kinode/packages/kns-indexer/pkg/scripts.json index cff799d4..38d26c7d 100644 --- a/kinode/packages/kns-indexer/pkg/scripts.json +++ b/kinode/packages/kns-indexer/pkg/scripts.json @@ -28,5 +28,17 @@ "kns-indexer:kns-indexer:sys" ], "wit_version": 1 + }, + "node-info.wasm": { + "root": false, + "public": false, + "request_networking": false, + "request_capabilities": [ + "kns-indexer:kns-indexer:sys" + ], + "grant_capabilities": [ + "kns-indexer:kns-indexer:sys" + ], + "wit_version": 1 } } From 85bc87b7a6b11fdbcd3b595371e558ad713f2d5e Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Thu, 19 Dec 2024 20:32:15 +0200 Subject: [PATCH 26/30] kns: fix unnecessary recursion --- .../kns-indexer/kns-indexer/src/lib.rs | 37 ++----------------- 1 file changed, 3 insertions(+), 34 deletions(-) diff --git a/kinode/packages/kns-indexer/kns-indexer/src/lib.rs b/kinode/packages/kns-indexer/kns-indexer/src/lib.rs index 4e2cca7f..5b5c216d 100644 --- a/kinode/packages/kns-indexer/kns-indexer/src/lib.rs +++ b/kinode/packages/kns-indexer/kns-indexer/src/lib.rs @@ -314,7 +314,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { let mut pending_notes: BTreeMap> = BTreeMap::new(); // if block in state is < current_block, get logs from that part. - println!("syncing old logs..."); + println!("syncing old logs from block: {}", last_block); fetch_and_process_logs( ð_provider, &mut state, @@ -516,8 +516,7 @@ fn handle_note(state: &mut State, note: &kimap::contract::Note) -> anyhow::Resul if !kimap::valid_note(¬e_label) { return Err(anyhow::anyhow!("skipping invalid note: {note_label}")); } - - let Some(node_name) = get_parent_name(&state, &node_hash) else { + let Some(node_name) = state.get_name(&node_hash) else { return Err(KnsError::NoParentError.into()); }; @@ -591,7 +590,7 @@ fn handle_log( return Err(anyhow::anyhow!("skipping invalid name: {name}")); } - let full_name = match get_parent_name(&state, &parent_hash) { + let full_name = match state.get_name(&parent_hash) { Some(parent_name) => format!("{name}.{parent_name}"), None => name, }; @@ -666,36 +665,6 @@ fn fetch_and_process_logs( } } -fn get_parent_name(state: &State, parent_hash: &str) -> Option { - let mut current_hash = parent_hash.to_string(); - let mut components = Vec::new(); // Collect components in a vector - let mut visited_hashes = std::collections::HashSet::new(); - - while let Some(parent_name) = state.get_name(¤t_hash) { - if !visited_hashes.insert(current_hash.clone()) { - break; - } - - if !parent_name.is_empty() { - components.push(parent_name.clone()); - } - - // Update current_hash to the parent's hash for the next iteration - if let Some(new_parent_hash) = state.get_name(&parent_name) { - current_hash = new_parent_hash; - } else { - break; - } - } - - if components.is_empty() { - return None; - } - - components.reverse(); - Some(components.join(".")) -} - // TEMP. Either remove when event reimitting working with anvil, // or refactor into better structure(!) #[cfg(feature = "simulation-mode")] From 6da51a771dbcce19c70f52299f0fc37cf9acaa52 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Thu, 19 Dec 2024 21:20:53 +0200 Subject: [PATCH 27/30] kns: fix notes error --- kinode/packages/kns-indexer/kns-indexer/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kinode/packages/kns-indexer/kns-indexer/src/lib.rs b/kinode/packages/kns-indexer/kns-indexer/src/lib.rs index 5b5c216d..ff2f2323 100644 --- a/kinode/packages/kns-indexer/kns-indexer/src/lib.rs +++ b/kinode/packages/kns-indexer/kns-indexer/src/lib.rs @@ -299,7 +299,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { // 60s timeout -- these calls can take a long time // if they do time out, we try them again let eth_provider: eth::Provider = eth::Provider::new(chain_id, SUBSCRIPTION_TIMEOUT); - let _kimap_helper = kimap::Kimap::new(eth_provider.clone(), kimap_address); + // let _kimap_helper = kimap::Kimap::new(eth_provider.clone(), kimap_address); // subscribe to logs first, so no logs are missed eth_provider.subscribe_loop(1, mints_filter.clone()); @@ -646,10 +646,10 @@ fn fetch_and_process_logs( filter: eth::Filter, pending_notes: &mut BTreeMap>, ) { - let filter = filter.from_block(state.last_block); loop { match eth_provider.get_logs(&filter) { Ok(logs) => { + println!("log len: {}", logs.len()); for log in logs { if let Err(e) = handle_log(state, pending_notes, &log) { print_to_terminal(1, &format!("log-handling error! {e:?}")); From e54665745eb063bafd58e32b6a6e0b5e46dbe8cf Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Thu, 19 Dec 2024 14:25:54 -0500 Subject: [PATCH 28/30] doria's nits --- .../kns-indexer/kns-indexer/src/lib.rs | 152 ++++++++---------- 1 file changed, 70 insertions(+), 82 deletions(-) diff --git a/kinode/packages/kns-indexer/kns-indexer/src/lib.rs b/kinode/packages/kns-indexer/kns-indexer/src/lib.rs index ff2f2323..a5b7521d 100644 --- a/kinode/packages/kns-indexer/kns-indexer/src/lib.rs +++ b/kinode/packages/kns-indexer/kns-indexer/src/lib.rs @@ -9,7 +9,6 @@ use kinode_process_lib::{ kv::{self, Kv}, net, print_to_terminal, println, timer, Address, Capability, Message, Request, Response, }; -use serde::{Deserialize, Serialize}; use std::{ collections::BTreeMap, net::{IpAddr, Ipv4Addr, Ipv6Addr}, @@ -44,64 +43,66 @@ const MAX_PENDING_ATTEMPTS: u8 = 3; const SUBSCRIPTION_TIMEOUT: u64 = 60; const DELAY_MS: u64 = 1_000; // 1s -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] struct State { - // version of the state in kv + /// version of the state in kv version: u32, - // last block we have an update from + /// last block we have an update from last_block: u64, - // kv handle - // includes keys and values for: - // "meta:chain_id", "meta:version", "meta:last_block", "meta:contract_address", - // "names:{namehash}" -> "{name}", "nodes:{name}" -> "{node_info}" + /// kv handle + /// includes keys and values for: + /// "meta:chain_id", "meta:version", "meta:last_block", "meta:contract_address", + /// "names:{namehash}" -> "{name}", "nodes:{name}" -> "{node_info}" kv: Kv>, } impl State { - fn load(our: &Address) -> Self { + fn new(our: &Address) -> Self { let kv: Kv> = match kv::open(our.package_id(), "kns_indexer", Some(10)) { Ok(kv) => kv, Err(e) => panic!("fatal: error opening kns_indexer key_value database: {e:?}"), }; - - let mut state = Self { + Self { + version: CURRENT_VERSION, + last_block: KIMAP_FIRST_BLOCK, kv, - version: 0, - last_block: 0, - }; + } + } + + /// Loads the state from kv, and updates it with the current block number and version. + /// The result of this function will be that the constants for chain ID and contract address + /// are always matching the values in the kv. + fn load(our: &Address) -> Self { + let mut state = Self::new(our); + + let desired_contract_address = eth::Address::from_str(KIMAP_ADDRESS).unwrap(); let version = state.get_version(); let chain_id = state.get_chain_id(); let contract_address = state.get_contract_address(); let last_block = state.get_last_block(); - if version != CURRENT_VERSION - || chain_id != CHAIN_ID - || contract_address != eth::Address::from_str(KIMAP_ADDRESS).unwrap() + if version != Some(CURRENT_VERSION) + || chain_id != Some(CHAIN_ID) + || contract_address != Some(desired_contract_address) { // if version/contract/chain_id are new, run migrations here. + state.set_version(CURRENT_VERSION); + state.set_chain_id(CHAIN_ID); + state.set_contract_address(desired_contract_address); } - state.set_chain_id(chain_id); - state.set_contract_address(contract_address); - state.set_version(CURRENT_VERSION); - - // update state struct with final values - state.version = version; - state.last_block = last_block; + state.last_block = last_block.unwrap_or(state.last_block); println!( - "\n 🐦‍⬛ KNS Indexer State\n\ - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n\ - Version {}\n\ - Last Block {}\n\ - Chain ID {}\n\ - KIMAP {}\n\ - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\n", - state.version, - state.last_block, - chain_id, - contract_address.to_string(), + "\n 🐦‍⬛ KNS Indexer State\n\ + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n\ + Version {}\n\ + Chain ID {}\n\ + Last Block {}\n\ + KIMAP {}\n\ + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\n", + state.version, state.last_block, CHAIN_ID, desired_contract_address, ); state @@ -139,11 +140,8 @@ impl State { format!("node:{}", name) } - fn get_last_block(&self) -> u64 { - self.kv - .get_as::(&Self::meta_last_block_key()) - .ok() - .unwrap_or(KIMAP_FIRST_BLOCK) + fn get_last_block(&self) -> Option { + self.kv.get_as::(&Self::meta_last_block_key()).ok() } fn set_last_block(&mut self, block: u64) { @@ -153,11 +151,8 @@ impl State { self.last_block = block; } - fn get_version(&self) -> u32 { - self.kv - .get_as::(&Self::meta_version_key()) - .ok() - .unwrap_or(CURRENT_VERSION) + fn get_version(&self) -> Option { + self.kv.get_as::(&Self::meta_version_key()).ok() } fn set_version(&mut self, version: u32) { @@ -190,11 +185,8 @@ impl State { .unwrap(); } - fn get_chain_id(&self) -> u64 { - self.kv - .get_as::(&Self::meta_chain_id_key()) - .ok() - .unwrap_or(CHAIN_ID) + fn get_chain_id(&self) -> Option { + self.kv.get_as::(&Self::meta_chain_id_key()).ok() } fn set_chain_id(&mut self, chain_id: u64) { @@ -203,15 +195,10 @@ impl State { .unwrap(); } - fn get_contract_address(&self) -> eth::Address { - match self - .kv + fn get_contract_address(&self) -> Option { + self.kv .get_as::(&Self::meta_contract_address_key()) - { - Ok(addr) => addr, - Err(_) => eth::Address::from_str(KIMAP_ADDRESS) - .expect("Failed to parse KIMAP_ADDRESS constant"), - } + .ok() } fn set_contract_address(&mut self, contract_address: eth::Address) { @@ -269,42 +256,40 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { #[cfg(feature = "simulation-mode")] add_temp_hardcoded_tlzs(&mut state); - let chain_id = state.get_chain_id(); - let kimap_address = state.get_contract_address(); - let last_block = state.get_last_block(); + let chain_id = CHAIN_ID; + let kimap_address = eth::Address::from_str(KIMAP_ADDRESS).unwrap(); // sub_id: 1 + // listen to all mint events in kimap let mints_filter = eth::Filter::new() .address(kimap_address) - .from_block(last_block) + .from_block(state.last_block) .to_block(eth::BlockNumberOrTag::Latest) .event("Mint(bytes32,bytes32,bytes,bytes)"); - let notes = vec![ - keccak256("~ws-port"), - keccak256("~tcp-port"), - keccak256("~net-key"), - keccak256("~routers"), - keccak256("~ip"), - ]; - // sub_id: 2 + // listen to all note events that are relevant to the KNS protocol within kimap let notes_filter = eth::Filter::new() .address(kimap_address) - .from_block(last_block) + .from_block(state.last_block) .to_block(eth::BlockNumberOrTag::Latest) .event("Note(bytes32,bytes32,bytes,bytes,bytes)") - .topic3(notes); + .topic3(vec![ + keccak256("~ws-port"), + keccak256("~tcp-port"), + keccak256("~net-key"), + keccak256("~routers"), + keccak256("~ip"), + ]); // 60s timeout -- these calls can take a long time // if they do time out, we try them again let eth_provider: eth::Provider = eth::Provider::new(chain_id, SUBSCRIPTION_TIMEOUT); - // let _kimap_helper = kimap::Kimap::new(eth_provider.clone(), kimap_address); // subscribe to logs first, so no logs are missed eth_provider.subscribe_loop(1, mints_filter.clone()); eth_provider.subscribe_loop(2, notes_filter.clone()); - println!("done subscribing to new logs."); + // if subscription results come back in the wrong order, we store them here // until the right block is reached. @@ -314,7 +299,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { let mut pending_notes: BTreeMap> = BTreeMap::new(); // if block in state is < current_block, get logs from that part. - println!("syncing old logs from block: {}", last_block); + println!("syncing old logs from block: {}", state.last_block); fetch_and_process_logs( ð_provider, &mut state, @@ -327,6 +312,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { notes_filter.clone(), &mut pending_notes, ); + // set a timer tick so any pending logs will be processed timer::set_timer(DELAY_MS, None); println!("done syncing old logs."); @@ -335,6 +321,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { let Ok(message) = await_message() else { continue; }; + // if true, time to go check current block number and handle pending notes. let tick = message.is_local(&our) && message.source().process == "timer:distro:sys"; let Message::Request { @@ -358,7 +345,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { continue; }; - if source.process == "eth:distro:sys" { + if source.node() == our.node() && source.process == "eth:distro:sys" { handle_eth_message( &mut state, ð_provider, @@ -369,9 +356,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { ¬es_filter, )?; } else { - let request = serde_json::from_slice(&body)?; - - match request { + match serde_json::from_slice(&body)? { IndexerRequest::NamehashToName(NamehashToNameRequest { ref hash, .. }) => { // TODO: make sure we've seen the whole block, while actually // sending a response to the proper place. @@ -445,6 +430,7 @@ fn handle_eth_message( } _ => {} } + if tick { let block_number = eth_provider.get_block_number(); if let Ok(block_number) = block_number { @@ -638,8 +624,7 @@ fn handle_log( Ok(()) } -// helpers - +/// Get logs for a filter then process them while taking pending notes into account. fn fetch_and_process_logs( eth_provider: ð::Provider, state: &mut State, @@ -680,7 +665,8 @@ fn add_temp_hardcoded_tlzs(state: &mut State) { ); } -/// Decodes bytes into an array of keccak256 hashes (32 bytes each) and returns their full names. +/// Decodes bytes under ~routers in kimap into an array of keccak256 hashes (32 bytes each) +/// and returns the associated node identities. fn decode_routers(data: &[u8], state: &State) -> Vec { if data.len() % 32 != 0 { print_to_terminal( @@ -706,6 +692,7 @@ fn decode_routers(data: &[u8], state: &State) -> Vec { routers } +/// convert IP address stored at ~ip in kimap to IpAddr pub fn bytes_to_ip(bytes: &[u8]) -> anyhow::Result { match bytes.len() { 4 => { @@ -722,6 +709,7 @@ pub fn bytes_to_ip(bytes: &[u8]) -> anyhow::Result { } } +/// convert port stored at ~[protocol]-port in kimap to u16 pub fn bytes_to_port(bytes: &[u8]) -> anyhow::Result { match bytes.len() { 2 => Ok(u16::from_be_bytes([bytes[0], bytes[1]])), From ca8c9e3ba0525d5c38cb66d7580305d072d7af05 Mon Sep 17 00:00:00 2001 From: dr-frmr Date: Thu, 19 Dec 2024 15:42:46 -0500 Subject: [PATCH 29/30] WIT cleanups --- kinode/packages/chess/api/chess:sys-v0.wit | 1 - .../packages/contacts/api/contacts:sys-v0.wit | 26 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/kinode/packages/chess/api/chess:sys-v0.wit b/kinode/packages/chess/api/chess:sys-v0.wit index bbf93504..742c60b8 100644 --- a/kinode/packages/chess/api/chess:sys-v0.wit +++ b/kinode/packages/chess/api/chess:sys-v0.wit @@ -1,7 +1,6 @@ interface chess { /// Our "chess protocol" request/response format. We'll always serialize these /// to a byte vector and send them over IPC. - variant request { /// lazy-load-blob: none. new-game(new-game-request), diff --git a/kinode/packages/contacts/api/contacts:sys-v0.wit b/kinode/packages/contacts/api/contacts:sys-v0.wit index 76e36e30..ef64744b 100644 --- a/kinode/packages/contacts/api/contacts:sys-v0.wit +++ b/kinode/packages/contacts/api/contacts:sys-v0.wit @@ -7,24 +7,29 @@ interface contacts { } variant request { + /// requires ReadNameOnly capability /// lazy-load-blob: none. - get-names, // requires read-names-only + get-names, + /// requires Read capability /// lazy-load-blob: none. - get-all-contacts, // requires read + get-all-contacts, + /// requires Read capability /// lazy-load-blob: none. - get-contact(string), // requires read + get-contact(string), + /// requires Add capability + /// lazy-load-blob: none. + add-contact(string), + /// requires Add capability /// lazy-load-blob: none. - add-contact(string), // requires add /// tuple - /// + add-field(tuple), + /// requires Remove capability /// lazy-load-blob: none. - add-field(tuple), // requires add + remove-contact(string), + /// requires Remove capability /// lazy-load-blob: none. - remove-contact(string), // requires remove /// tuple - /// - /// lazy-load-blob: none. - remove-field(tuple), // requires remove + remove-field(tuple), } variant response { @@ -43,7 +48,6 @@ interface contacts { /// lazy-load-blob: none. remove-field, /// any failed request will receive this response - /// /// lazy-load-blob: none. err(string), } From 592863fce056432729d9eff02f856b1f41c46d07 Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Thu, 19 Dec 2024 23:12:04 +0200 Subject: [PATCH 30/30] app_store UI: add reset state button --- .../ui/src/components/ResetButton.tsx | 69 ++++++++++++++++ .../app-store/ui/src/components/index.ts | 3 +- kinode/packages/app-store/ui/src/index.css | 80 ++++++++++++++++++- .../app-store/ui/src/pages/MyAppsPage.tsx | 5 ++ .../packages/app-store/ui/src/store/index.ts | 23 ++++++ 5 files changed, 177 insertions(+), 3 deletions(-) create mode 100644 kinode/packages/app-store/ui/src/components/ResetButton.tsx diff --git a/kinode/packages/app-store/ui/src/components/ResetButton.tsx b/kinode/packages/app-store/ui/src/components/ResetButton.tsx new file mode 100644 index 00000000..67d86950 --- /dev/null +++ b/kinode/packages/app-store/ui/src/components/ResetButton.tsx @@ -0,0 +1,69 @@ +import React, { useState } from 'react'; +import { FaExclamationTriangle } from 'react-icons/fa'; +import useAppsStore from '../store'; + +const ResetButton: React.FC = () => { + const resetStore = useAppsStore(state => state.resetStore); + const [isOpen, setIsOpen] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + const handleReset = async () => { + try { + setIsLoading(true); + await resetStore(); + setIsOpen(false); + } catch (error) { + console.error('Reset failed:', error); + alert('Failed to reset the app store. Please try again.'); + } finally { + setIsLoading(false); + } + }; + + return ( + <> + + + {isOpen && ( +
    setIsOpen(false)}> +
    e.stopPropagation()}> + +
    + +

    Warning

    +
    + +

    + This action will re-index all apps and reset the store state. + Only proceed if you know what you're doing. +

    + +
    + + +
    +
    +
    + )} + + ); +}; + +export default ResetButton; diff --git a/kinode/packages/app-store/ui/src/components/index.ts b/kinode/packages/app-store/ui/src/components/index.ts index d2e77394..c47652b7 100644 --- a/kinode/packages/app-store/ui/src/components/index.ts +++ b/kinode/packages/app-store/ui/src/components/index.ts @@ -2,4 +2,5 @@ export { default as Header } from './Header'; export { default as MirrorSelector } from './MirrorSelector'; export { default as PackageSelector } from './PackageSelector'; export { default as ManifestDisplay } from './ManifestDisplay'; -export { default as NotificationBay } from './NotificationBay'; \ No newline at end of file +export { default as NotificationBay } from './NotificationBay'; +export { default as ResetButton } from './ResetButton'; \ No newline at end of file diff --git a/kinode/packages/app-store/ui/src/index.css b/kinode/packages/app-store/ui/src/index.css index 1e33865d..7c526125 100644 --- a/kinode/packages/app-store/ui/src/index.css +++ b/kinode/packages/app-store/ui/src/index.css @@ -841,7 +841,7 @@ td { 50%, 70% { transform: translate3d(-4px, 0, 0); - } +} 40%, 60% { @@ -856,7 +856,7 @@ td { 50% { opacity: 0.6; - } +} 100% { opacity: 1; @@ -1287,4 +1287,80 @@ td { display: flex; align-items: center; gap: 4px; +} + +.page-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; +} + +.header-actions { + display: flex; + gap: 1rem; + align-items: center; +} + +/* Modal styles */ +.modal-overlay { + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 50; +} + +.modal-container { + padding: 1rem; + width: 100%; + max-width: 28rem; +} + +.modal-content { + background-color: light-dark(var(--surface-light), var(--surface-dark)); + padding: 1.5rem; + border-radius: var(--border-radius); +} + +.modal-header { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 1rem; +} + +.modal-title { + font-size: 1.25rem; + font-weight: 500; + margin: 0; + color: inherit; +} + +.modal-description { + color: light-dark(var(--text-light), var(--text-dark)); + margin-bottom: 1.5rem; +} + +.modal-footer { + display: flex; + justify-content: flex-end; + gap: 0.75rem; +} + +/* Button variants */ +.danger-button { + background-color: var(--red) !important; + color: white !important; +} + +.danger-button:hover { + opacity: 0.9; +} + +.danger-button:disabled { + opacity: 0.5; + cursor: not-allowed; } \ No newline at end of file diff --git a/kinode/packages/app-store/ui/src/pages/MyAppsPage.tsx b/kinode/packages/app-store/ui/src/pages/MyAppsPage.tsx index 2913460e..c513268d 100644 --- a/kinode/packages/app-store/ui/src/pages/MyAppsPage.tsx +++ b/kinode/packages/app-store/ui/src/pages/MyAppsPage.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react"; import { FaFolder, FaFile, FaChevronLeft, FaSync, FaRocket, FaSpinner, FaCheck, FaTrash, FaExclamationTriangle, FaTimesCircle, FaChevronDown, FaChevronRight } from "react-icons/fa"; import { useNavigate } from "react-router-dom"; import useAppsStore from "../store"; +import { ResetButton} from "../components"; import { DownloadItem, PackageManifestEntry, PackageState, Updates, DownloadError, UpdateInfo } from "../types/Apps"; // Core packages that cannot be uninstalled @@ -308,6 +309,10 @@ export default function MyAppsPage() { return (
    +
    +

    My Apps

    + +
    {error &&
    {error}
    } {renderUpdates()} diff --git a/kinode/packages/app-store/ui/src/store/index.ts b/kinode/packages/app-store/ui/src/store/index.ts index 36e3ca3b..1d056ddb 100644 --- a/kinode/packages/app-store/ui/src/store/index.ts +++ b/kinode/packages/app-store/ui/src/store/index.ts @@ -27,6 +27,7 @@ interface AppsStore { fetchOurApps: () => Promise fetchDownloadsForApp: (id: string) => Promise checkMirror: (node: string) => Promise + resetStore: () => Promise fetchHomepageApps: () => Promise getLaunchUrl: (id: string) => string | null @@ -410,6 +411,28 @@ const useAppsStore = create()((set, get) => ({ } }, + resetStore: async () => { + try { + const response = await fetch(`${BASE_URL}/reset`, { + method: 'POST', + }); + + if (!response.ok) { + throw new Error('Reset failed'); + } + + // Refresh the store data + await Promise.all([ + get().fetchInstalled(), + get().fetchListings(), + get().fetchUpdates(), + ]); + } catch (error) { + console.error('Reset failed:', error); + throw error; + } + }, + ws: new KinodeClientApi({ uri: WEBSOCKET_URL, nodeId: (window as any).our?.node,