Skip to content
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
715 changes: 398 additions & 317 deletions Cargo.lock

Large diffs are not rendered by default.

11 changes: 5 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ electrum-discovery = ["electrum-client"]
arrayref = "0.3.6"
base64 = "0.13.0"
bincode-do-not-use-directly = { version = "1.3.1", package = "bincode" }
bitcoin = { version = "0.28", features = [ "use-serde" ] }
bitcoin = { version = "0.32.8", features = [ "serde" ] }
bounded-vec-deque = "0.1.1"
clap = "2.33.3"
crossbeam-channel = "0.5.0"
dirs = "4.0.0"
elements = { version = "0.19.1", features = [ "serde-feature" ], optional = true }
elements = { version = "0.26.1", features = [ "serde" ], optional = true }
error-chain = "0.12.4"
glob = "0.3"
hex = "0.4.2"
Expand Down Expand Up @@ -62,8 +62,7 @@ hyperlocal = "0.8"
tokio = { version = "1", features = ["sync", "macros"] }

# optional dependencies for electrum-discovery
electrum-client = { version = "0.8", optional = true }

electrum-client = { version = "0.24.1", optional = true }

[dev-dependencies]
tempfile = "3.0"
Expand All @@ -74,5 +73,5 @@ panic = 'abort'
codegen-units = 1

