diff --git a/lib/modules/blockchain/blockchain-dal/src/entities/block.rs b/lib/modules/blockchain/blockchain-dal/src/entities/block.rs index cc4ca8b09d778031f48f99611045d92ec583fd9c..b22ade1daf8031c65e13e5010e327bf3803bea9d 100644 --- a/lib/modules/blockchain/blockchain-dal/src/entities/block.rs +++ b/lib/modules/blockchain/blockchain-dal/src/entities/block.rs @@ -14,7 +14,7 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. use crate::*; -use dubp_documents::documents::block::BlockDocument; +use dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait}; use dubp_documents::Document; use dubp_documents::{BlockNumber, Blockstamp}; use durs_wot::NodeId; diff --git a/lib/modules/blockchain/blockchain-dal/src/readers/block.rs b/lib/modules/blockchain/blockchain-dal/src/readers/block.rs index a9414bc44e780bcd320c689ad410d2b945c6fbda..8df1aa1bb6954bde68076ba2f2752df2876368e6 100644 --- a/lib/modules/blockchain/blockchain-dal/src/readers/block.rs +++ b/lib/modules/blockchain/blockchain-dal/src/readers/block.rs @@ -14,7 +14,7 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. use crate::*; -use dubp_documents::documents::block::BlockDocument; +use dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait}; use dubp_documents::Document; use dubp_documents::{BlockHash, BlockNumber, Blockstamp}; use dup_crypto::keys::*; @@ -42,7 +42,7 @@ pub fn get_block_hash( ) -> Result<Option<BlockHash>, DALError> { Ok(db.read(|db| { if let Some(dal_block) = db.get(&block_number) { - dal_block.block.hash + dal_block.block.hash() } else { None } @@ -86,7 +86,7 @@ pub fn already_have_block( } else { return Ok(blockchain_db.read(|db| { if let Some(dal_block) = db.get(&blockstamp.id) { - if dal_block.block.hash.unwrap_or_default() == blockstamp.hash { + if dal_block.block.hash().unwrap_or_default() == blockstamp.hash { return true; } } @@ -151,10 +151,11 @@ pub fn get_current_frame( current_block: &DALBlock, db: &BinDB<LocalBlockchainV10Datas>, ) -> Result<HashMap<PubKey, usize>, DALError> { - let frame_begin = current_block.block.number.0 - current_block.block.issuers_frame as u32; + let frame_begin = + current_block.block.number().0 - current_block.block.current_frame_size() as u32; Ok(db.read(|db| { let mut current_frame: HashMap<PubKey, usize> = HashMap::new(); - for block_number in frame_begin..current_block.block.number.0 { + for block_number in frame_begin..current_block.block.number().0 { let issuer = db .get(&BlockNumber(block_number)) .unwrap_or_else(|| fatal_error!("Fail to get block #{} !", block_number)) diff --git a/lib/modules/blockchain/blockchain-dal/src/readers/currency_params.rs b/lib/modules/blockchain/blockchain-dal/src/readers/currency_params.rs index 44dec3e618a6825def8bbe750daad512091d51d2..2be4b61dff636d7a413db27956c5fd7c937923e7 100644 --- a/lib/modules/blockchain/blockchain-dal/src/readers/currency_params.rs +++ b/lib/modules/blockchain/blockchain-dal/src/readers/currency_params.rs @@ -14,7 +14,8 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. use crate::*; -use dubp_documents::documents::block::BlockDocument; +use dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait}; +use dubp_documents::Document; use dup_currency_params::db::write_currency_params; use dup_currency_params::genesis_block_params::GenesisBlockParams; use dup_currency_params::CurrencyParameters; @@ -25,17 +26,26 @@ pub fn get_and_write_currency_params( db_path: &PathBuf, genesis_block: &BlockDocument, ) -> CurrencyParameters { - if genesis_block.number.0 != 0 { + if genesis_block.number().0 != 0 { fatal_error!("The genesis block must have number equal to zero !"); - } else if genesis_block.parameters.is_none() { - fatal_error!("The genesis block must have parameters !"); - } else if let Err(e) = write_currency_params( - db_path.clone(), - genesis_block.currency.clone(), - GenesisBlockParams::V10(unwrap!(genesis_block.parameters)), - ) { - fatal_error!("Fail to write currency parameters: {}", e); - } else { - CurrencyParameters::from((&genesis_block.currency, unwrap!(genesis_block.parameters))) + } + + match genesis_block { + BlockDocument::V10(genesis_block_v10) => { + if genesis_block_v10.parameters.is_none() { + fatal_error!("The genesis block must have parameters !"); + } else if let Err(e) = write_currency_params( + db_path.clone(), + genesis_block_v10.currency().into(), + GenesisBlockParams::V10(unwrap!(genesis_block_v10.parameters)), + ) { + fatal_error!("Fail to write currency parameters: {}", e); + } else { + CurrencyParameters::from(( + &genesis_block_v10.currency().into(), + unwrap!(genesis_block_v10.parameters), + )) + } + } } } diff --git a/lib/modules/blockchain/blockchain-dal/src/tools.rs b/lib/modules/blockchain/blockchain-dal/src/tools.rs index 7554cdd84d40365b2a214aab71acd1512dd0302e..dc12918b2afc099f838917e4be9a220ed1b09407 100644 --- a/lib/modules/blockchain/blockchain-dal/src/tools.rs +++ b/lib/modules/blockchain/blockchain-dal/src/tools.rs @@ -14,6 +14,7 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. use crate::entities::block::DALBlock; +use dubp_documents::documents::block::BlockDocumentTrait; use dup_crypto::keys::PubKey; use durs_common_tools::fatal_error; use durs_wot::operations::centrality::{CentralitiesCalculator, UlrikBrandesCentralityCalculator}; @@ -151,12 +152,12 @@ pub fn compute_median_issuers_frame<S: std::hash::BuildHasher>( current_frame_vec.sort_unstable(); // Calculate median - let mut median_index = match current_block.block.issuers_count % 2 { - 1 => (current_block.block.issuers_count / 2) + 1, - _ => current_block.block.issuers_count / 2, + let mut median_index = match current_block.block.issuers_count() % 2 { + 1 => (current_block.block.issuers_count() / 2) + 1, + _ => current_block.block.issuers_count() / 2, }; - if median_index >= current_block.block.issuers_count { - median_index = current_block.block.issuers_count - 1; + if median_index >= current_block.block.issuers_count() { + median_index = current_block.block.issuers_count() - 1; } current_frame_vec[median_index] diff --git a/lib/modules/blockchain/blockchain-dal/src/writers/block.rs b/lib/modules/blockchain/blockchain-dal/src/writers/block.rs index ecbf6ddea5ee51235c44e2ceadc9c6b2c8797235..47a27058f9cb376de986c68cacd7b801bffac153 100644 --- a/lib/modules/blockchain/blockchain-dal/src/writers/block.rs +++ b/lib/modules/blockchain/blockchain-dal/src/writers/block.rs @@ -16,6 +16,7 @@ use crate::entities::block::DALBlock; use crate::*; use crate::{BinDB, DALError, LocalBlockchainV10Datas}; +use dubp_documents::documents::block::BlockDocumentTrait; use dubp_documents::Document; use unwrap::unwrap; @@ -27,7 +28,7 @@ pub fn insert_new_head_block( ) -> Result<(), DALError> { // Insert head block in blockchain blockchain_db.write(|db| { - db.insert(dal_block.block.number, dal_block.clone()); + db.insert(dal_block.block.number(), dal_block.clone()); })?; // Insert head block in fork tree @@ -56,7 +57,7 @@ pub fn insert_new_fork_block(forks_dbs: &ForksDBs, dal_block: DALBlock) -> Resul if crate::writers::fork_tree::insert_new_fork_block( &forks_dbs.fork_tree_db, dal_block.block.blockstamp(), - unwrap!(dal_block.block.previous_hash), + unwrap!(dal_block.block.previous_hash()), )? { // Insert in ForksBlocks forks_dbs.fork_blocks_db.write(|db| { diff --git a/lib/modules/blockchain/blockchain-dal/src/writers/requests.rs b/lib/modules/blockchain/blockchain-dal/src/writers/requests.rs index 22fb9a3fc5a8c40b50c868e9d19634285ef4d2f1..be8610a5591634531aed0d601189531f254d8740 100644 --- a/lib/modules/blockchain/blockchain-dal/src/writers/requests.rs +++ b/lib/modules/blockchain/blockchain-dal/src/writers/requests.rs @@ -17,7 +17,7 @@ use crate::entities::block::DALBlock; use crate::entities::sources::SourceAmount; use crate::writers::transaction::DALTxV10; use crate::*; -use dubp_documents::documents::block::BlockDocument; +use dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait}; use dubp_documents::documents::certification::CompactCertificationDocument; use dubp_documents::documents::identity::IdentityDocument; use dubp_documents::Blockstamp; @@ -74,7 +74,7 @@ impl BlocksDBsWriteQuery { } else { // Insert block in blockchain blockchain_db.write(|db| { - db.insert(dal_block.block.number, dal_block); + db.insert(dal_block.block.number(), dal_block); })?; } } @@ -82,7 +82,7 @@ impl BlocksDBsWriteQuery { trace!("BlocksDBsWriteQuery::WriteBlock..."); // Remove block in blockchain blockchain_db.write(|db| { - db.remove(&dal_block.block.number); + db.remove(&dal_block.block.number()); })?; trace!("BlocksDBsWriteQuery::WriteBlock...finish"); } diff --git a/lib/modules/blockchain/blockchain/src/dbex.rs b/lib/modules/blockchain/blockchain/src/dbex.rs index e74145a6fad65960f6c5f9c0dcee62b58f23749c..ab4767a4e5348b013d2b91ef649ab4c7f5767486 100644 --- a/lib/modules/blockchain/blockchain/src/dbex.rs +++ b/lib/modules/blockchain/blockchain/src/dbex.rs @@ -14,6 +14,7 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. use crate::*; +use dubp_documents::documents::block::BlockDocumentTrait; use dubp_documents::documents::transaction::*; use dup_crypto::keys::*; use durs_wot::data::rusty::RustyWebOfTrust; @@ -242,9 +243,9 @@ pub fn dbex_wot(profile_path: PathBuf, csv: bool, query: &DBExWotQuery) { let (current_bc_time, blocks_times): (u64, HashMap<BlockNumber, u64>) = blockchain_db .read(|db| { ( - db[&BlockNumber(db.len() as u32 - 1)].block.median_time, + db[&BlockNumber(db.len() as u32 - 1)].block.common_time(), db.iter() - .map(|(block_id, dal_block)| (*block_id, dal_block.block.median_time)) + .map(|(block_id, dal_block)| (*block_id, dal_block.block.common_time())) .collect(), ) }) diff --git a/lib/modules/blockchain/blockchain/src/dubp/apply/mod.rs b/lib/modules/blockchain/blockchain/src/dubp/apply/mod.rs index 058865a0fc459f4d4c40729d583f1e7818547267..3cef4d90d013e789be6015cf410b4f30eb081b9b 100644 --- a/lib/modules/blockchain/blockchain/src/dubp/apply/mod.rs +++ b/lib/modules/blockchain/blockchain/src/dubp/apply/mod.rs @@ -15,7 +15,7 @@ //! Sub-module that applies the content of a block to the indexes of the local blockchain. -use dubp_documents::documents::block::BlockDocument; +use dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait, BlockDocumentV10}; use dubp_documents::documents::transaction::{TxAmount, TxBase}; use dubp_documents::{BlockNumber, Document}; use dup_crypto::keys::*; @@ -44,8 +44,22 @@ pub enum ApplyValidBlockError { RevokeUnknowNodeId, } +#[inline] pub fn apply_valid_block<W: WebOfTrust>( - mut block: BlockDocument, + block: BlockDocument, + wot_index: &mut HashMap<PubKey, NodeId>, + wot_db: &BinDB<W>, + expire_certs: &HashMap<(NodeId, NodeId), BlockNumber>, +) -> Result<ValidBlockApplyReqs, ApplyValidBlockError> { + match block { + BlockDocument::V10(block_v10) => { + apply_valid_block_v10(block_v10, wot_index, wot_db, expire_certs) + } + } +} + +pub fn apply_valid_block_v10<W: WebOfTrust>( + mut block: BlockDocumentV10, wot_index: &mut HashMap<PubKey, NodeId>, wot_db: &BinDB<W>, expire_certs: &HashMap<(NodeId, NodeId), BlockNumber>, @@ -270,7 +284,7 @@ pub fn apply_valid_block<W: WebOfTrust>( // Create DALBlock block.reduce(); let dal_block = DALBlock { - block, + block: BlockDocument::V10(block), expire_certs: Some(expire_certs.clone()), }; // Return DBs requests diff --git a/lib/modules/blockchain/blockchain/src/dubp/check/hashs.rs b/lib/modules/blockchain/blockchain/src/dubp/check/hashs.rs index bee2034936d8f4b70c7c4f1c65018e2c0663c5df..81a9d7850b5b4f50a48d4c1370c6ec02610e3342 100644 --- a/lib/modules/blockchain/blockchain/src/dubp/check/hashs.rs +++ b/lib/modules/blockchain/blockchain/src/dubp/check/hashs.rs @@ -16,28 +16,21 @@ //! Verify block inner hash and block hash use crate::VerifyBlockHashsError; -use dubp_documents::documents::block::BlockDocument; -use durs_common_tools::fatal_error; +use dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait}; /// Verify block hashs pub fn verify_block_hashs(block_doc: &BlockDocument) -> Result<(), VerifyBlockHashsError> { - trace!("complete_block #{}...", block_doc.number); - - if block_doc.inner_hash.is_none() { - fatal_error!( - "BlockchainModule : verify_block_hashs() : fatal error : block.inner_hash = None", - ); - } + trace!("complete_block #{}...", block_doc.number()); if block_doc.verify_inner_hash() { if block_doc.verify_hash() { - trace!("Succes to verify_block_hashs #{}", block_doc.number.0); + trace!("Succes to verify_block_hashs #{}", block_doc.number().0); Ok(()) } else { warn!("BlockchainModule : Refuse Bloc : invalid hash !"); Err(VerifyBlockHashsError::InvalidHash( - block_doc.number, - block_doc.hash, + block_doc.number(), + block_doc.hash(), )) } } else { diff --git a/lib/modules/blockchain/blockchain/src/dubp/check/mod.rs b/lib/modules/blockchain/blockchain/src/dubp/check/mod.rs index 0c9959bae305d01dd121f60d7127e2871d1a796e..b14762ea17a0fb7e373689f5b8495bab3d35ec5d 100644 --- a/lib/modules/blockchain/blockchain/src/dubp/check/mod.rs +++ b/lib/modules/blockchain/blockchain/src/dubp/check/mod.rs @@ -18,7 +18,7 @@ pub mod hashs; use crate::dubp::BlockError; -use dubp_documents::documents::block::BlockDocument; +use dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait}; use dubp_documents::*; use dup_crypto::keys::PubKey; use durs_blockchain_dal::*; @@ -39,11 +39,11 @@ pub fn verify_block_validity<W: WebOfTrust>( _wot_db: &BinDB<W>, ) -> Result<(), BlockError> { // Rules that do not concern genesis block - if block.number.0 > 0 { + if block.number().0 > 0 { // Get previous block let previous_block_opt = readers::block::get_block_in_local_blockchain( blockchain_db, - BlockNumber(block.number.0 - 1), + BlockNumber(block.number().0 - 1), )?; // Previous block must exist @@ -53,7 +53,7 @@ pub fn verify_block_validity<W: WebOfTrust>( let previous_block = previous_block_opt.expect("safe unwrap"); // Block version must not decrease - if previous_block.version > block.version { + if previous_block.version() > block.version() { return Err(BlockError::InvalidBlock(InvalidBlockError::VersionDecrease)); } } diff --git a/lib/modules/blockchain/blockchain/src/dubp/mod.rs b/lib/modules/blockchain/blockchain/src/dubp/mod.rs index 2b2b7feed61a5df6e8e3bdcd80bbc543d65fa364..87a588b54666e5fe1817924b327be1535bc700c2 100644 --- a/lib/modules/blockchain/blockchain/src/dubp/mod.rs +++ b/lib/modules/blockchain/blockchain/src/dubp/mod.rs @@ -21,6 +21,7 @@ pub mod check; use crate::*; use apply::*; use check::*; +use dubp_documents::documents::block::BlockDocumentTrait; use dubp_documents::Blockstamp; use dubp_documents::Document; use durs_blockchain_dal::entities::block::DALBlock; @@ -71,16 +72,16 @@ pub fn check_and_apply_block( &bc.blocks_databases.blockchain_db, &bc.forks_dbs, block_doc.blockstamp(), - block_doc.previous_hash, + block_doc.previous_hash(), )?; // Verify block hashs dubp::check::hashs::verify_block_hashs(&block_doc)?; // Check block chainability - if (block_doc.number.0 == 0 && bc.current_blockstamp == Blockstamp::default()) - || (block_doc.number.0 == bc.current_blockstamp.id.0 + 1 - && unwrap!(block_doc.previous_hash).to_string() + if (block_doc.number().0 == 0 && bc.current_blockstamp == Blockstamp::default()) + || (block_doc.number().0 == bc.current_blockstamp.id.0 + 1 + && unwrap!(block_doc.previous_hash()).to_string() == bc.current_blockstamp.hash.0.to_string()) { debug!( @@ -104,7 +105,7 @@ pub fn check_and_apply_block( )?; // If we're in block genesis, get the currency parameters - if block_doc.number == BlockNumber(0) { + if block_doc.number() == BlockNumber(0) { // Open currency_params_db let datas_path = durs_conf::get_datas_path(bc.profile_path.clone()); // Get and write currency params @@ -124,8 +125,8 @@ pub fn check_and_apply_block( )?)) } else if already_have_block { Err(BlockError::AlreadyHaveBlock) - } else if block_doc.number.0 >= bc.current_blockstamp.id.0 - || (bc.current_blockstamp.id.0 - block_doc.number.0) + } else if block_doc.number().0 >= bc.current_blockstamp.id.0 + || (bc.current_blockstamp.id.0 - block_doc.number().0) < unwrap!(bc.currency_params).fork_window_size as u32 { debug!( diff --git a/lib/modules/blockchain/blockchain/src/fork/fork_algo.rs b/lib/modules/blockchain/blockchain/src/fork/fork_algo.rs index d0d9b1780fcdb3e5f8d6cba2ca1bd1d26c4f861f..2784da53c25de61845252bb67a121538bdc5b311 100644 --- a/lib/modules/blockchain/blockchain/src/fork/fork_algo.rs +++ b/lib/modules/blockchain/blockchain/src/fork/fork_algo.rs @@ -13,6 +13,7 @@ // 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/>. +use dubp_documents::documents::block::BlockDocumentTrait; use dubp_documents::Blockstamp; use durs_blockchain_dal::entities::fork_tree::ForkTree; use durs_blockchain_dal::{DALError, ForksDBs}; @@ -33,7 +34,7 @@ pub fn fork_resolution_algo( db.get(¤t_blockstamp) .expect("safe unwrap") .block - .median_time + .common_time() })?; let mut sheets = forks_dbs.fork_tree_db.read(ForkTree::get_sheets)?; @@ -55,7 +56,7 @@ pub fn fork_resolution_algo( db.get(&branch_head_blockstamp) .expect("safe unwrap") .block - .median_time + .common_time() })?; if branch_head_blockstamp.id.0 >= current_blockstamp.id.0 + *ADVANCE_BLOCKS && branch_head_median_time >= current_bc_time + *ADVANCE_TIME @@ -102,7 +103,10 @@ mod tests { // Generate `FORK_WINDOW_SIZE + 2` mock blocks let main_branch: Vec<BlockDocument> = - dubp_documents_tests_tools::mocks::gen_empty_timed_blocks(fork_window_size + 2, 0u64); + dubp_documents_tests_tools::mocks::gen_empty_timed_blocks_v10( + fork_window_size + 2, + 0u64, + ); // Insert mock blocks in forks_dbs for block in &main_branch { @@ -136,17 +140,19 @@ mod tests { let fork_point = &main_branch[main_branch.len() - 2]; let fork_blocks: Vec<BlockDocument> = (0..3) .map(|i| { - dubp_documents_tests_tools::mocks::gen_empty_timed_block( - Blockstamp { - id: BlockNumber(fork_point.number.0 + i + 1), - hash: BlockHash(dup_crypto_tests_tools::mocks::hash('A')), - }, - ADVANCE_TIME - 1, - if i == 0 { - fork_point.hash.expect("safe unwrap").0 - } else { - dup_crypto_tests_tools::mocks::hash('A') - }, + BlockDocument::V10( + dubp_documents_tests_tools::mocks::gen_empty_timed_block_v10( + Blockstamp { + id: BlockNumber(fork_point.number().0 + i + 1), + hash: BlockHash(dup_crypto_tests_tools::mocks::hash('A')), + }, + ADVANCE_TIME - 1, + if i == 0 { + fork_point.hash().expect("safe unwrap").0 + } else { + dup_crypto_tests_tools::mocks::hash('A') + }, + ), ) }) .collect(); @@ -173,7 +179,7 @@ mod tests { // Add the determining fork block let determining_blockstamp = Blockstamp { - id: BlockNumber(fork_point.number.0 + 4), + id: BlockNumber(fork_point.number().0 + 4), hash: BlockHash(dup_crypto_tests_tools::mocks::hash('A')), }; assert_eq!( @@ -181,10 +187,12 @@ mod tests { durs_blockchain_dal::writers::block::insert_new_fork_block( &forks_dbs, DALBlock { - block: dubp_documents_tests_tools::mocks::gen_empty_timed_block( - determining_blockstamp, - *ADVANCE_TIME, - dup_crypto_tests_tools::mocks::hash('A'), + block: BlockDocument::V10( + dubp_documents_tests_tools::mocks::gen_empty_timed_block_v10( + determining_blockstamp, + *ADVANCE_TIME, + dup_crypto_tests_tools::mocks::hash('A'), + ) ), expire_certs: None, }, @@ -211,17 +219,19 @@ mod tests { // The old main branch catches up and overlaps with the fork let new_main_blocks: Vec<BlockDocument> = (0..7) .map(|i| { - dubp_documents_tests_tools::mocks::gen_empty_timed_block( - Blockstamp { - id: BlockNumber(fork_point.number.0 + i + 1), - hash: BlockHash(dup_crypto_tests_tools::mocks::hash('B')), - }, - ADVANCE_TIME * 2, - if i == 0 { - fork_point.hash.expect("safe unwrap").0 - } else { - dup_crypto_tests_tools::mocks::hash('B') - }, + BlockDocument::V10( + dubp_documents_tests_tools::mocks::gen_empty_timed_block_v10( + Blockstamp { + id: BlockNumber(fork_point.number().0 + i + 1), + hash: BlockHash(dup_crypto_tests_tools::mocks::hash('B')), + }, + ADVANCE_TIME * 2, + if i == 0 { + fork_point.hash().expect("safe unwrap").0 + } else { + dup_crypto_tests_tools::mocks::hash('B') + }, + ), ) }) .collect(); diff --git a/lib/modules/blockchain/blockchain/src/fork/revert_block.rs b/lib/modules/blockchain/blockchain/src/fork/revert_block.rs index 9966c0baa0bf23154a98268aaa20a5b224f2614c..09e0993517d86c89974363d6f1d32048769eb1d3 100644 --- a/lib/modules/blockchain/blockchain/src/fork/revert_block.rs +++ b/lib/modules/blockchain/blockchain/src/fork/revert_block.rs @@ -15,9 +15,10 @@ //! Sub-module that applies a block backwards. -use dubp_documents::documents::block::TxDocOrTxHash; +use dubp_documents::documents::block::v10::TxDocOrTxHash; +use dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait, BlockDocumentV10}; use dubp_documents::documents::transaction::{TxAmount, TxBase}; -use dubp_documents::Document; +use dubp_documents::{BlockNumber, Document}; use dup_crypto::keys::*; use durs_blockchain_dal::entities::block::DALBlock; use durs_blockchain_dal::entities::sources::SourceAmount; @@ -28,6 +29,7 @@ use durs_common_tools::fatal_error; use durs_wot::data::{NewLinkResult, RemLinkResult}; use durs_wot::{NodeId, WebOfTrust}; use std::collections::HashMap; +use unwrap::unwrap; #[derive(Debug)] /// Stores all queries to apply in database to "apply" the block @@ -52,18 +54,29 @@ impl From<DALError> for RevertValidBlockError { } pub fn revert_block<W: WebOfTrust>( - dal_block: &DALBlock, + dal_block: DALBlock, wot_index: &mut HashMap<PubKey, NodeId>, wot_db: &BinDB<W>, txs_db: &BinDB<TxV10Datas>, ) -> Result<ValidBlockRevertReqs, RevertValidBlockError> { - // Revert DALBlock - let mut block = dal_block.block.clone(); - let expire_certs = dal_block - .expire_certs - .clone() - .expect("Try to get expire_certs of an uncompleted block !"); + match dal_block.block { + BlockDocument::V10(block_v10) => revert_block_v10( + block_v10, + unwrap!(dal_block.expire_certs), + wot_index, + wot_db, + txs_db, + ), + } +} +pub fn revert_block_v10<W: WebOfTrust>( + mut block: BlockDocumentV10, + expire_certs: HashMap<(NodeId, NodeId), BlockNumber>, + wot_index: &mut HashMap<PubKey, NodeId>, + wot_db: &BinDB<W>, + txs_db: &BinDB<TxV10Datas>, +) -> Result<ValidBlockRevertReqs, RevertValidBlockError> { // Get transactions let dal_txs: Vec<DALTxV10> = block .transactions @@ -81,7 +94,7 @@ pub fn revert_block<W: WebOfTrust>( .collect(); // Revert reduce block - block.compute_inner_hash(); + block.generate_inner_hash(); debug!( "BlockchainModule : revert_valid_block({})", block.blockstamp() @@ -116,10 +129,10 @@ pub fn revert_block<W: WebOfTrust>( let mut wot_dbs_requests = Vec::new(); // Revert expire_certs if !expire_certs.is_empty() { - for ((source, target), created_block_id) in expire_certs { + for ((source, target), created_block_id) in &expire_certs { wot_db .write(|db| { - let result = db.add_link(source, target); + let result = db.add_link(*source, *target); match result { NewLinkResult::Ok(_) => {} _ => fatal_error!( @@ -132,9 +145,9 @@ pub fn revert_block<W: WebOfTrust>( }) .expect("Fail to write in WotDB"); wot_dbs_requests.push(WotsDBsWriteQuery::RevertExpireCert( - source, - target, - created_block_id, + *source, + *target, + *created_block_id, )); } } @@ -247,14 +260,17 @@ pub fn revert_block<W: WebOfTrust>( wot_dbs_requests.push(WotsDBsWriteQuery::RevertRenewalIdentity( joiner.issuers()[0], wot_id, - block.median_time, + block.common_time(), joiner.blockstamp().id, )); } } // Return DBs requests Ok(ValidBlockRevertReqs( - BlocksDBsWriteQuery::RevertBlock(dal_block.clone()), + BlocksDBsWriteQuery::RevertBlock(DALBlock { + block: BlockDocument::V10(block), + expire_certs: Some(expire_certs), + }), wot_dbs_requests, currency_dbs_requests, )) diff --git a/lib/modules/blockchain/blockchain/src/fork/rollback.rs b/lib/modules/blockchain/blockchain/src/fork/rollback.rs index fa060ea3c94c9851fdecfe458b88f508119046f4..df2f602f21bbbbd819b219f98817ab97a02d36cd 100644 --- a/lib/modules/blockchain/blockchain/src/fork/rollback.rs +++ b/lib/modules/blockchain/blockchain/src/fork/rollback.rs @@ -37,9 +37,10 @@ pub fn apply_rollback(bc: &mut BlockchainModule, new_bc_branch: Vec<Blockstamp>) fatal_error!("revert block {} fail !", bc.current_blockstamp); }) { + let blockstamp = dal_block.block.blockstamp(); let ValidBlockRevertReqs(bc_db_query, wot_dbs_queries, tx_dbs_queries) = super::revert_block::revert_block( - &dal_block, + dal_block, &mut bc.wot_index, &bc.wot_databases.wot_db, &bc.currency_databases.tx_db, @@ -47,7 +48,6 @@ pub fn apply_rollback(bc: &mut BlockchainModule, new_bc_branch: Vec<Blockstamp>) .unwrap_or_else(|_| { fatal_error!("revert block {} fail !", bc.current_blockstamp); }); - let blockstamp = dal_block.block.blockstamp(); // Apply db requests bc_db_query .apply( diff --git a/lib/modules/blockchain/blockchain/src/fork/stackable_blocks.rs b/lib/modules/blockchain/blockchain/src/fork/stackable_blocks.rs index f6aeaf8a8fb1f3908ec0ecb4926494a505781800..905d059dabf4dd94b4163f9590dd746aebdc400a 100644 --- a/lib/modules/blockchain/blockchain/src/fork/stackable_blocks.rs +++ b/lib/modules/blockchain/blockchain/src/fork/stackable_blocks.rs @@ -16,6 +16,7 @@ //! Sub-module that finds and applies the orphaned blocks that have become stackable on the local blockchain. use crate::*; +use dubp_documents::documents::block::BlockDocumentTrait; use unwrap::unwrap; pub fn apply_stackable_blocks(bc: &mut BlockchainModule) { @@ -29,9 +30,9 @@ pub fn apply_stackable_blocks(bc: &mut BlockchainModule) { break 'blockchain; } else { for stackable_block in stackable_blocks { - debug!("stackable_block({})", stackable_block.block.number); + debug!("stackable_block({})", stackable_block.block.number()); - let stackable_block_number = stackable_block.block.number; + let stackable_block_number = stackable_block.block.number(); let stackable_block_blockstamp = stackable_block.block.blockstamp(); if let Ok(CheckAndApplyBlockReturn::ValidMainBlock(ValidBlockApplyReqs( diff --git a/lib/modules/blockchain/blockchain/src/sync/download/json_reader_worker.rs b/lib/modules/blockchain/blockchain/src/sync/download/json_reader_worker.rs index 6e2dac28d08c179a305a1451f78b008a04a129a6..1c038957f4ddabe441c90d1a11d3deda29ee5734 100644 --- a/lib/modules/blockchain/blockchain/src/sync/download/json_reader_worker.rs +++ b/lib/modules/blockchain/blockchain/src/sync/download/json_reader_worker.rs @@ -87,7 +87,7 @@ pub fn json_reader_worker( // Send TargetBlockcstamp sender_sync_thread .send(MessForSyncThread::Target( - last_block.currency.clone(), + last_block.currency().into(), last_block.blockstamp(), )) .expect("Fatal error : sync_thread unrechable !"); diff --git a/lib/modules/blockchain/blockchain/src/sync/mod.rs b/lib/modules/blockchain/blockchain/src/sync/mod.rs index 5986d6f1cadeacb7c367810425e7160fd809ae0d..7c9b07bd3c08db4e0f127bc03057316de05bfc40 100644 --- a/lib/modules/blockchain/blockchain/src/sync/mod.rs +++ b/lib/modules/blockchain/blockchain/src/sync/mod.rs @@ -18,6 +18,7 @@ mod download; use crate::dubp::apply::apply_valid_block; use crate::*; +use dubp_documents::documents::block::BlockDocumentTrait; use dubp_documents::{BlockHash, BlockNumber}; use dup_crypto::keys::*; use dup_currency_params::{CurrencyName, CurrencyParameters}; @@ -251,7 +252,7 @@ pub fn local_sync<DC: DursConfTrait>(profile_path: PathBuf, conf: &DC, sync_opts // Get and write currency params if !get_currency_params { let datas_path = durs_conf::get_datas_path(profile_path.clone()); - if block_doc.number == BlockNumber(0) { + if block_doc.number() == BlockNumber(0) { currency_params = Some( durs_blockchain_dal::readers::currency_params::get_and_write_currency_params( &datas_path, @@ -270,11 +271,11 @@ pub fn local_sync<DC: DursConfTrait>(profile_path: PathBuf, conf: &DC, sync_opts let currency_params = unwrap!(currency_params); // Push block median_time in blocks_not_expiring - blocks_not_expiring.push_back(block_doc.median_time); + blocks_not_expiring.push_back(block_doc.common_time()); // Get blocks_expiring let mut blocks_expiring = Vec::new(); while blocks_not_expiring.front().copied() - < Some(block_doc.median_time - currency_params.sig_validity) + < Some(block_doc.common_time() - currency_params.sig_validity) { last_block_expiring += 1; blocks_expiring.push(BlockNumber(last_block_expiring as u32)); diff --git a/lib/modules/ws2p-v1-legacy/src/lib.rs b/lib/modules/ws2p-v1-legacy/src/lib.rs index 45c14d5181caef7de4120d3ddaaad1f90cb7878b..d2ee5314c93ca5a6b4c1afcf1076057efff582b8 100644 --- a/lib/modules/ws2p-v1-legacy/src/lib.rs +++ b/lib/modules/ws2p-v1-legacy/src/lib.rs @@ -878,7 +878,7 @@ mod tests { use super::*; use crate::ws_connections::requests::sent::network_request_to_json; use crate::ws_connections::requests::*; - use dubp_documents::documents::block::BlockDocument; + use dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait}; use dubp_documents::parsers::blocks::parse_json_block_from_serde_value; use dubp_documents::BlockNumber; @@ -1028,19 +1028,19 @@ mod tests { } ], }); - let mut block: BlockDocument = parse_json_block_from_serde_value(&json_block) + let block: BlockDocument = parse_json_block_from_serde_value(&json_block) .expect("Fail to parse test json block !"); assert_eq!( block - .inner_hash + .inner_hash() .expect("Try to get inner_hash of an uncompleted or reduce block !") .to_hex(), "61F02B1A6AE2E4B9A1FD66CE673258B4B21C0076795571EE3C9DC440DD06C46C" ); - block.compute_hash(); + //block.generate_hash(); assert_eq!( block - .hash + .hash() .expect("Try to get hash of an uncompleted or reduce block !") .0 .to_hex(), diff --git a/lib/modules/ws2p-v1-legacy/src/serializers/block.rs b/lib/modules/ws2p-v1-legacy/src/serializers/block.rs index a896d954ce458443f614882a1f609f3b1d45201a..95c8afe927b153faab8043127544dec28ce375a8 100644 --- a/lib/modules/ws2p-v1-legacy/src/serializers/block.rs +++ b/lib/modules/ws2p-v1-legacy/src/serializers/block.rs @@ -16,9 +16,17 @@ //! Sub-module that serialize BlockDocument into WS2Pv1 json format use super::IntoWS2Pv1Json; -use dubp_documents::documents::block::BlockDocumentStringified; +use dubp_documents::documents::block::{BlockDocumentStringified, BlockDocumentV10Stringified}; impl IntoWS2Pv1Json for BlockDocumentStringified { + fn into_ws2p_v1_json(self) -> serde_json::Value { + match self { + BlockDocumentStringified::V10(block_str_v10) => block_str_v10.into_ws2p_v1_json(), + } + } +} + +impl IntoWS2Pv1Json for BlockDocumentV10Stringified { fn into_ws2p_v1_json(self) -> serde_json::Value { let actives = self .actives diff --git a/lib/tests-tools/documents-tests-tools/src/mocks/mod.rs b/lib/tests-tools/documents-tests-tools/src/mocks/mod.rs index f00c42fecea02158cda0d8b272beec5ea2f97acd..18a189aee25a12bc3d823c80475b76091a7ea01c 100644 --- a/lib/tests-tools/documents-tests-tools/src/mocks/mod.rs +++ b/lib/tests-tools/documents-tests-tools/src/mocks/mod.rs @@ -17,7 +17,7 @@ pub mod identity; -use dubp_documents::documents::block::BlockDocument; +use dubp_documents::documents::block::{BlockDocument, BlockDocumentV10}; use dubp_documents::*; use dup_crypto::hashs::Hash; use dup_currency_params::CurrencyName; @@ -35,10 +35,10 @@ pub fn generate_blockstamps(n: usize) -> Vec<Blockstamp> { } /// Generate n empty timed block document -pub fn gen_empty_timed_blocks(n: usize, time_step: u64) -> Vec<BlockDocument> { +pub fn gen_empty_timed_blocks_v10(n: usize, time_step: u64) -> Vec<BlockDocument> { (0..n) .map(|i| { - gen_empty_timed_block( + BlockDocument::V10(gen_empty_timed_block_v10( Blockstamp { id: BlockNumber(i as u32), hash: BlockHash(dup_crypto_tests_tools::mocks::hash_from_byte( @@ -51,19 +51,19 @@ pub fn gen_empty_timed_blocks(n: usize, time_step: u64) -> Vec<BlockDocument> { } else { dup_crypto_tests_tools::mocks::hash_from_byte(((i - 1) % 255) as u8) }, - ) + )) }) .collect() } /// Generate empty timed block document /// (usefull for tests that only need blockstamp and median_time fields) -pub fn gen_empty_timed_block( +pub fn gen_empty_timed_block_v10( blockstamp: Blockstamp, time: u64, previous_hash: Hash, -) -> BlockDocument { - BlockDocument { +) -> BlockDocumentV10 { + BlockDocumentV10 { version: 10, nonce: 0, number: blockstamp.id, @@ -93,6 +93,5 @@ pub fn gen_empty_timed_block( certifications: vec![], transactions: vec![], inner_hash: None, - inner_hash_and_nonce_str: "".to_owned(), } } diff --git a/lib/tools/currency-params/src/lib.rs b/lib/tools/currency-params/src/lib.rs index e566fdc7acba11b22fcb4e470cf3b32e7064c620..fd20b2e6b27b8f686a1604f5754908b1d36fe83b 100644 --- a/lib/tools/currency-params/src/lib.rs +++ b/lib/tools/currency-params/src/lib.rs @@ -46,6 +46,12 @@ impl Display for CurrencyName { } } +impl From<&str> for CurrencyName { + fn from(s: &str) -> Self { + CurrencyName(s.to_owned()) + } +} + #[derive(Debug, Copy, Clone)] /// Currency parameters pub struct CurrencyParameters { diff --git a/lib/tools/documents/src/documents/block.rs b/lib/tools/documents/src/documents/block.rs index ebac6bcb578cbafc7a6c134d1d73515313bdc16f..f9ded4b747a4a46d9f4106c9f2e1465f8f3c6ec5 100644 --- a/lib/tools/documents/src/documents/block.rs +++ b/lib/tools/documents/src/documents/block.rs @@ -1,4 +1,4 @@ -// Copyright (C) 2018 The Duniter Project Developers. +// Copyright (C) 2018 The Durs Project Developers. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as @@ -15,922 +15,236 @@ //! Wrappers around Block document. -use dup_crypto::hashs::Hash; -use dup_crypto::keys::*; -use dup_currency_params::genesis_block_params::v10::BlockV10Parameters; -use dup_currency_params::CurrencyName; -use durs_common_tools::fatal_error; -use std::ops::Deref; -use unwrap::unwrap; +pub mod v10; -use crate::blockstamp::Blockstamp; -use crate::documents::certification::CertificationDocument; -use crate::documents::identity::IdentityDocument; -use crate::documents::membership::MembershipDocument; -use crate::documents::revocation::RevocationDocument; -use crate::documents::transaction::TransactionDocument; -use crate::documents::*; -use crate::text_document_traits::*; +pub use v10::{BlockDocumentV10, BlockDocumentV10Stringified}; -/// Store a transaction document or just its hash. -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] -pub enum TxDocOrTxHash { - /// Transaction document - TxDoc(Box<TransactionDocument>), - /// transaction hash - TxHash(Hash), -} - -impl TxDocOrTxHash { - /// Lightens the TxDocOrTxHash (for example to store it while minimizing the space required) - /// lightening consists in transforming the document by its hash. - pub fn reduce(&self) -> TxDocOrTxHash { - if let TxDocOrTxHash::TxDoc(ref tx_doc) = self { - let tx_doc = tx_doc.deref(); - if let Some(ref hash) = tx_doc.get_hash_opt() { - TxDocOrTxHash::TxHash(*hash) - } else { - TxDocOrTxHash::TxHash(tx_doc.clone().compute_hash()) - } - } else { - self.clone() - } - } - /// Get TxDoc variant - pub fn unwrap_doc(&self) -> TransactionDocument { - if let TxDocOrTxHash::TxDoc(ref tx_doc) = self { - tx_doc.deref().clone() - } else { - fatal_error!("Try to unwrap_doc() in a TxHash() variant of TxDocOrTxHash !") - } - } -} +use crate::blockstamp::Blockstamp; +use crate::{BlockHash, BlockNumber, Document, ToStringObject}; +use dup_crypto::hashs::Hash; +use dup_crypto::keys::{PrivKey, PubKey, PublicKey}; /// Wrap a Block document. /// /// Must be created by parsing a text document or using a builder. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] -pub struct BlockDocument { - /// Version - pub version: u32, - /// Nonce - pub nonce: u64, - /// number - pub number: BlockNumber, - /// Minimal proof of work difficulty - pub pow_min: usize, - /// Local time of the block issuer - pub time: u64, - /// Average time - pub median_time: u64, - /// Members count - pub members_count: usize, - /// Monetary mass - pub monetary_mass: usize, - /// Unit base (power of ten) - pub unit_base: usize, - /// Number of compute members in the current frame - pub issuers_count: usize, - /// Current frame size (in blocks) - pub issuers_frame: isize, - /// Current frame variation buffer - pub issuers_frame_var: isize, - /// Currency. - pub currency: CurrencyName, - /// Document issuer (there should be only one). - pub issuers: Vec<PubKey>, - /// Document signature (there should be only one). - /// This vector is empty, when the block is generated but the proof of work has not yet started - pub signatures: Vec<Sig>, - /// The hash is None, when the block is generated but the proof of work has not yet started - pub hash: Option<BlockHash>, - /// Currency parameters (only in genesis block) - pub parameters: Option<BlockV10Parameters>, - /// Hash of the previous block - pub previous_hash: Option<Hash>, - /// Issuer of the previous block - pub previous_issuer: Option<PubKey>, - /// Hash of the deterministic content of the block - pub inner_hash: Option<Hash>, - /// Amount of new dividend created at this block, None if no dividend is created at this block - pub dividend: Option<usize>, - /// Identities - pub identities: Vec<IdentityDocument>, - /// joiners - pub joiners: Vec<MembershipDocument>, - /// Actives (=renewals) - pub actives: Vec<MembershipDocument>, - /// Leavers - pub leavers: Vec<MembershipDocument>, - /// Revokeds - pub revoked: Vec<TextDocumentFormat<RevocationDocument>>, - /// Excludeds - pub excluded: Vec<PubKey>, - /// Certifications - pub certifications: Vec<TextDocumentFormat<CertificationDocument>>, - /// Transactions - pub transactions: Vec<TxDocOrTxHash>, - /// Part to sign - pub inner_hash_and_nonce_str: String, +pub enum BlockDocument { + V10(BlockDocumentV10), } -impl BlockDocument { - /// Return previous blockstamp - pub fn previous_blockstamp(&self) -> Blockstamp { - if self.number.0 > 0 { - Blockstamp { - id: BlockNumber(self.number.0 - 1), - hash: BlockHash(unwrap!(self.previous_hash)), - } - } else { - Blockstamp::default() - } - } - /// Compute inner hash - pub fn compute_inner_hash(&mut self) { - self.inner_hash = Some(Hash::compute_str(&self.generate_compact_inner_text())); - } +pub trait BlockDocumentTrait { + /// Common time in block (also known as 'blockchain time') + fn common_time(&self) -> u64; + /// Compute hash + fn compute_hash(&self) -> BlockHash; /// Compute inner hash - pub fn verify_inner_hash(&self) -> bool { - match self.inner_hash { - Some(inner_hash) => { - inner_hash == Hash::compute_str(&self.generate_compact_inner_text()) - } - None => false, - } - } - // Generate the character string that will be hashed - fn generate_will_hashed_string(&self) -> String { - format!( - "InnerHash: {}\nNonce: {}\n", - self.inner_hash - .expect("Try to get inner_hash of an uncompleted or reduce block !") - .to_hex(), - self.nonce - ) - } + fn compute_inner_hash(&self) -> Hash { + Hash::compute_str(&self.generate_compact_inner_text()) + } + /// Compute the character string that will be hashed + fn compute_will_hashed_string(&self) -> String; + /// Compute the character string that will be signed + fn compute_will_signed_string(&self) -> String; + /// Get current frame size (in blocks) + fn current_frame_size(&self) -> usize; + /// Generate compact inner text (for compute inner_hash) + fn generate_compact_inner_text(&self) -> String; + /// Compute hash and save it in document + fn generate_hash(&mut self); + /// Compute inner hash and save it in document + fn generate_inner_hash(&mut self); + /// Get block hash + fn hash(&self) -> Option<BlockHash>; + /// Increment nonce + fn increment_nonce(&mut self); + /// Get block inner hash + fn inner_hash(&self) -> Option<Hash>; + /// Get number of compute members in the current frame + fn issuers_count(&self) -> usize; + /// Get block number + fn number(&self) -> BlockNumber; + /// Get previous hash + fn previous_hash(&self) -> Option<Hash>; + /// Get previous blockstamp + fn previous_blockstamp(&self) -> Blockstamp; + /// Lightens the block (for example to store it while minimizing the space required) + fn reduce(&mut self); + /// Verify inner hash + fn verify_inner_hash(&self) -> bool; /// Verify block hash - pub fn verify_hash(&self) -> bool { - match self.hash { - Some(hash) => { - let datas = format!( - "{}{}\n", - self.generate_will_hashed_string(), - self.signatures[0] - ); - let expected_hash = BlockHash(Hash::compute_str(&datas)); - if hash == expected_hash { - true - } else { - warn!( - "Block #{} have invalid hash (expected='{}', actual='{}', datas='{}').", - self.number.0, expected_hash, hash, datas - ); - false - } - } - None => false, + fn verify_hash(&self) -> bool; + /// Sign block + fn sign(&mut self, privkey: PrivKey); +} + +impl BlockDocumentTrait for BlockDocument { + #[inline] + fn compute_hash(&self) -> BlockHash { + match self { + BlockDocument::V10(block) => block.compute_hash(), } } - /// Fill inner_hash_and_nonce_str - pub fn fill_inner_hash_and_nonce_str(&mut self, new_nonce: Option<u64>) { - if let Some(new_nonce) = new_nonce { - self.nonce = new_nonce; + #[inline] + fn compute_will_hashed_string(&self) -> String { + match self { + BlockDocument::V10(block) => block.compute_will_hashed_string(), } - self.inner_hash_and_nonce_str = self.generate_will_hashed_string(); - } - /// Sign block - pub fn sign(&mut self, privkey: PrivKey) { - self.fill_inner_hash_and_nonce_str(None); - self.signatures = vec![privkey.sign(self.inner_hash_and_nonce_str.as_bytes())]; - } - /// Compute hash - pub fn compute_hash(&mut self) { - self.hash = Some(BlockHash(Hash::compute_str(&format!( - "{}{}\n", - self.generate_will_hashed_string(), - self.signatures[0] - )))); } - /// Lightens the block (for example to store it while minimizing the space required) - pub fn reduce(&mut self) { - //self.hash = None; - self.inner_hash = None; - self.inner_hash_and_nonce_str = String::with_capacity(0); - for i in &mut self.identities { - i.reduce(); - } - for i in &mut self.joiners { - i.reduce(); + #[inline] + fn compute_will_signed_string(&self) -> String { + match self { + BlockDocument::V10(block) => block.compute_will_signed_string(), } - for i in &mut self.actives { - i.reduce(); + } + #[inline] + fn current_frame_size(&self) -> usize { + match self { + BlockDocument::V10(block) => block.current_frame_size(), } - for i in &mut self.leavers { - i.reduce(); + } + #[inline] + fn generate_compact_inner_text(&self) -> String { + match self { + BlockDocument::V10(block) => block.generate_compact_inner_text(), } - for i in &mut self.transactions { - i.reduce(); + } + #[inline] + fn generate_hash(&mut self) { + match self { + BlockDocument::V10(block) => block.generate_hash(), } } - /// Generate compact inner text (for compute inner_hash) - pub fn generate_compact_inner_text(&self) -> String { - let mut identities_str = String::from(""); - for identity in self.identities.clone() { - identities_str.push_str("\n"); - identities_str.push_str(&identity.generate_compact_text()); + #[inline] + fn generate_inner_hash(&mut self) { + match self { + BlockDocument::V10(block) => block.generate_inner_hash(), } - let mut joiners_str = String::from(""); - for joiner in self.joiners.clone() { - joiners_str.push_str("\n"); - joiners_str.push_str(&joiner.generate_compact_text()); + } + #[inline] + fn hash(&self) -> Option<BlockHash> { + match self { + BlockDocument::V10(block) => block.hash(), } - let mut actives_str = String::from(""); - for active in self.actives.clone() { - actives_str.push_str("\n"); - actives_str.push_str(&active.generate_compact_text()); + } + #[inline] + fn increment_nonce(&mut self) { + match self { + BlockDocument::V10(block) => block.increment_nonce(), } - let mut leavers_str = String::from(""); - for leaver in self.leavers.clone() { - leavers_str.push_str("\n"); - leavers_str.push_str(&leaver.generate_compact_text()); + } + #[inline] + fn inner_hash(&self) -> Option<Hash> { + match self { + BlockDocument::V10(block) => block.inner_hash(), } - let mut identities_str = String::from(""); - for identity in self.identities.clone() { - identities_str.push_str("\n"); - identities_str.push_str(&identity.generate_compact_text()); + } + #[inline] + fn issuers_count(&self) -> usize { + match self { + BlockDocument::V10(block) => block.issuers_count(), } - let mut revokeds_str = String::from(""); - for revocation in self.revoked.clone() { - revokeds_str.push_str("\n"); - revokeds_str.push_str(&revocation.as_compact_text()); + } + #[inline] + fn common_time(&self) -> u64 { + match self { + BlockDocument::V10(block) => block.common_time(), } - let mut excludeds_str = String::from(""); - for exclusion in self.excluded.clone() { - excludeds_str.push_str("\n"); - excludeds_str.push_str(&exclusion.to_string()); + } + #[inline] + fn number(&self) -> BlockNumber { + match self { + BlockDocument::V10(block) => block.number(), } - let mut certifications_str = String::from(""); - for certification in self.certifications.clone() { - certifications_str.push_str("\n"); - certifications_str.push_str(&certification.as_compact_text()); + } + #[inline] + fn previous_blockstamp(&self) -> Blockstamp { + match self { + BlockDocument::V10(block) => block.previous_blockstamp(), } - let mut transactions_str = String::from(""); - for transaction in self.transactions.clone() { - if let TxDocOrTxHash::TxDoc(transaction) = transaction { - transactions_str.push_str("\n"); - transactions_str.push_str(&transaction.deref().generate_compact_text()); - } + } + #[inline] + fn previous_hash(&self) -> Option<Hash> { + match self { + BlockDocument::V10(block) => block.previous_hash(), } - let mut dividend_str = String::from(""); - if let Some(dividend) = self.dividend { - if dividend > 0 { - dividend_str.push_str("UniversalDividend: "); - dividend_str.push_str(÷nd.to_string()); - dividend_str.push_str("\n"); - } + } + #[inline] + fn reduce(&mut self) { + match self { + BlockDocument::V10(block) => block.reduce(), } - let mut parameters_str = String::from(""); - if let Some(params) = self.parameters { - parameters_str.push_str("Parameters: "); - parameters_str.push_str(¶ms.to_string()); - parameters_str.push_str("\n"); + } + #[inline] + fn verify_inner_hash(&self) -> bool { + match self { + BlockDocument::V10(block) => block.verify_inner_hash(), } - let mut previous_hash_str = String::from(""); - if self.number.0 > 0 { - previous_hash_str.push_str("PreviousHash: "); - previous_hash_str.push_str(&unwrap!(self.previous_hash).to_string()); - previous_hash_str.push_str("\n"); + } + #[inline] + fn verify_hash(&self) -> bool { + match self { + BlockDocument::V10(block) => block.verify_hash(), } - let mut previous_issuer_str = String::from(""); - if self.number.0 > 0 { - previous_issuer_str.push_str("PreviousIssuer: "); - previous_issuer_str.push_str( - &self - .previous_issuer - .expect("No genesis block must have previous issuer") - .to_string(), - ); - previous_issuer_str.push_str("\n"); + } + #[inline] + fn sign(&mut self, privkey: PrivKey) { + match self { + BlockDocument::V10(block) => block.sign(privkey), } - format!( - "Version: {version} -Type: Block -Currency: {currency} -Number: {block_number} -PoWMin: {pow_min} -Time: {time} -MedianTime: {median_time} -{dividend}UnitBase: {unit_base} -Issuer: {issuer} -IssuersFrame: {issuers_frame} -IssuersFrameVar: {issuers_frame_var} -DifferentIssuersCount: {issuers_count} -{parameters}{previous_hash}{previous_issuer}MembersCount: {members_count} -Identities:{identities} -Joiners:{joiners} -Actives:{actives} -Leavers:{leavers} -Revoked:{revoked} -Excluded:{excluded} -Certifications:{certifications} -Transactions:{transactions} -", - version = self.version, - currency = self.currency, - block_number = self.number, - pow_min = self.pow_min, - time = self.time, - median_time = self.median_time, - dividend = dividend_str, - unit_base = self.unit_base, - issuer = self.issuers[0], - issuers_frame = self.issuers_frame, - issuers_frame_var = self.issuers_frame_var, - issuers_count = self.issuers_count, - parameters = parameters_str, - previous_hash = previous_hash_str, - previous_issuer = previous_issuer_str, - members_count = self.members_count, - identities = identities_str, - joiners = joiners_str, - actives = actives_str, - leavers = leavers_str, - revoked = revokeds_str, - excluded = excludeds_str, - certifications = certifications_str, - transactions = transactions_str, - ) } } impl Document for BlockDocument { type PublicKey = PubKey; - type CurrencyType = str; fn version(&self) -> u16 { - 10 + match self { + BlockDocument::V10(_) => 10u16, + } } fn currency(&self) -> &str { - &self.currency.0 + match self { + BlockDocument::V10(block) => block.currency(), + } } fn blockstamp(&self) -> Blockstamp { - Blockstamp { - id: self.number, - hash: self - .hash - .expect("Fatal error : try to get blockstamp of an uncomplete or reduce block !"), + match self { + BlockDocument::V10(block) => block.blockstamp(), } } - fn issuers(&self) -> &Vec<PubKey> { - &self.issuers + fn issuers(&self) -> &Vec<Self::PublicKey> { + match self { + BlockDocument::V10(block) => block.issuers(), + } } - fn signatures(&self) -> &Vec<Sig> { - &self.signatures + fn signatures(&self) -> &Vec<<Self::PublicKey as PublicKey>::Signature> { + match self { + BlockDocument::V10(block) => block.signatures(), + } } fn as_bytes(&self) -> &[u8] { - self.inner_hash_and_nonce_str.as_bytes() - } -} - -impl CompactTextDocument for BlockDocument { - fn as_compact_text(&self) -> String { - let compact_inner_text = self.generate_compact_inner_text(); - format!( - "{}InnerHash: {}\nNonce: ", - compact_inner_text, - self.inner_hash - .expect("Try to get inner_hash of an uncompleted or reduce block !") - .to_hex() - ) - } -} - -impl TextDocument for BlockDocument { - type CompactTextDocument_ = BlockDocument; - - fn as_text(&self) -> &str { - fatal_error!( - "Dev error: function not implemented. Please use to_compact_document() instead" - ); - } - - fn to_compact_document(&self) -> Self::CompactTextDocument_ { - self.clone() - } -} - -impl IntoSpecializedDocument<DUBPDocument> for BlockDocument { - fn into_specialized(self) -> DUBPDocument { - DUBPDocument::Block(Box::new(self)) + match self { + BlockDocument::V10(block) => block.as_bytes(), + } } } #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct BlockDocumentStringified { - /// Version - pub version: u64, - /// Nonce - pub nonce: u64, - /// number - pub number: u64, - /// Minimal proof of work difficulty - pub pow_min: u64, - /// Local time of the block issuer - pub time: u64, - /// Average time - pub median_time: u64, - /// Members count - pub members_count: u64, - /// Monetary mass - pub monetary_mass: u64, - /// Unit base (power of ten) - pub unit_base: u64, - /// Number of compute members in the current frame - pub issuers_count: u64, - /// Current frame size (in blocks) - pub issuers_frame: i64, - /// Current frame variation buffer - pub issuers_frame_var: i64, - /// Currency. - pub currency: String, - /// Document issuer (there should be only one). - pub issuers: Vec<String>, - /// Document signature (there should be only one). - /// This vector is empty, when the block is generated but the proof of work has not yet started - pub signatures: Vec<String>, - /// The hash is None, when the block is generated but the proof of work has not yet started - pub hash: Option<String>, - /// Currency parameters (only in genesis block) - pub parameters: Option<String>, - /// Hash of the previous block - pub previous_hash: Option<String>, - /// Issuer of the previous block - pub previous_issuer: Option<String>, - /// Hash of the deterministic content of the block - pub inner_hash: Option<String>, - /// Amount of new dividend created at this block, None if no dividend is created at this block - pub dividend: Option<u64>, - /// Identities - pub identities: Vec<IdentityStringDocument>, - /// joiners - pub joiners: Vec<MembershipStringDocument>, - /// Actives (=renewals) - pub actives: Vec<MembershipStringDocument>, - /// Leavers - pub leavers: Vec<MembershipStringDocument>, - /// Revokeds - pub revoked: Vec<CompactRevocationStringDocument>, - /// Excludeds - pub excluded: Vec<String>, - /// Certifications - pub certifications: Vec<CompactCertificationStringDocument>, - /// Transactions - pub transactions: Vec<TransactionDocumentStringified>, +pub enum BlockDocumentStringified { + V10(BlockDocumentV10Stringified), } impl ToStringObject for BlockDocument { type StringObject = BlockDocumentStringified; - /// Transforms an object into a json object - fn to_string_object(&self) -> BlockDocumentStringified { - BlockDocumentStringified { - version: u64::from(self.version), - nonce: self.nonce, - number: u64::from(self.number.0), - pow_min: self.pow_min as u64, - time: self.time, - median_time: self.median_time, - members_count: self.members_count as u64, - monetary_mass: self.monetary_mass as u64, - unit_base: self.unit_base as u64, - issuers_count: self.issuers_count as u64, - issuers_frame: self.issuers_frame as i64, - issuers_frame_var: self.issuers_frame_var as i64, - currency: self.currency.to_string(), - issuers: self.issuers.iter().map(ToString::to_string).collect(), - signatures: self.signatures.iter().map(ToString::to_string).collect(), - hash: self.hash.map(|hash| hash.to_string()), - parameters: self.parameters.map(|parameters| parameters.to_string()), - previous_hash: self.previous_hash.map(|hash| hash.to_string()), - previous_issuer: self.previous_issuer.map(|p| p.to_string()), - inner_hash: self.inner_hash.map(|hash| hash.to_string()), - dividend: self.dividend.map(|dividend| dividend as u64), - identities: self - .identities - .iter() - .map(ToStringObject::to_string_object) - .collect(), - joiners: self - .joiners - .iter() - .map(ToStringObject::to_string_object) - .collect(), - actives: self - .actives - .iter() - .map(ToStringObject::to_string_object) - .collect(), - leavers: self - .leavers - .iter() - .map(ToStringObject::to_string_object) - .collect(), - revoked: self - .revoked - .iter() - .map(|revocation_doc| match revocation_doc { - TextDocumentFormat::Complete(complete_revoc_doc) => { - complete_revoc_doc.to_compact_document().to_string_object() - } - TextDocumentFormat::Compact(compact_revoc_doc) => { - compact_revoc_doc.to_string_object() - } - }) - .collect(), - excluded: self.excluded.iter().map(ToString::to_string).collect(), - certifications: self - .certifications - .iter() - .map(|cert_doc| match cert_doc { - TextDocumentFormat::Complete(complete_cert_doc) => { - complete_cert_doc.to_compact_document().to_string_object() - } - TextDocumentFormat::Compact(compact_cert_doc) => { - compact_cert_doc.to_string_object() - } - }) - .collect(), - transactions: self - .transactions - .iter() - .map(|tx_doc_or_tx_hash| match tx_doc_or_tx_hash { - TxDocOrTxHash::TxDoc(tx_doc) => tx_doc.to_string_object(), - TxDocOrTxHash::TxHash(_) => { - fatal_error!("Try to stringify block without their tx documents") - } - }) - .collect(), - } - } -} - -#[cfg(test)] -mod tests { - use super::certification::CertificationDocumentParser; - use super::transaction::TransactionDocumentParser; - use super::*; - use crate::{Document, VerificationResult}; - - #[test] - fn generate_and_verify_empty_block() { - let mut block = BlockDocument { - nonce: 100_010_200_000_006_940, - version: 10, - number: BlockNumber(174_260), - pow_min: 68, - time: 1_525_296_873, - median_time: 1_525_292_577, - members_count: 33, - monetary_mass: 15_633_687, - unit_base: 0, - issuers_count: 8, - issuers_frame: 41, - issuers_frame_var: 0, - currency: CurrencyName(String::from("g1-test")), - issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58("39Fnossy1GrndwCnAXGDw3K5UYXhNXAFQe7yhYZp8ELP").unwrap())], - signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64("lqXrNOopjM39oM7hgB7Vq13uIohdCuLlhh/q8RVVEZ5UVASphow/GXikCdhbWID19Bn0XrXzTbt/R7akbE9xAg==").unwrap())], - hash: None, - parameters: None, - previous_hash: Some(Hash::from_hex("0000A7D4361B9EBF4CE974A521149A73E8A5DE9B73907AB3BC918726AED7D40A").expect("fail to parse previous_hash")), - previous_issuer: Some(PubKey::Ed25519(ed25519::PublicKey::from_base58("EPKuZA1Ek5y8S1AjAmAPtGrVCMFqUGzUEAa7Ei62CY2L").unwrap())), - 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(), - inner_hash_and_nonce_str: String::new(), - }; - // test inner_hash computation - block.compute_inner_hash(); - println!("{}", block.generate_compact_text()); - assert_eq!( - block - .inner_hash - .expect("Try to get inner_hash of an uncompleted or reduce block !") - .to_hex(), - "58E4865A47A46E0DF1449AABC449B5406A12047C413D61B5E17F86BE6641E7B0" - ); - // Test signature validity - block.fill_inner_hash_and_nonce_str(Some(100_010_200_000_006_940)); - assert_eq!(block.verify_signatures(), VerificationResult::Valid()); - // Test hash computation - block.compute_hash(); - assert_eq!( - block - .hash - .expect("Try to get hash of an uncompleted or reduce block !") - .0 - .to_hex(), - "00002EE584F36C15D3EB21AAC78E0896C75EF9070E73B4EC33BFA2C3D561EEB2" - ); - } - #[test] - fn generate_and_verify_block() { - 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 tx1 = 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"); - - 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"); - - let mut block = BlockDocument { - nonce: 0, - version: 10, - number: BlockNumber(107_984), - pow_min: 88, - time: 1_522_685_861, - median_time: 1522683184, - 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: None, - 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![TxDocOrTxHash::TxDoc(Box::new(tx1)), TxDocOrTxHash::TxDoc(Box::new(tx2))], - inner_hash_and_nonce_str: String::new(), - }; - // test inner_hash computation - block.compute_inner_hash(); - println!("{}", block.generate_compact_text()); - assert_eq!( - block - .inner_hash - .expect("Try to get inner_hash of an uncompleted or reduce block !") - .to_hex(), - "C8AB69E33ECE2612EADC7AB30D069B1F1A3D8C95EBBFD50DE583AC8E3666CCA1" - ); - // test generate_compact_text() - assert_eq!( - block.generate_compact_text(), - "Version: 10 -Type: Block -Currency: g1 -Number: 107984 -PoWMin: 88 -Time: 1522685861 -MedianTime: 1522683184 -UnitBase: 0 -Issuer: DA4PYtXdvQqk1nCaprXH52iMsK5Ahxs1nRWbWKLhpVkQ -IssuersFrame: 211 -IssuersFrameVar: 0 -DifferentIssuersCount: 42 -PreviousHash: 000001144968D0C3516BE6225E4662F182E28956AF46DD7FB228E3D0F9413FEB -PreviousIssuer: D3krfq6J9AmfpKnS3gQVYoy7NzGCc61vokteTS8LJ4YH -MembersCount: 896 -Identities: -Joiners: -Actives: -Leavers: -Revoked: -Excluded: -Certifications: -6TAzLWuNcSqgNDNpAutrKpPXcGJwy1ZEMeVvZSZNs2e3:CYPsYTdt87Tx6cCiZs9KD4jqPgYxbcVEqVZpRgJ9jjoV:106669:UmseG2XKNwKcY8RFi6gUCT91udGnnNmSh7se10J1jeRVlwf+O2Tyb2Cccot9Dt7BO4+Kx2P6vFJB3oVGGHMxBA== -Transactions: -TX:10:1:1:1:1:1:0 -107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B -8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3 -1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106345 -0:SIG(0) -1002:0:SIG(CitdnuQgZ45tNFCagay7Wh12gwwHM8VLej1sWmfHWnQX) -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== -TX:10:1:1:1:1:1:0 -107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B -8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3 -1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106614 -0:SIG(0) -1002:0:SIG(78ZwwgpgdH5uLZLbThUQH7LKwPgjMunYfLiCfUCySkM8) -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== -InnerHash: C8AB69E33ECE2612EADC7AB30D069B1F1A3D8C95EBBFD50DE583AC8E3666CCA1 -Nonce: " - ); - // Test signature validity - block.fill_inner_hash_and_nonce_str(Some(10_300_000_018_323)); - assert_eq!(block.verify_signatures(), VerificationResult::Valid()); - // Test hash computation - block.compute_hash(); - assert_eq!( - block - .hash - .expect("Try to get hash of an uncompleted or reduce block !") - .0 - .to_hex(), - "000004F8B84A3590243BA562E5F2BA379F55A0B387C5D6FAC1022DFF7FFE6014" - ); - } - - #[test] - fn generate_and_verify_block_2() { - let ms1 = MembershipDocumentParser::parse( - "Version: 10 -Type: Membership -Currency: g1 -Issuer: 4VZkro3N7VonygybESHngKUABA6gSrbW77Ktb94zE969 -Block: 165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2 -Membership: IN -UserID: piaaf31 -CertTS: 74077-0000022816648B2F7801E059F67CCD0C023FF0ED84459D52C70494D74DDCC6F6 -gvaZ1QnJf8FjjRDJ0cYusgpBgQ8r0NqEz39BooH6DtIrgX+WTeXuLSnjZDl35VCBjokvyjry+v0OkTT8FKpABA==", - ) - .expect("Fail to parse ms1"); - - let tx1 = TransactionDocumentParser::parse( - "Version: 10 -Type: Transaction -Currency: g1 -Blockstamp: 165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2 -Locktime: 0 -Issuers: -51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2 -Inputs: -1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:163766 -1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164040 -1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164320 -1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164584 -1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164849 -1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165118 -1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165389 -Unlocks: -0:SIG(0) -1:SIG(0) -2:SIG(0) -3:SIG(0) -4:SIG(0) -5:SIG(0) -6:SIG(0) -Outputs: -7000:0:SIG(98wxzS683Tc1WWm1YxpL5WpxS7wBa1mZBccKSsYpaant) -28:0:SIG(51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2) -Comment: Panier mixte plus 40 pommes merci -7o/yIh0BNSAv5pNmHz04uUBl8TuP2s4HRFMtKeGFQfXNYJPUyJTP/dj6hdrgKtJkm5dCfbxT4KRy6wJf+dj1Cw==", - ) - .expect("Fail to parse tx1"); - - let tx2 = TransactionDocumentParser::parse( - "Version: 10 -Type: Transaction -Currency: g1 -Blockstamp: 165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2 -Locktime: 0 -Issuers: -3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX -Inputs: -1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:148827 -1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149100 -1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149370 -1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149664 -1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149943 -1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:150222 -Unlocks: -0:SIG(0) -1:SIG(0) -2:SIG(0) -3:SIG(0) -4:SIG(0) -5:SIG(0) -Outputs: -6000:0:SIG(AopwTfXhj8VqZReFJYGGWnoWnXNj3RgaqFcGGywXpZrD) -12:0:SIG(3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX) -Comment: En reglement de tes bons bocaux de fruits et legumes -nxr4exGrt16jteN9ZX3XZPP9l+X0OUbZ1o/QjE1hbWQNtVU3HhH9SJoEvNj2iVl3gCRr9u2OA9uj9vCyUDyjAg== -", - ) - .expect("Fail to parse tx2"); - - let mut block = BlockDocument { - nonce: 0, - version: 10, - number: BlockNumber(165_647), - pow_min: 90, - time: 1_540_633_175, - median_time: 1_540_627_811, - members_count: 1402, - monetary_mass: 386_008_811, - unit_base: 0, - issuers_count: 37, - issuers_frame: 186, - issuers_frame_var: 0, - currency: CurrencyName(String::from("g1")), - issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58("A4pc9Uuk4NXkWG8CibicjjPpEPdiup1mhjMoRWUZsonq").unwrap())], - signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64("2Z/+9ADdZvHXs19YR8+qDzgfl8WJlBG5PcbFvBG9TOuUJbjAdxhcgxrFrSRIABGWcCrIgLkB805fZVLP8jOjBA==").unwrap())], - hash: None, - parameters: None, - previous_hash: Some(Hash::from_hex("000003E78FA4133F2C13B416F330C8DFB5A41EB87E37190615DB334F2C914A51").expect("fail to parse previous_hash")), - previous_issuer: Some(PubKey::Ed25519(ed25519::PublicKey::from_base58("8NmGZmGjL1LUgJQRg282yQF7KTdQuRNAg8QfSa2qvd65").unwrap())), - inner_hash: None,//Some(Hash::from_hex("3B49ECC1475549CFD94CA7B399311548A0FD0EC93C8EDD5670DAA5A958A41846").expect("fail to parse inner_hash")), - dividend: None, - identities: vec![], - joiners: vec![], - actives: vec![ms1], - leavers: vec![], - revoked: vec![], - excluded: vec![], - certifications: vec![], - transactions: vec![TxDocOrTxHash::TxDoc(Box::new(tx1)), TxDocOrTxHash::TxDoc(Box::new(tx2))], - inner_hash_and_nonce_str: String::new(), - }; - // test inner_hash computation - block.compute_inner_hash(); - println!("{}", block.generate_compact_text()); - assert_eq!( - block - .inner_hash - .expect("Try to get inner_hash of an uncompleted or reduce block !") - .to_hex(), - "3B49ECC1475549CFD94CA7B399311548A0FD0EC93C8EDD5670DAA5A958A41846" - ); - // test generate_compact_text() - let block_compact_text = block.generate_compact_text(); - assert_eq!( - block_compact_text, - "Version: 10\nType: Block\nCurrency: g1\nNumber: 165647\nPoWMin: 90\nTime: 1540633175\nMedianTime: 1540627811\nUnitBase: 0\nIssuer: A4pc9Uuk4NXkWG8CibicjjPpEPdiup1mhjMoRWUZsonq\nIssuersFrame: 186\nIssuersFrameVar: 0\nDifferentIssuersCount: 37\nPreviousHash: 000003E78FA4133F2C13B416F330C8DFB5A41EB87E37190615DB334F2C914A51\nPreviousIssuer: 8NmGZmGjL1LUgJQRg282yQF7KTdQuRNAg8QfSa2qvd65\nMembersCount: 1402\nIdentities:\nJoiners:\nActives:\n4VZkro3N7VonygybESHngKUABA6gSrbW77Ktb94zE969:gvaZ1QnJf8FjjRDJ0cYusgpBgQ8r0NqEz39BooH6DtIrgX+WTeXuLSnjZDl35VCBjokvyjry+v0OkTT8FKpABA==:165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2:74077-0000022816648B2F7801E059F67CCD0C023FF0ED84459D52C70494D74DDCC6F6:piaaf31\nLeavers:\nRevoked:\nExcluded:\nCertifications:\nTransactions:\nTX:10:1:7:7:2:1:0\n165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2\n51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:163766\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164040\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164320\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164584\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164849\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165118\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165389\n0:SIG(0)\n1:SIG(0)\n2:SIG(0)\n3:SIG(0)\n4:SIG(0)\n5:SIG(0)\n6:SIG(0)\n7000:0:SIG(98wxzS683Tc1WWm1YxpL5WpxS7wBa1mZBccKSsYpaant)\n28:0:SIG(51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2)\nPanier mixte plus 40 pommes merci\n7o/yIh0BNSAv5pNmHz04uUBl8TuP2s4HRFMtKeGFQfXNYJPUyJTP/dj6hdrgKtJkm5dCfbxT4KRy6wJf+dj1Cw==\nTX:10:1:6:6:2:1:0\n165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2\n3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:148827\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149100\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149370\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149664\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149943\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:150222\n0:SIG(0)\n1:SIG(0)\n2:SIG(0)\n3:SIG(0)\n4:SIG(0)\n5:SIG(0)\n6000:0:SIG(AopwTfXhj8VqZReFJYGGWnoWnXNj3RgaqFcGGywXpZrD)\n12:0:SIG(3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX)\nEn reglement de tes bons bocaux de fruits et legumes\nnxr4exGrt16jteN9ZX3XZPP9l+X0OUbZ1o/QjE1hbWQNtVU3HhH9SJoEvNj2iVl3gCRr9u2OA9uj9vCyUDyjAg==\nInnerHash: 3B49ECC1475549CFD94CA7B399311548A0FD0EC93C8EDD5670DAA5A958A41846\nNonce: " - ); - // Test signature validity - block.fill_inner_hash_and_nonce_str(Some(10_300_000_090_296)); - assert_eq!(block.verify_signatures(), VerificationResult::Valid()); - // Test hash computation - block.compute_hash(); - assert_eq!( - block - .hash - .expect("Try to get hash of an uncompleted or reduce block !") - .0 - .to_hex(), - "000002026E32A3D649B34968AAF9D03C4F19A5954229C54A801BBB1CD216B230" - ); + fn to_string_object(&self) -> Self::StringObject { + match self { + BlockDocument::V10(block) => BlockDocumentStringified::V10(block.to_string_object()), + } } } diff --git a/lib/tools/documents/src/documents/block/v10.rs b/lib/tools/documents/src/documents/block/v10.rs new file mode 100644 index 0000000000000000000000000000000000000000..f0ffc6b8c0e0e77bd844d818a85ab1307ff705ed --- /dev/null +++ b/lib/tools/documents/src/documents/block/v10.rs @@ -0,0 +1,954 @@ +// Copyright (C) 2018 The Duniter Project Developers. +// +// 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/>. + +//! Wrappers around Block document v10. + +use dup_crypto::hashs::Hash; +use dup_crypto::keys::*; +use dup_currency_params::genesis_block_params::v10::BlockV10Parameters; +use dup_currency_params::CurrencyName; +use durs_common_tools::fatal_error; +use std::ops::Deref; +use unwrap::unwrap; + +use super::BlockDocumentTrait; +use crate::blockstamp::Blockstamp; +use crate::documents::certification::CertificationDocument; +use crate::documents::identity::IdentityDocument; +use crate::documents::membership::MembershipDocument; +use crate::documents::revocation::RevocationDocument; +use crate::documents::transaction::TransactionDocument; +use crate::documents::*; +use crate::text_document_traits::*; + +/// Store a transaction document or just its hash. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +pub enum TxDocOrTxHash { + /// Transaction document + TxDoc(Box<TransactionDocument>), + /// transaction hash + TxHash(Hash), +} + +impl TxDocOrTxHash { + /// Lightens the TxDocOrTxHash (for example to store it while minimizing the space required) + /// lightening consists in transforming the document by its hash. + pub fn reduce(&self) -> TxDocOrTxHash { + if let TxDocOrTxHash::TxDoc(ref tx_doc) = self { + let tx_doc = tx_doc.deref(); + if let Some(ref hash) = tx_doc.get_hash_opt() { + TxDocOrTxHash::TxHash(*hash) + } else { + TxDocOrTxHash::TxHash(tx_doc.clone().compute_hash()) + } + } else { + self.clone() + } + } + /// Get TxDoc variant + pub fn unwrap_doc(&self) -> TransactionDocument { + if let TxDocOrTxHash::TxDoc(ref tx_doc) = self { + tx_doc.deref().clone() + } else { + fatal_error!("Try to unwrap_doc() in a TxHash() variant of TxDocOrTxHash !") + } + } +} + +/// Wrap a Block document. +/// +/// Must be created by parsing a text document or using a builder. +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +pub struct BlockDocumentV10 { + /// Version + pub version: u32, + /// Nonce + pub nonce: u64, + /// number + pub number: BlockNumber, + /// Minimal proof of work difficulty + pub pow_min: usize, + /// Local time of the block issuer + pub time: u64, + /// Average time + pub median_time: u64, + /// Members count + pub members_count: usize, + /// Monetary mass + pub monetary_mass: usize, + /// Unit base (power of ten) + pub unit_base: usize, + /// Number of compute members in the current frame + pub issuers_count: usize, + /// Current frame size (in blocks) + pub issuers_frame: usize, + /// Current frame variation buffer + pub issuers_frame_var: isize, + /// Currency. + pub currency: CurrencyName, + /// Document issuer (there should be only one). + pub issuers: Vec<PubKey>, + /// Document signature (there should be only one). + /// This vector is empty, when the block is generated but the proof of work has not yet started + pub signatures: Vec<Sig>, + /// The hash is None, when the block is generated but the proof of work has not yet started + pub hash: Option<BlockHash>, + /// Currency parameters (only in genesis block) + pub parameters: Option<BlockV10Parameters>, + /// Hash of the previous block + pub previous_hash: Option<Hash>, + /// Issuer of the previous block + pub previous_issuer: Option<PubKey>, + /// Hash of the deterministic content of the block + pub inner_hash: Option<Hash>, + /// Amount of new dividend created at this block, None if no dividend is created at this block + pub dividend: Option<usize>, + /// Identities + pub identities: Vec<IdentityDocument>, + /// joiners + pub joiners: Vec<MembershipDocument>, + /// Actives (=renewals) + pub actives: Vec<MembershipDocument>, + /// Leavers + pub leavers: Vec<MembershipDocument>, + /// Revokeds + pub revoked: Vec<TextDocumentFormat<RevocationDocument>>, + /// Excludeds + pub excluded: Vec<PubKey>, + /// Certifications + pub certifications: Vec<TextDocumentFormat<CertificationDocument>>, + /// Transactions + pub transactions: Vec<TxDocOrTxHash>, +} + +impl BlockDocumentTrait for BlockDocumentV10 { + fn common_time(&self) -> u64 { + self.median_time + } + fn compute_hash(&self) -> BlockHash { + BlockHash(Hash::compute_str(&self.compute_will_hashed_string())) + } + fn compute_will_hashed_string(&self) -> String { + format!( + "{}{}\n", + self.compute_will_signed_string(), + self.signatures[0] + ) + } + fn compute_will_signed_string(&self) -> String { + format!( + "InnerHash: {}\nNonce: {}\n", + self.inner_hash + .expect("compute_will_signed_string(): Try to get inner_hash of an uncompleted or reduce block !") + .to_hex(), + self.nonce + ) + } + fn current_frame_size(&self) -> usize { + self.issuers_frame + } + fn generate_compact_inner_text(&self) -> String { + let mut identities_str = String::from(""); + for identity in self.identities.clone() { + identities_str.push_str("\n"); + identities_str.push_str(&identity.generate_compact_text()); + } + let mut joiners_str = String::from(""); + for joiner in self.joiners.clone() { + joiners_str.push_str("\n"); + joiners_str.push_str(&joiner.generate_compact_text()); + } + let mut actives_str = String::from(""); + for active in self.actives.clone() { + actives_str.push_str("\n"); + actives_str.push_str(&active.generate_compact_text()); + } + let mut leavers_str = String::from(""); + for leaver in self.leavers.clone() { + leavers_str.push_str("\n"); + leavers_str.push_str(&leaver.generate_compact_text()); + } + let mut identities_str = String::from(""); + for identity in self.identities.clone() { + identities_str.push_str("\n"); + identities_str.push_str(&identity.generate_compact_text()); + } + let mut revokeds_str = String::from(""); + for revocation in self.revoked.clone() { + revokeds_str.push_str("\n"); + revokeds_str.push_str(&revocation.as_compact_text()); + } + let mut excludeds_str = String::from(""); + for exclusion in self.excluded.clone() { + excludeds_str.push_str("\n"); + excludeds_str.push_str(&exclusion.to_string()); + } + let mut certifications_str = String::from(""); + for certification in self.certifications.clone() { + certifications_str.push_str("\n"); + certifications_str.push_str(&certification.as_compact_text()); + } + let mut transactions_str = String::from(""); + for transaction in self.transactions.clone() { + if let TxDocOrTxHash::TxDoc(transaction) = transaction { + transactions_str.push_str("\n"); + transactions_str.push_str(&transaction.deref().generate_compact_text()); + } + } + let mut dividend_str = String::from(""); + if let Some(dividend) = self.dividend { + if dividend > 0 { + dividend_str.push_str("UniversalDividend: "); + dividend_str.push_str(÷nd.to_string()); + dividend_str.push_str("\n"); + } + } + let mut parameters_str = String::from(""); + if let Some(params) = self.parameters { + parameters_str.push_str("Parameters: "); + parameters_str.push_str(¶ms.to_string()); + parameters_str.push_str("\n"); + } + let mut previous_hash_str = String::from(""); + if self.number.0 > 0 { + previous_hash_str.push_str("PreviousHash: "); + previous_hash_str.push_str(&unwrap!(self.previous_hash).to_string()); + previous_hash_str.push_str("\n"); + } + let mut previous_issuer_str = String::from(""); + if self.number.0 > 0 { + previous_issuer_str.push_str("PreviousIssuer: "); + previous_issuer_str.push_str( + &self + .previous_issuer + .expect("No genesis block must have previous issuer") + .to_string(), + ); + previous_issuer_str.push_str("\n"); + } + format!( + "Version: {version} +Type: Block +Currency: {currency} +Number: {block_number} +PoWMin: {pow_min} +Time: {time} +MedianTime: {median_time} +{dividend}UnitBase: {unit_base} +Issuer: {issuer} +IssuersFrame: {issuers_frame} +IssuersFrameVar: {issuers_frame_var} +DifferentIssuersCount: {issuers_count} +{parameters}{previous_hash}{previous_issuer}MembersCount: {members_count} +Identities:{identities} +Joiners:{joiners} +Actives:{actives} +Leavers:{leavers} +Revoked:{revoked} +Excluded:{excluded} +Certifications:{certifications} +Transactions:{transactions} +", + version = self.version, + currency = self.currency, + block_number = self.number, + pow_min = self.pow_min, + time = self.time, + median_time = self.median_time, + dividend = dividend_str, + unit_base = self.unit_base, + issuer = self.issuers[0], + issuers_frame = self.issuers_frame, + issuers_frame_var = self.issuers_frame_var, + issuers_count = self.issuers_count, + parameters = parameters_str, + previous_hash = previous_hash_str, + previous_issuer = previous_issuer_str, + members_count = self.members_count, + identities = identities_str, + joiners = joiners_str, + actives = actives_str, + leavers = leavers_str, + revoked = revokeds_str, + excluded = excludeds_str, + certifications = certifications_str, + transactions = transactions_str, + ) + } + fn generate_hash(&mut self) { + self.hash = Some(self.compute_hash()); + } + fn generate_inner_hash(&mut self) { + self.inner_hash = Some(self.compute_inner_hash()); + } + fn hash(&self) -> Option<BlockHash> { + self.hash + } + fn increment_nonce(&mut self) { + self.nonce += 1; + } + fn inner_hash(&self) -> Option<Hash> { + self.inner_hash + } + fn issuers_count(&self) -> usize { + self.issuers_count + } + fn number(&self) -> BlockNumber { + self.number + } + fn previous_blockstamp(&self) -> Blockstamp { + if self.number.0 > 0 { + Blockstamp { + id: BlockNumber(self.number.0 - 1), + hash: BlockHash(unwrap!(self.previous_hash)), + } + } else { + Blockstamp::default() + } + } + fn previous_hash(&self) -> Option<Hash> { + self.previous_hash + } + fn reduce(&mut self) { + //self.hash = None; + self.inner_hash = None; + for i in &mut self.identities { + i.reduce(); + } + for i in &mut self.joiners { + i.reduce(); + } + for i in &mut self.actives { + i.reduce(); + } + for i in &mut self.leavers { + i.reduce(); + } + for i in &mut self.transactions { + i.reduce(); + } + } + fn sign(&mut self, privkey: PrivKey) { + self.signatures = vec![privkey.sign(self.compute_will_signed_string().as_bytes())]; + } + fn verify_inner_hash(&self) -> bool { + match self.inner_hash { + Some(inner_hash) => inner_hash == self.compute_inner_hash(), + None => false, + } + } + fn verify_hash(&self) -> bool { + match self.hash { + Some(hash) => { + let expected_hash = self.compute_hash(); + if hash == expected_hash { + true + } else { + warn!( + "Block #{} have invalid hash (expected='{}', actual='{}', datas='{}').", + self.number.0, + expected_hash, + hash, + self.compute_will_hashed_string() + ); + false + } + } + None => false, + } + } +} + +impl Document for BlockDocumentV10 { + type PublicKey = PubKey; + + #[inline] + fn version(&self) -> u16 { + 10 + } + + #[inline] + fn currency(&self) -> &str { + &self.currency.0 + } + + #[inline] + fn blockstamp(&self) -> Blockstamp { + Blockstamp { + id: self.number, + hash: self + .hash + .expect("Fatal error : try to get blockstamp of an uncomplete or reduce block !"), + } + } + + #[inline] + fn issuers(&self) -> &Vec<PubKey> { + &self.issuers + } + + #[inline] + fn signatures(&self) -> &Vec<Sig> { + &self.signatures + } + + #[inline] + fn as_bytes(&self) -> &[u8] { + fatal_error!("as_bytes() must not be call for BlockDocumentV10 !") + } + + #[inline] + fn no_as_bytes(&self) -> bool { + true + } + + #[inline] + fn to_bytes(&self) -> Vec<u8> { + self.compute_will_signed_string().as_bytes().to_vec() + } +} + +impl CompactTextDocument for BlockDocumentV10 { + fn as_compact_text(&self) -> String { + let compact_inner_text = self.generate_compact_inner_text(); + format!( + "{}InnerHash: {}\nNonce: ", + compact_inner_text, + self.inner_hash + .expect( + "as_compact_text(): Try to get inner_hash of an uncompleted or reduce block !" + ) + .to_hex() + ) + } +} + +impl TextDocument for BlockDocumentV10 { + type CompactTextDocument_ = BlockDocumentV10; + + fn as_text(&self) -> &str { + fatal_error!( + "Dev error: function not implemented. Please use to_compact_document() instead" + ); + } + + fn to_compact_document(&self) -> Self::CompactTextDocument_ { + self.clone() + } +} + +impl IntoSpecializedDocument<DUBPDocument> for BlockDocumentV10 { + fn into_specialized(self) -> DUBPDocument { + DUBPDocument::Block(Box::new(self)) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct BlockDocumentV10Stringified { + /// Version + pub version: u64, + /// Nonce + pub nonce: u64, + /// number + pub number: u64, + /// Minimal proof of work difficulty + pub pow_min: u64, + /// Local time of the block issuer + pub time: u64, + /// Average time + pub median_time: u64, + /// Members count + pub members_count: u64, + /// Monetary mass + pub monetary_mass: u64, + /// Unit base (power of ten) + pub unit_base: u64, + /// Number of compute members in the current frame + pub issuers_count: u64, + /// Current frame size (in blocks) + pub issuers_frame: i64, + /// Current frame variation buffer + pub issuers_frame_var: i64, + /// Currency. + pub currency: String, + /// Document issuer (there should be only one). + pub issuers: Vec<String>, + /// Document signature (there should be only one). + /// This vector is empty, when the block is generated but the proof of work has not yet started + pub signatures: Vec<String>, + /// The hash is None, when the block is generated but the proof of work has not yet started + pub hash: Option<String>, + /// Currency parameters (only in genesis block) + pub parameters: Option<String>, + /// Hash of the previous block + pub previous_hash: Option<String>, + /// Issuer of the previous block + pub previous_issuer: Option<String>, + /// Hash of the deterministic content of the block + pub inner_hash: Option<String>, + /// Amount of new dividend created at this block, None if no dividend is created at this block + pub dividend: Option<u64>, + /// Identities + pub identities: Vec<IdentityStringDocument>, + /// joiners + pub joiners: Vec<MembershipStringDocument>, + /// Actives (=renewals) + pub actives: Vec<MembershipStringDocument>, + /// Leavers + pub leavers: Vec<MembershipStringDocument>, + /// Revokeds + pub revoked: Vec<CompactRevocationStringDocument>, + /// Excludeds + pub excluded: Vec<String>, + /// Certifications + pub certifications: Vec<CompactCertificationStringDocument>, + /// Transactions + pub transactions: Vec<TransactionDocumentStringified>, +} + +impl ToStringObject for BlockDocumentV10 { + type StringObject = BlockDocumentV10Stringified; + /// Transforms an object into a json object + fn to_string_object(&self) -> BlockDocumentV10Stringified { + BlockDocumentV10Stringified { + version: u64::from(self.version), + nonce: self.nonce, + number: u64::from(self.number.0), + pow_min: self.pow_min as u64, + time: self.time, + median_time: self.median_time, + members_count: self.members_count as u64, + monetary_mass: self.monetary_mass as u64, + unit_base: self.unit_base as u64, + issuers_count: self.issuers_count as u64, + issuers_frame: self.issuers_frame as i64, + issuers_frame_var: self.issuers_frame_var as i64, + currency: self.currency.to_string(), + issuers: self.issuers.iter().map(ToString::to_string).collect(), + signatures: self.signatures.iter().map(ToString::to_string).collect(), + hash: self.hash.map(|hash| hash.to_string()), + parameters: self.parameters.map(|parameters| parameters.to_string()), + previous_hash: self.previous_hash.map(|hash| hash.to_string()), + previous_issuer: self.previous_issuer.map(|p| p.to_string()), + inner_hash: self.inner_hash.map(|hash| hash.to_string()), + dividend: self.dividend.map(|dividend| dividend as u64), + identities: self + .identities + .iter() + .map(ToStringObject::to_string_object) + .collect(), + joiners: self + .joiners + .iter() + .map(ToStringObject::to_string_object) + .collect(), + actives: self + .actives + .iter() + .map(ToStringObject::to_string_object) + .collect(), + leavers: self + .leavers + .iter() + .map(ToStringObject::to_string_object) + .collect(), + revoked: self + .revoked + .iter() + .map(|revocation_doc| match revocation_doc { + TextDocumentFormat::Complete(complete_revoc_doc) => { + complete_revoc_doc.to_compact_document().to_string_object() + } + TextDocumentFormat::Compact(compact_revoc_doc) => { + compact_revoc_doc.to_string_object() + } + }) + .collect(), + excluded: self.excluded.iter().map(ToString::to_string).collect(), + certifications: self + .certifications + .iter() + .map(|cert_doc| match cert_doc { + TextDocumentFormat::Complete(complete_cert_doc) => { + complete_cert_doc.to_compact_document().to_string_object() + } + TextDocumentFormat::Compact(compact_cert_doc) => { + compact_cert_doc.to_string_object() + } + }) + .collect(), + transactions: self + .transactions + .iter() + .map(|tx_doc_or_tx_hash| match tx_doc_or_tx_hash { + TxDocOrTxHash::TxDoc(tx_doc) => tx_doc.to_string_object(), + TxDocOrTxHash::TxHash(_) => { + fatal_error!("Try to stringify block without their tx documents") + } + }) + .collect(), + } + } +} + +#[cfg(test)] +mod tests { + use super::certification::CertificationDocumentParser; + use super::transaction::TransactionDocumentParser; + use super::*; + use crate::{Document, VerificationResult}; + + #[test] + fn generate_and_verify_empty_block() { + let mut block = BlockDocumentV10 { + nonce: 100_010_200_000_006_940, + version: 10, + number: BlockNumber(174_260), + pow_min: 68, + time: 1_525_296_873, + median_time: 1_525_292_577, + members_count: 33, + monetary_mass: 15_633_687, + unit_base: 0, + issuers_count: 8, + issuers_frame: 41, + issuers_frame_var: 0, + currency: CurrencyName(String::from("g1-test")), + issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58("39Fnossy1GrndwCnAXGDw3K5UYXhNXAFQe7yhYZp8ELP").unwrap())], + signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64("lqXrNOopjM39oM7hgB7Vq13uIohdCuLlhh/q8RVVEZ5UVASphow/GXikCdhbWID19Bn0XrXzTbt/R7akbE9xAg==").unwrap())], + hash: None, + parameters: None, + previous_hash: Some(Hash::from_hex("0000A7D4361B9EBF4CE974A521149A73E8A5DE9B73907AB3BC918726AED7D40A").expect("fail to parse previous_hash")), + previous_issuer: Some(PubKey::Ed25519(ed25519::PublicKey::from_base58("EPKuZA1Ek5y8S1AjAmAPtGrVCMFqUGzUEAa7Ei62CY2L").unwrap())), + 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(), + }; + // test inner_hash computation + block.generate_inner_hash(); + println!("{}", block.generate_compact_text()); + assert_eq!( + block + .inner_hash + .expect("tests::generate_and_verify_empty_block: Try to get inner_hash of an uncompleted or reduce block !") + .to_hex(), + "58E4865A47A46E0DF1449AABC449B5406A12047C413D61B5E17F86BE6641E7B0" + ); + // Test signature validity + assert_eq!(block.verify_signatures(), VerificationResult::Valid()); + // Test hash computation + block.generate_hash(); + assert_eq!( + block + .hash + .expect("Try to get hash of an uncompleted or reduce block !") + .0 + .to_hex(), + "00002EE584F36C15D3EB21AAC78E0896C75EF9070E73B4EC33BFA2C3D561EEB2" + ); + } + + #[test] + fn generate_and_verify_block() { + 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 tx1 = 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"); + + 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"); + + let mut block = BlockDocumentV10 { + nonce: 10_300_000_018_323, + version: 10, + number: BlockNumber(107_984), + pow_min: 88, + time: 1_522_685_861, + median_time: 1522683184, + 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: None, + 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![TxDocOrTxHash::TxDoc(Box::new(tx1)), TxDocOrTxHash::TxDoc(Box::new(tx2))], + }; + // test inner_hash computation + block.generate_inner_hash(); + println!("{}", block.generate_compact_text()); + assert_eq!( + block + .inner_hash + .expect("tests::generate_and_verify_block: Try to get inner_hash of an uncompleted or reduce block !") + .to_hex(), + "C8AB69E33ECE2612EADC7AB30D069B1F1A3D8C95EBBFD50DE583AC8E3666CCA1" + ); + // test generate_compact_text() + assert_eq!( + block.generate_compact_text(), + "Version: 10 +Type: Block +Currency: g1 +Number: 107984 +PoWMin: 88 +Time: 1522685861 +MedianTime: 1522683184 +UnitBase: 0 +Issuer: DA4PYtXdvQqk1nCaprXH52iMsK5Ahxs1nRWbWKLhpVkQ +IssuersFrame: 211 +IssuersFrameVar: 0 +DifferentIssuersCount: 42 +PreviousHash: 000001144968D0C3516BE6225E4662F182E28956AF46DD7FB228E3D0F9413FEB +PreviousIssuer: D3krfq6J9AmfpKnS3gQVYoy7NzGCc61vokteTS8LJ4YH +MembersCount: 896 +Identities: +Joiners: +Actives: +Leavers: +Revoked: +Excluded: +Certifications: +6TAzLWuNcSqgNDNpAutrKpPXcGJwy1ZEMeVvZSZNs2e3:CYPsYTdt87Tx6cCiZs9KD4jqPgYxbcVEqVZpRgJ9jjoV:106669:UmseG2XKNwKcY8RFi6gUCT91udGnnNmSh7se10J1jeRVlwf+O2Tyb2Cccot9Dt7BO4+Kx2P6vFJB3oVGGHMxBA== +Transactions: +TX:10:1:1:1:1:1:0 +107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B +8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3 +1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106345 +0:SIG(0) +1002:0:SIG(CitdnuQgZ45tNFCagay7Wh12gwwHM8VLej1sWmfHWnQX) +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== +TX:10:1:1:1:1:1:0 +107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B +8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3 +1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106614 +0:SIG(0) +1002:0:SIG(78ZwwgpgdH5uLZLbThUQH7LKwPgjMunYfLiCfUCySkM8) +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== +InnerHash: C8AB69E33ECE2612EADC7AB30D069B1F1A3D8C95EBBFD50DE583AC8E3666CCA1 +Nonce: " + ); + // Test signature validity + assert_eq!(block.verify_signatures(), VerificationResult::Valid()); + // Test hash computation + block.generate_hash(); + assert_eq!( + block + .hash + .expect("Try to get hash of an uncompleted or reduce block !") + .0 + .to_hex(), + "000004F8B84A3590243BA562E5F2BA379F55A0B387C5D6FAC1022DFF7FFE6014" + ); + } + + #[test] + fn generate_and_verify_block_2() { + let ms1 = MembershipDocumentParser::parse( + "Version: 10 +Type: Membership +Currency: g1 +Issuer: 4VZkro3N7VonygybESHngKUABA6gSrbW77Ktb94zE969 +Block: 165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2 +Membership: IN +UserID: piaaf31 +CertTS: 74077-0000022816648B2F7801E059F67CCD0C023FF0ED84459D52C70494D74DDCC6F6 +gvaZ1QnJf8FjjRDJ0cYusgpBgQ8r0NqEz39BooH6DtIrgX+WTeXuLSnjZDl35VCBjokvyjry+v0OkTT8FKpABA==", + ) + .expect("Fail to parse ms1"); + + let tx1 = TransactionDocumentParser::parse( + "Version: 10 +Type: Transaction +Currency: g1 +Blockstamp: 165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2 +Locktime: 0 +Issuers: +51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2 +Inputs: +1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:163766 +1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164040 +1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164320 +1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164584 +1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164849 +1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165118 +1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165389 +Unlocks: +0:SIG(0) +1:SIG(0) +2:SIG(0) +3:SIG(0) +4:SIG(0) +5:SIG(0) +6:SIG(0) +Outputs: +7000:0:SIG(98wxzS683Tc1WWm1YxpL5WpxS7wBa1mZBccKSsYpaant) +28:0:SIG(51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2) +Comment: Panier mixte plus 40 pommes merci +7o/yIh0BNSAv5pNmHz04uUBl8TuP2s4HRFMtKeGFQfXNYJPUyJTP/dj6hdrgKtJkm5dCfbxT4KRy6wJf+dj1Cw==", + ) + .expect("Fail to parse tx1"); + + let tx2 = TransactionDocumentParser::parse( + "Version: 10 +Type: Transaction +Currency: g1 +Blockstamp: 165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2 +Locktime: 0 +Issuers: +3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX +Inputs: +1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:148827 +1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149100 +1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149370 +1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149664 +1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149943 +1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:150222 +Unlocks: +0:SIG(0) +1:SIG(0) +2:SIG(0) +3:SIG(0) +4:SIG(0) +5:SIG(0) +Outputs: +6000:0:SIG(AopwTfXhj8VqZReFJYGGWnoWnXNj3RgaqFcGGywXpZrD) +12:0:SIG(3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX) +Comment: En reglement de tes bons bocaux de fruits et legumes +nxr4exGrt16jteN9ZX3XZPP9l+X0OUbZ1o/QjE1hbWQNtVU3HhH9SJoEvNj2iVl3gCRr9u2OA9uj9vCyUDyjAg== +", + ) + .expect("Fail to parse tx2"); + + let mut block = BlockDocumentV10 { + nonce: 10_300_000_090_296, + version: 10, + number: BlockNumber(165_647), + pow_min: 90, + time: 1_540_633_175, + median_time: 1_540_627_811, + members_count: 1402, + monetary_mass: 386_008_811, + unit_base: 0, + issuers_count: 37, + issuers_frame: 186, + issuers_frame_var: 0, + currency: CurrencyName(String::from("g1")), + issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58("A4pc9Uuk4NXkWG8CibicjjPpEPdiup1mhjMoRWUZsonq").unwrap())], + signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64("2Z/+9ADdZvHXs19YR8+qDzgfl8WJlBG5PcbFvBG9TOuUJbjAdxhcgxrFrSRIABGWcCrIgLkB805fZVLP8jOjBA==").unwrap())], + hash: None, + parameters: None, + previous_hash: Some(Hash::from_hex("000003E78FA4133F2C13B416F330C8DFB5A41EB87E37190615DB334F2C914A51").expect("fail to parse previous_hash")), + previous_issuer: Some(PubKey::Ed25519(ed25519::PublicKey::from_base58("8NmGZmGjL1LUgJQRg282yQF7KTdQuRNAg8QfSa2qvd65").unwrap())), + inner_hash: None,//Some(Hash::from_hex("3B49ECC1475549CFD94CA7B399311548A0FD0EC93C8EDD5670DAA5A958A41846").expect("fail to parse inner_hash")), + dividend: None, + identities: vec![], + joiners: vec![], + actives: vec![ms1], + leavers: vec![], + revoked: vec![], + excluded: vec![], + certifications: vec![], + transactions: vec![TxDocOrTxHash::TxDoc(Box::new(tx1)), TxDocOrTxHash::TxDoc(Box::new(tx2))], + }; + // test inner_hash computation + block.generate_inner_hash(); + println!("{}", block.generate_compact_text()); + assert_eq!( + block + .inner_hash + .expect("tests::generate_and_verify_block_2: Try to get inner_hash of an uncompleted or reduce block !") + .to_hex(), + "3B49ECC1475549CFD94CA7B399311548A0FD0EC93C8EDD5670DAA5A958A41846" + ); + // test generate_compact_text() + let block_compact_text = block.generate_compact_text(); + assert_eq!( + block_compact_text, + "Version: 10\nType: Block\nCurrency: g1\nNumber: 165647\nPoWMin: 90\nTime: 1540633175\nMedianTime: 1540627811\nUnitBase: 0\nIssuer: A4pc9Uuk4NXkWG8CibicjjPpEPdiup1mhjMoRWUZsonq\nIssuersFrame: 186\nIssuersFrameVar: 0\nDifferentIssuersCount: 37\nPreviousHash: 000003E78FA4133F2C13B416F330C8DFB5A41EB87E37190615DB334F2C914A51\nPreviousIssuer: 8NmGZmGjL1LUgJQRg282yQF7KTdQuRNAg8QfSa2qvd65\nMembersCount: 1402\nIdentities:\nJoiners:\nActives:\n4VZkro3N7VonygybESHngKUABA6gSrbW77Ktb94zE969:gvaZ1QnJf8FjjRDJ0cYusgpBgQ8r0NqEz39BooH6DtIrgX+WTeXuLSnjZDl35VCBjokvyjry+v0OkTT8FKpABA==:165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2:74077-0000022816648B2F7801E059F67CCD0C023FF0ED84459D52C70494D74DDCC6F6:piaaf31\nLeavers:\nRevoked:\nExcluded:\nCertifications:\nTransactions:\nTX:10:1:7:7:2:1:0\n165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2\n51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:163766\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164040\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164320\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164584\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164849\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165118\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165389\n0:SIG(0)\n1:SIG(0)\n2:SIG(0)\n3:SIG(0)\n4:SIG(0)\n5:SIG(0)\n6:SIG(0)\n7000:0:SIG(98wxzS683Tc1WWm1YxpL5WpxS7wBa1mZBccKSsYpaant)\n28:0:SIG(51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2)\nPanier mixte plus 40 pommes merci\n7o/yIh0BNSAv5pNmHz04uUBl8TuP2s4HRFMtKeGFQfXNYJPUyJTP/dj6hdrgKtJkm5dCfbxT4KRy6wJf+dj1Cw==\nTX:10:1:6:6:2:1:0\n165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2\n3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:148827\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149100\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149370\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149664\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149943\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:150222\n0:SIG(0)\n1:SIG(0)\n2:SIG(0)\n3:SIG(0)\n4:SIG(0)\n5:SIG(0)\n6000:0:SIG(AopwTfXhj8VqZReFJYGGWnoWnXNj3RgaqFcGGywXpZrD)\n12:0:SIG(3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX)\nEn reglement de tes bons bocaux de fruits et legumes\nnxr4exGrt16jteN9ZX3XZPP9l+X0OUbZ1o/QjE1hbWQNtVU3HhH9SJoEvNj2iVl3gCRr9u2OA9uj9vCyUDyjAg==\nInnerHash: 3B49ECC1475549CFD94CA7B399311548A0FD0EC93C8EDD5670DAA5A958A41846\nNonce: " + ); + // Test signature validity + assert_eq!(block.verify_signatures(), VerificationResult::Valid()); + // Test hash computation + block.generate_hash(); + assert_eq!( + block + .hash + .expect("Try to get hash of an uncompleted or reduce block !") + .0 + .to_hex(), + "000002026E32A3D649B34968AAF9D03C4F19A5954229C54A801BBB1CD216B230" + ); + } +} diff --git a/lib/tools/documents/src/documents/certification.rs b/lib/tools/documents/src/documents/certification.rs index b853ade4291b84f06b9ad2fcaf3322cbfe7a86ee..9726e78e6149f532e0bd79ac1cca74bbc8863a24 100644 --- a/lib/tools/documents/src/documents/certification.rs +++ b/lib/tools/documents/src/documents/certification.rs @@ -159,7 +159,6 @@ impl CertificationDocument { impl Document for CertificationDocument { type PublicKey = PubKey; - type CurrencyType = str; fn version(&self) -> u16 { 10 diff --git a/lib/tools/documents/src/documents/identity.rs b/lib/tools/documents/src/documents/identity.rs index 15cb0135894126b596220977d7c96c28d4b0755a..f91bed48de217fecfbcb2cf0bdfbee633508f3f1 100644 --- a/lib/tools/documents/src/documents/identity.rs +++ b/lib/tools/documents/src/documents/identity.rs @@ -88,7 +88,6 @@ impl IdentityDocument { impl Document for IdentityDocument { type PublicKey = PubKey; - type CurrencyType = str; fn version(&self) -> u16 { 10 diff --git a/lib/tools/documents/src/documents/membership.rs b/lib/tools/documents/src/documents/membership.rs index 528772f56f0b535d3f5d8856957431cf91f28423..90810d8ee11d165fb405ac53e80bc5ef618d9724 100644 --- a/lib/tools/documents/src/documents/membership.rs +++ b/lib/tools/documents/src/documents/membership.rs @@ -141,7 +141,6 @@ impl MembershipDocument { impl Document for MembershipDocument { type PublicKey = PubKey; - type CurrencyType = str; fn version(&self) -> u16 { 10 diff --git a/lib/tools/documents/src/documents/mod.rs b/lib/tools/documents/src/documents/mod.rs index c2535657228556b52ac7c2f1074cad44bc1c08fe..db92485f286c3164b151509b23c9e14490c568c7 100644 --- a/lib/tools/documents/src/documents/mod.rs +++ b/lib/tools/documents/src/documents/mod.rs @@ -39,7 +39,7 @@ pub mod transaction; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum DUBPDocument { /// Block document. - Block(Box<BlockDocument>), + Block(Box<BlockDocumentV10>), /// Transaction document. Transaction(Box<TransactionDocument>), @@ -61,7 +61,7 @@ pub enum DUBPDocument { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum DUBPDocumentStr { /// Block document (not yet implemented) - Block(Box<BlockDocumentStringified>), + Block(Box<BlockDocumentV10Stringified>), /// Transaction document. Transaction(Box<TransactionDocumentStringified>), @@ -172,7 +172,6 @@ mod tests { impl Document for PlainTextDocument { type PublicKey = PubKey; - type CurrencyType = str; fn version(&self) -> u16 { unimplemented!() diff --git a/lib/tools/documents/src/documents/revocation.rs b/lib/tools/documents/src/documents/revocation.rs index 37ee248f6f4ccefc8fa64d005b1dec5356d7febe..9d64bbb88b0a8c553fe01faf69a54ae3571b0eca 100644 --- a/lib/tools/documents/src/documents/revocation.rs +++ b/lib/tools/documents/src/documents/revocation.rs @@ -127,7 +127,6 @@ impl RevocationDocument { impl Document for RevocationDocument { type PublicKey = PubKey; - type CurrencyType = str; fn version(&self) -> u16 { 10 diff --git a/lib/tools/documents/src/documents/transaction.rs b/lib/tools/documents/src/documents/transaction.rs index 3f237bed6a6bafc730d7a8d2b84b6a1e92ebde7a..c293fa75bff576ee2c8727f36b15255a336c95b9 100644 --- a/lib/tools/documents/src/documents/transaction.rs +++ b/lib/tools/documents/src/documents/transaction.rs @@ -648,7 +648,6 @@ impl TransactionDocument { impl Document for TransactionDocument { type PublicKey = PubKey; - type CurrencyType = str; fn version(&self) -> u16 { 10 diff --git a/lib/tools/documents/src/lib.rs b/lib/tools/documents/src/lib.rs index 282c50f61560c24d5d5fb297150bbb7b96b41cc1..e0778c898b68fc731cc9f3435fd6a8804b4319ab 100644 --- a/lib/tools/documents/src/lib.rs +++ b/lib/tools/documents/src/lib.rs @@ -152,26 +152,46 @@ impl Debug for BlockHash { pub trait Document: Debug + Clone + PartialEq + Eq { /// Type of the `PublicKey` used by the document. type PublicKey: PublicKey; - /// Data type of the currency code used by the document. - type CurrencyType: ?Sized; - /// Get document version. - fn version(&self) -> u16; - - /// Get document currency. - fn currency(&self) -> &Self::CurrencyType; + /// Get document as bytes for signature verification. + fn as_bytes(&self) -> &[u8]; /// Get document blockstamp fn blockstamp(&self) -> Blockstamp; + /// Get document currency name. + fn currency(&self) -> &str; + /// Iterate over document issuers. fn issuers(&self) -> &Vec<Self::PublicKey>; + /// Some documents do not directly store the sequence of bytes that will be signed but generate + // it on request, so these types of documents cannot provide a reference to the signed bytes. + fn no_as_bytes(&self) -> bool { + false + } + + /// Get document to bytes for signature verification. + fn to_bytes(&self) -> Vec<u8> { + self.as_bytes().to_vec() + } + /// Iterate over document signatures. fn signatures(&self) -> &Vec<<Self::PublicKey as PublicKey>::Signature>; - /// Get document as bytes for signature verification. - fn as_bytes(&self) -> &[u8]; + /// Verify one signature + #[inline] + fn verify_one_signature( + &self, + public_key: &Self::PublicKey, + signature: &<Self::PublicKey as PublicKey>::Signature, + ) -> bool { + if self.no_as_bytes() { + public_key.verify(&self.to_bytes(), signature) + } else { + public_key.verify(self.as_bytes(), signature) + } + } /// Verify signatures of document content (as text format) fn verify_signatures(&self) -> VerificationResult { @@ -183,11 +203,12 @@ pub trait Document: Debug + Clone + PartialEq + Eq { } else { let issuers = self.issuers(); let signatures = self.signatures(); + let mismatches: Vec<_> = issuers .iter() .zip(signatures) .enumerate() - .filter(|&(_, (key, signature))| !key.verify(self.as_bytes(), signature)) + .filter(|&(_, (key, signature))| !self.verify_one_signature(key, signature)) .map(|(i, _)| i) .collect(); @@ -198,6 +219,9 @@ pub trait Document: Debug + Clone + PartialEq + Eq { } } } + + /// Get document version. + fn version(&self) -> u16; } /// List of possible results for signature verification. diff --git a/lib/tools/documents/src/parsers/blocks.rs b/lib/tools/documents/src/parsers/blocks.rs index 6f5b75eef93e5e3241804f903a4cd77cd45f86e6..8c74f377cafddb644bee542ac2b178526d7a9274 100644 --- a/lib/tools/documents/src/parsers/blocks.rs +++ b/lib/tools/documents/src/parsers/blocks.rs @@ -13,7 +13,7 @@ // 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/>. -use crate::documents::block::{BlockDocument, TxDocOrTxHash}; +use crate::documents::block::{v10::TxDocOrTxHash, BlockDocument, BlockDocumentV10}; use crate::documents::membership::MembershipType; use crate::parsers::{serde_json_value_to_pest_json_value, DefaultHasher}; use crate::*; @@ -46,7 +46,7 @@ pub fn parse_json_block(json_block: &JSONValue<DefaultHasher>) -> Result<BlockDo let block_number = get_number(json_block, "number")?.trunc() as u32; - Ok(BlockDocument { + Ok(BlockDocument::V10(BlockDocumentV10 { version: get_number(json_block, "version")?.trunc() as u32, nonce: get_u64(json_block, "nonce")?, number: BlockNumber(block_number), @@ -59,7 +59,7 @@ pub fn parse_json_block(json_block: &JSONValue<DefaultHasher>) -> Result<BlockDo .trunc() as usize, unit_base: get_number(json_block, "unitbase")?.trunc() as usize, issuers_count: get_number(json_block, "issuersCount")?.trunc() as usize, - issuers_frame: get_number(json_block, "issuersFrame")?.trunc() as isize, + issuers_frame: get_number(json_block, "issuersFrame")?.trunc() as usize, issuers_frame_var: get_number(json_block, "issuersFrameVar")?.trunc() as isize, currency: CurrencyName(currency.to_owned()), issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58(get_str( @@ -134,13 +134,13 @@ pub fn parse_json_block(json_block: &JSONValue<DefaultHasher>) -> Result<BlockDo .map(|tx| crate::parsers::transactions::parse_json_transaction(tx)) .map(|tx_result| tx_result.map(|tx_doc| TxDocOrTxHash::TxDoc(Box::new(tx_doc)))) .collect::<Result<Vec<TxDocOrTxHash>, Error>>()?, - inner_hash_and_nonce_str: "".to_owned(), - }) + })) } #[cfg(test)] mod tests { use super::*; + use crate::documents::block::BlockDocumentTrait; #[test] fn parse_empty_json_block() { @@ -180,7 +180,7 @@ mod tests { let block_json_value = json_pest_parser::parse_json_string(block_json_str) .expect("Fail to parse json block !"); assert_eq!( - BlockDocument { + BlockDocument::V10(BlockDocumentV10 { version: 10, nonce: 10200000037108, number: BlockNumber(7), @@ -231,8 +231,7 @@ mod tests { excluded: vec![], certifications: vec![], transactions: vec![], - inner_hash_and_nonce_str: "".to_owned(), - }, + }), parse_json_block(&block_json_value).expect("Fail to parse block_json_value !") ); } @@ -302,7 +301,7 @@ mod tests { let block_json_value = json_pest_parser::parse_json_string(block_json_str) .expect("Fail to parse json block !"); - let expected_block = BlockDocument { + let expected_block = BlockDocument::V10(BlockDocumentV10 { version: 10, nonce: 10100000033688, number: BlockNumber(52), @@ -353,8 +352,7 @@ mod tests { excluded: vec![], certifications: vec![], transactions: vec![TxDocOrTxHash::TxDoc(Box::new(crate::parsers::tests::first_g1_tx_doc()))], - inner_hash_and_nonce_str: "".to_owned(), - }; + }); assert_eq!( expected_block, parse_json_block(&block_json_value).expect("Fail to parse block_json_value !") diff --git a/lib/tools/documents/src/text_document_traits.rs b/lib/tools/documents/src/text_document_traits.rs index 0c4ce9146cbccc6ec6d157aa597d58dec5a0140e..806ff22a31716404c0887c1cb296ff3cdf045104 100644 --- a/lib/tools/documents/src/text_document_traits.rs +++ b/lib/tools/documents/src/text_document_traits.rs @@ -57,7 +57,7 @@ impl<D: TextDocument> CompactTextDocument for TextDocumentFormat<D> { } /// Trait for a V10 document. -pub trait TextDocument: Document<PublicKey = PubKey, CurrencyType = str> { +pub trait TextDocument: Document<PublicKey = PubKey> { /// Type of associated compact document. type CompactTextDocument_: CompactTextDocument;