From 741afb5c400f7b979b852cca414872ef1d838edc Mon Sep 17 00:00:00 2001 From: bitful-pannul Date: Tue, 4 Jun 2024 22:03:52 -0700 Subject: [PATCH] register kimap name, set ip, pubkey and wsport --- .../kns_indexer/kns_indexer/src/lib.rs | 304 ++++++++---------- kinode/src/fakenet/helpers.rs | 37 +++ kinode/src/fakenet/mod.rs | 162 ++++++---- kinode/src/main.rs | 2 +- 4 files changed, 260 insertions(+), 245 deletions(-) diff --git a/kinode/packages/kns_indexer/kns_indexer/src/lib.rs b/kinode/packages/kns_indexer/kns_indexer/src/lib.rs index 6a2ca140..7260054b 100644 --- a/kinode/packages/kns_indexer/kns_indexer/src/lib.rs +++ b/kinode/packages/kns_indexer/kns_indexer/src/lib.rs @@ -2,14 +2,9 @@ use crate::kinode::process::kns_indexer::{ GetStateRequest, IndexerRequests, NamehashToNameRequest, NodeInfoRequest, }; use alloy_sol_types::{sol, SolEvent}; -use kinode_process_lib::{ - await_message, call_init, eth, net, println, Address, Message, Request, Response, -}; +use kinode_process_lib::{await_message, call_init, eth, println, Address, Message, Response}; use serde::{Deserialize, Serialize}; -use std::collections::{ - hash_map::{Entry, HashMap}, - BTreeMap, -}; +use std::collections::{hash_map::HashMap, BTreeMap}; wit_bindgen::generate!({ path: "target/wit", @@ -19,9 +14,9 @@ wit_bindgen::generate!({ }); #[cfg(not(feature = "simulation-mode"))] -const KNS_ADDRESS: &'static str = "0xca5b5811c0c40aab3295f932b1b5112eb7bb4bd6"; // optimism +const KIMAP_ADDRESS: &'static str = "0xca5b5811c0c40aab3295f932b1b5112eb7bb4bd6"; // optimism #[cfg(feature = "simulation-mode")] -const KNS_ADDRESS: &'static str = "0x5FbDB2315678afecb367f032d93F642f64180aa3"; // local +const KIMAP_ADDRESS: &'static str = "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707"; // local #[cfg(not(feature = "simulation-mode"))] const CHAIN_ID: u64 = 10; // optimism @@ -40,23 +35,43 @@ struct State { contract_address: String, // namehash to human readable name names: HashMap, - // human readable name to most recent on-chain routing information as json - // NOTE: not every namehash will have a node registered - nodes: HashMap, + // temporary hash->name mapping + hashes: HashMap, + // notehash->note mapping + // note, do not need this here, adding relevant notes directly to KNS rn. + // notes: HashMap, + // NOTE: wip knsUpdates not 1-1 rn + nodes: HashMap, // last block we have an update from block: u64, } +#[derive(Clone, Debug, Serialize, Deserialize)] +struct Node { + pub name: String, // actual username / domain name + pub hash: String, // hex namehash of node + // pub tba: String, can query for this as events come in too. + pub parent_hash: String, // hex namehash of parent node, top level = 0x0? + pub public_key: Option, + pub ips: Vec, + pub ports: BTreeMap, + pub routers: Vec, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct Note { + pub name: String, // note full name + pub hash: String, // hex namehash of note (in key already?) + pub node_hash: String, // hex namehash of node + pub value: String, // note value, hex/bytes instead? +} + sol! { - // Logged whenever a KNS node is created - event NodeRegistered(bytes32 indexed node, bytes name); - event KeyUpdate(bytes32 indexed node, bytes32 key); - event IpUpdate(bytes32 indexed node, uint128 ip); - event WsUpdate(bytes32 indexed node, uint16 port); - event WtUpdate(bytes32 indexed node, uint16 port); - event TcpUpdate(bytes32 indexed node, uint16 port); - event UdpUpdate(bytes32 indexed node, uint16 port); - event RoutingUpdate(bytes32 indexed node, bytes32[] routers); + // Kimap events + event Name(bytes32 indexed parent, bytes32 indexed child, bytes label); + event NewNote(bytes32 indexed node, bytes32 indexed notehash, bytes note, bytes data); + event UpdateNote(bytes32 indexed note, bytes data); + event Zeroth(address zerotba); } fn subscribe_to_logs(eth_provider: ð::Provider, from_block: u64, filter: eth::Filter) { @@ -75,43 +90,20 @@ fn subscribe_to_logs(eth_provider: ð::Provider, from_block: u64, filter: eth: call_init!(init); fn init(our: Address) { - println!("indexing on contract address {}", KNS_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 = match get_typed_state(|bytes| Ok(bincode::deserialize::(bytes)?)) { - // Some(s) => { - // // if chain id or contract address changed from a previous run, reset state - // if s.chain_id != CHAIN_ID || s.contract_address != KNS_ADDRESS { - // println!("resetting state because runtime contract address or chain ID changed"); - // State { - // chain_id: CHAIN_ID, - // contract_address: KNS_ADDRESS.to_string(), - // names: HashMap::new(), - // nodes: HashMap::new(), - // block: KNS_FIRST_BLOCK, - // } - // } else { - // println!("loading in {} persisted PKI entries", s.nodes.len()); - // s - // } - // } - // None => State { - // chain_id: CHAIN_ID, - // contract_address: KNS_ADDRESS.to_string(), - // names: HashMap::new(), - // nodes: HashMap::new(), - // block: KNS_FIRST_BLOCK, - // }, - // }; + let state = State { chain_id: CHAIN_ID, - contract_address: KNS_ADDRESS.to_string(), + contract_address: KIMAP_ADDRESS.to_string(), names: HashMap::new(), + hashes: HashMap::new(), nodes: HashMap::new(), + // notes: HashMap::new(), block: KNS_FIRST_BLOCK, }; @@ -129,12 +121,10 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { .from_block(state.block - 1) .to_block(eth::BlockNumberOrTag::Latest) .events(vec![ - "NodeRegistered(bytes32,bytes)", - "KeyUpdate(bytes32,bytes32)", - "IpUpdate(bytes32,uint128)", - "WsUpdate(bytes32,uint16)", - "TcpUpdate(bytes32,uint16)", - "RoutingUpdate(bytes32,bytes32[])", + "Name(bytes32,bytes32,bytes)", + "NewNote(bytes32,bytes32,bytes,bytes)", + "UpdateNote(bytes32,bytes)", + "Zeroth(address)", ]); // 60s timeout -- these calls can take a long time @@ -174,16 +164,6 @@ fn main(our: Address, mut state: State) -> anyhow::Result<()> { } } - // shove initial state into net::net - // Request::new() - // .target((&our.node, "net", "distro", "sys")) - // .body(rmp_serde::to_vec(&net::NetAction::KnsBatchUpdate( - // state.nodes.values().cloned().collect::>(), - // ))?) - // .send()?; - - // set_state(&bincode::serialize(&state)?); - let mut pending_requests: BTreeMap> = BTreeMap::new(); loop { @@ -321,125 +301,103 @@ fn handle_eth_message( Ok(()) } -fn handle_log(our: &Address, state: &mut State, log: ð::Log) -> anyhow::Result<()> { - let node_id = log.topics()[1]; +fn get_full_name(state: &mut State, label: &str, parent_hash: &str) -> String { + let mut current_hash = parent_hash; + let mut full_name = label.to_string(); - let name = match state.names.entry(node_id.to_string()) { - Entry::Occupied(o) => o.into_mut(), - Entry::Vacant(v) => v.insert(get_name(&log)?), - }; + // Traverse up the hierarchy by following the node hash to find its parent name + while let Some(parent_name) = state.names.get(current_hash) { + full_name = format!("{}.{}", full_name, parent_name); + // Update current_hash to the parent's hash for the next iteration + if let Some(new_parent_hash) = state.hashes.get(parent_name) { + current_hash = new_parent_hash; + } else { + break; + } + } - let node = state - .nodes - .entry(name.to_string()) - .or_insert_with(|| net::KnsUpdate { - name: name.to_string(), - owner: "".to_string(), - node: node_id.to_string(), - public_key: "".to_string(), - ips: vec![], - ports: BTreeMap::new(), - routers: vec![], - }); - - let mut send = true; + full_name +} +fn handle_log(_our: &Address, state: &mut State, log: ð::Log) -> anyhow::Result<()> { match log.topics()[0] { - KeyUpdate::SIGNATURE_HASH => { - node.public_key = KeyUpdate::decode_log_data(log.data(), true) - .unwrap() - .key - .to_string(); + Name::SIGNATURE_HASH => { + let decoded = Name::decode_log_data(log.data(), true).unwrap(); + + let label = String::from_utf8(decoded.label.to_vec())?; + let parent_hash = decoded.parent.to_string(); + let node_hash = decoded.child.to_string(); + + println!( + "got name, node_hash, parent_node: {:?}, {:?}, {:?}", + label, node_hash, parent_hash + ); + + let full_name = get_full_name(state, &label, &parent_hash); + + println!("got full hierarchical name: {:?}", full_name); + state.names.insert(node_hash.clone(), full_name); + state.hashes.insert(node_hash, label); } - IpUpdate::SIGNATURE_HASH => { - let ip = IpUpdate::decode_log_data(log.data(), true).unwrap().ip; - node.ips = vec![format!( - "{}.{}.{}.{}", - (ip >> 24) & 0xFF, - (ip >> 16) & 0xFF, - (ip >> 8) & 0xFF, - ip & 0xFF - )]; - // when we get ip data, we should delete any router data, - // since the assignment of ip indicates an direct node - node.routers = vec![]; + NewNote::SIGNATURE_HASH => { + let decoded = NewNote::decode_log_data(log.data(), true).unwrap(); + + let note = String::from_utf8(decoded.note.to_vec())?; + let _notehash: String = decoded.notehash.to_string(); + let node = decoded.node.to_string(); + + let full_note_name = get_full_name(state, ¬e, &node); + + println!("got full note name: {:?}", full_note_name); + + let note_value = String::from_utf8(decoded.data.to_vec())?; + + println!("got note: {:?}", note); + println!("got note value: {:?}", note_value); + + // generalize, cleaner system + match note.as_str() { + "~wsport" => { + state.nodes.entry(node.clone()).and_modify(|node| { + node.ports + .insert("ws".to_string(), note_value.parse().unwrap()); + }); + } + "~networkingkey" => { + state.nodes.entry(node.clone()).and_modify(|node| { + node.public_key = Some(note_value.clone()); + }); + } + "~routers" => { + state.nodes.entry(node.clone()).and_modify(|node| { + node.routers.push(note_value.clone()); + }); + } + "~ip" => { + state.nodes.entry(node.clone()).and_modify(|node| { + node.ips.push(note_value.clone()); + }); + } + _ => {} + } + + // todo: update corresponding node info at right time and send to KNS. } - WsUpdate::SIGNATURE_HASH - | TcpUpdate::SIGNATURE_HASH - | WtUpdate::SIGNATURE_HASH - | UdpUpdate::SIGNATURE_HASH => { - match log.topics()[0] { - WsUpdate::SIGNATURE_HASH => node.ports.insert( - "ws".to_string(), - WsUpdate::decode_log_data(log.data(), true).unwrap().port, - ), - TcpUpdate::SIGNATURE_HASH => node.ports.insert( - "tcp".to_string(), - TcpUpdate::decode_log_data(log.data(), true).unwrap().port, - ), - WtUpdate::SIGNATURE_HASH => node.ports.insert( - "wt".to_string(), - WtUpdate::decode_log_data(log.data(), true).unwrap().port, - ), - UdpUpdate::SIGNATURE_HASH => node.ports.insert( - "udp".to_string(), - UdpUpdate::decode_log_data(log.data(), true).unwrap().port, - ), - _ => None, - }; - // when we get port data, we should delete any router data, - // since the assignment of port indicates an direct node - node.routers = vec![]; + UpdateNote::SIGNATURE_HASH => { + let _decoded = UpdateNote::decode_log_data(log.data(), true).unwrap(); + + println!("got updated note!"); + // state.notes.entry(note_hash).and_modify(|note| { + // note.value = note_data.clone(); + // }); } - RoutingUpdate::SIGNATURE_HASH => { - node.routers = RoutingUpdate::decode_log_data(log.data(), true) - .unwrap() - .routers - .iter() - .map(|r| r.to_string()) - .collect::>(); - // when we get routing data, we should delete any ws/ip data, - // since the assignment of routers indicates an indirect node - node.ips = vec![]; - node.ports.clear(); + Zeroth::SIGNATURE_HASH => { + // println!("got zeroth log: {:?}", log); } _ => { - send = false; + println!("got other log: {:?}", log); } } - if node.public_key != "" - && ((!node.ips.is_empty() && !node.ports.is_empty()) || node.routers.len() > 0) - && send - { - Request::new() - .target((&our.node, "net", "distro", "sys")) - .body(rmp_serde::to_vec(&net::NetAction::KnsUpdate(node.clone()))?) - .send()?; - } - - // if new block is > 100 from last block, save state - // let block = log.block_number.expect("expect"); - // if block > state.block + 100 { - // kinode_process_lib::print_to_terminal( - // 1, - // &format!( - // "persisting {} PKI entries at block {}", - // state.nodes.len(), - // block - // ), - // ); - // state.block = block; - // set_state(&bincode::serialize(state)?); - // } Ok(()) } - -fn get_name(log: ð::Log) -> anyhow::Result { - let decoded = NodeRegistered::decode_log_data(log.data(), false).map_err(|_e| { - anyhow::anyhow!( - "got event other than NodeRegistered without knowing about existing node name" - ) - })?; - net::dnswire_decode(&decoded.name).map_err(|e| anyhow::anyhow!(e)) -} diff --git a/kinode/src/fakenet/helpers.rs b/kinode/src/fakenet/helpers.rs index cf543b85..e7979355 100644 --- a/kinode/src/fakenet/helpers.rs +++ b/kinode/src/fakenet/helpers.rs @@ -2,6 +2,7 @@ use alloy_sol_macro::sol; use sha3::{Digest, Keccak256}; sol! { + #[allow(missing_docs)] #[sol(rpc)] contract RegisterHelpers { function register( @@ -26,6 +27,42 @@ sol! { function ownerOf(uint256 node) returns (address); function multicall(bytes[] calldata data); + + // new kimap contracts + function replicate ( + address who, + bytes calldata name, + bytes calldata initialization, + bytes calldata erc721Data, + address implementation + ) external returns ( + address tba + ); + + function get ( + bytes32 node + ) external view returns ( + address tba, + address owner, + bytes, + ); + + function note ( + bytes calldata note, + bytes calldata data + ) external returns ( + bytes32 notenode + ); + + // tba account + function execute( + address to, + uint256 value, + bytes calldata data, + uint8 operation + ) external payable returns (bytes memory returnData); + + function token() external view returns (uint256,address,uint256); } } diff --git a/kinode/src/fakenet/mod.rs b/kinode/src/fakenet/mod.rs index 9e2c1276..95320ac1 100644 --- a/kinode/src/fakenet/mod.rs +++ b/kinode/src/fakenet/mod.rs @@ -17,11 +17,15 @@ pub use helpers::RegisterHelpers::*; pub use helpers::*; const FAKE_DOTDEV: &str = "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"; +const FAKE_DOTOS: &str = "0xC466dc53e3e2a29A296fE38Bdeab60a7C023A383"; + +const KINO_ACCOUNT_IMPL: &str = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"; +const KINOMAP: &str = "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707"; // "0x68B1D87F95878fE05B998F19b66F4baba5De1aed"; /// Attempts to connect to a local anvil fakechain, -/// registering a name with its KNS contract. +/// registering a name with its KiMap contract. /// If name is already registered, resets it. -pub async fn register_local( +pub async fn mint_local( name: &str, ws_port: u16, pubkey: &str, @@ -31,8 +35,8 @@ pub async fn register_local( "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", )?; - let dotdev = Address::from_str(FAKE_DOTDEV)?; - let kns = Address::from_str(KNS_ADDRESS)?; + let dotos = Address::from_str(FAKE_DOTOS)?; + let kimap = Address::from_str(KINOMAP)?; let endpoint = format!("ws://localhost:{}", fakechain_port); let ws = WsConnect { @@ -43,84 +47,26 @@ pub async fn register_local( let client = ClientBuilder::default().ws(ws).await?; let provider = Provider::new_with_client(client); - let fqdn = dns_encode_fqdn(name); let namehash = encode_namehash(name); - // todo: find a better way? - let namehash_bint: B256 = namehash.into(); - let namehash_uint: U256 = namehash_bint.into(); - let ip: u128 = 0x7F000001; // localhost IP (127.0.0.1) - - let set_ip = setAllIpCall { - _node: namehash.into(), - _ip: ip, - _ws: ws_port, - _wt: 0, - _tcp: 0, - _udp: 0, + // interesting, even if we have a minted name, this does not explicitly fail. + let replicate_call = replicateCall { + who: wallet.address(), + name: name.as_bytes().to_vec(), + initialization: vec![], + erc721Data: vec![], + implementation: Address::from_str(KINO_ACCOUNT_IMPL).unwrap(), } .abi_encode(); - let set_key = setKeyCall { - _node: namehash.into(), - _key: pubkey.parse()?, - } - .abi_encode(); - - let exists_call = ownerOfCall { - node: namehash_uint, - } - .abi_encode(); - - let exists_tx = TransactionRequest::default() - .to(Some(dotdev)) - .input(TransactionInput::new(exists_call.into())); - - let exists = provider.call(exists_tx, None).await; - - let (call_input, to) = match exists { - Err(_e) => { - // name is not taken, register normally - let register = registerCall { - _name: fqdn.into(), - _to: Address::from_str("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")?, - _data: vec![set_ip.into(), set_key.into()], - } - .abi_encode(); - - (register, dotdev) - } - Ok(_owner) => { - // name is taken, call setAllIp an setKey directly with multicall - let set_ip = setAllIpCall { - _node: namehash.into(), - _ip: ip, - _ws: ws_port, - _wt: 0, - _tcp: 0, - _udp: 0, - }; - let set_key = setKeyCall { - _node: namehash.into(), - _key: pubkey.parse()?, - }; - - let multicall = multicallCall { - data: vec![set_ip.abi_encode(), set_key.abi_encode()], - } - .abi_encode(); - - (multicall, kns) - } - }; let nonce = provider .get_transaction_count(wallet.address(), None) .await?; let mut tx = TxLegacy { - to: TxKind::Call(to), + to: TxKind::Call(dotos), nonce: nonce.to::(), - input: call_input.into(), + input: replicate_call.into(), chain_id: Some(31337), gas_limit: 3000000, gas_price: 100000000000, @@ -134,6 +80,80 @@ pub async fn register_local( let _tx_hash = provider.send_raw_transaction(buf.into()).await?; + // try to get name, if there isn't one, replicate it from .dev + let get_call = getCall { + node: namehash.into(), + } + .abi_encode(); + + let get_tx = TransactionRequest::default() + .to(Some(kimap)) + .input(TransactionInput::new(get_call.into())); + + let exists = provider.call(get_tx, None).await?; + println!("exists: {:?}", exists); + + // todo abi_decode() properly into getReturn. + // tba, owner. + // also note, should be (Address, Address, Bytes), but that fails on a normal node!? + // fix the sol Value decoding, update alloy deps. currently we do not need the note bytes but will in the future. + let decoded = <(Address, Address)>::abi_decode(&exists, false)?; + + let tba = decoded.0; + let _owner = decoded.1; + // now set ip, port and pubkey + // multicall coming on contracts soon, will make it easier. + + let port_call = noteCall { + note: "~wsport".as_bytes().to_vec(), + data: ws_port.to_string().as_bytes().to_vec(), + }; + + let ip_call = noteCall { + note: "~ip".as_bytes().to_vec(), + data: "127.0.0.1".as_bytes().to_vec(), + }; + + let pubkey_call = noteCall { + note: "~networkingkey".as_bytes().to_vec(), + data: pubkey.as_bytes().to_vec(), + }; + + let calls = vec![port_call, ip_call, pubkey_call]; + + for call in calls { + let note_call = call.abi_encode(); + + let execute_call = executeCall { + to: kimap, + value: U256::from(0), + data: note_call, + operation: 0, + } + .abi_encode(); + + let nonce = provider + .get_transaction_count(wallet.address(), None) + .await?; + + let mut tx = TxLegacy { + to: TxKind::Call(tba), + nonce: nonce.to::(), + input: execute_call.into(), + chain_id: Some(31337), + gas_limit: 3000000, + gas_price: 100000000000, + ..Default::default() + }; + + let sig = wallet.sign_transaction_sync(&mut tx)?; + let signed_tx = tx.into_signed(sig); + let mut buf = vec![]; + signed_tx.encode_signed(&mut buf); + + let _tx_hash = provider.send_raw_transaction(buf.into()).await?; + } + Ok(()) } diff --git a/kinode/src/main.rs b/kinode/src/main.rs index 0b4fd477..23e2ae89 100644 --- a/kinode/src/main.rs +++ b/kinode/src/main.rs @@ -566,7 +566,7 @@ pub async fn simulate_node( let fakechain_port: u16 = fakechain_port.unwrap_or(8545); let ws_port = ws_networking.local_addr().unwrap().port(); - fakenet::register_local(&name, ws_port, &pubkey, fakechain_port) + fakenet::mint_local(&name, ws_port, &pubkey, fakechain_port) .await .unwrap();