diff --git a/blockchain/dbex.rs b/blockchain/dbex.rs index e61e7537b4db5bb37c9b061300f50f3cc1fdd159..a1efe2b42252ea2364e5030fb23b999917cb81c7 100644 --- a/blockchain/dbex.rs +++ b/blockchain/dbex.rs @@ -59,7 +59,7 @@ pub fn dbex_tx(conf: &DuniterConf, query: &DBExTxQuery) { // Open databases let load_dbs_begin = SystemTime::now(); //let blocks_databases = BlocksV10DBs::open(&db_path, false); - let currency_databases = CurrencyV10DBs::open(&db_path, false); + let currency_databases = CurrencyV10DBs::<FileBackend>::open(&db_path); let wot_databases = WotsV10DBs::open(&db_path, false); let load_dbs_duration = SystemTime::now() .duration_since(load_dbs_begin) diff --git a/blockchain/lib.rs b/blockchain/lib.rs index 9a4d2e27d2c845431e30ffd2c771617bf135601d..a676c924789855f9951210589febc859696e5ef4 100644 --- a/blockchain/lib.rs +++ b/blockchain/lib.rs @@ -34,6 +34,7 @@ extern crate duniter_message; extern crate duniter_module; extern crate duniter_network; extern crate duniter_wotb; +extern crate rustbreak; extern crate serde; extern crate serde_json; extern crate sqlite; @@ -74,6 +75,7 @@ use duniter_network::{ use duniter_wotb::data::rusty::RustyWebOfTrust; use duniter_wotb::operations::file::BinaryFileFormater; use duniter_wotb::{NodeId, WebOfTrust}; +use rustbreak::backend::FileBackend; /// The blocks are requested by packet groups. This constant sets the block packet size. pub static CHUNK_SIZE: &'static u32 = &50; @@ -100,7 +102,7 @@ pub struct BlockchainModule { /// Blocks Databases pub blocks_databases: BlocksV10DBs, /// Currency databases - currency_databases: CurrencyV10DBs, + currency_databases: CurrencyV10DBs<FileBackend>, /// The block under construction pub pending_block: Option<Box<BlockDocument>>, /// Current state of all forks @@ -154,7 +156,7 @@ impl BlockchainModule { // Open databases let blocks_databases = BlocksV10DBs::open(&db_path, false); let wot_databases = WotsV10DBs::open(&db_path, false); - let currency_databases = CurrencyV10DBs::open(&db_path, false); + let currency_databases = CurrencyV10DBs::<FileBackend>::open(&db_path); // Get current blockstamp let current_blockstamp = duniter_dal::block::get_current_blockstamp(&blocks_databases) diff --git a/blockchain/sync.rs b/blockchain/sync.rs index f8ef153fb5d0a30004d94b4dab18f67d50a56d9d..5d5c29df4e1f9b0980177354b9b454686d14b90e 100644 --- a/blockchain/sync.rs +++ b/blockchain/sync.rs @@ -15,12 +15,10 @@ extern crate num_cpus; extern crate pbr; -extern crate rustbreak; extern crate sqlite; extern crate threadpool; use self::pbr::ProgressBar; -use self::rustbreak::{deser::Bincode, MemoryDatabase}; use self::threadpool::ThreadPool; use duniter_crypto::keys::*; use duniter_dal::currency_params::CurrencyParameters; @@ -30,6 +28,7 @@ use duniter_documents::{BlockHash, BlockId, Hash}; use duniter_network::NetworkBlock; use duniter_wotb::operations::file::FileFormater; use duniter_wotb::{NodeId, WebOfTrust}; +use rustbreak::{deser::Bincode, MemoryDatabase}; use std::collections::{HashMap, VecDeque}; use std::fs; use std::ops::Deref; @@ -437,7 +436,7 @@ pub fn sync_ts( let tx_job_begin = SystemTime::now(); // Open databases let db_path = duniter_conf::get_blockchain_db_path(&profile_copy, ¤cy_copy); - let databases = CurrencyV10DBs::open(&db_path, false); + let databases = CurrencyV10DBs::<FileBackend>::open(&db_path); // Listen db requets let mut all_wait_duration = Duration::from_millis(0); diff --git a/dal/lib.rs b/dal/lib.rs index 30dd41b0e785fb8e7f94a19846008cff10ccdc5f..3327c62df8bdb567fe46dc85336fcc5c346c7801 100644 --- a/dal/lib.rs +++ b/dal/lib.rs @@ -54,7 +54,7 @@ use duniter_documents::blockchain::v10::documents::transaction::*; use duniter_documents::{BlockHash, BlockId, Blockstamp, Hash, PreviousBlockstamp}; use duniter_wotb::operations::file::FileFormater; use duniter_wotb::{NodeId, WebOfTrust}; -use rustbreak::backend::{FileBackend, MemoryBackend}; +use rustbreak::backend::{Backend, FileBackend, MemoryBackend}; use rustbreak::error::{RustbreakError, RustbreakErrorKind}; use rustbreak::{deser::Bincode, Database, FileDatabase, MemoryDatabase}; use serde::de::DeserializeOwned; @@ -166,20 +166,31 @@ impl WotsV10DBs { #[derive(Debug)] /// Set of databases storing currency information -pub struct CurrencyV10DBs { +pub struct CurrencyV10DBs<B: Backend + Debug> { /// Store all DU sources - pub du_db: BinFileDB<DUsV10Datas>, + pub du_db: BinDB<DUsV10Datas, B>, /// Store all Transactions - pub tx_db: BinFileDB<TxV10Datas>, + pub tx_db: BinDB<TxV10Datas, B>, /// Store all UTXOs - pub utxos_db: BinFileDB<UTXOsV10Datas>, + pub utxos_db: BinDB<UTXOsV10Datas, B>, /// Store balances of all address (and theirs UTXOs indexs) - pub balances_db: BinFileDB<BalancesV10Datas>, + pub balances_db: BinDB<BalancesV10Datas, B>, } -impl CurrencyV10DBs { +impl CurrencyV10DBs<MemoryBackend> { + pub fn open_memory_mode() -> CurrencyV10DBs<MemoryBackend> { + CurrencyV10DBs { + du_db: open_memory_db::<DUsV10Datas>().expect("Fail to open DUsV10DB"), + tx_db: open_memory_db::<TxV10Datas>().expect("Fail to open TxV10DB"), + utxos_db: open_memory_db::<UTXOsV10Datas>().expect("Fail to open UTXOsV10DB"), + balances_db: open_memory_db::<BalancesV10Datas>().expect("Fail to open BalancesV10DB"), + } + } +} + +impl CurrencyV10DBs<FileBackend> { /// Open currency databases from their respective files - pub fn open(db_path: &PathBuf, _memory_mode: bool) -> CurrencyV10DBs { + pub fn open(db_path: &PathBuf) -> CurrencyV10DBs<FileBackend> { CurrencyV10DBs { du_db: open_db::<DUsV10Datas>(&db_path, "du.db").expect("Fail to open DUsV10DB"), tx_db: open_db::<TxV10Datas>(&db_path, "tx.db").expect("Fail to open TxV10DB"), diff --git a/dal/writers/dividend.rs b/dal/writers/dividend.rs index 363a8fa761ed97ed3055f75bbf684316d5f02a08..7f7a4a350bdc0872de0fac405f48fe6f0ba4dd1a 100644 --- a/dal/writers/dividend.rs +++ b/dal/writers/dividend.rs @@ -16,13 +16,15 @@ use duniter_crypto::keys::PubKey; use duniter_documents::blockchain::v10::documents::transaction::*; use duniter_documents::BlockId; +use rustbreak::backend::Backend; use sources::SourceAmount; use std::collections::{HashMap, HashSet}; +use std::fmt::Debug; use *; -pub fn create_du( - du_db: &BinFileDB<DUsV10Datas>, - balances_db: &BinFileDB<BalancesV10Datas>, +pub fn create_du<B: Backend + Debug>( + du_db: &BinDB<DUsV10Datas, B>, + balances_db: &BinDB<BalancesV10Datas, B>, du_amount: &SourceAmount, du_block_id: &BlockId, members: &[PubKey], diff --git a/dal/writers/requests.rs b/dal/writers/requests.rs index 992590125020235b513900be7ece2328fd8418d7..ef5ca6216a1bf9948080e0e3a60a22b24b4cf29f 100644 --- a/dal/writers/requests.rs +++ b/dal/writers/requests.rs @@ -8,7 +8,9 @@ use duniter_documents::blockchain::v10::documents::identity::IdentityDocument; use duniter_documents::Blockstamp; use duniter_wotb::NodeId; use identity::DALIdentity; +use rustbreak::backend::Backend; use sources::SourceAmount; +use std::fmt::Debug; use std::ops::Deref; use *; @@ -181,10 +183,10 @@ pub enum CurrencyDBsWriteQuery { } impl CurrencyDBsWriteQuery { - pub fn apply(&self, databases: &CurrencyV10DBs) -> Result<(), DALError> { + pub fn apply<B: Backend + Debug>(&self, databases: &CurrencyV10DBs<B>) -> Result<(), DALError> { match *self { CurrencyDBsWriteQuery::WriteTx(ref tx_doc) => { - super::transaction::apply_and_write_tx( + super::transaction::apply_and_write_tx::<B>( &databases.tx_db, &databases.utxos_db, &databases.du_db, @@ -193,7 +195,7 @@ impl CurrencyDBsWriteQuery { )?; } CurrencyDBsWriteQuery::CreateDU(ref du_amount, ref block_id, ref members) => { - super::dividend::create_du( + super::dividend::create_du::<B>( &databases.du_db, &databases.balances_db, du_amount, diff --git a/dal/writers/transaction.rs b/dal/writers/transaction.rs index 8dccbcc15c6fc91f5fa3fbacdfff5440336a5273..662dc1712148fb84e5480bf1a184612758c49c88 100644 --- a/dal/writers/transaction.rs +++ b/dal/writers/transaction.rs @@ -14,7 +14,9 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. use duniter_documents::blockchain::v10::documents::transaction::*; +use rustbreak::backend::Backend; use sources::{SourceAmount, SourceIndexV10, UTXOIndexV10, UTXOV10}; +use std::fmt::Debug; use *; #[derive(Debug, Copy, Clone)] @@ -35,11 +37,11 @@ pub struct DALTxV10 { sources_destroyed: HashSet<UTXOIndexV10>, } -pub fn apply_and_write_tx( - tx_db: &BinFileDB<TxV10Datas>, - utxos_db: &BinFileDB<UTXOsV10Datas>, - dus_db: &BinFileDB<DUsV10Datas>, - balances_db: &BinFileDB<BalancesV10Datas>, +pub fn apply_and_write_tx<B: Backend + Debug>( + tx_db: &BinDB<TxV10Datas, B>, + utxos_db: &BinDB<UTXOsV10Datas, B>, + dus_db: &BinDB<DUsV10Datas, B>, + balances_db: &BinDB<BalancesV10Datas, B>, tx_doc: &TransactionDocument, ) -> Result<(), DALError> { let mut tx_doc = tx_doc.clone(); @@ -112,11 +114,43 @@ pub fn apply_and_write_tx( } new_balances_consumed_adress .push((conditions.clone(), (new_balance, new_sources_index))); + } else { + panic!("Apply Tx : try to consume a source, but the owner address is not found in balances db : {:?}", conditions) } } new_balances_consumed_adress })?; + // Write new balance of consumed adress + balances_db.write(|db| { + for (conditions, (balance, sources_index)) in new_balances_consumed_adress { + db.insert(conditions, (balance, sources_index)); + } + })?; + // Remove consumed sources + for source_index in consumed_sources.keys() { + if let SourceIndexV10::UTXO(utxo_index) = source_index { + utxos_db.write(|db| { + db.remove(utxo_index); + })?; + } else if let SourceIndexV10::DU(pubkey, block_id) = source_index { + let mut pubkey_dus: HashSet<BlockId> = + dus_db.read(|db| db.get(&pubkey).cloned().unwrap_or_default())?; + pubkey_dus.remove(block_id); + dus_db.write(|db| { + db.insert(*pubkey, pubkey_dus); + })?; + } + } // Index created sources + /*let mut created_utxos: Vec<UTXOV10> = Vec::new(); + let mut output_index = 0; + for output in tx_doc.get_outputs() { + created_utxos.push(UTXOV10( + UTXOIndexV10(tx_hash, TxIndex(output_index)), + output.clone(), + )); + output_index += 1; + }*/ let created_utxos: Vec<UTXOV10> = tx_doc .get_outputs() .iter() @@ -152,32 +186,14 @@ pub fn apply_and_write_tx( } new_balances_supplied_adress })?; - // Remove consumed sources - for source_index in consumed_sources.keys() { - if let SourceIndexV10::UTXO(utxo_index) = source_index { - utxos_db.write(|db| { - db.remove(utxo_index); - })?; - } else if let SourceIndexV10::DU(pubkey, block_id) = source_index { - let mut pubkey_dus: HashSet<BlockId> = - dus_db.read(|db| db.get(&pubkey).cloned().unwrap_or_default())?; - pubkey_dus.remove(block_id); - dus_db.write(|db| { - db.insert(*pubkey, pubkey_dus); - })?; - } - } // Insert created UTXOs utxos_db.write(|db| { for utxo_v10 in created_utxos { db.insert(utxo_v10.0, utxo_v10.1); } })?; - // Write new balance of consumed adress and supplied adress + // Write new balance of supplied adress balances_db.write(|db| { - for (conditions, (balance, sources_index)) in new_balances_consumed_adress { - db.insert(conditions, (balance, sources_index)); - } for (conditions, (balance, sources_index)) in new_balances_supplied_adress { db.insert(conditions, (balance, sources_index)); } @@ -195,3 +211,127 @@ pub fn apply_and_write_tx( })?; Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use duniter_documents::blockchain::{Document, DocumentBuilder, VerificationResult}; + + fn build_first_tx_of_g1() -> TransactionDocument { + let pubkey = PubKey::Ed25519( + ed25519::PublicKey::from_base58("2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ") + .unwrap(), + ); + let sig = Sig::Ed25519(ed25519::Signature::from_base64( + "fAH5Gor+8MtFzQZ++JaJO6U8JJ6+rkqKtPrRr/iufh3MYkoDGxmjzj6jCADQL+hkWBt8y8QzlgRkz0ixBcKHBw==", + ).unwrap()); + let block = Blockstamp::from_string( + "50-00001DAA4559FEDB8320D1040B0F22B631459F36F237A0D9BC1EB923C12A12E7", + ).unwrap(); + let builder = TransactionDocumentBuilder { + currency: "g1", + blockstamp: &block, + locktime: &0, + issuers: &vec![pubkey], + inputs: &vec![ + TransactionInput::parse_from_str( + "1000:0:D:2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ:1", + ).expect("fail to parse input !"), + ], + unlocks: &vec![ + TransactionInputUnlocks::parse_from_str("0:SIG(0)") + .expect("fail to parse unlock !"), + ], + outputs: &vec![ + TransactionOutput::parse_from_str( + "1:0:SIG(Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm)", + ).expect("fail to parse output !"), + TransactionOutput::parse_from_str( + "999:0:SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ)", + ).expect("fail to parse output !"), + ], + comment: "TEST", + }; + builder.build_with_signature(vec![sig]) + } + + #[test] + fn apply_one_tx() { + // Get document of first g1 transaction + let tx_doc = build_first_tx_of_g1(); + assert_eq!(tx_doc.verify_signatures(), VerificationResult::Valid()); + // Get pubkey of receiver + let tortue_pubkey = PubKey::Ed25519( + ed25519::PublicKey::from_base58("Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm") + .unwrap(), + ); + // Open currencys_db in memory mode + let currency_dbs = CurrencyV10DBs::open_memory_mode(); + // Create first g1 DU for cgeek and tortue + writers::dividend::create_du( + ¤cy_dbs.du_db, + ¤cy_dbs.balances_db, + &SourceAmount(TxAmount(1000), TxBase(0)), + &BlockId(1), + &vec![tx_doc.issuers()[0], tortue_pubkey], + ).expect("Fail to create first g1 DU !"); + // Check members balance + let cgeek_new_balance = currency_dbs + .balances_db + .read(|db| { + db.get(&TransactionOutputConditionGroup::Single( + TransactionOutputCondition::Sig(tx_doc.issuers()[0]), + )).cloned() + }) + .expect("Fail to read cgeek new balance") + .expect("Error : cgeek is not referenced in balances_db !"); + assert_eq!(cgeek_new_balance.0, SourceAmount(TxAmount(1000), TxBase(0))); + let tortue_new_balance = currency_dbs + .balances_db + .read(|db| { + db.get(&TransactionOutputConditionGroup::Single( + TransactionOutputCondition::Sig(tortue_pubkey), + )).cloned() + }) + .expect("Fail to read receiver new balance") + .expect("Error : receiver is not referenced in balances_db !"); + assert_eq!( + tortue_new_balance.0, + SourceAmount(TxAmount(1000), TxBase(0)) + ); + // Apply first g1 transaction + apply_and_write_tx( + ¤cy_dbs.tx_db, + ¤cy_dbs.utxos_db, + ¤cy_dbs.du_db, + ¤cy_dbs.balances_db, + &tx_doc, + ).expect("Fail to apply first g1 tx"); + // Check issuer new balance + let cgeek_new_balance = currency_dbs + .balances_db + .read(|db| { + db.get(&TransactionOutputConditionGroup::Single( + TransactionOutputCondition::Sig(tx_doc.issuers()[0]), + )).cloned() + }) + .expect("Fail to read cgeek new balance") + .expect("Error : cgeek is not referenced in balances_db !"); + assert_eq!(cgeek_new_balance.0, SourceAmount(TxAmount(999), TxBase(0))); + + // Check receiver new balance + let receiver_new_balance = currency_dbs + .balances_db + .read(|db| { + db.get(&TransactionOutputConditionGroup::Single( + TransactionOutputCondition::Sig(tortue_pubkey), + )).cloned() + }) + .expect("Fail to read receiver new balance") + .expect("Error : receiver is not referenced in balances_db !"); + assert_eq!( + receiver_new_balance.0, + SourceAmount(TxAmount(1001), TxBase(0)) + ); + } +} diff --git a/documents/blockchain/v10/documents/block.rs b/documents/blockchain/v10/documents/block.rs index f653956c425d96b3416db6b0ee79603083e48292..5cc23ba33cddede3460126e7457a694bee571d9d 100644 --- a/documents/blockchain/v10/documents/block.rs +++ b/documents/blockchain/v10/documents/block.rs @@ -264,9 +264,13 @@ pub struct BlockDocument { impl BlockDocument { /// Return previous blockstamp pub fn previous_blockstamp(&self) -> Blockstamp { - Blockstamp { - id: BlockId(self.number.0 - 1), - hash: BlockHash(self.previous_hash), + if self.number.0 > 0 { + Blockstamp { + id: BlockId(self.number.0 - 1), + hash: BlockHash(self.previous_hash), + } + } else { + Blockstamp::default() } } /// Compute inner hash @@ -537,7 +541,6 @@ mod tests { assert_eq!( block .inner_hash - .hash .expect("Try to get inner_hash of an uncompleted or reduce block !") .to_hex(), "95948AC4D45E46DA07CE0713EDE1CE0295C227EE4CA5557F73F56B7DD46FE89C" @@ -578,7 +581,6 @@ Nonce: " block.compute_hash(); assert_eq!( block - .hash .hash .expect("Try to get hash of an uncompleted or reduce block !") .0