Skip to content

Commit 46cd53c

Browse files
authored
Merge pull request Blockstream#5 from mempool/fix/difficulty_f64
REST: Return an f64 for difficulty from the block value endpoint
2 parents 34e29f4 + f0e1db3 commit 46cd53c

File tree

1 file changed

+150
-5
lines changed

1 file changed

+150
-5
lines changed

src/rest.rs

Lines changed: 150 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ struct BlockValue {
6969
#[cfg(not(feature = "liquid"))]
7070
bits: u32,
7171
#[cfg(not(feature = "liquid"))]
72-
difficulty: u64,
72+
difficulty: f64,
7373

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

7979
impl BlockValue {
8080
#[cfg_attr(feature = "liquid", allow(unused_variables))]
81-
fn new(blockhm: BlockHeaderMeta, network: Network) -> Self {
81+
fn new(blockhm: BlockHeaderMeta) -> Self {
8282
let header = blockhm.header_entry.header();
8383
BlockValue {
8484
id: header.block_hash().to_hex(),
@@ -106,14 +106,35 @@ impl BlockValue {
106106
#[cfg(not(feature = "liquid"))]
107107
nonce: header.nonce,
108108
#[cfg(not(feature = "liquid"))]
109-
difficulty: header.difficulty(bitcoin::Network::from(network)),
109+
difficulty: difficulty_new(header),
110110

111111
#[cfg(feature = "liquid")]
112112
ext: Some(json!(header.ext)),
113113
}
114114
}
115115
}
116116

117+
/// Calculate the difficulty of a BlockHeader
118+
/// using Bitcoin Core code ported to Rust.
119+
///
120+
/// https://github.com/bitcoin/bitcoin/blob/v25.0/src/rpc/blockchain.cpp#L75-L97
121+
#[cfg_attr(feature = "liquid", allow(dead_code))]
122+
fn difficulty_new(bh: &bitcoin::BlockHeader) -> f64 {
123+
let mut n_shift = bh.bits >> 24 & 0xff;
124+
let mut d_diff = (0x0000ffff as f64) / ((bh.bits & 0x00ffffff) as f64);
125+
126+
while n_shift < 29 {
127+
d_diff *= 256.0;
128+
n_shift += 1;
129+
}
130+
while n_shift > 29 {
131+
d_diff /= 256.0;
132+
n_shift -= 1;
133+
}
134+
135+
d_diff
136+
}
137+
117138
#[derive(Serialize, Deserialize)]
118139
struct TransactionValue {
119140
txid: Txid,
@@ -681,7 +702,7 @@ fn handle_request(
681702
.chain()
682703
.get_block_with_meta(&hash)
683704
.ok_or_else(|| HttpError::not_found("Block not found".to_string()))?;
684-
let block_value = BlockValue::new(blockhm, config.network_type);
705+
let block_value = BlockValue::new(blockhm);
685706
json_response(block_value, TTL_LONG)
686707
}
687708
(&Method::GET, Some(&"block"), Some(hash), Some(&"status"), None, None) => {
@@ -1325,7 +1346,7 @@ fn blocks(
13251346
current_hash = blockhm.header_entry.header().prev_blockhash;
13261347

13271348
#[allow(unused_mut)]
1328-
let mut value = BlockValue::new(blockhm, config.network_type);
1349+
let mut value = BlockValue::new(blockhm);
13291350

13301351
#[cfg(feature = "liquid")]
13311352
{
@@ -1533,4 +1554,128 @@ mod tests {
15331554

15341555
assert!(err.is_err());
15351556
}
1557+
1558+
#[test]
1559+
fn test_difficulty_new() {
1560+
use super::difficulty_new;
1561+
1562+
let vectors = [
1563+
(
1564+
// bits in header
1565+
0x17053894,
1566+
// expected output (Rust)
1567+
53911173001054.586,
1568+
// Block hash where found (for getblockheader)
1569+
"0000000000000000000050b050758dd2ccb0ba96ad5e95db84efd2f6c05e4e90",
1570+
// difficulty returned by Bitcoin Core v25
1571+
"53911173001054.59",
1572+
),
1573+
(
1574+
0x1a0c2a12,
1575+
1379192.2882280778,
1576+
"0000000000000bc7636ffbc1cf90cf4a2674de7fcadbc6c9b63d31f07cb3c2c2",
1577+
"1379192.288228078",
1578+
),
1579+
(
1580+
0x19262222,
1581+
112628548.66634709,
1582+
"000000000000000996b1f06771a81bcf7b15c5f859b6f8329016f01b0442ca72",
1583+
"112628548.6663471",
1584+
),
1585+
(
1586+
0x1d00c428,
1587+
1.3050621315915245,
1588+
"0000000034014d731a3e1ad6078662ce19b08179dcc7ec0f5f717d4b58060736",
1589+
"1.305062131591525",
1590+
),
1591+
(
1592+
0,
1593+
f64::INFINITY,
1594+
"[No Blockhash]",
1595+
"[No Core difficulty, just checking edge cases]",
1596+
),
1597+
(
1598+
0x00000001,
1599+
4.523059468369196e74,
1600+
"[No Blockhash]",
1601+
"[No Core difficulty, just checking edge cases]",
1602+
),
1603+
(
1604+
0x1d00ffff,
1605+
1.0,
1606+
"[No Blockhash]",
1607+
"[No Core difficulty, just checking MAX_TARGET]",
1608+
),
1609+
(
1610+
0x1c7fff80,
1611+
2.0,
1612+
"[No Blockhash]",
1613+
"[No Core difficulty, just checking MAX_TARGET >> 1]",
1614+
),
1615+
(
1616+
0x1b00ffff,
1617+
65536.0,
1618+
"[No Blockhash]",
1619+
"[No Core difficulty, just checking MAX_TARGET >> 16]",
1620+
),
1621+
(
1622+
0x1a7fff80,
1623+
131072.0,
1624+
"[No Blockhash]",
1625+
"[No Core difficulty, just checking MAX_TARGET >> 17]",
1626+
),
1627+
(
1628+
0x1d01fffe,
1629+
0.5,
1630+
"[No Blockhash]",
1631+
"[No Core difficulty, just checking MAX_TARGET << 1]",
1632+
),
1633+
(
1634+
0x1f000080,
1635+
0.007812380790710449,
1636+
"[No Blockhash]",
1637+
"[No Core difficulty, just checking 2**255]",
1638+
),
1639+
(
1640+
0x1e00ffff,
1641+
0.00390625, // 2.0**-8
1642+
"[No Blockhash]",
1643+
"[No Core difficulty, just checking MAX_TARGET << 8]",
1644+
),
1645+
(
1646+
0x1e00ff00,
1647+
0.0039215087890625,
1648+
"[No Blockhash]",
1649+
"[No Core difficulty, just checking MAX_TARGET << 8 - two `f` chars]",
1650+
),
1651+
(
1652+
0x1f0000ff,
1653+
0.0039215087890625,
1654+
"[No Blockhash]",
1655+
"[No Core difficulty, just checking MAX_TARGET << 8]",
1656+
),
1657+
];
1658+
1659+
let to_bh = |b| bitcoin::BlockHeader {
1660+
version: 1,
1661+
prev_blockhash: "0000000000000000000000000000000000000000000000000000000000000000"
1662+
.parse()
1663+
.unwrap(),
1664+
merkle_root: "0000000000000000000000000000000000000000000000000000000000000000"
1665+
.parse()
1666+
.unwrap(),
1667+
time: 0,
1668+
bits: b,
1669+
nonce: 0,
1670+
};
1671+
1672+
for (bits, expected, hash, core_difficulty) in vectors {
1673+
let result = difficulty_new(&to_bh(bits));
1674+
assert_eq!(
1675+
result, expected,
1676+
"Block {} difficulty is {} but Core difficulty is {}",
1677+
hash, result, core_difficulty,
1678+
);
1679+
}
1680+
}
15361681
}

0 commit comments

Comments
 (0)