Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix merge conflicts #1

Merged
merged 3 commits into from
Feb 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
target
*.iml
Cargo.lock
client.db.*
9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ keywords = [ "bitcoin" ]
readme = "README.md"
edition = "2018"

[features]
default = ["hammersbald"]

[lib]
name = "murmel"
path = "src/lib.rs"

[dependencies]
bitcoin = "0.26"
hammersbald = { version = "3.0.1", features = [ "bitcoin_support" ] }
lightning = { version ="0.0.9", optional=true }
bitcoin_hashes = "0.7"
mio = "0.6"
rand = "0.7"
log = "0.4"
Expand All @@ -29,6 +33,9 @@ futures-timer = "0.3"
serde="1"
serde_derive="1"

## optional
hammersbald = { version = "3.0.1", features = [ "bitcoin_support" ], optional=true }

[dev-dependencies]
rustc-serialize = "0.3"
hex = "0.3"
Expand Down
219 changes: 35 additions & 184 deletions src/chaindb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,177 +14,71 @@
// limitations under the License.
//
//!
//! # Blockchain DB for a node
//! # Blockchain DB API for a node
//!

use std::io;
use std::sync::{Arc, RwLock};
use std::path::Path;

use bitcoin::Network;
use bitcoin::blockdata::block::BlockHeader;
use bitcoin::consensus::encode::{Decodable, Encodable};
use bitcoin::{BlockHash, blockdata::constants::genesis_block};
use bitcoin::BlockHash;

use hammersbald::{BitcoinAdaptor, BitcoinObject, HammersbaldAPI, persistent, transient};
use hammersbald::BitcoinObject;

use crate::error::Error;
use crate::headercache::{CachedHeader, HeaderCache};
use log::{debug, info, warn, error};
use crate::headercache::CachedHeader;

use serde_derive::{Serialize, Deserialize};

/// Shared handle to a database storing the block chain
/// protected by an RwLock
pub type SharedChainDB = Arc<RwLock<ChainDB>>;

/// Database storing the block chain
pub struct ChainDB {
db: BitcoinAdaptor,
headercache: HeaderCache,
network: Network,
}

impl ChainDB {
/// Create an in-memory database instance
pub fn mem(network: Network) -> Result<ChainDB, Error> {
info!("working with in memory chain db");
let db = BitcoinAdaptor::new(transient(2)?);
let headercache = HeaderCache::new(network);
Ok(ChainDB { db, network, headercache })
}
pub type SharedChainDB = Arc<RwLock<Box<dyn ChainDB>>>;

/// Create or open a persistent database instance identified by the path
pub fn new(path: &Path, network: Network) -> Result<ChainDB, Error> {
let basename = path.to_str().unwrap().to_string();
let db = BitcoinAdaptor::new(persistent((basename.clone()).as_str(), 100, 2)?);
let headercache = HeaderCache::new(network);
Ok(ChainDB { db, network, headercache })
}
/// Blockchain DB API for a client node.
pub trait ChainDB: Send + Sync {

/// Initialize caches
pub fn init(&mut self) -> Result<(), Error> {
self.init_headers()?;
Ok(())
}
/// Initialize caches.
fn init(&mut self) -> Result<(), Error>;

/// Batch updates. Updates are permanent after finishing a batch.
pub fn batch(&mut self) -> Result<(), Error> {
self.db.batch()?;
Ok(())
}
fn batch(&mut self) -> Result<(), Error>;

fn init_headers(&mut self) -> Result<(), Error> {
if let Some(tip) = self.fetch_header_tip()? {
info!("reading stored header chain from tip {}", tip);
if self.fetch_header(tip)?.is_some() {
let mut h = tip;
while let Some(stored) = self.fetch_header(h)? {
debug!("read stored header {}", &stored.block_hash());
self.headercache.add_header_unchecked(&h, &stored);
if stored.header.prev_blockhash != BlockHash::default() {
h = stored.header.prev_blockhash;
} else {
break;
}
}
self.headercache.reverse_trunk();
info!("read {} headers", self.headercache.len());
} else {
warn!("unable to read header for tip {}", tip);
self.init_to_genesis()?;
}
} else {
info!("no header tip found");
self.init_to_genesis()?;
}
Ok(())
}
/// Store a header.
fn add_header(&mut self, header: &BlockHeader) -> Result<Option<(StoredHeader, Option<Vec<BlockHash>>, Option<Vec<BlockHash>>)>, Error>;

fn init_to_genesis(&mut self) -> Result<(), Error> {
let genesis = genesis_block(self.network).header;
if let Some((cached, _, _)) = self.headercache.add_header(&genesis)? {
info!("initialized with genesis header {}", genesis.block_hash());
self.db.put_object_by_hash(&cached.stored)?;
self.db.batch()?;
self.store_header_tip(&cached.block_hash())?;
self.db.batch()?;
} else {
error!("failed to initialize with genesis header");
return Err(Error::NoTip);
}
Ok(())
}
/// Return position of hash on trunk if hash is on trunk.
fn pos_on_trunk(&self, hash: &BlockHash) -> Option<u32>;

/// Store a header
pub fn add_header(&mut self, header: &BlockHeader) -> Result<Option<(StoredHeader, Option<Vec<BlockHash>>, Option<Vec<BlockHash>>)>, Error> {
if let Some((cached, unwinds, forward)) = self.headercache.add_header(header)? {
self.db.put_object_by_hash::<_, _>(&cached.stored)?;
if let Some(forward) = forward.clone() {
if forward.len() > 0 {
self.store_header_tip(forward.last().unwrap())?;
}
}
return Ok(Some((cached.stored, unwinds, forward)));
}
Ok(None)
}
/// Iterate trunk [from .. tip].
fn iter_trunk<'a>(&'a self, from: u32) -> Box<dyn Iterator<Item=&'a CachedHeader> + 'a>;

/// return position of hash on trunk if hash is on trunk
pub fn pos_on_trunk(&self, hash: &BlockHash) -> Option<u32> {
self.headercache.pos_on_trunk(hash)
}
/// Iterate trunk [genesis .. from] in reverse order from is the tip if not specified.
fn iter_trunk_rev<'a>(&'a self, from: Option<u32>) -> Box<dyn Iterator<Item=&'a CachedHeader> + 'a>;

