diff --git a/Cargo.lock b/Cargo.lock index 6dd32815..31a28ee3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,23 +95,73 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bech32" +version = "0.10.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cc1dec4c25e5a78c52802eda8e2adf0d2aca57ffc536326b75c0e531ea0a9b" + [[package]] name = "bitcoin" version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" dependencies = [ - "bech32", - "bitcoin_hashes", - "secp256k1", + "bech32 0.9.1", + "bitcoin_hashes 0.11.0", + "secp256k1 0.24.1", +] + +[[package]] +name = "bitcoin" +version = "0.30.0" +source = "git+https://github.com/vincenzopalazzo/rust-bitcoin.git?branch=macros/calculate-next-work-requried#003673d1cb0495b80fcea5e01333eba9594d8e20" +dependencies = [ + "bech32 0.10.0-alpha", + "bitcoin-internals", + "bitcoin_hashes 0.13.0", + "hex-conservative", + "hex_lit", + "secp256k1 0.27.0", ] +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin-private" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" + [[package]] name = "bitcoin_hashes" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" +[[package]] +name = "bitcoin_hashes" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" +dependencies = [ + "bitcoin-private", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals", + "hex-conservative", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -156,7 +206,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a4b7b647ac3591f204635a704dcbf23b74f0b2be60dd23c9b2aa63caac4eb19" dependencies = [ "aes-ctr", - "bitcoin", + "bitcoin 0.29.2", "hidapi", ] @@ -254,6 +304,18 @@ dependencies = [ "libc", ] +[[package]] +name = "hex-conservative" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + [[package]] name = "hidapi" version = "1.4.2" @@ -374,8 +436,7 @@ dependencies = [ name = "nakamoto-common" version = "0.4.0" dependencies = [ - "bitcoin", - "bitcoin_hashes", + "bitcoin 0.30.0", "fastrand", "log", "microserde", @@ -661,9 +722,19 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff55dc09d460954e9ef2fa8a7ced735a964be9981fd50e870b2b3b0705e14964" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.11.0", "rand", - "secp256k1-sys", + "secp256k1-sys 0.6.1", +] + +[[package]] +name = "secp256k1" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" +dependencies = [ + "bitcoin_hashes 0.12.0", + "secp256k1-sys 0.8.1", ] [[package]] @@ -675,6 +746,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +dependencies = [ + "cc", +] + [[package]] name = "signal-hook" version = "0.3.14" diff --git a/chain/src/block.rs b/chain/src/block.rs index 279ebc14..0f4de162 100644 --- a/chain/src/block.rs +++ b/chain/src/block.rs @@ -2,7 +2,7 @@ pub mod cache; pub mod store; -pub use nakamoto_common::bitcoin::blockdata::block::{Block, BlockHeader}; +pub use nakamoto_common::bitcoin::blockdata::block::{Block, Header as BlockHeader}; pub use nakamoto_common::bitcoin::blockdata::transaction::Transaction; pub use nakamoto_common::bitcoin::hash_types::BlockHash; pub use nakamoto_common::block::tree::*; diff --git a/chain/src/block/cache.rs b/chain/src/block/cache.rs index 8312782a..cfb68371 100644 --- a/chain/src/block/cache.rs +++ b/chain/src/block/cache.rs @@ -11,15 +11,16 @@ use std::cmp::Ordering; use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; use std::ops::ControlFlow; +use common::bitcoin::block::ValidationError; +use common::bitcoin::CompactTarget; +use common::block::Target; use nakamoto_common as common; use nakamoto_common::bitcoin; -use nakamoto_common::bitcoin::blockdata::block::BlockHeader; +use nakamoto_common::bitcoin::blockdata::block::Header as BlockHeader; use nakamoto_common::bitcoin::consensus::params::Params; use nakamoto_common::bitcoin::hash_types::BlockHash; -use nakamoto_common::bitcoin::network::constants::Network; -use nakamoto_common::bitcoin::util::BitArray; +use nakamoto_common::bitcoin::network::Network; -use nakamoto_common::bitcoin::util::uint::Uint256; use nakamoto_common::block::tree::{self, BlockReader, BlockTree, Branch, Error, ImportResult}; use nakamoto_common::block::{ self, @@ -76,7 +77,7 @@ pub struct BlockCache { checkpoints: BTreeMap, params: Params, /// Total cumulative work on the active chain. - chainwork: Uint256, + chainwork: Work, store: S, } @@ -240,20 +241,16 @@ impl> BlockCache { // // We do this because it's cheap to verify and prevents flooding attacks. let target = header.target(); - match header.validate_pow(&target) { + match header.validate_pow(target) { Ok(_) => { - let limit = self.params.pow_limit; + let limit = self.params.pow_limit.to_target(); if target > limit { return Err(Error::InvalidBlockTarget(target, limit)); } } - Err(bitcoin::util::Error::BlockBadProofOfWork) => { + Err(ValidationError::BadProofOfWork) => { return Err(Error::InvalidBlockPoW); } - Err(bitcoin::util::Error::BlockBadTarget) => unreachable! { - // The only way to get a 'bad target' error is to pass a different target - // than the one specified in the header. - }, Err(_) => unreachable! { // We've handled all possible errors above. }, @@ -288,7 +285,9 @@ impl> BlockCache { let mut best_branch = None; let mut best_hash = tip.hash(); - let mut best_work = Uint256::zero(); + // FIXME: this need to be a Work by network? or the min is equal for + // every network? + let mut best_work = Work::MAINNET_MIN; for branch in candidates.iter() { // Total work included in this branch. @@ -453,15 +452,12 @@ impl> BlockCache { self.next_difficulty_target(tip.height, tip.time, tip.target(), &self.params) }; - let target = BlockHeader::u256_from_compact_target(compact_target); + let target = Target::from_compact(CompactTarget::from_consensus(compact_target)); - match header.validate_pow(&target) { - Err(bitcoin::util::Error::BlockBadProofOfWork) => { + match header.validate_pow(target) { + Err(ValidationError::BadProofOfWork) => { return Err(Error::InvalidBlockPoW); } - Err(bitcoin::util::Error::BlockBadTarget) => { - return Err(Error::InvalidBlockTarget(header.target(), target)); - } Err(_) => unreachable!(), Ok(_) => {} } @@ -497,10 +493,10 @@ impl> BlockCache { let pow_limit_bits = block::pow_limit_bits(¶ms.network); for (height, header) in self.iter().rev() { - if header.bits != pow_limit_bits + if header.bits.to_consensus() != pow_limit_bits || height % self.params.difficulty_adjustment_interval() == 0 { - return header.bits; + return header.bits.to_consensus(); } } pow_limit_bits @@ -682,7 +678,7 @@ impl> BlockReader for BlockCache { } /// Get the "chainwork", ie. the total accumulated proof-of-work of the active chain. - fn chain_work(&self) -> Uint256 { + fn chain_work(&self) -> Work { self.chainwork } diff --git a/chain/src/block/cache/test.rs b/chain/src/block/cache/test.rs index 6b498c23..14c4da3b 100644 --- a/chain/src/block/cache/test.rs +++ b/chain/src/block/cache/test.rs @@ -1,9 +1,10 @@ use super::BlockCache; +use nakamoto_common::bitcoin::block::Version; use nakamoto_common::bitcoin_hashes::Hash; use nakamoto_common::block::time::{AdjustedTime, Clock, LocalTime}; use nakamoto_common::block::tree::{BlockReader, BlockTree, Error, ImportResult}; -use nakamoto_common::block::{BlockTime, Height, Target}; +use nakamoto_common::block::{BlockTime, Height, Target, Work}; use nakamoto_common::nonempty::NonEmpty; use nakamoto_test::assert_matches; @@ -15,30 +16,24 @@ use crate::block::store::{self, Store}; use std::collections::{BTreeMap, VecDeque}; use std::iter; use std::net; +use std::str::FromStr; use std::sync::{Arc, RwLock}; use quickcheck::{Arbitrary, Gen}; use quickcheck_macros::quickcheck; -use nakamoto_common::bitcoin; -use nakamoto_common::bitcoin::blockdata::block::BlockHeader; +use nakamoto_common::bitcoin::blockdata::block::Header as BlockHeader; use nakamoto_common::bitcoin::blockdata::constants; use nakamoto_common::bitcoin::consensus::params::Params; use nakamoto_common::bitcoin::hash_types::{BlockHash, TxMerkleNode}; +use nakamoto_common::bitcoin::{self, CompactTarget}; use nakamoto_common::bitcoin_hashes::hex::FromHex; -use nakamoto_common::bitcoin::util::uint::Uint256; - /// Sun, 12 Jul 2020 15:03:05 +0000. const LOCAL_TIME: LocalTime = LocalTime::from_secs(1594566185); /// Lowest possible difficulty. -const TARGET: Uint256 = Uint256([ - 0xffffffffffffffffu64, - 0xffffffffffffffffu64, - 0xffffffffffffffffu64, - 0x7fffffffffffffffu64, -]); +const TARGET: Target = Target::ZERO; /// Target block time (1 minute). const TARGET_SPACING: BlockTime = 60 * 10; /// Target time span (1 hour). @@ -92,7 +87,7 @@ impl BlockReader for HeightCache { self.headers.get(&height) } - fn chain_work(&self) -> Uint256 { + fn chain_work(&self) -> Work { unimplemented!() } @@ -183,7 +178,7 @@ mod arbitrary { height, header.block_hash(), header.time, - header.bits, + header.bits.to_consensus(), header.nonce )?; } @@ -259,7 +254,7 @@ mod arbitrary { header.block_hash(), header.prev_blockhash, header.time, - header.bits, + header.bits.to_consensus(), )?; } Ok(()) @@ -276,10 +271,10 @@ fn arbitrary_header( let delta = u32::arbitrary(g) % (TARGET_SPACING * 2) + TARGET_SPACING / 2; let time = prev_time + delta; - let bits = BlockHeader::compact_target_from_u256(target); + let bits = target.to_compact_lossy(); let mut header = BlockHeader { - version: 1, + version: Version::from_consensus(1), time, nonce: 0, bits, @@ -369,14 +364,15 @@ fn prop_invalid_block_target(import: BlockImport) -> bool { assert!(cache.clone().import_block(header, &ctx).is_ok()); let header = BlockHeader { - bits: genesis.bits - 1, + bits: CompactTarget::from_consensus(genesis.bits.to_consensus() - 1), ..header }; + let result = CompactTarget::from_consensus(genesis.bits.to_consensus() - 1); matches! { cache.import_block(header, &ctx).err(), Some(Error::InvalidBlockTarget(actual, expected)) - if actual == BlockHeader::u256_from_compact_target(genesis.bits - 1) + if actual == Target::from_compact(result) && expected == genesis.target() } } @@ -390,18 +386,13 @@ fn test_invalid_orphan_block_target() { let params = Params::new(network); // A lower difficulty target than expected. - let invalid_bits: Uint256 = Uint256([ - 0xffffffffffffffffu64, - 0xffffffffffffffffu64, - 0xffffffffffffffffu64, - 0x9fffffffffffffffu64, - ]); + let invalid_bits = Target::ZERO; let mut cache = BlockCache::from(store, params.clone(), &[]).unwrap(); // Some arbitrary previous block we don't have. let prev_blockhash = - BlockHash::from_hex("0f9188f13cb7b2c71f2a345e3a4fc328bf5bbb436012afca590b1a11466e2206") + BlockHash::from_str("0f9188f13cb7b2c71f2a345e3a4fc328bf5bbb436012afca590b1a11466e2206") .unwrap(); // A valid header. @@ -422,7 +413,7 @@ fn test_invalid_orphan_block_target() { // An invalid header. let mut header = BlockHeader { - bits: BlockHeader::compact_target_from_u256(&invalid_bits), + bits: invalid_bits.to_compact_lossy(), ..header }; block::solve(&mut header); @@ -430,13 +421,10 @@ fn test_invalid_orphan_block_target() { // Even though the header can't be connected to the main chain, we still get an error. match cache.import_block(header, &clock).unwrap_err() { Error::InvalidBlockTarget(actual, expected) => { + assert_eq!(actual.to_compact_lossy(), invalid_bits.to_compact_lossy()); assert_eq!( - BlockHeader::compact_target_from_u256(&actual), - BlockHeader::compact_target_from_u256(&invalid_bits) - ); - assert_eq!( - BlockHeader::compact_target_from_u256(&expected), - BlockHeader::compact_target_from_u256(¶ms.pow_limit) + expected.to_compact_lossy(), + params.pow_limit.to_target().to_compact_lossy(), ); } err => panic!("wrong error returned: {:?}", err), @@ -455,7 +443,7 @@ fn test_invalid_orphan_block_pow() { // Some arbitrary previous block we don't have. let prev_blockhash = - BlockHash::from_hex("0f9188f13cb7b2c71f2a345e3a4fc328bf5bbb436012afca590b1a11466e2206") + BlockHash::from_str("0f9188f13cb7b2c71f2a345e3a4fc328bf5bbb436012afca590b1a11466e2206") .unwrap(); // An invalid header. @@ -484,7 +472,7 @@ fn prop_invalid_block_pow(import: BlockImport) -> bool { let mut header = header; // Find an *invalid* nonce. - while header.validate_pow(&header.target()).is_ok() { + while header.validate_pow(header.target()).is_ok() { header.nonce += 1; } @@ -509,7 +497,7 @@ fn test_bitcoin_difficulty() { let target = cache.next_difficulty_target( height - 1, prev_time, - BlockHeader::u256_from_compact_target(prev_bits), + Target::from_compact(CompactTarget::from_consensus(prev_bits)), ¶ms, ); @@ -520,9 +508,9 @@ fn test_bitcoin_difficulty() { cache.import( height, BlockHeader { - version: 1, + version: Version::from_consensus(1), time, - bits, + bits: CompactTarget::from_consensus(bits), merkle_root: TxMerkleNode::all_zeros(), prev_blockhash: BlockHash::all_zeros(), nonce: 0, @@ -617,10 +605,10 @@ impl Tree { fn next(&self, g: &mut fastrand::Rng) -> Tree { let nonce = g.u32(..); let mut header = BlockHeader { - version: 1, + version: Version::from_consensus(1), prev_blockhash: self.hash, merkle_root: TxMerkleNode::all_zeros(), - bits: BlockHeader::compact_target_from_u256(&TARGET), + bits: TARGET.to_compact_lossy(), time: self.time + TARGET_SPACING, nonce, }; @@ -640,15 +628,15 @@ impl Tree { fn next_invalid(&self, g: &mut fastrand::Rng) -> Tree { let nonce = g.u32(..); let mut header = BlockHeader { - version: 1, + version: Version::from_consensus(1), prev_blockhash: self.hash, merkle_root: TxMerkleNode::all_zeros(), - bits: BlockHeader::compact_target_from_u256(&TARGET), + bits: TARGET.to_compact_lossy(), time: self.time + TARGET_SPACING, nonce, }; let target = header.target(); - while header.validate_pow(&target).is_ok() { + while header.validate_pow(target).is_ok() { header.nonce += 1; } @@ -1012,36 +1000,36 @@ fn test_cache_import_back_and_forth() { fn test_cache_import_equal_difficulty_blocks() { let mut headers = vec![ BlockHeader { - version: 1, - prev_blockhash: BlockHash::from_hex( + version: Version::from_consensus(1), + prev_blockhash: BlockHash::from_str( "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206", ) .unwrap(), merkle_root: TxMerkleNode::all_zeros(), time: 1296688662, - bits: 545259519, + bits: CompactTarget::from_consensus(545259519), nonce: 3705677718, }, BlockHeader { - version: 1, - prev_blockhash: BlockHash::from_hex( + version: Version::from_consensus(1), + prev_blockhash: BlockHash::from_str( "40e6856aba3aa0bab2ba97b5612dc22a485c3a583dc98a9f1cd1706dd858f623", ) .unwrap(), merkle_root: TxMerkleNode::all_zeros(), time: 1296688722, - bits: 545259519, + bits: CompactTarget::from_consensus(545259519), nonce: 3581550584, }, BlockHeader { - version: 1, - prev_blockhash: BlockHash::from_hex( + version: Version::from_consensus(1), + prev_blockhash: BlockHash::from_str( "40e6856aba3aa0bab2ba97b5612dc22a485c3a583dc98a9f1cd1706dd858f623", ) .unwrap(), merkle_root: TxMerkleNode::all_zeros(), time: 1296688722, - bits: 545259519, + bits: CompactTarget::from_consensus(545259519), nonce: 3850925874, }, ]; @@ -1060,7 +1048,7 @@ fn test_cache_import_equal_difficulty_blocks() { assert_eq!(real.tip(), model.tip()); let expected = - BlockHash::from_hex("79cdea612df7f65b541da8ff45913f472eb0bf9376e1b9e3cd2c6ce78f261954") + BlockHash::from_str("79cdea612df7f65b541da8ff45913f472eb0bf9376e1b9e3cd2c6ce78f261954") .unwrap(); assert_eq!(real.tip().0, expected); @@ -1462,7 +1450,7 @@ fn test_cache_locate_headers() { ); let unknown = - BlockHash::from_hex("0f9188f13cb7b2c71f2a345e3a4fc328bf5bbb436012afca590b1a11466e2206") + BlockHash::from_str("0f9188f13cb7b2c71f2a345e3a4fc328bf5bbb436012afca590b1a11466e2206") .unwrap(); assert_eq!( diff --git a/chain/src/block/store/io.rs b/chain/src/block/store/io.rs index 91f518f8..14240fd9 100644 --- a/chain/src/block/store/io.rs +++ b/chain/src/block/store/io.rs @@ -252,7 +252,9 @@ impl Store for File { mod test { use std::{io, iter}; - use nakamoto_common::bitcoin::TxMerkleNode; + use nakamoto_common::bitcoin::string::FromHexStr; + use nakamoto_common::bitcoin::CompactTarget; + use nakamoto_common::bitcoin::{block::Version, hash_types::TxMerkleNode}; use nakamoto_common::bitcoin_hashes::Hash; use nakamoto_common::block::BlockHash; @@ -264,10 +266,10 @@ mod test { fn store(path: &str) -> File { let tmp = tempfile::tempdir().unwrap(); let genesis = BlockHeader { - version: 1, + version: Version::from_consensus(1), prev_blockhash: BlockHash::all_zeros(), merkle_root: TxMerkleNode::all_zeros(), - bits: 0x2ffffff, + bits: CompactTarget::from_consensus(0x2ffffff), time: 39123818, nonce: 0, }; @@ -280,10 +282,10 @@ mod test { let mut store = store("headers.db"); let header = BlockHeader { - version: 1, + version: Version::from_consensus(1), prev_blockhash: store.genesis.block_hash(), merkle_root: TxMerkleNode::all_zeros(), - bits: 0x2ffffff, + bits: CompactTarget::from_consensus(0x2ffffff), time: 1842918273, nonce: 312143, }; @@ -313,10 +315,10 @@ mod test { let count = 32; let header = BlockHeader { - version: 1, + version: Version::from_consensus(1), prev_blockhash: store.genesis().block_hash(), merkle_root: TxMerkleNode::all_zeros(), - bits: 0x2ffffff, + bits: CompactTarget::from_consensus(0x2ffffff), time: 1842918273, nonce: 0, }; @@ -377,10 +379,10 @@ mod test { let count = 32; let header = BlockHeader { - version: 1, + version: Version::from_consensus(1), prev_blockhash: store.genesis().block_hash(), merkle_root: TxMerkleNode::all_zeros(), - bits: 0x2ffffff, + bits: CompactTarget::from_consensus(0x2ffffff), time: 1842918273, nonce: 0, }; @@ -409,18 +411,18 @@ mod test { let headers = &[ BlockHeader { - version: 1, + version: Version::from_consensus(1), prev_blockhash: store.genesis().block_hash(), merkle_root: TxMerkleNode::all_zeros(), - bits: 0x2ffffff, + bits: CompactTarget::from_consensus(0x2ffffff), time: 1842918273, nonce: 312143, }, BlockHeader { - version: 1, + version: Version::from_consensus(1), prev_blockhash: BlockHash::all_zeros(), merkle_root: TxMerkleNode::all_zeros(), - bits: 0x1ffffff, + bits: CompactTarget::from_consensus(0x1ffffff), time: 1842918920, nonce: 913716378, }, diff --git a/chain/src/filter.rs b/chain/src/filter.rs index 7d85adab..75d9b97d 100644 --- a/chain/src/filter.rs +++ b/chain/src/filter.rs @@ -2,4 +2,4 @@ pub mod cache; pub mod store; -pub use nakamoto_common::bitcoin::util::bip158::BlockFilter; +pub use nakamoto_common::bitcoin::bip158::BlockFilter; diff --git a/client/src/client.rs b/client/src/client.rs index c3eefdf4..c094cd67 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -21,11 +21,13 @@ use nakamoto_chain::{block::cache::BlockCache, filter::BlockFilter}; use nakamoto_common::bitcoin::network::constants::ServiceFlags; use nakamoto_common::bitcoin::network::message::NetworkMessage; use nakamoto_common::bitcoin::network::Address; -use nakamoto_common::bitcoin::util::uint::Uint256; +use nakamoto_common::bitcoin::CompactTarget; use nakamoto_common::bitcoin::Txid; use nakamoto_common::block::store::{Genesis as _, Store as _}; use nakamoto_common::block::time::{AdjustedTime, RefClock}; use nakamoto_common::block::tree::{self, BlockReader, ImportResult}; +use nakamoto_common::block::Target; +use nakamoto_common::block::Work; use nakamoto_common::block::{BlockHash, BlockHeader, Height, Transaction}; use nakamoto_common::nonempty::NonEmpty; use nakamoto_common::p2p::peer::{Source, Store as _}; @@ -482,8 +484,8 @@ impl Handle { } impl handle::Handle for Handle { - fn get_tip(&self) -> Result<(Height, BlockHeader, Uint256), handle::Error> { - let (transmit, receive) = chan::bounded::<(Height, BlockHeader, Uint256)>(1); + fn get_tip(&self) -> Result<(Height, BlockHeader, Work), handle::Error> { + let (transmit, receive) = chan::bounded::<(Height, BlockHeader, Work)>(1); self._command(Command::GetTip(transmit))?; Ok(receive.recv()?) diff --git a/client/src/event.rs b/client/src/event.rs index 1b084ab1..78975be5 100644 --- a/client/src/event.rs +++ b/client/src/event.rs @@ -584,6 +584,7 @@ mod test { use std::io; use nakamoto_common::bitcoin_hashes::Hash; + use nakamoto_common::block::{Target, Work}; use quickcheck::TestResult; use quickcheck_macros::quickcheck; @@ -787,7 +788,7 @@ mod test { let mut mock = mock::Client::new(network); let mut client = mock.handle(); - client.tip = (height, chain[height as usize].header, Default::default()); + client.tip = (height, chain[height as usize].header, Work::REGTEST_MIN); let mut spent = 0; let (watch, heights, balance) = gen::watchlist_rng(birth, chain.iter(), &mut rng); diff --git a/client/src/handle.rs b/client/src/handle.rs index 70a4fb29..fa3e68e5 100644 --- a/client/src/handle.rs +++ b/client/src/handle.rs @@ -8,13 +8,12 @@ use thiserror::Error; use nakamoto_common::bitcoin::network::constants::ServiceFlags; use nakamoto_common::bitcoin::network::Address; -use nakamoto_common::bitcoin::util::uint::Uint256; -use nakamoto_common::bitcoin::{Script, Txid}; +use nakamoto_common::bitcoin::{Script, ScriptBuf, Txid}; use nakamoto_common::bitcoin::network::message::NetworkMessage; use nakamoto_common::block::filter::BlockFilter; use nakamoto_common::block::tree::{BlockReader, ImportResult}; -use nakamoto_common::block::{self, Block, BlockHash, BlockHeader, Height, Transaction}; +use nakamoto_common::block::{self, Block, BlockHash, BlockHeader, Height, Transaction, Work}; use nakamoto_common::nonempty::NonEmpty; use nakamoto_p2p::fsm::Link; use nakamoto_p2p::fsm::{self, Command, CommandError, GetFiltersError, Peer}; @@ -66,7 +65,7 @@ impl From> for Error { pub trait Handle: Sized + Send + Sync + Clone { /// Get the tip of the active chain. Returns the height of the chain, the header, /// and the total accumulated work. - fn get_tip(&self) -> Result<(Height, BlockHeader, Uint256), Error>; + fn get_tip(&self) -> Result<(Height, BlockHeader, Work), Error>; /// Get a block header from the block header cache. fn get_block(&self, hash: &BlockHash) -> Result, Error>; /// Get a block header by height, from the block header cache. @@ -106,7 +105,7 @@ pub trait Handle: Sized + Send + Sync + Clone { fn rescan( &self, range: impl RangeBounds, - watch: impl Iterator, + watch: impl Iterator, ) -> Result<(), Error> { // TODO: Handle invalid/empty ranges. @@ -126,7 +125,7 @@ pub trait Handle: Sized + Send + Sync + Clone { /// Note that this won't trigger a rescan of any existing blocks. To avoid /// missing matching blocks, always watch scripts before sharing their /// corresponding address. - fn watch(&self, watch: impl Iterator) -> Result<(), Error> { + fn watch(&self, watch: impl Iterator) -> Result<(), Error> { self.command(Command::Watch { watch: watch.collect(), })?; diff --git a/client/src/tests/mock.rs b/client/src/tests/mock.rs index b9a93981..db320231 100644 --- a/client/src/tests/mock.rs +++ b/client/src/tests/mock.rs @@ -8,14 +8,13 @@ use nakamoto_chain::filter::BlockFilter; use nakamoto_common::bitcoin::network::constants::ServiceFlags; use nakamoto_common::bitcoin::network::message::{NetworkMessage, RawNetworkMessage}; -use nakamoto_common::bitcoin::network::Address; -use nakamoto_common::bitcoin::util::uint::Uint256; +use nakamoto_common::bitcoin::network::{Address, Magic}; use nakamoto_common::bitcoin::Txid; use nakamoto_common::block::filter::FilterHeader; use nakamoto_common::block::store::Genesis as _; use nakamoto_common::block::time::{AdjustedTime, LocalTime}; use nakamoto_common::block::tree::{self, ImportResult}; -use nakamoto_common::block::{BlockHash, BlockHeader, Height, Transaction}; +use nakamoto_common::block::{BlockHash, BlockHeader, Height, Transaction, Work}; use nakamoto_common::network::Network; use nakamoto_common::nonempty::NonEmpty; use nakamoto_common::p2p::peer::KnownAddress; @@ -137,7 +136,7 @@ impl Default for Client { #[derive(Clone)] pub struct TestHandle { - pub tip: (Height, BlockHeader, Uint256), + pub tip: (Height, BlockHeader, Work), #[allow(dead_code)] network: Network, @@ -148,7 +147,7 @@ pub struct TestHandle { } impl Handle for TestHandle { - fn get_tip(&self) -> Result<(Height, BlockHeader, Uint256), handle::Error> { + fn get_tip(&self) -> Result<(Height, BlockHeader, Work), handle::Error> { Ok(self.tip) } diff --git a/common/Cargo.toml b/common/Cargo.toml index 2a9fe19e..39fb7f40 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -11,8 +11,7 @@ edition = "2021" [dependencies] nakamoto-net = { version = "0.4.0", path = "../net" } -bitcoin = "0.29.2" -bitcoin_hashes = "0.11.0" +bitcoin = { git = "https://github.com/vincenzopalazzo/rust-bitcoin.git", branch = "macros/calculate-next-work-requried" } thiserror = "1.0" fastrand = "1.3.5" nonempty = "0.7" diff --git a/common/src/block.rs b/common/src/block.rs index 8fcf5100..72393a63 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -7,15 +7,10 @@ pub mod store; pub mod time; pub mod tree; -pub use bitcoin::blockdata::block::{Block, BlockHeader}; +pub use bitcoin::blockdata::block::{Block, Header as BlockHeader}; pub use bitcoin::blockdata::transaction::Transaction; pub use bitcoin::hash_types::BlockHash; - -/// Difficulty target of a block. -pub type Target = bitcoin::util::uint::Uint256; - -/// Block work. -pub type Work = bitcoin::util::uint::Uint256; +pub use bitcoin::pow::{Target, Work}; /// Compact difficulty bits (target) of a block. pub type Bits = u32; @@ -62,5 +57,6 @@ pub fn pow_limit_bits(network: &bitcoin::Network) -> Bits { bitcoin::Network::Testnet => 0x1d00ffff, bitcoin::Network::Regtest => 0x207fffff, bitcoin::Network::Signet => 0x1e0377ae, + _ => unreachable!(), } } diff --git a/common/src/block/filter.rs b/common/src/block/filter.rs index 2451ee29..e6b0f768 100644 --- a/common/src/block/filter.rs +++ b/common/src/block/filter.rs @@ -3,13 +3,13 @@ use std::ops::RangeInclusive; -use bitcoin_hashes::Hash; use thiserror::Error; +pub use bitcoin::bip158::BlockFilter; pub use bitcoin::hash_types::{FilterHash, FilterHeader}; -pub use bitcoin::util::bip158::BlockFilter; use super::Height; +use crate::bitcoin_hashes::Hash; use crate::block::store::{self, Genesis}; use crate::network::Network; diff --git a/common/src/block/store.rs b/common/src/block/store.rs index ea755699..7b71e58d 100644 --- a/common/src/block/store.rs +++ b/common/src/block/store.rs @@ -1,11 +1,14 @@ //! Block header storage. #![allow(clippy::len_without_is_empty)] +use std::borrow::Borrow; + use crate::block::Height; -use bitcoin::blockdata::block::BlockHeader; +use bitcoin::bip158::BlockFilter; +use bitcoin::block::Header; use bitcoin::consensus::encode; use bitcoin::hash_types::FilterHash; -use bitcoin::util::bip158::BlockFilter; +use bitcoin::{OutPoint, Script, ScriptBuf}; use thiserror::Error; use crate::network::Network; @@ -35,7 +38,7 @@ pub trait Genesis { } /// Genesis implementation for `bitcoin`'s header. -impl Genesis for BlockHeader { +impl Genesis for Header { fn genesis(network: Network) -> Self { network.genesis() } @@ -47,9 +50,12 @@ impl Genesis for FilterHash { use bitcoin::hashes::Hash; let genesis = network.genesis_block(); - let filter = BlockFilter::new_script_filter(&genesis, |_| { - panic!("{}: genesis block should have no inputs", source!()) - }) + let filter = BlockFilter::new_script_filter( + &genesis, + |_| -> Result { + panic!("{}: genesis block should have no inputs", source!()) + }, + ) .unwrap(); FilterHash::hash(&filter.content) @@ -61,7 +67,7 @@ impl Genesis for BlockFilter { fn genesis(network: Network) -> Self { let genesis = network.genesis_block(); - BlockFilter::new_script_filter(&genesis, |_| { + BlockFilter::new_script_filter(&genesis, |_| -> Result { panic!("{}: genesis block should have no inputs", source!()) }) .unwrap() diff --git a/common/src/block/tree.rs b/common/src/block/tree.rs index 41058914..4d927732 100644 --- a/common/src/block/tree.rs +++ b/common/src/block/tree.rs @@ -2,11 +2,12 @@ #![warn(missing_docs)] use std::collections::BTreeMap; -use bitcoin::blockdata::block::BlockHeader; +use bitcoin::block::Header as BlockHeader; use bitcoin::consensus::params::Params; use bitcoin::hash_types::BlockHash; -use bitcoin::util::uint::Uint256; +use bitcoin::CompactTarget; +use bitcoin::pow::calculate_next_work_required; use thiserror::Error; use crate::block::store; @@ -102,7 +103,8 @@ pub struct Branch<'a, H: Header>(pub &'a [H]); impl<'a, H: Header> Branch<'a, H> { /// Compute the total proof-of-work carried by this branch. pub fn work(&self) -> Work { - let mut work = Work::default(); + // FIXME: this should be by network + let mut work = Work::MAINNET_MIN; for header in self.0.iter() { work = work + header.work(); } @@ -145,7 +147,7 @@ pub trait BlockReader { Box::new(self.iter().map(|(_, h)| h)) } /// Get the "chainwork", ie. the total accumulated proof-of-work of the active chain. - fn chain_work(&self) -> Uint256; + fn chain_work(&self) -> Work; /// Iterate over the longest chain, starting from genesis, including heights. fn iter<'a>(&'a self) -> Box + 'a>; /// Iterate over a range of blocks. @@ -203,42 +205,15 @@ pub trait BlockReader { last_target: Target, params: &Params, ) -> Bits { - // Only adjust on set intervals. Otherwise return current target. - // Since the height is 0-indexed, we add `1` to check it against the interval. - if (last_height + 1) % params.difficulty_adjustment_interval() != 0 { - return BlockHeader::compact_target_from_u256(&last_target); - } - - let last_adjustment_height = - last_height.saturating_sub(params.difficulty_adjustment_interval() - 1); - let last_adjustment_block = self - .get_block_by_height(last_adjustment_height) - .unwrap_or_else(|| self.genesis()); - let last_adjustment_time = last_adjustment_block.time; - - if params.no_pow_retargeting { - return last_adjustment_block.bits; - } - - let actual_timespan = last_time - last_adjustment_time; - let mut adjusted_timespan = actual_timespan; - - if actual_timespan < params.pow_target_timespan as BlockTime / 4 { - adjusted_timespan = params.pow_target_timespan as BlockTime / 4; - } else if actual_timespan > params.pow_target_timespan as BlockTime * 4 { - adjusted_timespan = params.pow_target_timespan as BlockTime * 4; - } - - let mut target = last_target; - - target = target.mul_u32(adjusted_timespan); - target = target / Target::from_u64(params.pow_target_timespan).unwrap(); - - // Ensure a difficulty floor. - if target > params.pow_limit { - target = params.pow_limit; - } - - BlockHeader::compact_target_from_u256(&target) + // FIXME: improve the API usage and change the nakamoto height + calculate_next_work_required( + bitcoin::absolute::Height::from_consensus(last_height as u32).unwrap(), + last_time, + last_target, + params.difficulty_adjustment_interval(), + params.pow_target_timespan, + params.no_pow_retargeting, + |_| todo!(), + ) } } diff --git a/common/src/collections.rs b/common/src/collections.rs index 766262bb..6e1c12e7 100644 --- a/common/src/collections.rs +++ b/common/src/collections.rs @@ -1,5 +1,5 @@ //! Collections used in `nakamoto`. -use bitcoin_hashes::siphash24::Hash; +use bitcoin::hashes::siphash24::Hash; use std::ops::{Deref, DerefMut}; use crate::nonempty::NonEmpty; diff --git a/common/src/lib.rs b/common/src/lib.rs index fff261ca..36af265e 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -7,7 +7,8 @@ pub mod network; pub mod p2p; pub use bitcoin; -pub use bitcoin_hashes; +// re-aliasing to bitcoin_hashes for retro-compatibility +pub use bitcoin::hashes as bitcoin_hashes; pub use nakamoto_net as net; pub use nonempty; diff --git a/common/src/network.rs b/common/src/network.rs index b77dd9cd..bb38a29e 100644 --- a/common/src/network.rs +++ b/common/src/network.rs @@ -1,13 +1,12 @@ //! Bitcoin peer network. Eg. *Mainnet*. use std::str::FromStr; -use bitcoin::blockdata::block::{Block, BlockHeader}; +use bitcoin::blockdata::block::{Block, Header}; use bitcoin::consensus::params::Params; use bitcoin::hash_types::BlockHash; -use bitcoin::hashes::hex::FromHex; -use bitcoin::network::constants::ServiceFlags; +use bitcoin::p2p::{Magic, ServiceFlags}; -use bitcoin_hashes::sha256d; +use bitcoin::hashes::sha256d; use crate::block::Height; @@ -86,6 +85,7 @@ impl From for Network { bitcoin::Network::Testnet => Self::Testnet, bitcoin::Network::Signet => Self::Signet, bitcoin::Network::Regtest => Self::Regtest, + _ => unreachable!(), } } } @@ -114,7 +114,7 @@ impl Network { .iter() .cloned() .map(|(height, hash)| { - let hash = BlockHash::from_hex(hash).unwrap(); + let hash = BlockHash::from_str(hash).unwrap(); (height, hash) }); @@ -169,7 +169,7 @@ impl Network { /// /// assert_eq!(network.genesis_hash(), genesis.block_hash()); /// ``` - pub fn genesis(&self) -> BlockHeader { + pub fn genesis(&self) -> Header { self.genesis_block().header } @@ -183,7 +183,7 @@ impl Network { /// Get the hash of the genesis block of this network. pub fn genesis_hash(&self) -> BlockHash { use crate::block::genesis; - use bitcoin_hashes::Hash; + use bitcoin::hashes::Hash; let hash = match self { Self::Mainnet => genesis::MAINNET, @@ -191,7 +191,7 @@ impl Network { Self::Regtest => genesis::REGTEST, Self::Signet => genesis::SIGNET, }; - BlockHash::from_hash( + BlockHash::from_raw_hash( sha256d::Hash::from_slice(hash) .expect("the genesis hash has the right number of bytes"), ) @@ -203,7 +203,7 @@ impl Network { } /// Get the network magic number for this network. - pub fn magic(&self) -> u32 { + pub fn magic(&self) -> Magic { bitcoin::Network::from(*self).magic() } } diff --git a/common/src/p2p/peer.rs b/common/src/p2p/peer.rs index 1949f39f..1446603d 100644 --- a/common/src/p2p/peer.rs +++ b/common/src/p2p/peer.rs @@ -5,9 +5,7 @@ use std::net; use microserde as serde; -use bitcoin::network::address::Address; -use bitcoin::network::constants::ServiceFlags; - +use crate::bitcoin::p2p::{Address, ServiceFlags}; use crate::block::time::Clock; use crate::net::time::LocalTime; diff --git a/p2p/src/fsm.rs b/p2p/src/fsm.rs index eb8af178..ed1dccfe 100644 --- a/p2p/src/fsm.rs +++ b/p2p/src/fsm.rs @@ -45,22 +45,19 @@ use std::net; use std::ops::{Bound, RangeInclusive}; use std::sync::Arc; -use nakamoto_common::bitcoin::blockdata::block::BlockHeader; use nakamoto_common::bitcoin::consensus::encode; use nakamoto_common::bitcoin::consensus::params::Params; -use nakamoto_common::bitcoin::network::constants::ServiceFlags; -use nakamoto_common::bitcoin::network::message::{NetworkMessage, RawNetworkMessage}; -use nakamoto_common::bitcoin::network::message_blockdata::{GetHeadersMessage, Inventory}; -use nakamoto_common::bitcoin::network::message_filter::GetCFilters; -use nakamoto_common::bitcoin::network::message_network::VersionMessage; -use nakamoto_common::bitcoin::network::Address; -use nakamoto_common::bitcoin::util::uint::Uint256; -use nakamoto_common::bitcoin::{Script, Txid}; +use nakamoto_common::bitcoin::p2p::message::{NetworkMessage, RawNetworkMessage}; +use nakamoto_common::bitcoin::p2p::message_blockdata::{GetHeadersMessage, Inventory}; +use nakamoto_common::bitcoin::p2p::message_filter::GetCFilters; +use nakamoto_common::bitcoin::p2p::message_network::VersionMessage; +use nakamoto_common::bitcoin::p2p::{Address, ServiceFlags}; +use nakamoto_common::bitcoin::{ScriptBuf, Txid}; use nakamoto_common::block::filter::Filters; use nakamoto_common::block::time::AdjustedClock; use nakamoto_common::block::time::{LocalDuration, LocalTime}; use nakamoto_common::block::tree::{self, BlockReader, BlockTree, ImportResult}; -use nakamoto_common::block::{BlockHash, Height}; +use nakamoto_common::block::{BlockHash, BlockHeader, Height, Work}; use nakamoto_common::block::{BlockTime, Transaction}; use nakamoto_common::network; use nakamoto_common::nonempty::NonEmpty; @@ -233,7 +230,7 @@ pub enum Command { /// Get connected peers. GetPeers(ServiceFlags, chan::Sender>), /// Get the tip of the active chain. - GetTip(chan::Sender<(Height, BlockHeader, Uint256)>), + GetTip(chan::Sender<(Height, BlockHeader, Work)>), /// Get a block from the active chain. RequestBlock(BlockHash), /// Get block filters. @@ -248,12 +245,12 @@ pub enum Command { /// Stop scanning at this height. If unbounded, don't stop scanning. to: Bound, /// Scripts to match on. - watch: Vec