diff --git a/bin/darkfid2/src/tests/forks.rs b/bin/darkfid2/src/tests/forks.rs new file mode 100644 index 000000000..591797c28 --- /dev/null +++ b/bin/darkfid2/src/tests/forks.rs @@ -0,0 +1,58 @@ +/* This file is part of DarkFi (https://dark.fi) + * + * Copyright (C) 2020-2023 Dyne.org foundation + * + * 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 . + */ + +use darkfi::{blockchain::Blockchain, validator::consensus::Fork, Result}; + +#[test] +fn forks() -> Result<()> { + // Dummy records we will insert + let record0 = blake3::hash(b"Let there be dark!"); + let record1 = blake3::hash(b"Never skip brain day."); + + // Create a temporary blockchain + let blockchain = Blockchain::new(&sled::Config::new().temporary(true).open()?)?; + + // Create a fork + let fork = Fork::new(&blockchain)?; + + // Add a dummy record to fork + fork.overlay.lock().unwrap().order.insert(&[0], &[record0])?; + + // Verify blockchain doesn't contain the record + assert_eq!(blockchain.order.get(&[0], false)?, [None]); + assert_eq!(fork.overlay.lock().unwrap().order.get(&[0], true)?, [Some(record0)]); + + // Now we are going to clone the fork + let fork_clone = fork.full_clone()?; + + // Verify it cointains the original record + assert_eq!(fork_clone.overlay.lock().unwrap().order.get(&[0], true)?, [Some(record0)]); + + // Add another dummy record to cloned fork + fork_clone.overlay.lock().unwrap().order.insert(&[1], &[record1])?; + + // Verify blockchain and original fork don't contain the record + assert_eq!(blockchain.order.get(&[0], false)?, [None]); + assert_eq!(fork.overlay.lock().unwrap().order.get(&[0, 1], false)?, [Some(record0), None]); + assert_eq!( + fork_clone.overlay.lock().unwrap().order.get(&[0, 1], true)?, + [Some(record0), Some(record1)] + ); + + Ok(()) +} diff --git a/bin/darkfid2/src/tests/mod.rs b/bin/darkfid2/src/tests/mod.rs index fde18e6f0..ce32ed782 100644 --- a/bin/darkfid2/src/tests/mod.rs +++ b/bin/darkfid2/src/tests/mod.rs @@ -25,6 +25,8 @@ use url::Url; mod harness; use harness::{generate_node, Harness, HarnessConfig}; +mod forks; + async fn sync_blocks_real(ex: Arc>) -> Result<()> { init_logger(); diff --git a/src/blockchain/mod.rs b/src/blockchain/mod.rs index b7fbce605..c1a3529e4 100644 --- a/src/blockchain/mod.rs +++ b/src/blockchain/mod.rs @@ -540,6 +540,30 @@ impl BlockchainOverlay { Ok(()) } + + /// Auxiliary function to create a full clone using SledDbOverlay::clone, + /// and creating new pointers of underlying overlays. + pub fn full_clone(&self) -> Result { + let overlay = Arc::new(Mutex::new(self.overlay.lock().unwrap().clone())); + let headers = HeaderStoreOverlay::new(&overlay)?; + let blocks = BlockStoreOverlay::new(&overlay)?; + let order = BlockOrderStoreOverlay::new(&overlay)?; + let slots = SlotStoreOverlay::new(&overlay)?; + let transactions = TxStoreOverlay::new(&overlay)?; + let contracts = ContractStateStoreOverlay::new(&overlay)?; + let wasm_bincode = WasmStoreOverlay::new(&overlay)?; + + Ok(Arc::new(Mutex::new(Self { + overlay, + headers, + blocks, + order, + slots, + transactions, + contracts, + wasm_bincode, + }))) + } } /// Parse a sled record with a u64 keyin the form of a tuple (`key`, `value`). diff --git a/src/validator/consensus/mod.rs b/src/validator/consensus/mod.rs index 3e469ab89..966f84d58 100644 --- a/src/validator/consensus/mod.rs +++ b/src/validator/consensus/mod.rs @@ -95,6 +95,16 @@ impl Fork { let overlay = BlockchainOverlay::new(blockchain)?; Ok(Self { overlay, proposals: vec![] }) } + + /// Auxiliary function to create a full clone using BlockchainOverlay::full_clone. + /// Changes to this clone don't affect original record, since underlying overlay + /// is cloned and pointers have been updated to the new one. + pub fn full_clone(&self) -> Result { + let overlay = self.overlay.lock().unwrap().full_clone()?; + let proposals = self.proposals.clone(); + + Ok(Self { overlay, proposals }) + } } /// Block producer reward.