From 494c6b7e26964a6fe40ae372fac9375a54529621 Mon Sep 17 00:00:00 2001 From: danda Date: Wed, 23 Nov 2022 22:06:48 -0800 Subject: [PATCH] feat(core): load genesis file by network id Addresses #243 Support for loading a different genesis block for each network. The genesis block is loaded from a file whose path is the pattern: ~/.kindelia/genesis/.kdl Changes: core: * add util::genesis_path() with associated error enum * add util::genesis_code() with associated error enum * modify hvm::test_statements*() to accept network_id and load genesis block from file instead of compiled string * modify node::new() to to accept network_id and load genesis block from file instead of compiled string cli: * add network_id to test command, required by hvm::test_statements() * fix parsing of hex values for --network_id * add clap_num dep for parsing hex values --- kindelia/Cargo.toml | 1 + kindelia/src/cli.rs | 6 +++++- kindelia/src/main.rs | 19 +++++++++++++---- kindelia_core/src/node.rs | 3 +-- kindelia_core/src/runtime/mod.rs | 15 +++++++------- kindelia_core/src/util.rs | 35 ++++++++++++++++++++++++++++++++ 6 files changed, 64 insertions(+), 15 deletions(-) diff --git a/kindelia/Cargo.toml b/kindelia/Cargo.toml index 29e23979..6a9f7425 100644 --- a/kindelia/Cargo.toml +++ b/kindelia/Cargo.toml @@ -36,6 +36,7 @@ derive_builder = "0.11.2" # CLI / configuration clap = { version = "4.0.18", features = ["derive"] } clap_complete = "4.0.3" +clap-num = "1.0.2" toml = "0.5.9" # Datastructures diff --git a/kindelia/src/cli.rs b/kindelia/src/cli.rs index bdf3c82e..f3d6166a 100644 --- a/kindelia/src/cli.rs +++ b/kindelia/src/cli.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use clap::{Parser, Subcommand}; use clap_complete::Shell; +use clap_num::maybe_hex; use kindelia_common::Name; use kindelia_core::api::LimitStats; @@ -120,6 +121,9 @@ pub enum CliCommand { /// Whether to consider size and mana in the execution. #[clap(long)] sudo: bool, + /// Network id / magic number. + #[clap(long, value_parser=maybe_hex::)] + network_id: Option, }, /// Checks for statements in kdl file Check { @@ -205,7 +209,7 @@ pub enum CliCommand { #[clap(long)] data_dir: Option, /// Network id / magic number. - #[clap(long)] + #[clap(long, value_parser=maybe_hex::)] network_id: Option, }, /// Generate auto-completion for a shell. diff --git a/kindelia/src/main.rs b/kindelia/src/main.rs index 48b8491a..5b8a1e3e 100644 --- a/kindelia/src/main.rs +++ b/kindelia/src/main.rs @@ -123,9 +123,20 @@ pub fn run_cli() -> anyhow::Result<()> { ); match parsed.command { - CliCommand::Test { file, sudo } => { + CliCommand::Test { file, sudo, network_id } => { + let config = handle_config_file(&config_path).map_err(|e| anyhow!(e))?; + let config = Some(&config); + + let network_id = resolve_cfg!( + env = "KINDELIA_NETWORK_ID", + prop = "node.network.network_id".to_string(), + no_default = anyhow!("Missing `network_id` parameter."), + cli_val = network_id, + cfg = config, + ); + let code: String = file.read_to_string()?; - test_code(&code, sudo); + test_code(network_id, &code, sudo); Ok(()) } CliCommand::Check { file, encoded, command } => { @@ -655,8 +666,8 @@ async fn join_all( Ok(()) } -pub fn test_code(code: &str, sudo: bool) { - runtime::test_statements_from_code(code, sudo); +pub fn test_code(network_id: u32, code: &str, sudo: bool) { + runtime::test_statements_from_code(network_id, code, sudo); } fn init_socket() -> Option { diff --git a/kindelia_core/src/node.rs b/kindelia_core/src/node.rs index 095727f8..a5dc9d59 100644 --- a/kindelia_core/src/node.rs +++ b/kindelia_core/src/node.rs @@ -24,7 +24,6 @@ use crate::api::{BlockInfo, FuncInfo, NodeRequest}; use crate::bits; use crate::bits::ProtoSerialize; use crate::config::MineConfig; -use crate::constants; use crate::net::{ProtoAddr, ProtoComm}; use crate::persistence::BlockStorage; use crate::runtime::*; @@ -776,7 +775,7 @@ impl Node { let (query_sender, query_receiver) = mpsc::sync_channel(1); let genesis_stmts = - parser::parse_code(constants::GENESIS_CODE).expect("Genesis code parses"); + parser::parse_code(&genesis_code(network_id).unwrap()).expect("Genesis code parses"); let genesis_block = build_genesis_block(&genesis_stmts); let genesis_block = genesis_block.hashed(); let genesis_hash = genesis_block.get_hash().into(); diff --git a/kindelia_core/src/runtime/mod.rs b/kindelia_core/src/runtime/mod.rs index 51aae575..b7896dbd 100644 --- a/kindelia_core/src/runtime/mod.rs +++ b/kindelia_core/src/runtime/mod.rs @@ -260,9 +260,8 @@ use kindelia_lang::ast::{Func, Oper, Statement, Term, Var}; use kindelia_lang::parser::{parse_code, parse_statements, ParseErr}; use crate::bits::ProtoSerialize; -use crate::constants; use crate::persistence::DiskSer; -use crate::util::{self, mask, U128_SIZE}; +use crate::util::{self, genesis_code, mask, U128_SIZE}; use crate::util::{LocMap, NameMap, U120Map, U128Map}; // Functions @@ -4227,7 +4226,7 @@ pub fn print_io_consts() { } // Serializes, deserializes and evaluates statements -pub fn test_statements(statements: &Vec, debug: bool) { +pub fn test_statements(network_id: u32, statements: &Vec, debug: bool) { let str_0 = ast::view_statements(statements); let statements = &Vec::proto_deserialized(&statements.proto_serialized()).unwrap(); let str_1 = ast::view_statements(statements); @@ -4238,7 +4237,7 @@ pub fn test_statements(statements: &Vec, debug: bool) { // TODO: code below does not need heaps_path at all. extract heap persistence out of Runtime. let heaps_path = dirs::home_dir().unwrap().join(".kindelia").join("state").join("heaps"); - let genesis_smts = parse_code(constants::GENESIS_CODE).expect("Genesis code parses"); + let genesis_smts = parse_code(&genesis_code(network_id).unwrap()).expect("Genesis code parses"); let mut rt = init_runtime(heaps_path, &genesis_smts); let init = Instant::now(); rt.run_statements(&statements, false, debug); @@ -4254,14 +4253,14 @@ pub fn test_statements(statements: &Vec, debug: bool) { println!("[time] {} ms", init.elapsed().as_millis()); } -pub fn test_statements_from_code(code: &str, debug: bool) { +pub fn test_statements_from_code(network_id: u32, code: &str, debug: bool) { let statments = parse_statements(code); match statments { - Ok((.., statements)) => test_statements(&statements, debug), + Ok((.., statements)) => test_statements(network_id, &statements, debug), Err(ParseErr { code, erro }) => println!("{}", erro), } } -pub fn test_statements_from_file(file: &str, debug: bool) { - test_statements_from_code(&std::fs::read_to_string(file).expect("file not found"), debug); +pub fn test_statements_from_file(network_id: u32, file: &str, debug: bool) { + test_statements_from_code(network_id, &std::fs::read_to_string(file).expect("file not found"), debug); } diff --git a/kindelia_core/src/util.rs b/kindelia_core/src/util.rs index 9c851d3f..77fe8b64 100644 --- a/kindelia_core/src/util.rs +++ b/kindelia_core/src/util.rs @@ -5,6 +5,8 @@ // #![allow(clippy::style)] use std::collections::HashMap; +use std::path::PathBuf; +use thiserror::Error; use bit_vec::BitVec; @@ -181,3 +183,36 @@ macro_rules! print_with_timestamp { println!("{} ~~ {}", get_time_micro(), format!($($arg)*)); }; } + +#[derive(Error, Debug)] +pub(crate) enum GenesisPathError { + #[error("Home directory not found")] + HomeDirNotFound, + #[error("File not found in {0}")] + FileNotFound(PathBuf) +} + +pub(crate) fn genesis_path(network_id: u32) -> Result { + let path = dirs::home_dir().ok_or(GenesisPathError::HomeDirNotFound)?.join(".kindelia").join("genesis").join(format!("{:#02X}.kdl", network_id)); + match path.exists() { + true => Ok(path), + false => Err(GenesisPathError::FileNotFound(path)), + } +} + +#[derive(Error, Debug)] +pub(crate) enum GenesisCodeError { + #[error(transparent)] + PathError(#[from] GenesisPathError), + + #[error("Genesis block could not be read from {path:?}.")] + ReadError { + path: PathBuf, + cause: std::io::Error, + } +} + +pub(crate) fn genesis_code(network_id: u32) -> Result { + let path = genesis_path(network_id)?; + std::fs::read_to_string(&path).map_err(|e| GenesisCodeError::ReadError{path, cause: e}) +}