From 3f30e7a7e7fa40ba3ac78c93dfa536948e5ef015 Mon Sep 17 00:00:00 2001 From: Aleksandr Petrosyan Date: Thu, 14 Apr 2022 22:59:33 +0400 Subject: [PATCH] [ci]: Add genesis check and update documentation. Signed-off-by: Aleksandr Petrosyan --- .github/workflows/iroha2-dev-pr-static.yml | 6 - .github/workflows/iroha2-dev-pr.yml | 9 + configs/peer/genesis.json | 4 +- core/src/genesis.rs | 21 +- data_model/src/domain.rs | 24 +- hooks/pre-commit.sample | 2 +- scripts/check.sh | 19 ++ scripts/check_docs.sh | 8 - tools/generator/src/main.rs | 248 +++++++++++---------- 9 files changed, 188 insertions(+), 153 deletions(-) create mode 100755 scripts/check.sh delete mode 100755 scripts/check_docs.sh diff --git a/.github/workflows/iroha2-dev-pr-static.yml b/.github/workflows/iroha2-dev-pr-static.yml index 2def0484fe6..76f2d9a90d2 100644 --- a/.github/workflows/iroha2-dev-pr-static.yml +++ b/.github/workflows/iroha2-dev-pr-static.yml @@ -32,9 +32,3 @@ jobs: - name: Verify iroha_data_model still supports no_std run: cargo nono check --package iroha_data_model --no-default-features if: always() - - - name: Documentation check - run: | - cargo doc --no-deps --quiet - ./scripts/check_docs.sh - if: always() diff --git a/.github/workflows/iroha2-dev-pr.yml b/.github/workflows/iroha2-dev-pr.yml index b29a20c3a20..3d52da7aea9 100644 --- a/.github/workflows/iroha2-dev-pr.yml +++ b/.github/workflows/iroha2-dev-pr.yml @@ -39,6 +39,15 @@ jobs: working-directory: wasm env: RUSTC_BOOTSTRAP: 1 + - name: Documentation check + run: | + cargo doc --no-deps --quiet + ./scripts/check.sh docs + if: always() + - name: Check genesis + run: | + ./scripts/check.sh genesis + if: always() # Coverage is both in PR and in push pipelines so that: # 1. PR can get coverage report from bot. diff --git a/configs/peer/genesis.json b/configs/peer/genesis.json index e800bab5cb6..b0b8523644b 100644 --- a/configs/peer/genesis.json +++ b/configs/peer/genesis.json @@ -54,8 +54,8 @@ } }, "value_type": "Quantity", - "metadata": {}, - "mintable": "Infinitely" + "mintable": "Infinitely", + "metadata": {} } } } diff --git a/core/src/genesis.rs b/core/src/genesis.rs index 59dd968cd44..da537b36102 100644 --- a/core/src/genesis.rs +++ b/core/src/genesis.rs @@ -1,4 +1,5 @@ -//! This module contains execution Genesis Block logic, and `GenesisBlock` definition. +//! Genesis-related logic and constructs. Contains the `GenesisBlock`, +//! `RawGenesisBlock` and the `RawGenesisBlockBuilder` structures. #![allow(clippy::module_name_repetitions)] #![allow(clippy::new_without_default)] @@ -50,8 +51,9 @@ pub trait GenesisNetworkTrait: transaction_limits: &TransactionLimits, ) -> Result>; - /// Waits for a minimum number of [`Peer`]s needed for consensus to be online. - /// Returns initialized network [`Topology`] with the set A consisting of online peers. + /// Waits for a minimum number of [`Peer`]s needed for consensus + /// to be online. Returns initialized network [`Topology`] with + /// the set A consisting of online peers. async fn wait_for_peers( &self, this_peer_id: PeerId, @@ -59,9 +61,10 @@ pub trait GenesisNetworkTrait: network: Addr, ) -> Result; - // FIXME: Having `ctx` reference and `sumaregi` reference here is not ideal. - // The way it is currently designed, this function is called from sumeragi and then calls sumeragi, while being in an unrelated module. - // This needs to be restructured. + // FIXME: Having `ctx` reference and `sumaregi` reference here is + // not ideal. The way it is currently designed, this function is + // called from sumeragi and then calls sumeragi, while being in an + // unrelated module. This needs to be restructured. /// Submits genesis transactions. /// @@ -518,7 +521,7 @@ mod tests { let domain_id: DomainId = "wonderland".parse().unwrap(); assert_eq!( finished_genesis_block.transactions[0].isi[0], - RegisterBox::new(domain_id.clone()).into() + Instruction::from(RegisterBox::new(NewDomain::new(domain_id.clone()))) ); assert_eq!( finished_genesis_block.transactions[0].isi[1], @@ -534,7 +537,7 @@ mod tests { let domain_id: DomainId = "tulgey_wood".parse().unwrap(); assert_eq!( finished_genesis_block.transactions[0].isi[3], - RegisterBox::new(domain_id.clone()).into() + Instruction::from(RegisterBox::new(NewDomain::new(domain_id.clone()))) ); assert_eq!( finished_genesis_block.transactions[0].isi[4], @@ -545,7 +548,7 @@ mod tests { let domain_id: DomainId = "meadow".parse().unwrap(); assert_eq!( finished_genesis_block.transactions[0].isi[5], - RegisterBox::new(domain_id.clone()).into() + Instruction::from(RegisterBox::new(NewDomain::new(domain_id.clone()))) ); assert_eq!( finished_genesis_block.transactions[0].isi[6], diff --git a/data_model/src/domain.rs b/data_model/src/domain.rs index b404194def9..c7330e2f8e1 100644 --- a/data_model/src/domain.rs +++ b/data_model/src/domain.rs @@ -1,4 +1,9 @@ -//! This module contains [`Domain`](`crate::domain::Domain`) structure and related implementations and trait implementations. +//! This module contains [`Domain`](`crate::domain::Domain`) structure +//! and related implementations and trait implementations. +//! +//! Note that the Genesis domain and account have a temporary +//! privileged position, and permission validation is turned off for +//! the Genesis block. #[cfg(not(feature = "std"))] use alloc::{format, string::String, vec::Vec}; @@ -18,7 +23,9 @@ use crate::{ Identifiable, Name, ParseError, }; -/// Genesis domain name. Genesis domain should contain only genesis account. +/// The domain name of the genesis domain. +/// +/// The genesis domain should only contain the genesis account. pub const GENESIS_DOMAIN_NAME: &str = "genesis"; /// Genesis domain. It will contain only one `genesis` account. @@ -62,8 +69,11 @@ impl From for Domain { /// Builder which can be submitted in a transaction to create a new [`Domain`] #[derive(Debug, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] pub struct NewDomain { + /// The identification associated to the domain builder. id: ::Id, + /// The (IPFS) link to the logo of this domain. logo: Option, + /// metadata associated to the domain builder. pub metadata: Metadata, } @@ -82,6 +92,7 @@ impl Ord for NewDomain { } impl NewDomain { + /// Create a [`NewDomain`], reserved for internal use. #[must_use] pub fn new(id: ::Id) -> Self { Self { @@ -251,8 +262,8 @@ impl Domain { self.asset_definitions.get_mut(asset_definition_id) } - /// Add asset definition into the [`Domain`] returning previous asset definition stored under - /// the same id + /// Add asset definition into the [`Domain`] returning previous + /// asset definition stored under the same id #[inline] pub fn add_asset_definition( &mut self, @@ -284,9 +295,8 @@ impl FromIterator for crate::Value { } } -/// Represents path in IPFS. Performs some checks to ensure path validity. -/// -/// Should be constructed with `from_str()` method. +/// Represents path in IPFS. Performs checks to ensure path validity. +/// Construct using [`FromStr::from_str`] method. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Serialize, IntoSchema)] pub struct IpfsPath(String); diff --git a/hooks/pre-commit.sample b/hooks/pre-commit.sample index 1a8526044bb..2b40c1e290b 100755 --- a/hooks/pre-commit.sample +++ b/hooks/pre-commit.sample @@ -1,4 +1,4 @@ #!/bin/sh set -e -cargo run --bin iroha_docs >docs/source/references/config.md +cargo run --bin iroha_gen -- --docs >docs/source/references/config.md git add docs/source/references/config.md diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 00000000000..0d402180e74 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -e + +TMPFILE=$(mktemp) + +case $1 in + "docs") + cargo run --bin iroha_gen -- --docs >"$TMPFILE" + diff "$TMPFILE" docs/source/references/config.md || { + echo 'Please re-generate docs with git hook in ./hooks directory' + exit 1 + };; + "genesis") + cargo run --bin iroha_gen -- --genesis >"$TMPFILE" + diff "$TMPFILE" configs/peer/genesis.json || { + echo 'Please re-generate the genesis with `cargo run --bin iroha_gen -- --genesis`' + exit 1 + };; +esac diff --git a/scripts/check_docs.sh b/scripts/check_docs.sh deleted file mode 100755 index 87ba12f7594..00000000000 --- a/scripts/check_docs.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -set -ex -TMPFILE=$(mktemp) -cargo run --bin iroha_docs >$TMPFILE -diff $TMPFILE docs/source/references/config.md || { - echo "Please regenerate docs with git hook in ./hooks directory" - exit 1 -} diff --git a/tools/generator/src/main.rs b/tools/generator/src/main.rs index 2fdfa6dea21..3ce17232fcf 100644 --- a/tools/generator/src/main.rs +++ b/tools/generator/src/main.rs @@ -3,162 +3,170 @@ #![allow(clippy::restriction)] -use std::{ - fmt::Debug, - io::{stdout, BufWriter, Result, Write}, -}; +use std::io::{stdout, BufWriter, Write}; -use color_eyre::eyre::WrapErr; +use color_eyre::eyre::WrapErr as _; use iroha::config::Configuration; -use iroha_config::Configurable; -use iroha_core::{ - genesis::{RawGenesisBlock, RawGenesisBlockBuilder}, - tx::{AssetDefinition, MintBox}, -}; -use serde_json::{Map, Value}; // TODO: if we merge #2077 first, we should change this to sync up with the default in docs. static DEFAULT_PUBLIC_KEY: &str = "ed01207233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"; fn main() -> color_eyre::Result<()> { + use docs::PrintDocs as _; color_eyre::install().unwrap(); let mut output = BufWriter::new(stdout()); - if std::env::args().any(|a| is_genesis(&a)) { + if std::env::args().any(|a| cli::is_genesis(&a)) { writeln!( output, "{}", - serde_json::to_string_pretty(&generate_default_genesis()?)? + serde_json::to_string_pretty(&genesis::generate_default()?)? )?; Ok(()) - } else if std::env::args().any(|a| is_schema(&a)) { + } else if std::env::args().any(|a| cli::is_schema(&a)) { let schemas = iroha_schema_bin::build_schemas(); writeln!(output, "{}", serde_json::to_string_pretty(&schemas)?)?; Ok(()) - } else if std::env::args().any(|a| is_docs(&a)) { + } else if std::env::args().any(|a| cli::is_docs(&a)) { Configuration::get_markdown(&mut BufWriter::new(stdout())) .wrap_err("Failed to generate documentation") } else { - print_help(); + cli::print_help(); Ok(()) } } -fn print_help() { - println!("Tool for generating iroha-related data."); - println!(); - println!("pass `--docs` or `-d` to generate sample config and its documentation."); - println!("pass `--schema` or `-s` to generate the schema."); - println!("pass `--genesis` or `-g` to generate the genesis block."); -} +mod cli { + pub fn print_help() { + println!("Tool for generating iroha-related data."); + println!(); + println!("pass `--docs` or `-d` to generate sample config and its documentation."); + println!("pass `--schema` or `-s` to generate the schema."); + println!("pass `--genesis` or `-g` to generate the genesis block."); + } -fn is_docs(arg: &str) -> bool { - ["--docs", "-d"].contains(&arg) -} + pub fn is_docs(arg: &str) -> bool { + ["--docs", "-d"].contains(&arg) + } -fn is_schema(arg: &str) -> bool { - ["--schema", "-s"].contains(&arg) -} + pub fn is_schema(arg: &str) -> bool { + ["--schema", "-s"].contains(&arg) + } -fn is_genesis(arg: &str) -> bool { - ["--genesis", "-g"].contains(&arg) + pub fn is_genesis(arg: &str) -> bool { + ["--genesis", "-g"].contains(&arg) + } } -fn generate_default_genesis() -> color_eyre::Result { - let asset_definition = AssetDefinition::quantity("rose#wonderland".parse()?).build(); - let mut result = RawGenesisBlockBuilder::new() - .domain("wonderland".parse()?) - .with_account("alice".parse()?, DEFAULT_PUBLIC_KEY.parse()?) - .with_asset(asset_definition.clone()) - .finish_domain() - .build(); - let mint = MintBox::new( - iroha_data_model::prelude::Value::U32(13_u32), - iroha_data_model::IdBox::AssetId(iroha_data_model::prelude::AssetId::new( - asset_definition.id().clone(), // Probably redundant clone - "alice@wonderland".parse()?, - )), - ); - result.transactions[0].isi.push(mint.into()); - Ok(result) +mod genesis { + use iroha_core::{ + genesis::{RawGenesisBlock, RawGenesisBlockBuilder}, + tx::{AssetDefinition, MintBox}, + }; + + pub fn generate_default() -> color_eyre::Result { + let asset_definition = AssetDefinition::quantity("rose#wonderland".parse()?).build(); + let mut result = RawGenesisBlockBuilder::new() + .domain("wonderland".parse()?) + .with_account("alice".parse()?, crate::DEFAULT_PUBLIC_KEY.parse()?) + .with_asset(asset_definition.clone()) + .finish_domain() + .build(); + let mint = MintBox::new( + iroha_data_model::prelude::Value::U32(13_u32), + iroha_data_model::IdBox::AssetId(iroha_data_model::prelude::AssetId::new( + asset_definition.id().clone(), // Probably redundant clone + "alice@wonderland".parse()?, + )), + ); + result.transactions[0].isi.push(mint.into()); + Ok(result) + } } -impl + Send + Sync + Default> PrintDocs for C {} - -trait PrintDocs: Configurable + Send + Sync + Default -where - Self::Error: Debug, -{ - fn get_markdown(writer: &mut W) -> Result<()> { - let docs = match Self::get_docs() { - Value::Object(obj) => obj, - _ => unreachable!("As top level structure is always object"), - }; - let mut vec = Vec::new(); - let defaults = serde_json::to_string_pretty(&Self::default())?; - - writeln!(writer, "# Iroha Configuration reference\n")?; - writeln!(writer, "In this document we provide a reference and detailed descriptions of Iroha's configuration options.\n")?; - writeln!(writer, "## Default configuration\n")?; - writeln!( - writer, - "The following is the default configuration used by Iroha.\n" - )?; - writeln!(writer, "```json\n{}\n```\n", defaults)?; - Self::get_markdown_with_depth(writer, &docs, &mut vec, 2)?; - Ok(()) - } +mod docs { + use std::{fmt::Debug, io::Write}; - fn get_markdown_with_depth( - writer: &mut W, - docs: &Map, - field: &mut Vec, - depth: usize, - ) -> Result<()> { - let current_field = { - let mut docs = docs; - for f in &*field { - docs = match &docs[f] { - Value::Object(obj) => obj, - _ => unreachable!(), - }; - } - docs - }; - - for (f, value) in current_field { - field.push(f.clone()); - let get_field = field.iter().map(AsRef::as_ref).collect::>(); - let (doc, inner) = match value { - Value::Object(_) => { - let doc = Self::get_doc_recursive(&get_field) - .expect("Should be there, as already in docs"); - (doc.unwrap_or_default().to_owned(), true) + use iroha_config::Configurable; + use serde_json::Value; + + impl + Send + Sync + Default> PrintDocs for C {} + + pub trait PrintDocs: Configurable + Send + Sync + Default + where + Self::Error: Debug, + { + fn get_markdown(writer: &mut W) -> color_eyre::Result<()> { + let docs = match Self::get_docs() { + Value::Object(obj) => obj, + _ => unreachable!("As top level structure is always object"), + }; + let mut vec = Vec::new(); + let defaults = serde_json::to_string_pretty(&Self::default())?; + + writeln!(writer, "# Iroha Configuration reference\n")?; + writeln!(writer, "In this document we provide a reference and detailed descriptions of Iroha's configuration options.\n")?; + writeln!(writer, "## Default configuration\n")?; + writeln!( + writer, + "The following is the default configuration used by Iroha.\n" + )?; + writeln!(writer, "```json\n{}\n```\n", defaults)?; + Self::get_markdown_with_depth(writer, &docs, &mut vec, 2)?; + Ok(()) + } + + fn get_markdown_with_depth( + writer: &mut W, + docs: &serde_json::Map, + field: &mut Vec, + depth: usize, + ) -> color_eyre::Result<()> { + let current_field = { + let mut docs = docs; + for f in &*field { + docs = match &docs[f] { + Value::Object(obj) => obj, + _ => unreachable!(), + }; } - Value::String(s) => (s.clone(), false), - _ => unreachable!("Only strings and objects in docs"), + docs }; - let doc = doc.strip_prefix(' ').unwrap_or(&doc); - let defaults = Self::default() - .get_recursive(get_field) - .expect("Failed to get defaults."); - let defaults = serde_json::to_string_pretty(&defaults)?; - let field_str = field - .join(".") - .chars() - .filter(|&chr| chr != ' ') - .collect::(); - - write!(writer, "{} `{}`\n\n", "#".repeat(depth), field_str)?; - write!(writer, "{}\n\n", doc)?; - write!(writer, "```json\n{}\n```\n\n", defaults)?; - - if inner { - Self::get_markdown_with_depth(writer, docs, field, depth + 1)?; - } - field.pop(); + for (f, value) in current_field { + field.push(f.clone()); + let get_field = field.iter().map(AsRef::as_ref).collect::>(); + let (doc, inner) = match value { + Value::Object(_) => { + let doc = Self::get_doc_recursive(&get_field) + .expect("Should be there, as already in docs"); + (doc.unwrap_or_default().to_owned(), true) + } + Value::String(s) => (s.clone(), false), + _ => unreachable!("Only strings and objects in docs"), + }; + let doc = doc.strip_prefix(' ').unwrap_or(&doc); + let defaults = Self::default() + .get_recursive(get_field) + .expect("Failed to get defaults."); + let defaults = serde_json::to_string_pretty(&defaults)?; + let field_str = field + .join(".") + .chars() + .filter(|&chr| chr != ' ') + .collect::(); + + write!(writer, "{} `{}`\n\n", "#".repeat(depth), field_str)?; + write!(writer, "{}\n\n", doc)?; + write!(writer, "```json\n{}\n```\n\n", defaults)?; + + if inner { + Self::get_markdown_with_depth(writer, docs, field, depth + 1)?; + } + + field.pop(); + } + Ok(()) } - Ok(()) } }