From 7f204ec4a0564b23e1eb9e800bc57fb044946558 Mon Sep 17 00:00:00 2001
From: librelois <elois@ifee.fr>
Date: Mon, 25 Nov 2019 00:27:45 +0100
Subject: [PATCH] [feat] bc: implement local validation
---
Cargo.lock | 2 +
lib/modules/blockchain/blockchain/Cargo.toml | 1 +
.../blockchain/src/{dubp/mod.rs => dubp.rs} | 48 ++--
.../blockchain/blockchain/src/dubp/check.rs | 29 +++
.../src/dubp/check/{mod.rs => global.rs} | 34 +--
.../blockchain/src/dubp/check/local.rs | 209 +++++++++++++++++
.../src/dubp/check/local/genesis.rs | 194 ++++++++++++++++
.../src/dubp/check/local/not_genesis.rs | 157 +++++++++++++
.../blockchain/src/dubp/check/local/tx_doc.rs | 216 ++++++++++++++++++
.../blockchain/src/dubp/check/pow.rs | 148 ++++++++++++
.../blockchain/src/dunp/receiver.rs | 4 +-
.../blockchain/src/fork/rollback.rs | 2 +-
.../blocks-tests-tools/src/mocks.rs | 113 ++++++++-
.../src/mocks/block_params.rs | 49 ++++
.../user-docs-tests-tools/Cargo.toml | 1 +
.../user-docs-tests-tools/src/mocks/tx.rs | 39 ++++
lib/tools/common-tools/src/traits/bool_ext.rs | 49 ++++
lib/tools/common-tools/src/traits/mod.rs | 1 +
18 files changed, 1261 insertions(+), 35 deletions(-)
rename lib/modules/blockchain/blockchain/src/{dubp/mod.rs => dubp.rs} (78%)
create mode 100644 lib/modules/blockchain/blockchain/src/dubp/check.rs
rename lib/modules/blockchain/blockchain/src/dubp/check/{mod.rs => global.rs} (72%)
create mode 100644 lib/modules/blockchain/blockchain/src/dubp/check/local.rs
create mode 100644 lib/modules/blockchain/blockchain/src/dubp/check/local/genesis.rs
create mode 100644 lib/modules/blockchain/blockchain/src/dubp/check/local/not_genesis.rs
create mode 100644 lib/modules/blockchain/blockchain/src/dubp/check/local/tx_doc.rs
create mode 100644 lib/modules/blockchain/blockchain/src/dubp/check/pow.rs
create mode 100644 lib/tests-tools/blocks-tests-tools/src/mocks/block_params.rs
create mode 100644 lib/tools/common-tools/src/traits/bool_ext.rs
diff --git a/Cargo.lock b/Cargo.lock
index b727cd5f..edac3fe3 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 dd0a21d5..bf03e517 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 9066028a..cd964bd2 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 00000000..10b0ff01
--- /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 be5d5a91..483c2ece 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 00000000..3178bb26
--- /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 00000000..5099de4a
--- /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 00000000..0277999b
--- /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 00000000..9f0dd8f9
--- /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 00000000..706498f1
--- /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 f3d640fc..6277d7a3 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 8aacf3b8..27b15589 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 7a1fba29..ad83d128 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 00000000..6c0813d1
--- /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 a558a3ed..5906780f 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 5ed6cbcf..f6abdd0b 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 00000000..908b57c0
--- /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 f3f2049f..757fcc09 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;
--
GitLab