diff --git a/Cargo.lock b/Cargo.lock index b727cd5f2799ffd74bc980811a3f4fac2c8489dc..edac3fe38ec4e0ab9d8f8442236f46daac2b29cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -810,6 +810,7 @@ dependencies = [ name = "dubp-user-docs-tests-tools" version = "0.1.0" dependencies = [ + "dubp-block-doc 0.1.0", "dubp-common-doc 0.1.0", "dubp-currency-params 0.2.0", "dubp-user-docs 0.14.0", @@ -941,6 +942,7 @@ dependencies = [ "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "json-pest-parser 0.2.0", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "pbr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/lib/modules/blockchain/blockchain/Cargo.toml b/lib/modules/blockchain/blockchain/Cargo.toml index dd0a21d593afe7f195b462afeac4a742a41e7b48..bf03e517a8cac3cf1beee1e7eaa42eb3b64ece75 100644 --- a/lib/modules/blockchain/blockchain/Cargo.toml +++ b/lib/modules/blockchain/blockchain/Cargo.toml @@ -42,5 +42,6 @@ dup-crypto-tests-tools = { path = "../../../tests-tools/crypto-tests-tools" } dubp-user-docs-tests-tools = { path = "../../../tests-tools/user-docs-tests-tools" } dubp-blocks-tests-tools = { path = "../../../tests-tools/blocks-tests-tools" } durs-common-tests-tools = { path = "../../../tests-tools/common-tests-tools" } +maplit = "1.0.1" pretty_assertions = "0.5.1" tempfile = "3.1.0" diff --git a/lib/modules/blockchain/blockchain/src/dubp/mod.rs b/lib/modules/blockchain/blockchain/src/dubp.rs similarity index 78% rename from lib/modules/blockchain/blockchain/src/dubp/mod.rs rename to lib/modules/blockchain/blockchain/src/dubp.rs index 9066028a0905d7f09c91535fa85e4363c0f0327c..cd964bd215f94ec6190f82c8860ac99931e492f8 100644 --- a/lib/modules/blockchain/blockchain/src/dubp/mod.rs +++ b/lib/modules/blockchain/blockchain/src/dubp.rs @@ -18,14 +18,18 @@ pub mod apply; pub mod check; -use crate::*; -use apply::*; -use check::*; -use dubp_block_doc::block::{BlockDocumentTrait, VerifyBlockHashError}; +use crate::dubp::apply::{apply_valid_block, ApplyValidBlockError, ValidBlockApplyReqs}; +use crate::dubp::check::global::verify_global_validity_block; +use crate::dubp::check::local::verify_local_validity_block; +use crate::dubp::check::InvalidBlockError; +use crate::BlockchainModule; +use dubp_block_doc::block::BlockDocumentTrait; +use dubp_block_doc::BlockDocument; use dubp_common_doc::traits::Document; use dubp_common_doc::{BlockNumber, Blockstamp}; use durs_bc_db_reader::blocks::DbBlock; -use durs_bc_db_writer::*; +use durs_bc_db_reader::DbError; +use durs_bc_db_writer::{Db, DbWriter}; use unwrap::unwrap; #[derive(Debug, Clone)] @@ -38,16 +42,15 @@ pub enum CheckAndApplyBlockReturn { #[derive(Debug)] pub enum BlockError { AlreadyHaveBlock, + ApplyValidBlockError(ApplyValidBlockError), BlockOrOutForkWindow, - VerifyBlockHashError(VerifyBlockHashError), DbError(DbError), InvalidBlock(InvalidBlockError), - ApplyValidBlockError(ApplyValidBlockError), } -impl From<VerifyBlockHashError> for BlockError { - fn from(err: VerifyBlockHashError) -> Self { - BlockError::VerifyBlockHashError(err) +impl From<ApplyValidBlockError> for BlockError { + fn from(e: ApplyValidBlockError) -> Self { + Self::ApplyValidBlockError(e) } } @@ -57,9 +60,9 @@ impl From<DbError> for BlockError { } } -impl From<ApplyValidBlockError> for BlockError { - fn from(err: ApplyValidBlockError) -> Self { - BlockError::ApplyValidBlockError(err) +impl From<InvalidBlockError> for BlockError { + fn from(e: InvalidBlockError) -> Self { + Self::InvalidBlock(e) } } @@ -76,8 +79,15 @@ pub fn check_and_apply_block( block_doc.previous_hash(), )?; + // Verify proof of work + // The case where the block has none hash is captured by verify_block_hashs below + if let Some(hash) = block_doc.hash() { + self::check::pow::verify_hash_pattern(hash.0, block_doc.pow_min()) + .map_err(InvalidBlockError::Pow)?; + } + // Verify block hashs - dubp::check::hashs::verify_block_hashs(&block_doc)?; + crate::dubp::check::hashs::verify_block_hashs(&block_doc).map_err(InvalidBlockError::Hashs)?; // Check block chainability if (block_doc.number().0 == 0 && bc.current_blockstamp == Blockstamp::default()) @@ -89,19 +99,25 @@ pub fn check_and_apply_block( "check_and_apply_block: block {} chainable!", block_doc.blockstamp() ); + + // Local verification + verify_local_validity_block(&block_doc, bc.currency_params) + .map_err(InvalidBlockError::Local)?; + // Detect expire_certs let blocks_expiring = Vec::with_capacity(0); let expire_certs = durs_bc_db_reader::indexes::certs::find_expire_certs(db, w.as_ref(), blocks_expiring)?; // Verify block validity (check all protocol rule, very long !) - verify_block_validity( + verify_global_validity_block( &block_doc, db, w.as_ref(), &bc.wot_index, &bc.wot_databases.wot_db, - )?; + ) + .map_err(InvalidBlockError::Global)?; // If we're in block genesis, get the currency parameters if block_doc.number() == BlockNumber(0) { diff --git a/lib/modules/blockchain/blockchain/src/dubp/check.rs b/lib/modules/blockchain/blockchain/src/dubp/check.rs new file mode 100644 index 0000000000000000000000000000000000000000..10b0ff01bc3da03e29a4963157840abb43005980 --- /dev/null +++ b/lib/modules/blockchain/blockchain/src/dubp/check.rs @@ -0,0 +1,29 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Sub-module checking if a block complies with all the rules of the (DUBP DUniter Blockchain Protocol). + +pub mod global; +pub mod hashs; +pub mod local; +pub mod pow; + +#[derive(Debug)] +pub enum InvalidBlockError { + Global(global::GlobalVerifyBlockError), + Hashs(dubp_block_doc::block::VerifyBlockHashError), + Local(local::LocalVerifyBlockError), + Pow(pow::BlockPoWError), +} diff --git a/lib/modules/blockchain/blockchain/src/dubp/check/mod.rs b/lib/modules/blockchain/blockchain/src/dubp/check/global.rs similarity index 72% rename from lib/modules/blockchain/blockchain/src/dubp/check/mod.rs rename to lib/modules/blockchain/blockchain/src/dubp/check/global.rs index be5d5a91624de86d9de898795b4658492af70ca8..483c2ece4f78dc945fd678789b2c15db3b9b2bec 100644 --- a/lib/modules/blockchain/blockchain/src/dubp/check/mod.rs +++ b/lib/modules/blockchain/blockchain/src/dubp/check/global.rs @@ -15,31 +15,36 @@ //! Sub-module checking if a block complies with all the rules of the (DUBP DUniter Blockchain Protocol). -pub mod hashs; - -use crate::dubp::BlockError; use dubp_block_doc::block::{BlockDocument, BlockDocumentTrait}; use dubp_common_doc::traits::Document; use dubp_common_doc::BlockNumber; use dup_crypto::keys::PubKey; -use durs_bc_db_reader::DbReader; -use durs_bc_db_writer::*; +use durs_bc_db_reader::{DbError, DbReadable, DbReader}; +use durs_bc_db_writer::BinFreeStructDb; +use durs_common_tools::traits::bool_ext::BoolExt; use durs_wot::*; use std::collections::HashMap; -#[derive(Debug, Copy, Clone)] -pub enum InvalidBlockError { +#[derive(Debug)] +pub enum GlobalVerifyBlockError { + DbError(DbError), NoPreviousBlock, VersionDecrease, } -pub fn verify_block_validity<DB, R, W>( +impl From<DbError> for GlobalVerifyBlockError { + fn from(err: DbError) -> Self { + GlobalVerifyBlockError::DbError(err) + } +} + +pub fn verify_global_validity_block<DB, R, W>( block: &BlockDocument, db: &DB, r: &R, _wot_index: &HashMap<PubKey, WotId>, _wot_db: &BinFreeStructDb<W>, -) -> Result<(), BlockError> +) -> Result<(), GlobalVerifyBlockError> where DB: DbReadable, R: DbReader, @@ -55,15 +60,14 @@ where )?; // Previous block must exist - if previous_block_opt.is_none() { - return Err(BlockError::InvalidBlock(InvalidBlockError::NoPreviousBlock)); - } + previous_block_opt + .is_some() + .or_err(GlobalVerifyBlockError::NoPreviousBlock)?; let previous_block = previous_block_opt.expect("safe unwrap"); // Block version must not decrease - if previous_block.version() > block.version() { - return Err(BlockError::InvalidBlock(InvalidBlockError::VersionDecrease)); - } + (block.version() >= previous_block.version()) + .or_err(GlobalVerifyBlockError::VersionDecrease)?; } Ok(()) diff --git a/lib/modules/blockchain/blockchain/src/dubp/check/local.rs b/lib/modules/blockchain/blockchain/src/dubp/check/local.rs new file mode 100644 index 0000000000000000000000000000000000000000..3178bb2641f6d8608f60bf9168a15518f67a78c5 --- /dev/null +++ b/lib/modules/blockchain/blockchain/src/dubp/check/local.rs @@ -0,0 +1,209 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Sub-module checking if the content of a block is consistent. + +pub mod genesis; +pub mod not_genesis; +pub mod tx_doc; + +use self::genesis::LocalVerifyGenesisBlockError; +use self::not_genesis::LocalVerifyNotGenesisBlockError; +use self::tx_doc::TransactionDocumentError; + +use dubp_block_doc::block::v10::BlockDocumentV10; +use dubp_block_doc::{block::BlockDocumentTrait, BlockDocument}; +use dubp_common_doc::errors::DocumentSigsErr; +use dubp_common_doc::traits::Document; +use dubp_common_doc::BlockNumber; +use dubp_currency_params::CurrencyParameters; +use durs_common_tools::fatal_error; + +#[derive(Debug, PartialEq)] +/// Local verification of a block error +pub enum LocalVerifyBlockError { + /// Wrong block version + Version { + expected_version: Vec<usize>, + actual_version: u32, + }, + /// Genesis block specific rules + LocalVerifyGenesisBlockError(LocalVerifyGenesisBlockError), + /// Not-genesis block specific rules + LocalVerifyNotGenesisBlockError(LocalVerifyNotGenesisBlockError), + /// Signature error + BlockSignatureError(DocumentSigsErr), + /// Identity signature error + IdentitySignatureError(DocumentSigsErr), + /// Joiner signature error + JoinerSignatureError(DocumentSigsErr), + /// Active signature error + ActiveSignatureError(DocumentSigsErr), + /// Leaver signature error + LeaverSignatureError(DocumentSigsErr), + /// Missing issuer + MissingIssuer, + /// Too many issuers (> 1) + TooManyIssuers, + /// Transaction Document Error + TransactionDocumentError(TransactionDocumentError), +} + +impl From<LocalVerifyGenesisBlockError> for LocalVerifyBlockError { + fn from(err: LocalVerifyGenesisBlockError) -> Self { + Self::LocalVerifyGenesisBlockError(err) + } +} + +impl From<LocalVerifyNotGenesisBlockError> for LocalVerifyBlockError { + fn from(err: LocalVerifyNotGenesisBlockError) -> Self { + Self::LocalVerifyNotGenesisBlockError(err) + } +} + +impl From<TransactionDocumentError> for LocalVerifyBlockError { + fn from(err: TransactionDocumentError) -> Self { + Self::TransactionDocumentError(err) + } +} + +/// Local verification of a block document according to rules of RFC0009 +pub fn verify_local_validity_block( + block: &BlockDocument, + currency_parameters_opt: Option<CurrencyParameters>, +) -> Result<(), LocalVerifyBlockError> { + if block.number() == BlockNumber(0) { + // Check the local rules specific to genesis blocks + self::genesis::local_validation_genesis_block(block)?; + } else if let Some(currency_parameters) = currency_parameters_opt { + // Check the local rules specific to non-genesis blocks + self::not_genesis::local_validation_not_genesis_block(block, currency_parameters)?; + } else { + fatal_error!("We must have currency parameters when we check a non-genesis block."); + } + + match block { + BlockDocument::V10(block) => verify_local_validity_block_v10(block), + } +} + +/// Local verification of a block document V10 according to rules of RFC0009 +pub fn verify_local_validity_block_v10( + block: &BlockDocumentV10, +) -> Result<(), LocalVerifyBlockError> { + // Version + if !(block.version == 10 || block.version == 11) { + return Err(LocalVerifyBlockError::Version { + expected_version: vec![10, 11], + actual_version: block.version, + }); + } + + // Issuers + if block.issuers.is_empty() { + return Err(LocalVerifyBlockError::MissingIssuer); + } else if block.issuers.len() > 1 { + return Err(LocalVerifyBlockError::TooManyIssuers); + } + + // Check signatures of block and wot events + // As it has been checked that block.issuers.len() == 1 and as + // block.issuers.len() == block.signatures.len() is check in block.verify_signatures() + // there is no need to check that block.signatures.len() == 1 + block + .verify_signatures() + .map_err(LocalVerifyBlockError::BlockSignatureError)?; + for identity in &block.identities { + identity + .verify_signatures() + .map_err(LocalVerifyBlockError::IdentitySignatureError)?; + } + for joiner in &block.joiners { + joiner + .verify_signatures() + .map_err(LocalVerifyBlockError::JoinerSignatureError)?; + } + for active in &block.actives { + active + .verify_signatures() + .map_err(LocalVerifyBlockError::ActiveSignatureError)?; + } + for leaver in &block.leavers { + leaver + .verify_signatures() + .map_err(LocalVerifyBlockError::LeaverSignatureError)?; + } + + // Check transactions + for tx in &block.transactions { + self::tx_doc::local_verify_tx_doc(tx)?; + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + + use super::*; + use dubp_block_doc::BlockDocument; + use dubp_blocks_tests_tools::mocks::block_params::gen_mock_currency_parameters; + use dubp_blocks_tests_tools::mocks::gen_mock_normal_block_v10; + + #[test] + fn test_verify_not_genesis_block_valid() { + let currency_params = gen_mock_currency_parameters(); + let block = gen_mock_normal_block_v10(); + assert!( + verify_local_validity_block(&BlockDocument::V10(block), Some(currency_params)).is_ok() + ); + } + + #[test] + fn test_verify_not_genesis_block_wrong_version() { + let currency_params = gen_mock_currency_parameters(); + let mut block = gen_mock_normal_block_v10(); + block.version = 14; + + let expected = Err(LocalVerifyBlockError::Version { + expected_version: vec![10, 11], + actual_version: 14, + }); + let actual = verify_local_validity_block(&BlockDocument::V10(block), Some(currency_params)); + assert_eq!(expected, actual); + } + + #[test] + fn test_verify_not_genesis_block_issuers_empty() { + let currency_params = gen_mock_currency_parameters(); + let mut block = gen_mock_normal_block_v10(); + block.issuers.clear(); + + let expected = Err(LocalVerifyBlockError::MissingIssuer); + let actual = verify_local_validity_block(&BlockDocument::V10(block), Some(currency_params)); + assert_eq!(expected, actual); + } + + #[test] + fn test_verify_not_genesis_block_none_too_many_issuers() { + let currency_params = gen_mock_currency_parameters(); + let mut block = gen_mock_normal_block_v10(); + block.issuers.push(block.issuers[0]); + + let expected = Err(LocalVerifyBlockError::TooManyIssuers); + let actual = verify_local_validity_block(&BlockDocument::V10(block), Some(currency_params)); + assert_eq!(expected, actual); + } +} diff --git a/lib/modules/blockchain/blockchain/src/dubp/check/local/genesis.rs b/lib/modules/blockchain/blockchain/src/dubp/check/local/genesis.rs new file mode 100644 index 0000000000000000000000000000000000000000..5099de4ab854c2c985b00395a71fb66a8c2632ce --- /dev/null +++ b/lib/modules/blockchain/blockchain/src/dubp/check/local/genesis.rs @@ -0,0 +1,194 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Verifies if a genesis block verifies the local rules specific to genesis blocks + +use dubp_block_doc::block::v10::BlockDocumentV10; +use dubp_block_doc::BlockDocument; +use dubp_common_doc::BlockNumber; +use durs_common_tools::traits::bool_ext::BoolExt; + +/// Local verification of errors specific to a Genesis Block +#[derive(Debug, PartialEq)] +pub enum LocalVerifyGenesisBlockError { + /// Block number is not zero + NonZeroBlockNumber { block_number: BlockNumber }, + /// A dividend is provided + UnexpectedDividend, + /// A previous hash is provided + UnexpectedPreviousHash, + /// A previous issuer is provided + UnexpectedPreviousIssuer, + /// No paramters are provided + MissingParameters, + /// Unit base is not zero + NonZeroUnitBase { unit_base: usize }, + /// The block time is different from the median time + TimeError { block_time: u64, median_time: u64 }, +} + +/// Verifies local rules specific to a genesis block +pub fn local_validation_genesis_block( + block: &BlockDocument, +) -> Result<(), LocalVerifyGenesisBlockError> { + match block { + BlockDocument::V10(block) => local_validation_genesis_block_v10(block), + } +} + +fn local_validation_genesis_block_v10( + block: &BlockDocumentV10, +) -> Result<(), LocalVerifyGenesisBlockError> { + // block_number must be equal to zero + (block.number == BlockNumber(0)).or_err(LocalVerifyGenesisBlockError::NonZeroBlockNumber { + block_number: block.number, + })?; + + // Dividend must be none + block + .dividend + .is_none() + .or_err(LocalVerifyGenesisBlockError::UnexpectedDividend)?; + + // Previous Hash must be none + block + .previous_hash + .is_none() + .or_err(LocalVerifyGenesisBlockError::UnexpectedPreviousHash)?; + + // Previous issuer must be none + block + .previous_issuer + .is_none() + .or_err(LocalVerifyGenesisBlockError::UnexpectedPreviousIssuer)?; + + // Parameters + block + .parameters + .ok_or(LocalVerifyGenesisBlockError::MissingParameters)?; + + // unit_base must be equal to zero + (block.unit_base == 0).or_err(LocalVerifyGenesisBlockError::NonZeroUnitBase { + unit_base: block.unit_base, + })?; + + // time must be equal to median_time + (block.time == block.median_time).or_err(LocalVerifyGenesisBlockError::TimeError { + block_time: block.time, + median_time: block.median_time, + })?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use dubp_block_doc::BlockDocument; + use dubp_blocks_tests_tools::mocks::gen_mock_genesis_block_v10; + use dup_crypto::hashs::Hash; + use dup_crypto::keys::*; + + #[test] + fn test_genesis_block_valid() { + let block = gen_mock_genesis_block_v10(); + assert_eq!( + Ok(()), + local_validation_genesis_block(&BlockDocument::V10(block)) + ); + } + + #[test] + fn test_genesis_block_wrong_number() { + let mut block = gen_mock_genesis_block_v10(); + block.number = BlockNumber(1); + + let expected = Err(LocalVerifyGenesisBlockError::NonZeroBlockNumber { + block_number: BlockNumber(1), + }); + let actual = local_validation_genesis_block(&BlockDocument::V10(block)); + assert_eq!(expected, actual); + } + + #[test] + fn test_genesis_block_unexpected_dividend() { + let mut block = gen_mock_genesis_block_v10(); + block.dividend = Some(10); + + let expected = Err(LocalVerifyGenesisBlockError::UnexpectedDividend); + let actual = local_validation_genesis_block(&BlockDocument::V10(block)); + assert_eq!(expected, actual); + } + + #[test] + fn test_genesis_block_unexpected_previous_hash() { + let mut block = gen_mock_genesis_block_v10(); + block.previous_hash = Some( + Hash::from_hex("000001144968D0C3516BE6225E4662F182E28956AF46DD7FB228E3D0F9413FEB") + .expect("invalid hash"), + ); + + let expected = Err(LocalVerifyGenesisBlockError::UnexpectedPreviousHash); + let actual = local_validation_genesis_block(&BlockDocument::V10(block)); + assert_eq!(expected, actual); + } + + #[test] + fn test_genesis_block_unexpected_previous_issuer() { + let mut block = gen_mock_genesis_block_v10(); + block.previous_issuer = Some(PubKey::Ed25519( + ed25519::PublicKey::from_base58("D3krfq6J9AmfpKnS3gQVYoy7NzGCc61vokteTS8LJ4YH") + .expect("invalid pubkey"), + )); + + let expected = Err(LocalVerifyGenesisBlockError::UnexpectedPreviousIssuer); + let actual = local_validation_genesis_block(&BlockDocument::V10(block)); + assert_eq!(expected, actual); + } + + #[test] + fn test_genesis_block_missing_parameters() { + let mut block = gen_mock_genesis_block_v10(); + block.parameters = None; + + let expected = Err(LocalVerifyGenesisBlockError::MissingParameters); + let actual = local_validation_genesis_block(&BlockDocument::V10(block)); + assert_eq!(expected, actual); + } + + #[test] + fn test_genesis_block_non_zero_unit_base() { + let mut block = gen_mock_genesis_block_v10(); + block.unit_base = 3; + + let expected = Err(LocalVerifyGenesisBlockError::NonZeroUnitBase { unit_base: 3 }); + let actual = local_validation_genesis_block(&BlockDocument::V10(block)); + assert_eq!(expected, actual); + } + + #[test] + fn test_genesis_block_ntime_error() { + let mut block = gen_mock_genesis_block_v10(); + block.time = 3; + block.median_time = 4; + + let expected = Err(LocalVerifyGenesisBlockError::TimeError { + block_time: 3, + median_time: 4, + }); + let actual = local_validation_genesis_block(&BlockDocument::V10(block)); + assert_eq!(expected, actual); + } +} diff --git a/lib/modules/blockchain/blockchain/src/dubp/check/local/not_genesis.rs b/lib/modules/blockchain/blockchain/src/dubp/check/local/not_genesis.rs new file mode 100644 index 0000000000000000000000000000000000000000..0277999b22713c1a840a5edb92b7c1dfbc8a0ccd --- /dev/null +++ b/lib/modules/blockchain/blockchain/src/dubp/check/local/not_genesis.rs @@ -0,0 +1,157 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Verifies if a normal block verifies the local rules specific to normal blocks + +use dubp_block_doc::BlockDocument; +use dubp_currency_params::CurrencyParameters; + +#[derive(Debug, Clone, PartialEq)] +/// Local verification error specific to a not-genesis Block +pub enum LocalVerifyNotGenesisBlockError { + /// Is Genesis block + IsGenesisBlock, + /// No previous hash is provided + MissingPreviousHash, + /// No previous issuer is provided + MissingPreviousIssuer, + /// Some Paramters are provided in the Block Document + UnexpectedParameters, + /// Block Time + BlockTime { + actual_block_time: u64, + lower_block_time_bound: u64, + upper_block_time_bound: u64, + }, +} + +/// Local verification of rules specific of a not genesis block +pub fn local_validation_not_genesis_block( + block: &BlockDocument, + currency_parameters: CurrencyParameters, +) -> Result<(), LocalVerifyNotGenesisBlockError> { + let BlockDocument::V10(block) = block; + + // Provided block is a genesis block + if block.number.0 == 0 { + return Err(LocalVerifyNotGenesisBlockError::IsGenesisBlock); + } + + // PreviousHash + block + .previous_hash + .ok_or(LocalVerifyNotGenesisBlockError::MissingPreviousHash)?; + + // PreviousIssuer + block + .previous_issuer + .ok_or(LocalVerifyNotGenesisBlockError::MissingPreviousIssuer)?; + + // Parameters + if block.parameters.is_some() { + return Err(LocalVerifyNotGenesisBlockError::UnexpectedParameters); + } + + // Dates + let avg_gen_time = currency_parameters.avg_gen_time as f64; + let max_gen_time = (avg_gen_time * 1.189).ceil(); + let median_time_blocks = currency_parameters.median_time_blocks as f64; + let max_acceleration = max_gen_time * median_time_blocks; + let max_acceleration = max_acceleration.ceil() as u64; + if block.time < block.median_time || block.median_time + max_acceleration < block.time { + return Err(LocalVerifyNotGenesisBlockError::BlockTime { + actual_block_time: block.time, + lower_block_time_bound: block.median_time, + upper_block_time_bound: block.median_time + max_acceleration, + }); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use dubp_block_doc::BlockDocument; + use dubp_blocks_tests_tools::mocks::block_params::gen_mock_currency_parameters; + use dubp_blocks_tests_tools::mocks::gen_mock_normal_block_v10; + use dubp_common_doc::BlockNumber; + + #[test] + fn local_verify_normal_block() -> Result<(), LocalVerifyNotGenesisBlockError> { + let currency_params = gen_mock_currency_parameters(); + + // Normal Rules : IsGenesisBlock + let mut block = gen_mock_normal_block_v10(); + block.number = BlockNumber(0); + + let expected = Err(LocalVerifyNotGenesisBlockError::IsGenesisBlock); + let actual = + local_validation_not_genesis_block(&BlockDocument::V10(block), currency_params); + assert_eq!(expected, actual); + + // Normal Rules : Missing Previous Hash + let mut block = gen_mock_normal_block_v10(); + block.previous_hash = None; + + let expected = Err(LocalVerifyNotGenesisBlockError::MissingPreviousHash); + let actual = + local_validation_not_genesis_block(&BlockDocument::V10(block), currency_params); + assert_eq!(expected, actual); + + // Normal Rules : Missing Previous Hash + let mut block = gen_mock_normal_block_v10(); + block.previous_hash = None; + + let expected = Err(LocalVerifyNotGenesisBlockError::MissingPreviousHash); + let actual = + local_validation_not_genesis_block(&BlockDocument::V10(block), currency_params); + assert_eq!(expected, actual); + + // Normal Rules : Missing Previous Issuer + let mut block = gen_mock_normal_block_v10(); + block.previous_issuer = None; + + let expected = Err(LocalVerifyNotGenesisBlockError::MissingPreviousIssuer); + let actual = + local_validation_not_genesis_block(&BlockDocument::V10(block), currency_params); + assert_eq!(expected, actual); + + // Normal Rules : Unexpected Parameters + let mut block = gen_mock_normal_block_v10(); + block.parameters = + Some(dubp_currency_params::genesis_block_params::v10::BlockV10Parameters::default()); + + let expected = Err(LocalVerifyNotGenesisBlockError::UnexpectedParameters); + let actual = + local_validation_not_genesis_block(&BlockDocument::V10(block), currency_params); + assert_eq!(expected, actual); + + // Normal Rules : Block Time + let mut block = gen_mock_normal_block_v10(); + block.time = 685_861; + + let expected = Err(LocalVerifyNotGenesisBlockError::BlockTime { + actual_block_time: 685_861, + lower_block_time_bound: 1_522_683_184, + upper_block_time_bound: 1_522_695_084, + }); + let actual = + local_validation_not_genesis_block(&BlockDocument::V10(block), currency_params); + assert_eq!(expected, actual); + + Ok(()) + } +} diff --git a/lib/modules/blockchain/blockchain/src/dubp/check/local/tx_doc.rs b/lib/modules/blockchain/blockchain/src/dubp/check/local/tx_doc.rs new file mode 100644 index 0000000000000000000000000000000000000000..9f0dd8f94dc819dfa9ddb45c86272a7913ee3fd1 --- /dev/null +++ b/lib/modules/blockchain/blockchain/src/dubp/check/local/tx_doc.rs @@ -0,0 +1,216 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Verifies if transaction document verifies the local rules + +use dubp_common_doc::errors::DocumentSigsErr; +use dubp_common_doc::traits::text::CompactTextDocument; +use dubp_common_doc::traits::Document; +use dubp_user_docs::documents::transaction::TransactionDocument; +use durs_common_tools::traits::bool_ext::BoolExt; + +#[derive(Debug, PartialEq)] +/// Transaction Document Error +pub enum TransactionDocumentError { + /// Length is too long + TooLong { + expected_max_length: usize, + actual_length: usize, + }, + /// There is no input + MissingInput, + /// Signature error + TxSignatureError(DocumentSigsErr), +} + +/// Local verification of a Tx Document +pub fn local_verify_tx_doc(tx_doc: &TransactionDocument) -> Result<(), TransactionDocumentError> { + // A transaction in compact format must measure less than 100 lines + (tx_doc.as_compact_text().lines().count() < 100).or_err(TransactionDocumentError::TooLong { + expected_max_length: 100, + actual_length: tx_doc.as_compact_text().lines().count(), + })?; + + // A transaction must have at least 1 input + (tx_doc.get_inputs().is_empty().not()).or_err(TransactionDocumentError::MissingInput)?; + + // A transaction cannot have `SIG(INDEX)` unlocks with `INDEX >= ` issuers count. + // Question : règle à pas vérifier + /*if y.get_unlocks().len() >= y.issuers().len() { + return Err(TransactionDocumentError(SignatureIssuerError)); + }*/ + + // Signatures count must be the same as issuers count + // It's alreeady checked by `tx_doc.verify_signatures()` + + //////////////////////////////////////////////////////////////////////////////////// + // A transaction **must** have signatures matching its content **for each issuer** + // Signatures are ordered by issuer + // Signatures are made over the transaction's content, signatures excepted + //////////////////////////////////////////////////////////////////////////////////// + tx_doc + .verify_signatures() + .map_err(TransactionDocumentError::TxSignatureError)?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use dubp_common_doc::traits::DocumentBuilder; + use dubp_common_doc::Blockstamp; + use dubp_user_docs::documents::transaction::OutputIndex; + use dubp_user_docs::documents::transaction::TransactionDocumentBuilder; + use dubp_user_docs::documents::transaction::TransactionInput; + use dubp_user_docs::documents::transaction::TransactionInputUnlocks; + use dubp_user_docs::documents::transaction::TransactionOutput; + use dubp_user_docs::documents::transaction::TransactionUnlockProof; + use dubp_user_docs::documents::transaction::TxAmount; + use dubp_user_docs::documents::transaction::TxBase; + use dubp_user_docs_tests_tools::mocks::tx::gen_mock_tx_doc; + use dup_crypto::hashs::Hash; + use dup_crypto::keys::*; + use std::str::FromStr; + + #[inline] + fn blockstamp() -> Blockstamp { + Blockstamp::from_string( + "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", + ) + .expect("invalid blockstamp") + } + + #[inline] + fn issuers() -> Vec<PubKey> { + let keypair = ed25519::KeyPairFromSeed32Generator::generate( + Seed32::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV") + .expect("invalid seed32"), + ); + vec![PubKey::Ed25519(keypair.public_key())] + } + + #[inline] + fn sig1() -> Sig { + Sig::Ed25519(ed25519::Signature::from_base64( + "cq86RugQlqAEyS8zFkB9o0PlWPSb+a6D/MEnLe8j+okyFYf/WzI6pFiBkQ9PSOVn5I0dwzVXg7Q4N1apMWeGAg==", + ).unwrap()) + } + + #[inline] + fn input1() -> TransactionInput { + TransactionInput::T( + TxAmount(950), + TxBase(0), + Hash::from_hex("2CF1ACD8FE8DC93EE39A1D55881C50D87C55892AE8E4DB71D4EBAB3D412AA8FD") + .expect("invalid hash"), + OutputIndex(1), + ) + } + + #[inline] + fn unlocks() -> Vec<TransactionInputUnlocks> { + vec![TransactionInputUnlocks { + index: 0, + unlocks: vec![TransactionUnlockProof::Sig(0)], + }] + } + + #[inline] + fn outputs() -> Vec<TransactionOutput> { + vec![ + TransactionOutput::from_str("10:0:SIG(FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa)") + .expect("fail to parse output !"), + ] + } + + fn tx_builder<'a>( + blockstamp: &'a Blockstamp, + issuers: &'a Vec<PubKey>, + inputs: &'a Vec<TransactionInput>, + unlocks: &'a Vec<TransactionInputUnlocks>, + outputs: &'a Vec<TransactionOutput>, + ) -> TransactionDocumentBuilder<'a> { + TransactionDocumentBuilder { + currency: "duniter_unit_test_currency", + blockstamp, + locktime: &0, + issuers, + inputs, + unlocks, + outputs, + comment: "test", + hash: None, + } + } + + #[test] + fn test_tx_valid() { + let tx = gen_mock_tx_doc(); + assert_eq!(Ok(()), local_verify_tx_doc(&tx)); + } + + #[test] + fn test_tx_empty_inputs() { + let blockstamp = blockstamp(); + let issuers = issuers(); + let inputs = vec![]; + let unlocks = unlocks(); + let outputs = outputs(); + let tx_builder = tx_builder(&blockstamp, &issuers, &inputs, &unlocks, &outputs); + let tx = tx_builder.build_with_signature(vec![sig1()]); + + let expected = Err(TransactionDocumentError::MissingInput); + let actual = local_verify_tx_doc(&tx); + assert_eq!(expected, actual); + } + + #[test] + fn test_tx_too_long() { + let blockstamp = blockstamp(); + let issuers = issuers(); + let inputs = vec![input1(); 100]; + let unlocks = unlocks(); + let outputs = outputs(); + let tx_builder = tx_builder(&blockstamp, &issuers, &inputs, &unlocks, &outputs); + let tx = tx_builder.build_with_signature(vec![sig1()]); + + let expected = Err(TransactionDocumentError::TooLong { + expected_max_length: 100, + actual_length: 107, + }); + let actual = local_verify_tx_doc(&tx); + assert_eq!(expected, actual); + } + + #[test] + fn test_tx_invalid_sig() { + let blockstamp = blockstamp(); + let issuers = issuers(); + let inputs = vec![input1(); 2]; + let unlocks = unlocks(); + let outputs = outputs(); + let tx_builder = tx_builder(&blockstamp, &issuers, &inputs, &unlocks, &outputs); + let tx = tx_builder.build_with_signature(vec![sig1()]); + + let expected = Err(TransactionDocumentError::TxSignatureError( + DocumentSigsErr::Invalid(maplit::hashmap![ + 0 => SigError::InvalidSig, + ]), + )); + let actual = local_verify_tx_doc(&tx); + assert_eq!(expected, actual); + } +} diff --git a/lib/modules/blockchain/blockchain/src/dubp/check/pow.rs b/lib/modules/blockchain/blockchain/src/dubp/check/pow.rs new file mode 100644 index 0000000000000000000000000000000000000000..706498f1194b619753e9f2b1bd6f8ee0a69c865d --- /dev/null +++ b/lib/modules/blockchain/blockchain/src/dubp/check/pow.rs @@ -0,0 +1,148 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Verify block pow + +use dup_crypto::hashs::Hash; +use durs_common_tools::traits::bool_ext::BoolExt; + +static ZERO_STRING: &str = "0"; + +/// Proof of Work error +#[derive(Debug, PartialEq)] +pub enum BlockPoWError { + /// Invalid pow_min + _InvalidPoWMin, + /// Invalid PoW pattern + InvalidHashPattern { + expected_pattern: String, + actual_hash: String, + }, +} + +pub fn verify_hash_pattern(hash: Hash, diffi: usize) -> Result<(), BlockPoWError> { + let hash_string = hash.to_hex(); + let nb_zeros = diffi / 16; + let expected_pattern_last_hex_digit = 16 - (diffi % 16); + let repeated_zero_string = ZERO_STRING.repeat(nb_zeros); + let expected_pattern = if expected_pattern_last_hex_digit < 15 && nb_zeros < 64 { + let expected_pattern_last_char = + std::char::from_digit(expected_pattern_last_hex_digit as u32, 16) + .expect("expected_pattern_last_hex_digit is necessarily less than 16"); + let expected_pattern = format!( + "{}[0-{}]*", + repeated_zero_string, expected_pattern_last_char + ); + let actual_pattern_last_hex_digit = usize::from_str_radix( + hash_string + .get(nb_zeros..=nb_zeros) + .expect("Hash string is necessary greater than nb_zeros + 1."), + 16, + ) + .expect("Hash type guarantees a valid hexadecimal string."); + // remainder must be less than or equal to expected_end_pattern + (actual_pattern_last_hex_digit <= expected_pattern_last_hex_digit).or_err( + BlockPoWError::InvalidHashPattern { + expected_pattern: expected_pattern.clone(), + actual_hash: hash_string.clone(), + }, + )?; + expected_pattern + } else { + repeated_zero_string.clone() + }; + hash_string + .starts_with(&repeated_zero_string) + .or_err(BlockPoWError::InvalidHashPattern { + expected_pattern, + actual_hash: hash_string, + })?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_verify_hash_pattern() { + assert_eq!( + Ok(()), + verify_hash_pattern( + Hash::from_hex("000003619ACBF80298F074D8339175901425BC97EF528ED02EBD73CD4CA5C559") + .expect("invalid hash"), + 70 + ) + ); + assert_eq!( + Ok(()), + verify_hash_pattern( + Hash::from_hex("000013619ACBF80298F074D8339175901425BC97EF528ED02EBD73CD4CA5C559") + .expect("invalid hash"), + 70 + ) + ); + assert_eq!( + Err(BlockPoWError::InvalidHashPattern { + expected_pattern: "0000[0-a]*".to_owned(), + actual_hash: "0000B3619ACBF80298F074D8339175901425BC97EF528ED02EBD73CD4CA5C559" + .to_owned(), + }), + verify_hash_pattern( + Hash::from_hex("0000B3619ACBF80298F074D8339175901425BC97EF528ED02EBD73CD4CA5C559") + .expect("invalid hash"), + 70 + ) + ); + assert_eq!( + Err(BlockPoWError::InvalidHashPattern { + expected_pattern: "0000[0-a]*".to_owned(), + actual_hash: "000313619ACBF80298F074D8339175901425BC97EF528ED02EBD73CD4CA5C559" + .to_owned(), + }), + verify_hash_pattern( + Hash::from_hex("000313619ACBF80298F074D8339175901425BC97EF528ED02EBD73CD4CA5C559") + .expect("invalid hash"), + 70 + ) + ); + assert_eq!( + Err(BlockPoWError::InvalidHashPattern { + expected_pattern: "00000".to_owned(), + actual_hash: "000313619ACBF80298F074D8339175901425BC97EF528ED02EBD73CD4CA5C559" + .to_owned(), + }), + verify_hash_pattern( + Hash::from_hex("000313619ACBF80298F074D8339175901425BC97EF528ED02EBD73CD4CA5C559") + .expect("invalid hash"), + 80 + ) + ); + assert_eq!( + Err(BlockPoWError::InvalidHashPattern { + expected_pattern: "0000".to_owned(), + actual_hash: "000313619ACBF80298F074D8339175901425BC97EF528ED02EBD73CD4CA5C559" + .to_owned(), + }), + verify_hash_pattern( + Hash::from_hex("000313619ACBF80298F074D8339175901425BC97EF528ED02EBD73CD4CA5C559") + .expect("invalid hash"), + 65 + ) + ); + } +} diff --git a/lib/modules/blockchain/blockchain/src/dunp/receiver.rs b/lib/modules/blockchain/blockchain/src/dunp/receiver.rs index f3d640fc1c45d7d789441940b69c9a4671d44cba..6277d7a345095cc9ed5b970699558e353064f44f 100644 --- a/lib/modules/blockchain/blockchain/src/dunp/receiver.rs +++ b/lib/modules/blockchain/blockchain/src/dunp/receiver.rs @@ -108,8 +108,8 @@ pub fn receive_blocks(bc: &mut BlockchainModule, blocks: Vec<BlockDocument>) { } }, Err(e) => match e { - BlockError::VerifyBlockHashError(_) | BlockError::InvalidBlock(_) => { - warn!("InvalidBlock(#{})", blockstamp.id.0); + BlockError::InvalidBlock(e) => { + warn!("InvalidBlock #{}: {:?}", blockstamp.id.0, e); crate::events::sent::send_event( bc, &BlockchainEvent::RefusedBlock(blockstamp), diff --git a/lib/modules/blockchain/blockchain/src/fork/rollback.rs b/lib/modules/blockchain/blockchain/src/fork/rollback.rs index 8aacf3b83eb8bde3289b6ba518951e58897dc52f..27b155896e474e6a516373e318a6eef0dd5b2a9e 100644 --- a/lib/modules/blockchain/blockchain/src/fork/rollback.rs +++ b/lib/modules/blockchain/blockchain/src/fork/rollback.rs @@ -127,7 +127,7 @@ pub fn apply_rollback(bc: &mut BlockchainModule, new_bc_branch: Vec<Blockstamp>) Err(e) => { new_branch_is_valid = false; bc.invalid_forks.insert(*blockstamp); - info!( + warn!( "Blockchain: abort rollback: block {} is invalid: {:?}", blockstamp, e ); diff --git a/lib/tests-tools/blocks-tests-tools/src/mocks.rs b/lib/tests-tools/blocks-tests-tools/src/mocks.rs index 7a1fba293191a19bfd8399006c0cf96f925b9796..ad83d12825b5f1a4bf5ae820596d708caabbff93 100644 --- a/lib/tests-tools/blocks-tests-tools/src/mocks.rs +++ b/lib/tests-tools/blocks-tests-tools/src/mocks.rs @@ -15,13 +15,21 @@ //! Mocks for projects use dubp-block-doc +pub mod block_params; + use dubp_block_doc::block::BlockDocumentTrait; use dubp_block_doc::{BlockDocument, BlockDocumentV10}; use dubp_common_doc::blockstamp::Blockstamp; +use dubp_common_doc::parser::TextDocumentParser; +use dubp_common_doc::traits::text::*; use dubp_common_doc::{BlockHash, BlockNumber}; +use dubp_currency_params::genesis_block_params::v10::BlockV10Parameters; use dubp_currency_params::CurrencyName; +use dubp_user_docs::documents::certification::*; +use dubp_user_docs::documents::transaction::*; +use dup_crypto::bases::b16::str_hex_to_32bytes; use dup_crypto::hashs::Hash; -use dup_crypto::keys::{PubKey, Signator}; +use dup_crypto::keys::{ed25519, PubKey, PublicKey, Sig, Signator, Signature}; /// Generate n mock blockstamps pub fn generate_blockstamps(n: usize) -> Vec<Blockstamp> { @@ -67,6 +75,7 @@ pub fn gen_empty_timed_issued_hashed_block_v10( signator: &dup_crypto::keys::SignatorEnum, ) -> BlockDocumentV10 { let mut block = gen_empty_block_v10(block_number); + block.time = time; block.median_time = time; block.issuers = vec![signator.public_key()]; block.previous_issuer = Some(previous_issuer); @@ -124,3 +133,105 @@ fn gen_empty_block_v10(block_number: BlockNumber) -> BlockDocumentV10 { inner_hash: None, } } + +/// Generate mock block which is not a genesis block +pub fn gen_mock_normal_block_v10() -> BlockDocumentV10 { + let cert1 = CertificationDocumentParser::parse("Version: 10 +Type: Certification +Currency: g1 +Issuer: 6TAzLWuNcSqgNDNpAutrKpPXcGJwy1ZEMeVvZSZNs2e3 +IdtyIssuer: CYPsYTdt87Tx6cCiZs9KD4jqPgYxbcVEqVZpRgJ9jjoV +IdtyUniqueID: PascaleM +IdtyTimestamp: 97401-0000003821911909F98519CC773D2D3E5CFE3D5DBB39F4F4FF33B96B4D41800E +IdtySignature: QncUVXxZ2NfARjdJOn6luILvDuG1NuK9qSoaU4CST2Ij8z7oeVtEgryHl+EXOjSe6XniALsCT0gU8wtadcA/Cw== +CertTimestamp: 106669-000003682E6FE38C44433DCE92E8B2A26C69B6D7867A2BAED231E788DDEF4251 +UmseG2XKNwKcY8RFi6gUCT91udGnnNmSh7se10J1jeRVlwf+O2Tyb2Cccot9Dt7BO4+Kx2P6vFJB3oVGGHMxBA==").expect("Fail to parse cert1"); + let CertificationDocument::V10(cert1) = cert1; + + let tx1 = dubp_user_docs_tests_tools::mocks::tx::gen_mock_tx_doc(); + let tx2 = TransactionDocumentParser::parse("Version: 10 +Type: Transaction +Currency: g1 +Blockstamp: 107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B +Locktime: 0 +Issuers: +8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3 +Inputs: +1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106614 +Unlocks: +0:SIG(0) +Outputs: +1002:0:SIG(78ZwwgpgdH5uLZLbThUQH7LKwPgjMunYfLiCfUCySkM8) +Comment: DU symbolique pour demander le codage de nouvelles fonctionnalites cf. https://forum.monnaie-libre.fr/t/creer-de-nouvelles-fonctionnalites-dans-cesium-les-autres-applications/2025 Merci +a9PHPuSfw7jW8FRQHXFsGi/bnLjbtDnTYvEVgUC9u0WlR7GVofa+Xb+l5iy6NwuEXiwvueAkf08wPVY8xrNcCg==").expect("Fail to parse tx2"); + + BlockDocumentV10 { + nonce: 10_300_000_018_323, + version: 10, + number: BlockNumber(107_984), + pow_min: 88, + time: 1_522_685_861, + median_time: 1_522_683_184, + members_count: 896, + monetary_mass: 140_469_765, + unit_base: 0, + issuers_count: 42, + issuers_frame: 211, + issuers_frame_var: 0, + currency: CurrencyName(String::from("g1")), + issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58("DA4PYtXdvQqk1nCaprXH52iMsK5Ahxs1nRWbWKLhpVkQ").unwrap())], + signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64("92id58VmkhgVNee4LDqBGSm8u/ooHzAD67JM6fhAE/CV8LCz7XrMF1DvRl+eRpmlaVkp6I+Iy8gmZ1WUM5C8BA==").unwrap())], + hash: None, + parameters: None, + previous_hash: Some(Hash::from_hex("000001144968D0C3516BE6225E4662F182E28956AF46DD7FB228E3D0F9413FEB").expect("fail to parse previous_hash")), + previous_issuer: Some(PubKey::Ed25519(ed25519::PublicKey::from_base58("D3krfq6J9AmfpKnS3gQVYoy7NzGCc61vokteTS8LJ4YH").unwrap())), + inner_hash: Some(Hash( + str_hex_to_32bytes( + "C8AB69E33ECE2612EADC7AB30D069B1F1A3D8C95EBBFD50DE583AC8E3666CCA1", + ).unwrap() )), + dividend: None, + identities: Vec::new(), + joiners: Vec::new(), + actives: Vec::new(), + leavers: Vec::new(), + revoked: Vec::new(), + excluded: Vec::new(), + certifications: vec![TextDocumentFormat::Complete(cert1)], + transactions: vec![tx1, tx2], + } +} + +/// Generate a mock genesis block +pub fn gen_mock_genesis_block_v10() -> BlockDocumentV10 { + BlockDocumentV10 { + nonce: 0, + version: 10, + number: BlockNumber(0), + pow_min: 0, + time: 0, + median_time: 0, + members_count: 0, + monetary_mass: 0, + unit_base: 0, + issuers_count: 0, + issuers_frame: 0, + issuers_frame_var: 0, + currency: CurrencyName(String::from("g1")), + issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58("DA4PYtXdvQqk1nCaprXH52iMsK5Ahxs1nRWbWKLhpVkQ").unwrap())], + signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64("92id58VmkhgVNee4LDqBGSm8u/ooHzAD67JM6fhAE/CV8LCz7XrMF1DvRl+eRpmlaVkp6I+Iy8gmZ1WUM5C8BA==").unwrap())], + hash: None, + parameters: Some(BlockV10Parameters::default()), + previous_hash: None, + previous_issuer: None, + inner_hash: None, + dividend: None, + identities: Vec::new(), + joiners: Vec::new(), + actives: Vec::new(), + leavers: Vec::new(), + revoked: Vec::new(), + excluded: Vec::new(), + certifications: Vec::new(), + transactions: Vec::new(), + } +} diff --git a/lib/tests-tools/blocks-tests-tools/src/mocks/block_params.rs b/lib/tests-tools/blocks-tests-tools/src/mocks/block_params.rs new file mode 100644 index 0000000000000000000000000000000000000000..6c0813d1fc43b157439136f110b1fc30438b7608 --- /dev/null +++ b/lib/tests-tools/blocks-tests-tools/src/mocks/block_params.rs @@ -0,0 +1,49 @@ +// Copyright (C) 2019 Éloïs SANCHEZ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Mocks for projects use dubp-block-doc + +use dubp_currency_params::CurrencyParameters; + +/// Generate mock currency parameters +pub fn gen_mock_currency_parameters() -> CurrencyParameters { + CurrencyParameters { + protocol_version: 10, + c: 0.004, // UD target growth rate (see Relative Theorie of Money) + dt: 1000, // Duration between the creation of two UD (in seconds) + ud0: 10, // Amount of the initial UD + sig_period: 10000, // Minimum duration between the writing of 2 certifications from the same issuer (in seconds) + sig_renew_period: 1000, // Minimum duration between two renewals of the same certification + sig_stock: 100, // Maximum number of active certifications at the same time (for the same issuer) + sig_window: 100, + sig_validity: 100, + sig_qty: 100, + idty_window: 100, + ms_window: 100, + tx_window: 100, + x_percent: 0.8, + ms_validity: 100, + ms_period: 100, + step_max: 100, + median_time_blocks: 100, + avg_gen_time: 100, + dt_diff_eval: 100, + percent_rot: 0.5, + ud_time0: 100, + ud_reeval_time0: 100, + dt_reeval: 100, + fork_window_size: 100, + } +} diff --git a/lib/tests-tools/user-docs-tests-tools/Cargo.toml b/lib/tests-tools/user-docs-tests-tools/Cargo.toml index a558a3edde3599ef080ed3fd991c76ae3c1175ca..5906780fd37c1a1663874a7abbcb98ce41a64f90 100644 --- a/lib/tests-tools/user-docs-tests-tools/Cargo.toml +++ b/lib/tests-tools/user-docs-tests-tools/Cargo.toml @@ -13,6 +13,7 @@ edition = "2018" path = "src/lib.rs" [dependencies] +dubp-block-doc = { path = "../../dubp/block-doc"} #, version = "0.1.0" } dubp-common-doc = { path = "../../dubp/common-doc"} #, version = "0.1.0" } dubp-currency-params = { path = "../../dubp/currency-params" } dubp-user-docs = { path = "../../dubp/user-docs" } diff --git a/lib/tests-tools/user-docs-tests-tools/src/mocks/tx.rs b/lib/tests-tools/user-docs-tests-tools/src/mocks/tx.rs index 5ed6cbcf637f899d10bafb63167d384fd3ac773f..f6abdd0beebafdf3e3641183361c5826da321a38 100644 --- a/lib/tests-tools/user-docs-tests-tools/src/mocks/tx.rs +++ b/lib/tests-tools/user-docs-tests-tools/src/mocks/tx.rs @@ -15,6 +15,7 @@ //! Mocks for projects use dubp-user-docs +use dubp_common_doc::parser::TextDocumentParser; use dubp_common_doc::traits::DocumentBuilder; use dubp_common_doc::Blockstamp; use dubp_user_docs::documents::transaction::*; @@ -55,3 +56,41 @@ pub fn first_g1_tx_doc() -> TransactionDocument { ed25519::Signature::from_base64("fAH5Gor+8MtFzQZ++JaJO6U8JJ6+rkqKtPrRr/iufh3MYkoDGxmjzj6jCADQL+hkWBt8y8QzlgRkz0ixBcKHBw==").expect("Fail to parse sig !") )]) } + +/// Generate mock transaction document +pub fn gen_mock_tx_doc() -> TransactionDocument { + TransactionDocumentParser::parse("Version: 10 +Type: Transaction +Currency: g1 +Blockstamp: 107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B +Locktime: 0 +Issuers: +8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3 +Inputs: +1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106345 +Unlocks: +0:SIG(0) +Outputs: +1002:0:SIG(CitdnuQgZ45tNFCagay7Wh12gwwHM8VLej1sWmfHWnQX) +Comment: DU symbolique pour demander le codage de nouvelles fonctionnalites cf. https://forum.monnaie-libre.fr/t/creer-de-nouvelles-fonctionnalites-dans-cesium-les-autres-applications/2025 Merci +T0LlCcbIn7xDFws48H8LboN6NxxwNXXTovG4PROLf7tkUAueHFWjfwZFKQXeZEHxfaL1eYs3QspGtLWUHPRVCQ==").expect("Fail to parse tx1") +} + +/// Generate mock transaction document with wrong version +pub fn gen_mock_tx_doc_wrong_version() -> TransactionDocument { + TransactionDocumentParser::parse("Version: 12 +Type: Transaction +Currency: g1 +Blockstamp: 107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B +Locktime: 0 +Issuers: +8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3 +Inputs: +1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106345 +Unlocks: +0:SIG(0) +Outputs: +1002:0:SIG(CitdnuQgZ45tNFCagay7Wh12gwwHM8VLej1sWmfHWnQX) +Comment: DU symbolique pour demander le codage de nouvelles fonctionnalites cf. https://forum.monnaie-libre.fr/t/creer-de-nouvelles-fonctionnalites-dans-cesium-les-autres-applications/2025 Merci +T0LlCcbIn7xDFws48H8LboN6NxxwNXXTovG4PROLf7tkUAueHFWjfwZFKQXeZEHxfaL1eYs3QspGtLWUHPRVCQ==").expect("Fail to parse tx1") +} diff --git a/lib/tools/common-tools/src/traits/bool_ext.rs b/lib/tools/common-tools/src/traits/bool_ext.rs new file mode 100644 index 0000000000000000000000000000000000000000..908b57c0b125b80b85d1aad11424e3361e43e18d --- /dev/null +++ b/lib/tools/common-tools/src/traits/bool_ext.rs @@ -0,0 +1,49 @@ +// Copyright (C) 2019 Éloïs SANCHEZ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +//! Trait AsResult. + +/// Transform any type to Result +pub trait BoolExt { + /// Transform any type to result<(), E> + fn or_err<E>(self, err: E) -> Result<(), E>; + /// Transform any type to result<T, E> + fn as_result<T, E>(self, ok: T, err: E) -> Result<T, E>; + /// Reverse bool + fn not(self) -> bool; +} + +impl BoolExt for bool { + #[inline] + fn or_err<E>(self, err: E) -> Result<(), E> { + if self { + Ok(()) + } else { + Err(err) + } + } + #[inline] + fn as_result<T, E>(self, ok: T, err: E) -> Result<T, E> { + if self { + Ok(ok) + } else { + Err(err) + } + } + #[inline] + fn not(self) -> bool { + !self + } +} diff --git a/lib/tools/common-tools/src/traits/mod.rs b/lib/tools/common-tools/src/traits/mod.rs index f3f2049fbe4ef765faf8ff48f3aa00b5700d93ed..757fcc097538f2f88e40ce6aa7c69b658bdbaed6 100644 --- a/lib/tools/common-tools/src/traits/mod.rs +++ b/lib/tools/common-tools/src/traits/mod.rs @@ -15,4 +15,5 @@ //! Common rust traits for DURS project. +pub mod bool_ext; pub mod merge;