Skip to content

Commit

Permalink
Merge pull request Blockstream#5 from mempool/fix/difficulty_f64
Browse files Browse the repository at this point in the history
REST: Return an f64 for difficulty from the block value endpoint
  • Loading branch information
softsimon authored Jul 30, 2023
2 parents 34e29f4 + f0e1db3 commit 46cd53c
Showing 1 changed file with 150 additions and 5 deletions.
155 changes: 150 additions & 5 deletions src/rest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ struct BlockValue {
#[cfg(not(feature = "liquid"))]
bits: u32,
#[cfg(not(feature = "liquid"))]
difficulty: u64,
difficulty: f64,

#[cfg(feature = "liquid")]
#[serde(skip_serializing_if = "Option::is_none")]
Expand All @@ -78,7 +78,7 @@ struct BlockValue {

impl BlockValue {
#[cfg_attr(feature = "liquid", allow(unused_variables))]
fn new(blockhm: BlockHeaderMeta, network: Network) -> Self {
fn new(blockhm: BlockHeaderMeta) -> Self {
let header = blockhm.header_entry.header();
BlockValue {
id: header.block_hash().to_hex(),
Expand Down Expand Up @@ -106,14 +106,35 @@ impl BlockValue {
#[cfg(not(feature = "liquid"))]
nonce: header.nonce,
#[cfg(not(feature = "liquid"))]
difficulty: header.difficulty(bitcoin::Network::from(network)),
difficulty: difficulty_new(header),

#[cfg(feature = "liquid")]
ext: Some(json!(header.ext)),
}
}
}

/// Calculate the difficulty of a BlockHeader
/// using Bitcoin Core code ported to Rust.
///
/// https://github.com/bitcoin/bitcoin/blob/v25.0/src/rpc/blockchain.cpp#L75-L97
#[cfg_attr(feature = "liquid", allow(dead_code))]
fn difficulty_new(bh: &bitcoin::BlockHeader) -> f64 {
let mut n_shift = bh.bits >> 24 & 0xff;
let mut d_diff = (0x0000ffff as f64) / ((bh.bits & 0x00ffffff) as f64);

while n_shift < 29 {
d_diff *= 256.0;
n_shift += 1;
}
while n_shift > 29 {
d_diff /= 256.0;
n_shift -= 1;
}

d_diff
}

#[derive(Serialize, Deserialize)]
struct TransactionValue {
txid: Txid,
Expand Down Expand Up @@ -681,7 +702,7 @@ fn handle_request(
.chain()
.get_block_with_meta(&hash)
.ok_or_else(|| HttpError::not_found("Block not found".to_string()))?;
let block_value = BlockValue::new(blockhm, config.network_type);
let block_value = BlockValue::new(blockhm);
json_response(block_value, TTL_LONG)
}
(&Method::GET, Some(&"block"), Some(hash), Some(&"status"), None, None) => {
Expand Down Expand Up @@ -1325,7 +1346,7 @@ fn blocks(
current_hash = blockhm.header_entry.header().prev_blockhash;

#[allow(unused_mut)]
let mut value = BlockValue::new(blockhm, config.network_type);
let mut value = BlockValue::new(blockhm);

#[cfg(feature = "liquid")]
{
Expand Down Expand Up @@ -1533,4 +1554,128 @@ mod tests {

assert!(err.is_err());
}

#[test]
fn test_difficulty_new() {
use super::difficulty_new;

let vectors = [
(
// bits in header
0x17053894,
// expected output (Rust)
53911173001054.586,
// Block hash where found (for getblockheader)
"0000000000000000000050b050758dd2ccb0ba96ad5e95db84efd2f6c05e4e90",
// difficulty returned by Bitcoin Core v25
"53911173001054.59",
),
(
0x1a0c2a12,
1379192.2882280778,
"0000000000000bc7636ffbc1cf90cf4a2674de7fcadbc6c9b63d31f07cb3c2c2",
"1379192.288228078",
),
(
0x19262222,
112628548.66634709,
"000000000000000996b1f06771a81bcf7b15c5f859b6f8329016f01b0442ca72",
"112628548.6663471",
),
(
0x1d00c428,
1.3050621315915245,
"0000000034014d731a3e1ad6078662ce19b08179dcc7ec0f5f717d4b58060736",
"1.305062131591525",
),
(
0,
f64::INFINITY,
"[No Blockhash]",
"[No Core difficulty, just checking edge cases]",
),
(
0x00000001,
4.523059468369196e74,
"[No Blockhash]",
"[No Core difficulty, just checking edge cases]",
),
(
0x1d00ffff,
1.0,
"[No Blockhash]",
"[No Core difficulty, just checking MAX_TARGET]",
),
(
0x1c7fff80,
2.0,
"[No Blockhash]",
"[No Core difficulty, just checking MAX_TARGET >> 1]",
),
(
0x1b00ffff,
65536.0,
"[No Blockhash]",
"[No Core difficulty, just checking MAX_TARGET >> 16]",
),
(
0x1a7fff80,
131072.0,
"[No Blockhash]",
"[No Core difficulty, just checking MAX_TARGET >> 17]",
),
(
0x1d01fffe,
0.5,
"[No Blockhash]",
"[No Core difficulty, just checking MAX_TARGET << 1]",
),
(
0x1f000080,
0.007812380790710449,
"[No Blockhash]",
"[No Core difficulty, just checking 2**255]",
),
(
0x1e00ffff,
0.00390625, // 2.0**-8
"[No Blockhash]",
"[No Core difficulty, just checking MAX_TARGET << 8]",
),
(
0x1e00ff00,
0.0039215087890625,
"[No Blockhash]",
"[No Core difficulty, just checking MAX_TARGET << 8 - two `f` chars]",
),
(
0x1f0000ff,
0.0039215087890625,
"[No Blockhash]",
"[No Core difficulty, just checking MAX_TARGET << 8]",
),
];

let to_bh = |b| bitcoin::BlockHeader {
version: 1,
prev_blockhash: "0000000000000000000000000000000000000000000000000000000000000000"
.parse()
.unwrap(),
merkle_root: "0000000000000000000000000000000000000000000000000000000000000000"
.parse()
.unwrap(),
time: 0,
bits: b,
nonce: 0,
};

for (bits, expected, hash, core_difficulty) in vectors {
let result = difficulty_new(&to_bh(bits));
assert_eq!(
result, expected,
"Block {} difficulty is {} but Core difficulty is {}",
hash, result, core_difficulty,
);
}
}
}

0 comments on commit 46cd53c

Please sign in to comment.