/// iterate trunk [from .. tip]
pub fn iter_trunk<'a>(&'a self, from: u32) -> impl Iterator<Item=&'a CachedHeader> + 'a {
self.headercache.iter_trunk(from)
}
/// Retrieve the id of the block/header with most work.
fn header_tip(&self) -> Option<CachedHeader>;

/// iterate trunk [genesis .. from] in reverse order from is the tip if not specified
pub fn iter_trunk_rev<'a>(&'a self, from: Option<u32>) -> impl Iterator<Item=&'a CachedHeader> + 'a {
self.headercache.iter_trunk_rev(from)
}
/// Fetch a header by its id from cache.
fn get_header(&self, id: &BlockHash) -> Option<CachedHeader>;

/// retrieve the id of the block/header with most work
pub fn header_tip(&self) -> Option<CachedHeader> {
self.headercache.tip()
}
/// Fetch a header by its id from cache.
fn get_header_for_height(&self, height: u32) -> Option<CachedHeader>;

/// Fetch a header by its id from cache
pub fn get_header(&self, id: &BlockHash) -> Option<CachedHeader> {
self.headercache.get_header(id)
}
/// Locator for getheaders message.
fn header_locators(&self) -> Vec<BlockHash>;

/// Fetch a header by its id from cache
pub fn get_header_for_height(&self, height: u32) -> Option<CachedHeader> {
self.headercache.get_header_for_height(height)
}
/// Store the header id with most work.
fn store_header_tip(&mut self, tip: &BlockHash) -> Result<(), Error>;

/// locator for getheaders message
pub fn header_locators(&self) -> Vec<BlockHash> {
self.headercache.locator_hashes()
}
/// Find header id with most work.
fn fetch_header_tip(&self) -> Result<Option<BlockHash>, Error>;

/// Store the header id with most work
pub fn store_header_tip(&mut self, tip: &BlockHash) -> Result<(), Error> {
self.db.put_object_by_key(HEADER_TIP_KEY, tip)?;
Ok(())
}
/// Read header from the DB.
fn fetch_header(&self, id: BlockHash) -> Result<Option<StoredHeader>, Error>;

/// Find header id with most work
pub fn fetch_header_tip(&self) -> Result<Option<BlockHash>, Error> {
Ok(self.db.get_object_by_key::<BlockHash>(HEADER_TIP_KEY)?.map(|(_, h)| h.clone()))
}

/// Read header from the DB
pub fn fetch_header(&self, id: BlockHash) -> Result<Option<StoredHeader>, Error> {
Ok(self.db.get_object_by_hash(id)?.map(|(_, header)| header))
}

/// Shutdown db
pub fn shutdown(&mut self) {
self.db.shutdown();
debug!("shutdown chain db")
}
/// Shutdown the DB.
fn shutdown(&mut self);
}

/// A header enriched with information about its position on the blockchain
Expand All @@ -198,7 +92,6 @@ pub struct StoredHeader {
pub log2work: f64,
}

