Skip to content

Commit

Permalink
Merge pull request #634 from kinode-dao/dr/settings-userspace-api
Browse files Browse the repository at this point in the history
add settings userspace WIT API
  • Loading branch information
dr-frmr authored Dec 21, 2024
2 parents 58f3892 + db726ed commit 3e3d08a
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 96 deletions.
79 changes: 38 additions & 41 deletions kinode/packages/kns-indexer/kns-indexer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ 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
Expand All @@ -65,7 +63,7 @@ impl State {
contract_address: eth::Address::from_str(KIMAP_ADDRESS).unwrap(),
names: HashMap::new(),
nodes: HashMap::new(),
last_checkpoint_block: 0,
last_checkpoint_block: KIMAP_FIRST_BLOCK,
}
}

Expand Down Expand Up @@ -165,15 +163,20 @@ enum KnsError {

call_init!(init);
fn init(our: Address) {
println!("started");

// state is checkpointed regularly (default every 5 minutes if new events are found)
let state = State::load();
let mut state = State::load();

if let Err(e) = main(our, state) {
println!("fatal error: {e}");
loop {
if let Err(e) = main(&our, &mut state) {
println!("fatal error: {e}");
break;
}
}
}

fn main(our: Address, mut state: State) -> anyhow::Result<()> {
fn main(our: &Address, state: &mut State) -> anyhow::Result<()> {
#[cfg(feature = "simulation-mode")]
add_temp_hardcoded_tlzs(&mut state);

Expand Down Expand Up @@ -214,7 +217,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
// if they do time out, we try them again
let eth_provider: eth::Provider = eth::Provider::new(state.chain_id, SUBSCRIPTION_TIMEOUT);

// subscribe to logs first, so no logs are m issed
// subscribe to logs first, so no logs are missed
eth_provider.subscribe_loop(1, mints_filter.clone(), 2, 0);
eth_provider.subscribe_loop(2, notes_filter.clone(), 2, 0);

Expand All @@ -230,14 +233,14 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
print_to_terminal(2, &format!("syncing old logs from block: {}", last_block));
fetch_and_process_logs(
&eth_provider,
&mut state,
state,
mints_filter.clone(),
&mut pending_notes,
&mut last_block,
);
fetch_and_process_logs(
&eth_provider,
&mut state,
state,
notes_filter.clone(),
&mut pending_notes,
&mut last_block,
Expand Down Expand Up @@ -266,12 +269,13 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
source,
body,
capabilities,
expects_response,
..
} = message
else {
if tick {
handle_eth_message(
&mut state,
state,
&eth_provider,
tick,
checkpoint,
Expand All @@ -287,7 +291,7 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {

if source.node() == our.node() && source.process == "eth:distro:sys" {
handle_eth_message(
&mut state,
state,
&eth_provider,
tick,
checkpoint,
Expand All @@ -298,48 +302,41 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> {
&mut last_block,
)?;
} else {
match serde_json::from_slice(&body)? {
let response_body = 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.
Response::new()
.body(IndexerResponse::Name(state.names.get(hash).cloned()))
.send()?;
IndexerResponse::Name(state.names.get(hash).cloned())
}
IndexerRequest::NodeInfo(NodeInfoRequest { ref name, .. }) => {
Response::new()
.body(IndexerResponse::NodeInfo(
state.nodes.get(name).map(|n| n.clone().into()),
))
.send()?;
IndexerResponse::NodeInfo(state.nodes.get(name).map(|n| n.clone().into()))
}
IndexerRequest::Reset => {
// check for root capability
let root_cap = Capability {
issuer: our.clone(),
params: "{\"root\":true}".to_string(),
};
if source.package_id() != our.package_id() {
if !capabilities.contains(&root_cap) {
Response::new()
.body(IndexerResponse::Reset(ResetResult::Err(
ResetError::NoRootCap,
)))
.send()?;
continue;
}
let root_cap = Capability::new(our.clone(), "{\"root\":true}");
if source.package_id() != our.package_id() && !capabilities.contains(&root_cap)
{
IndexerResponse::Reset(ResetResult::Err(ResetError::NoRootCap))
} else {
// reload state fresh - this will create new db
state.reset();
IndexerResponse::Reset(ResetResult::Success)
}
// reload state fresh - this will create new db
state.reset();
}
IndexerRequest::GetState(_) => IndexerResponse::GetState(state.clone().into()),
};

if let IndexerResponse::Reset(ResetResult::Success) = response_body {
println!("resetting state");
if expects_response.is_some() {
Response::new()
.body(IndexerResponse::Reset(ResetResult::Success))
.send()?;
panic!("resetting state, restarting!");
}
IndexerRequest::GetState(_) => {
Response::new()
.body(IndexerResponse::GetState(state.clone().into()))
.send()?;
return Ok(());
} else {
if expects_response.is_some() {
Response::new().body(response_body).send()?;
}
}
}
Expand Down
87 changes: 87 additions & 0 deletions kinode/packages/settings/api/settings:sys-v0.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
interface settings {
variant request {
/// lazy-load-blob: none.
hi(hi-request),
/// lazy-load-blob: none.
peer-id(string),
/// lazy-load-blob: none.
eth-config(eth-config-request),
/// lazy-load-blob: none.
shutdown,
/// lazy-load-blob: none.
reset,
/// lazy-load-blob: none.
kill-process(string),
/// lazy-load-blob: none.
set-stylesheet(string),
}

type response = result<option<settings-data>, settings-error>;

record hi-request {
node: string,
content: string,
timeout: u64,
}

/// A subset of the actions that can be taken on the `eth`
/// runtime module. These are mostly used by the settings frontend.
variant eth-config-request {
add-provider(provider-config),
remove-provider(tuple<u64, string>),
set-public,
set-private,
allow-node(string),
unallow-node(string),
deny-node(string),
undeny-node(string),
}

/// This will be converted to the ProviderConfig type used in `eth`.
/// `trusted` in ProviderConfig will always be true.
/// Rather than provide full NodeOrRpcUrl, the settings
/// process will fetch KNS update for node-providers
/// and convert this to the type used in `eth`.
record provider-config {
chain-id: u64,
node-or-rpc-url: node-or-rpc-url,
}

variant node-or-rpc-url {
node(string),
rpc-url(string),
}

variant settings-data {
peer-id(identity),
}

record identity {
name: string,
networking-key: string,
routing: node-routing,
}

variant node-routing {
routers(list<string>),
direct(direct),
}

record direct {
ip: string,
ports: list<tuple<string, u16>>,
}

variant settings-error {
hi-timeout,
hi-offline,
kernel-nonresponsive,
malformed-request,
state-fetch-failed,
}
}

world settings-sys-v0 {
import settings;
include process-v1;
}
2 changes: 1 addition & 1 deletion kinode/packages/settings/pkg/ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
margin-left: 6px;
}
</style>
<script type="module" crossorigin src="/settings:settings:sys/assets/index-CwCaX2Ut.js"></script>
<script type="module" crossorigin src="/settings:settings:sys/assets/index-CrUC-dMu.js"></script>
<link rel="stylesheet" crossorigin href="/settings:settings:sys/assets/index-iGirBDd0.css">
</head>

Expand Down
Loading

0 comments on commit 3e3d08a

Please sign in to comment.