[patch.crates-io.electrum-client]
git = "https://github.com/Blockstream/rust-electrum-client"
rev = "d3792352992a539afffbe11501d1aff9fd5b919d" # add-peer branch
git = "https://github.com/mempool/rust-electrum-client"
rev = "4bbfc612d594fe23282c439d4bdc446cff01ba1c" # 0.24.1/add-peer branch
34 changes: 24 additions & 10 deletions src/bin/tx-fingerprint-stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fn main() {
use std::collections::HashSet;
use std::sync::Arc;

use bitcoin::blockdata::script::Script;
use bitcoin::blockdata::script::ScriptBuf;
use bitcoin::consensus::encode::deserialize;
use electrs::{
chain::Transaction,
Expand Down Expand Up @@ -62,7 +62,7 @@ fn main() {
}

let tx: Transaction = deserialize(value).expect("failed to parse Transaction");
let txid = tx.txid();
let txid = tx.compute_txid();

iter.next();

Expand All @@ -71,7 +71,7 @@ fn main() {
continue;
}
// skip coinbase txs
if tx.is_coin_base() {
if tx.is_coinbase() {
continue;
}

Expand All @@ -91,12 +91,26 @@ fn main() {
.collect(),
);

let total_out: u64 = tx.output.iter().map(|out| out.value).sum();
let small_out = tx.output.iter().map(|out| out.value).min().unwrap();
let large_out = tx.output.iter().map(|out| out.value).max().unwrap();

let total_in: u64 = prevouts.values().map(|out| out.value).sum();
let smallest_in = prevouts.values().map(|out| out.value).min().unwrap();
let total_out: u64 = tx.output.iter().map(|out| out.value.to_sat()).sum();
let small_out = tx
.output
.iter()
.map(|out| out.value.to_sat())
.min()
.unwrap();
let large_out = tx
.output
.iter()
.map(|out| out.value.to_sat())
.max()
.unwrap();

let total_in: u64 = prevouts.values().map(|out| out.value.to_sat()).sum();
let smallest_in = prevouts
.values()
.map(|out| out.value.to_sat())
.min()
.unwrap();

let fee = total_in - total_out;

Expand All @@ -119,7 +133,7 @@ fn main() {

// test for sending back to one of the spent spks
let has_reuse = {
let prev_spks: HashSet<Script> = prevouts
let prev_spks: HashSet<ScriptBuf> = prevouts
.values()
.map(|out| out.script_pubkey.clone())
.collect();
Expand Down
73 changes: 60 additions & 13 deletions src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,64 @@ use std::str::FromStr;

#[cfg(not(feature = "liquid"))] // use regular Bitcoin data structures
pub use bitcoin::{
blockdata::{opcodes, script, witness::Witness},
address,
block::Header as BlockHeader,
blockdata::{opcodes, script},
consensus::deserialize,
hashes,
util::address,
Block, BlockHash, BlockHeader, OutPoint, Script, Transaction, TxIn, TxOut, Txid,
hashes, Block, BlockHash, OutPoint, ScriptBuf as Script, Transaction, TxIn, TxOut, Txid,
Witness,
};

#[cfg(feature = "liquid")]
pub use {
crate::elements::asset,
elements::{
address, confidential, encode::deserialize, hashes, opcodes, script, Address, AssetId,
Block, BlockHash, BlockHeader, OutPoint, Script, Transaction, TxIn, TxInWitness as Witness,
TxOut, Txid,
address, bitcoin::bech32::Hrp, confidential, encode::deserialize, hashes, opcodes, script,
Address, AssetId, Block, BlockHash, BlockHeader, OutPoint, Script, Transaction, TxIn,
TxInWitness as Witness, TxOut, Txid,
},
};

use bitcoin::blockdata::constants::genesis_block;
pub use bitcoin::network::constants::Network as BNetwork;
pub use bitcoin::Network as BNetwork;

// Extension trait for getting txid in a cross-compatible way
pub trait TxidCompat {
fn get_txid(&self) -> Txid;
}

#[cfg(not(feature = "liquid"))]
impl TxidCompat for Transaction {
fn get_txid(&self) -> Txid {
self.compute_txid()
}
}

#[cfg(feature = "liquid")]
impl TxidCompat for Transaction {
fn get_txid(&self) -> Txid {
self.txid()
}
}

// Extension trait for getting block size in a cross-compatible way
pub trait BlockSizeCompat {
fn get_block_size(&self) -> usize;
}

#[cfg(not(feature = "liquid"))]
impl BlockSizeCompat for Block {
fn get_block_size(&self) -> usize {
self.total_size()
}
}

#[cfg(feature = "liquid")]
impl BlockSizeCompat for Block {
fn get_block_size(&self) -> usize {
self.size()
}
}

#[cfg(not(feature = "liquid"))]
pub type Value = u64;
Expand Down Expand Up @@ -53,8 +92,8 @@ pub const LIQUID_TESTNET_PARAMS: address::AddressParams = address::AddressParams
p2pkh_prefix: 36,
p2sh_prefix: 19,
blinded_prefix: 23,
bech_hrp: "tex",
blech_hrp: "tlq",
bech_hrp: Hrp::parse_unchecked("tex"),
blech_hrp: Hrp::parse_unchecked("tlq"),
Comment thread
junderw marked this conversation as resolved.
};

/// Magic for testnet4, 0x1c163f28 (from BIP94) with flipped endianness.
Expand All @@ -66,7 +105,10 @@ impl Network {
pub fn magic(self) -> u32 {
match self {
Self::Testnet4 => TESTNET4_MAGIC,
_ => BNetwork::from(self).magic(),
_ => {
let magic = BNetwork::from(self).magic();
u32::from_le_bytes(magic.to_bytes())
}
}
}

Expand Down Expand Up @@ -178,14 +220,18 @@ pub fn liquid_genesis_hash(network: Network) -> elements::BlockHash {
"1466275836220db2944ca059a3a10ef6fd2ea684b0688d2c379296888a206003"
.parse()
.unwrap();
static ref ZERO_HASH: BlockHash =
"0000000000000000000000000000000000000000000000000000000000000000"
.parse()
.unwrap();
}

match network {
Network::Liquid => *LIQUID_GENESIS,
// The genesis block for liquid regtest chains varies based on the chain configuration.
// This instead uses an all zeroed-out hash, which doesn't matter in practice because its
// only used for Electrum server discovery, which isn't active on regtest.
_ => Default::default(),
_ => *ZERO_HASH,
}
}

Expand Down Expand Up @@ -221,7 +267,7 @@ impl From<Network> for BNetwork {
match network {
Network::Bitcoin => BNetwork::Bitcoin,
Network::Testnet => BNetwork::Testnet,
Network::Testnet4 => BNetwork::Testnet,
Network::Testnet4 => BNetwork::Testnet4,
Network::Regtest => BNetwork::Regtest,
Network::Signet => BNetwork::Signet,
}
Expand All @@ -234,6 +280,7 @@ impl From<BNetwork> for Network {
match network {
BNetwork::Bitcoin => Network::Bitcoin,
BNetwork::Testnet => Network::Testnet,
BNetwork::Testnet4 => Network::Testnet4,
BNetwork::Regtest => Network::Regtest,
BNetwork::Signet => Network::Signet,
}
Expand Down
48 changes: 26 additions & 22 deletions src/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use std::collections::{HashMap, HashSet};
use std::io::{BufRead, BufReader, Lines, Write};
use std::net::{SocketAddr, TcpStream};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use std::time::Duration;

use base64;
use bitcoin::hashes::hex::{FromHex, ToHex};
use bitcoin::hashes::Hash;
use glob;
use hex;
use itertools::Itertools;
Expand All @@ -27,14 +28,14 @@ use crate::errors::*;

fn parse_hash<T>(value: &Value) -> Result<T>
where
T: FromHex,
T: FromStr,
<T as FromStr>::Err: std::fmt::Debug,
{
T::from_hex(
value
.as_str()
.chain_err(|| format!("non-string value: {}", value))?,
)
.chain_err(|| format!("non-hex value: {}", value))
value
.as_str()
.chain_err(|| format!("non-string value: {}", value))?
.parse::<T>()
.map_err(|e| format!("failed to parse hash: {:?}", e).into())
}

fn header_from_value(value: Value) -> Result<BlockHeader> {
Expand Down Expand Up @@ -547,7 +548,7 @@ impl Daemon {
pub fn getblockheader(&self, blockhash: &BlockHash) -> Result<BlockHeader> {
header_from_value(self.request(
"getblockheader",
json!([blockhash.to_hex(), /*verbose=*/ false]),
json!([blockhash.to_string(), /*verbose=*/ false]),
)?)
}

Expand All @@ -566,21 +567,22 @@ impl Daemon {
}

pub fn getblock(&self, blockhash: &BlockHash) -> Result<Block> {
let block = block_from_value(
self.request("getblock", json!([blockhash.to_hex(), /*verbose=*/ false]))?,
)?;
let block = block_from_value(self.request(
"getblock",
json!([blockhash.to_string(), /*verbose=*/ false]),
)?)?;
assert_eq!(block.block_hash(), *blockhash);
Ok(block)
}

pub fn getblock_raw(&self, blockhash: &BlockHash, verbose: u32) -> Result<Value> {
self.request("getblock", json!([blockhash.to_hex(), verbose]))
self.request("getblock", json!([blockhash.to_string(), verbose]))
}

pub fn getblocks(&self, blockhashes: &[BlockHash]) -> Result<Vec<Block>> {
let params_list: Vec<Value> = blockhashes
.iter()
.map(|hash| json!([hash.to_hex(), /*verbose=*/ false]))
.map(|hash| json!([hash.to_string(), /*verbose=*/ false]))
.collect();
let values = self.requests("getblock", &params_list)?;
let mut blocks = vec![];
Expand All @@ -593,7 +595,7 @@ impl Daemon {
pub fn gettransactions(&self, txhashes: &[&Txid]) -> Result<Vec<Transaction>> {
let params_list: Vec<Value> = txhashes
.iter()
.map(|txhash| json!([txhash.to_hex(), /*verbose=*/ false]))
.map(|txhash| json!([txhash.to_string(), /*verbose=*/ false]))
.collect();
let values = self.retry_request_batch("getrawtransaction", &params_list, 0.25)?;
let mut txs = vec![];
Expand All @@ -612,14 +614,14 @@ impl Daemon {
) -> Result<Value> {
self.request(
"getrawtransaction",
json!([txid.to_hex(), verbose, blockhash]),
json!([txid.to_string(), verbose, blockhash]),
)
}

pub fn getmempooltx(&self, txhash: &Txid) -> Result<Transaction> {
let value = self.request(
"getrawtransaction",
json!([txhash.to_hex(), /*verbose=*/ false]),
json!([txhash.to_string(), /*verbose=*/ false]),
)?;
tx_from_value(value)
}
Expand All @@ -635,8 +637,10 @@ impl Daemon {

pub fn broadcast_raw(&self, txhex: &str) -> Result<Txid> {
let txid = self.request("sendrawtransaction", json!([txhex]))?;
Txid::from_hex(txid.as_str().chain_err(|| "non-string txid")?)
.chain_err(|| "failed to parse txid")
txid.as_str()
.chain_err(|| "non-string txid")?
.parse::<Txid>()
.map_err(|e| format!("failed to parse txid: {:?}", e).into())
}

pub fn test_mempool_accept(
Expand Down Expand Up @@ -707,7 +711,7 @@ impl Daemon {
}

fn get_all_headers(&self, tip: &BlockHash) -> Result<Vec<BlockHeader>> {
let info: Value = self.request("getblockheader", json!([tip.to_hex()]))?;
let info: Value = self.request("getblockheader", json!([tip.to_string()]))?;
let tip_height = info
.get("height")
.expect("missing height")
Expand All @@ -723,7 +727,7 @@ impl Daemon {
result.append(&mut headers);
}

let mut blockhash = BlockHash::default();
let mut blockhash = BlockHash::all_zeros();
for header in &result {
assert_eq!(header.prev_blockhash, blockhash);
blockhash = header.block_hash();
Expand All @@ -749,7 +753,7 @@ impl Daemon {
bestblockhash,
);
let mut new_headers = vec![];
let null_hash = BlockHash::default();
let null_hash = BlockHash::all_zeros();
let mut blockhash = *bestblockhash;
while blockhash != null_hash {
if indexed_headers.header_by_blockhash(&blockhash).is_some() {
Expand Down
5 changes: 4 additions & 1 deletion src/electrum/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::HashMap;
use std::convert::TryFrom;

use bitcoin::hashes::sha256d;
use bitcoin::hashes::Hash;
pub use electrum_client::client::Client;
pub use electrum_client::ServerFeaturesRes;
Expand All @@ -19,7 +20,9 @@ impl TryFrom<ServerFeaturesRes> for ServerFeatures {
Ok(ServerFeatures {
// electrum-client doesn't retain the hosts map data, but we already have it from the add_peer request
hosts: HashMap::new(),
genesis_hash: BlockHash::from_inner(features.genesis_hash),
genesis_hash: BlockHash::from_raw_hash(sha256d::Hash::from_byte_array(
features.genesis_hash,
)),
server_version: features.server_version,
protocol_min: features
.protocol_min
Expand Down
Loading