// need to implement if put_hash_keyed and get_hash_keyed should be used
impl StoredHeader {
pub fn block_hash(&self) -> BlockHash {
self.header.block_hash()
Expand All @@ -224,45 +117,3 @@ impl BitcoinObject<BlockHash> for StoredHeader {
self.block_hash()
}
}

const HEADER_TIP_KEY: &[u8] = &[0u8; 1];

#[cfg(test)]
mod test {
use bitcoin::Network;
use bitcoin::blockdata::constants::genesis_block;

use crate::chaindb::ChainDB;

#[test]
fn init_tip_header() {
let network = Network::Testnet;
let genesis_header = genesis_block(network).header;

let mut chaindb = ChainDB::mem(network).unwrap();
chaindb.init().unwrap();
chaindb.init().unwrap();

let header_tip = chaindb.header_tip();
assert!(header_tip.is_some(), "failed to get header for tip");
assert!(header_tip.unwrap().stored.block_hash().eq(&genesis_header.block_hash()))
}

#[test]
fn init_recover_if_missing_tip_header() {
let network = Network::Testnet;
let genesis_header = genesis_block(network).header;

let mut chaindb = ChainDB::mem(network).unwrap();
let missing_tip_header_hash = "6cfb35868c4465b7c289d7d5641563aa973db6a929655282a7bf95c8257f53ef".parse().unwrap();
chaindb.store_header_tip(&missing_tip_header_hash).unwrap();

chaindb.init().unwrap();

let header_tip = chaindb.header_tip();
assert!(header_tip.is_some(), "failed to get header for tip");
assert!(header_tip.unwrap().stored.block_hash().eq(&genesis_header.block_hash()))
}
}


9 changes: 6 additions & 3 deletions src/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use bitcoin::{
constants::{ServiceFlags, Network}
}
};
use crate::chaindb::{ChainDB, SharedChainDB};
use crate::hammersbald::Hammersbald;
use crate::dispatcher::Dispatcher;
use crate::dns::dns_seed;
use crate::error::Error;
Expand Down Expand Up @@ -54,6 +54,7 @@ use bitcoin::network::message::NetworkMessage;
use bitcoin::network::message::RawNetworkMessage;
use crate::p2p::BitcoinP2PConfig;
use std::time::Duration;
use crate::chaindb::SharedChainDB;

const MAX_PROTOCOL_VERSION: u32 = 70001;
const USER_AGENT: &'static str = concat!("/Murmel:", env!("CARGO_PKG_VERSION"), '/');
Expand All @@ -70,9 +71,11 @@ impl Constructor {
pub fn open_db(path: Option<&Path>, network: Network, _birth: u64) -> Result<SharedChainDB, Error> {
let mut chaindb =
if let Some(path) = path {
ChainDB::new(path, network)?
#[cfg(feature = "default")]
Hammersbald::new(path, network)?
} else {
ChainDB::mem(network)?
#[cfg(feature = "default")]
Hammersbald::mem(network)?
};
chaindb.init()?;
Ok(Arc::new(RwLock::new(chaindb)))
Expand Down
19 changes: 11 additions & 8 deletions src/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,27 @@ use bitcoin::network::constants::Network;
use log::{info, trace};
use std::net::{SocketAddr, ToSocketAddrs};

const MAIN_SEEDER: [&str;5] = [
const MAIN_SEEDER: [&str; 9] = [
"seed.bitcoin.sipa.be",
"dnsseed.bluematt.me",
"dnsseed.bitcoin.dashjr.org",
"seed.bitcoinstats.com",
"seed.btc.petertodd.org"
"seed.bitcoin.jonasschnelli.ch",
"seed.btc.petertodd.org",
"seed.bitcoin.sprovoost.nl",
"dnsseed.emzy.de",
"seed.bitcoin.wiz.biz",
];

const TEST_SEEDER: [&str;4] = [
const TEST_SEEDER: [&str; 4] = [
"testnet-seed.bitcoin.jonasschnelli.ch",
"seed.tbtc.petertodd.org",
"seed.testnet.bitcoin.sprovoost.nl",
"testnet-seed.bluematt.me"
"testnet-seed.bluematt.me",
];


pub fn dns_seed (network: Network) -> Vec<SocketAddr> {
let mut seeds = Vec::new ();
pub fn dns_seed(network: Network) -> Vec<SocketAddr> {
let mut seeds = Vec::new();
if network == Network::Bitcoin {
info!("reaching out for DNS seed...");
for seedhost in MAIN_SEEDER.iter() {
Expand Down Expand Up @@ -71,4 +74,4 @@ pub fn dns_seed (network: Network) -> Vec<SocketAddr> {
info!("received {} DNS seeds", seeds.len());
}
seeds
}
}
Loading