Merge pull request #302 from kinode-dao/bp/appstore-fixes

OP app_store
This commit is contained in:
bitful-pannul 2024-04-15 15:08:11 -04:00 committed by GitHub
commit 8a929641b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 138 additions and 213 deletions

View File

@ -23,10 +23,19 @@ pub enum RemoteRequest {
#[derive(Debug, Serialize, Deserialize)]
pub enum RemoteResponse {
DownloadApproved,
DownloadDenied, // TODO expand on why
DownloadDenied(ReasonDenied),
Metadata,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum ReasonDenied {
NoPackage,
NotMirroring,
HashMismatch { requested: String, have: String },
FileNotFound,
WorkerSpawnFailed,
}
/// Local requests sent to the app store take this form.
#[derive(Debug, Serialize, Deserialize)]
pub enum LocalRequest {

View File

@ -1,4 +1,6 @@
use kinode_process_lib::http::{bind_http_path, serve_ui, HttpServerRequest};
use kinode_process_lib::http::{
bind_http_path, bind_ws_path, send_ws_push, serve_ui, HttpServerRequest, WsMessageType,
};
use kinode_process_lib::kernel_types as kt;
use kinode_process_lib::*;
use kinode_process_lib::{call_init, println};
@ -37,8 +39,8 @@ use ft_worker_lib::{
const ICON: &str = include_str!("icon");
const CHAIN_ID: u64 = 11155111; // sepolia
const CONTRACT_ADDRESS: &str = "0x18c39eB547A0060C6034f8bEaFB947D1C16eADF1"; // sepolia
const CHAIN_ID: u64 = 10; // optimism
const CONTRACT_ADDRESS: &str = "0x52185B6a6017E6f079B994452F234f7C2533787B"; // optimism
const EVENTS: [&str; 3] = [
"AppRegistered(uint256,string,bytes,string,bytes32)",
@ -79,7 +81,7 @@ fn fetch_logs(eth_provider: &eth::Provider, filter: &eth::Filter) -> Vec<eth::Lo
}
}
}
#[cfg(feature = "simulation-mode")]
#[cfg(feature = "simulation-mode")] // TODO use local testnet, provider_chainId: 31337
vec![]
}
@ -124,6 +126,8 @@ fn init(our: Address) {
)
.expect("failed to serve static UI");
bind_ws_path("/", true, true).expect("failed to bind ws path");
// add ourselves to the homepage
Request::to(("our", "homepage", "homepage", "sys"))
.body(
@ -172,6 +176,9 @@ fn init(our: Address) {
}
subscribe_to_logs(&eth_provider, filter);
// websocket channel to send errors/updates to UI
let channel_id: u32 = 154869;
loop {
match await_message() {
Err(send_error) => {
@ -186,7 +193,21 @@ fn init(our: Address) {
&mut requested_packages,
&message,
) {
println!("error handling message: {:?}", e)
println!("error handling message: {:?}", e);
send_ws_push(
channel_id,
WsMessageType::Text,
LazyLoadBlob {
mime: Some("application/json".to_string()),
bytes: serde_json::json!({
"kind": "error",
"data": e.to_string(),
})
.to_string()
.as_bytes()
.to_vec(),
},
)
}
}
}
@ -295,14 +316,23 @@ fn handle_remote_request(
desired_version_hash,
} => {
let Some(package_state) = state.get_downloaded_package(package_id) else {
return Resp::RemoteResponse(RemoteResponse::DownloadDenied);
return Resp::RemoteResponse(RemoteResponse::DownloadDenied(
ReasonDenied::NoPackage,
));
};
if !package_state.mirroring {
return Resp::RemoteResponse(RemoteResponse::DownloadDenied);
return Resp::RemoteResponse(RemoteResponse::DownloadDenied(
ReasonDenied::NotMirroring,
));
}
if let Some(hash) = desired_version_hash {
if &package_state.our_version != hash {
return Resp::RemoteResponse(RemoteResponse::DownloadDenied);
return Resp::RemoteResponse(RemoteResponse::DownloadDenied(
ReasonDenied::HashMismatch {
requested: hash.clone(),
have: package_state.our_version.clone(),
},
));
}
}
let file_name = format!("/{}.zip", package_id);
@ -318,12 +348,16 @@ fn handle_remote_request(
)
.send_and_await_response(5)
else {
return Resp::RemoteResponse(RemoteResponse::DownloadDenied);
return Resp::RemoteResponse(RemoteResponse::DownloadDenied(
ReasonDenied::FileNotFound,
));
};
// transfer will *inherit* the blob bytes we receive from VFS
match spawn_transfer(&our, &file_name, None, 60, &source) {
Ok(()) => Resp::RemoteResponse(RemoteResponse::DownloadApproved),
Err(_e) => Resp::RemoteResponse(RemoteResponse::DownloadDenied),
Err(_e) => Resp::RemoteResponse(RemoteResponse::DownloadDenied(
ReasonDenied::WorkerSpawnFailed,
)),
}
}
}

View File

@ -15,7 +15,7 @@
<meta name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover" />
<link href='https://fonts.googleapis.com/css?family=Montserrat' rel='stylesheet'>
<script type="module" crossorigin src="/main:app_store:sys/assets/index-przgvy-e.js"></script>
<script type="module" crossorigin src="/main:app_store:sys/assets/index-LQNA9SRw.js"></script>
<link rel="stylesheet" crossorigin href="/main:app_store:sys/assets/index-avBSgupm.css">
</head>

View File

@ -20,6 +20,7 @@ const connectors: [MetaMask, Web3ReactHooks][] = [
declare global {
interface ImportMeta {
env: {
VITE_OPTIMISM_RPC_URL: string;
VITE_SEPOLIA_RPC_URL: string;
BASE_URL: string;
VITE_NODE_URL?: string;
@ -38,7 +39,7 @@ const {
useProvider,
} = metaMaskHooks;
const RPC_URL = import.meta.env.VITE_SEPOLIA_RPC_URL;
const RPC_URL = import.meta.env.VITE_OPTIMISM_RPC_URL;
const BASE_URL = import.meta.env.BASE_URL;
if (window.our) window.our.process = BASE_URL?.replace("/", "");
@ -56,15 +57,15 @@ function App() {
const [packageAbi, setPackageAbi] = useState<PackageStore>(
PackageStore__factory.connect(
PACKAGE_STORE_ADDRESSES[ChainId.SEPOLIA],
PACKAGE_STORE_ADDRESSES[ChainId.OPTIMISM],
new ethers.providers.JsonRpcProvider(RPC_URL)) // TODO: get the RPC URL from the wallet
);
useEffect(() => {
provider?.getNetwork().then(network => {
if (network.chainId === ChainId.SEPOLIA) {
if (network.chainId === ChainId.OPTIMISM) {
setPackageAbi(PackageStore__factory.connect(
PACKAGE_STORE_ADDRESSES[ChainId.SEPOLIA],
PACKAGE_STORE_ADDRESSES[ChainId.OPTIMISM],
provider!.getSigner())
)
}

View File

@ -8,9 +8,11 @@ export enum ChainId {
export const SEPOLIA_OPT_HEX = '0xaa36a7';
export const OPTIMISM_OPT_HEX = '0xa';
export const SEPOLIA_OPT_INT = '11155111';
export const OPTIMISM_OPT_INT = '10';
// Sepolia (for now)
// Optimism (for now)
export const PACKAGE_STORE_ADDRESSES = {
[ChainId.SEPOLIA]: '0x18c39eB547A0060C6034f8bEaFB947D1C16eADF1',
// [ChainId.OPTIMISM]: '0x8f6e1c9C5a0fE0A7f9Cf0e9b3aF1A9c4f5c6A9e0',
[ChainId.OPTIMISM]: '0x52185B6a6017E6f079B994452F234f7C2533787B',
// [ChainId.SEPOLIA]: '0x18c39eB547A0060C6034f8bEaFB947D1C16eADF1',
};

View File

@ -6,7 +6,7 @@ import { useWeb3React } from "@web3-react/core";
import SearchHeader from "../components/SearchHeader";
import { PageProps } from "../types/Page";
import { setChain } from "../utils/chain";
import { SEPOLIA_OPT_HEX } from "../constants/chain";
import { OPTIMISM_OPT_HEX } from "../constants/chain";
import { hooks, metaMask } from "../utils/metamask";
import Loader from "../components/Loader";
import { toDNSWireFormat } from "../utils/dnsWire";
@ -58,7 +58,7 @@ export default function PublishPage({
await metaMask.activate().catch(() => { });
try {
setChain(SEPOLIA_OPT_HEX);
setChain(OPTIMISM_OPT_HEX);
} catch (error) {
console.error(error);
}
@ -100,7 +100,7 @@ export default function PublishPage({
setLoading("Please confirm the transaction in your wallet");
const publisherIdDnsWireFormat = toDNSWireFormat(publisherId);
await setChain(SEPOLIA_OPT_HEX);
await setChain(OPTIMISM_OPT_HEX);
// TODO: have a checkbox to show if it's an update of an existing package

View File

@ -40,21 +40,21 @@ export const CHAIN_DETAILS: { [key: string]: Chain } = {
export const getNetworkName = (networkId: string) => {
switch (networkId) {
case '1':
case '0x1':
return 'Ethereum'; // Ethereum Mainnet
case '10':
case 'a':
case '0xa':
return 'Optimism'; // Optimism
case '42161':
return 'Arbitrum'; // Arbitrum One
case '11155111':
case 'aa36a7':
case '0xaa36a7':
return 'Sepolia'; // Sepolia Testnet
default:
return 'Unknown';
case '1':
case '0x1':
return 'Ethereum'; // Ethereum Mainnet
case '10':
case 'a':
case '0xa':
return 'Optimism'; // Optimism
case '42161':
return 'Arbitrum'; // Arbitrum One
case '11155111':
case 'aa36a7':
case '0xaa36a7':
return 'Sepolia'; // Sepolia Testnet
default:
return 'Unknown';
}
};

View File

@ -15,6 +15,9 @@ wit_bindgen::generate!({
world: "process",
});
// perhaps a constant in process_lib?
const KNS_OPTIMISM_ADDRESS: &'static str = "0xca5b5811c0c40aab3295f932b1b5112eb7bb4bd6";
#[derive(Clone, Debug, Serialize, Deserialize)]
struct State {
chain_id: u64,
@ -114,20 +117,13 @@ fn subscribe_to_logs(eth_provider: &eth::Provider, from_block: u64, filter: eth:
call_init!(init);
fn init(our: Address) {
// first, await a message from the kernel which will contain the
// chain ID and contract address for the KNS version we want to track.
let chain_id: u64;
let contract_address: String;
loop {
let Ok(Message::Request { source, body, .. }) = await_message() else {
continue;
};
if source.process != "kernel:distro:sys" {
continue;
}
(chain_id, contract_address) = serde_json::from_slice(&body).unwrap();
break;
}
// first, depending on if we're a fakenode or not,
// set chain_id and contract address.
// #[cfg(feature = "simulation-mode")]
// let (chain_id, contract_address) = (31337, _KNS_FAKENET_ADDRESS.to_string());
// #[cfg(not(feature = "simulation-mode"))]
let (chain_id, contract_address) = (10, KNS_OPTIMISM_ADDRESS.to_string());
println!("indexing on contract address {}", contract_address);
// if we have state, load it in

View File

@ -6,6 +6,27 @@
"RpcUrl": "wss://ethereum.publicnode.com"
}
},
{
"chain_id": 31337,
"trusted": true,
"provider": {
"RpcUrl": "wss://localhost:8545"
}
},
{
"chain_id": 11155111,
"trusted": false,
"provider": {
"RpcUrl": "wss://ethereum-sepolia-rpc.publicnode.com"
}
},
{
"chain_id": 10,
"trusted": false,
"provider": {
"RpcUrl": "wss://optimism-rpc.publicnode.com"
}
},
{
"chain_id": 10,
"trusted": false,

View File

@ -1,81 +0,0 @@
[
{
"chain_id": 1,
"trusted": false,
"provider": {
"RpcUrl": "wss://ethereum.publicnode.com"
}
},
{
"chain_id": 11155111,
"trusted": false,
"provider": {
"Node": {
"use_as_provider": true,
"kns_update": {
"name": "default-router-1.os",
"owner": "",
"node": "0xb35eb347deb896bc3fb6132a07fca1601f83462385ed11e835c24c33ba4ef73d",
"public_key": "0xd9ccb8404e23a8db6bf709044a2882b65aae6aca3d34d5a0cedf5a12bf597867",
"ip": "147.135.114.167",
"port": 9002,
"routers": []
}
}
}
},
{
"chain_id": 11155111,
"trusted": false,
"provider": {
"Node": {
"use_as_provider": true,
"kns_update": {
"name": "default-router-2.os",
"owner": "",
"node": "0xd827ae579fafa604af79fbed977e8abe048497f10885c6473dfd343a3b7b4458",
"public_key": "0x612fff0a1ab72ec97f81995d52a5e7516fe31800e3d05850b5b62ed47b2620bd",
"ip": "147.135.114.167",
"port": 9003,
"routers": []
}
}
}
},
{
"chain_id": 11155111,
"trusted": false,
"provider": {
"Node": {
"use_as_provider": true,
"kns_update": {
"name": "default-router-3.os",
"owner": "",
"node": "0x96e36331c8f0882f2c0c46c13b15d812def04fe8606d503bc0e2be39db26486a",
"public_key": "0x310f22ebaf414241c272b42179634a8696020faaf979a9fc4c6958f44121a3bf",
"ip": "147.135.114.167",
"port": 9004,
"routers": []
}
}
}
},
{
"chain_id": 10,
"trusted": false,
"provider": {
"Node": {
"use_as_provider": true,
"kns_update": {
"name": "default-router-3.os",
"owner": "",
"node": "0x96e36331c8f0882f2c0c46c13b15d812def04fe8606d503bc0e2be39db26486a",
"public_key": "0x310f22ebaf414241c272b42179634a8696020faaf979a9fc4c6958f44121a3bf",
"ip": "147.135.114.167",
"port": 9004,
"routers": []
}
}
}
}
]

View File

@ -671,7 +671,6 @@ pub async fn kernel(
mut recv_debug_in_loop: t::DebugReceiver,
send_to_net: t::MessageSender,
home_directory_path: String,
contract_chain_and_address: (u64, String),
runtime_extensions: Vec<(
t::ProcessId,
t::MessageSender,
@ -870,33 +869,6 @@ pub async fn kernel(
.await
.expect("fatal: kernel event loop died");
// finally, in order to trigger the kns_indexer app to find the right
// contract, queue up a message that will send the contract address
// to it on boot.
send_to_loop
.send(t::KernelMessage {
id: rand::random(),
source: t::Address {
node: our.name.clone(),
process: KERNEL_PROCESS_ID.clone(),
},
target: t::Address {
node: our.name.clone(),
process: t::ProcessId::new(Some("kns_indexer"), "kns_indexer", "sys"),
},
rsvp: None,
message: t::Message::Request(t::Request {
inherit: false,
expects_response: None,
body: serde_json::to_vec(&contract_chain_and_address).unwrap(),
metadata: None,
capabilities: vec![],
}),
lazy_load_blob: None,
})
.await
.expect("fatal: kernel event loop died");
// main event loop
loop {
tokio::select! {

View File

@ -38,7 +38,6 @@ const SQLITE_CHANNEL_CAPACITY: usize = 1_000;
const VERSION: &str = env!("CARGO_PKG_VERSION");
/// default routers as a eth-provider fallback
const DEFAULT_PROVIDERS_TESTNET: &str = include_str!("eth/default_providers_testnet.json");
const DEFAULT_PROVIDERS_MAINNET: &str = include_str!("eth/default_providers_mainnet.json");
async fn serve_register_fe(
@ -112,11 +111,6 @@ async fn main() {
.alias("network-router-port")
.value_parser(value_parser!(u16)),
)
.arg(
arg!(--testnet "If set, use Sepolia testnet")
.default_value("false")
.value_parser(value_parser!(bool)),
)
.arg(
arg!(--verbosity <VERBOSITY> "Verbosity level: higher is more verbose")
.default_value("0")
@ -137,11 +131,12 @@ async fn main() {
arg!(--detached <IS_DETACHED> "Run in detached mode (don't accept input)")
.action(clap::ArgAction::SetTrue),
);
// add arg for fakechain bootup w/ kit?
let fakenode = cfg!(feature = "simulation-mode");
let matches = app.get_matches();
let home_directory_path = matches.get_one::<String>("home").unwrap();
let on_testnet = *matches.get_one::<bool>("testnet").unwrap();
let http_port = matches.get_one::<u16>("port");
let ws_networking_port = matches.get_one::<u16>("ws-port");
@ -155,37 +150,17 @@ async fn main() {
*matches.get_one::<bool>("detached").unwrap(),
);
let contract_chain_and_address: (u64, String) = if on_testnet {
(11155111, register::KNS_SEPOLIA_ADDRESS.to_string())
} else {
(10, register::KNS_OPTIMISM_ADDRESS.to_string())
};
let verbose_mode = *matches.get_one::<u8>("verbosity").unwrap();
// check .testnet file for true/false in order to enforce testnet mode on subsequent boots of this node
match fs::read(format!("{}/.testnet", home_directory_path)).await {
Ok(contents) => {
if contents == b"true" {
if !on_testnet {
println!("\x1b[38;5;196mfatal: this is a testnet node, and must be booted with the --testnet flag. exiting.\x1b[0m");
return;
}
} else if contents == b"false" {
if on_testnet {
println!("\x1b[38;5;196mfatal: this is a mainnet node, and must be booted without the --testnet flag. exiting.\x1b[0m");
return;
}
} else {
panic!("invalid contents of .testnet file");
println!("\x1b[38;5;196mfatal: this is a deprecated testnet node, either boot a fakenode or a real one. exiting.\x1b[0m");
return;
}
}
Err(_) => {
let _ = fs::write(
format!("{}/.testnet", home_directory_path),
format!("{}", on_testnet),
)
.await;
}
_ => {}
}
if let Err(e) = fs::create_dir_all(home_directory_path).await {
@ -200,14 +175,11 @@ async fn main() {
println!("loaded saved eth providers\r");
serde_json::from_str(&contents).unwrap()
}
Err(_) => match on_testnet {
true => serde_json::from_str(DEFAULT_PROVIDERS_TESTNET).unwrap(),
false => serde_json::from_str(DEFAULT_PROVIDERS_MAINNET).unwrap(),
},
Err(_) => serde_json::from_str(DEFAULT_PROVIDERS_MAINNET).unwrap(),
};
if let Some(rpc) = matches.get_one::<String>("rpc") {
eth_provider_config.push(lib::eth::ProviderConfig {
chain_id: if on_testnet { 11155111 } else { 10 },
chain_id: if fakenode { 31337 } else { 10 },
trusted: true,
provider: lib::eth::NodeOrRpcUrl::RpcUrl(rpc.to_string()),
});
@ -332,7 +304,7 @@ async fn main() {
our_ip.to_string(),
(ws_tcp_handle, flag_used),
http_server_port,
on_testnet, // true if testnet mode
fakenode, // true if fakenode
matches.get_one::<String>("rpc").cloned(),
)
.await;
@ -508,7 +480,6 @@ async fn main() {
kernel_debug_message_receiver,
net_message_sender.clone(),
home_directory_path.clone(),
contract_chain_and_address.clone(),
runtime_extensions,
// from saved eth provider config, filter for node identities which will be
// bootstrapped into the networking module, so that this node can start
@ -535,7 +506,7 @@ async fn main() {
print_sender.clone(),
net_message_sender,
net_message_receiver,
contract_chain_and_address.1,
register::KNS_OPTIMISM_ADDRESS.to_string(),
*matches.get_one::<bool>("reveal-ip").unwrap_or(&true),
));
#[cfg(feature = "simulation-mode")]