global: fixes

This commit is contained in:
nym21
2026-05-03 12:44:18 +02:00
parent 9cb5f2c880
commit 4663d13194
46 changed files with 1058 additions and 544 deletions

View File

@@ -1,4 +1,10 @@
"""GET /api/v1/mining/blocks/fee-rates/{time_period}"""
"""GET /api/v1/mining/blocks/fee-rates/{time_period}
Note: there is no values_match test here. brk emits float bucket means; mempool
stores integer per-block percentiles, averages, then casts to INT. Beyond
rounding, the per-block max (avgFee_100) also drifts by several sat/vB,
indicating a real methodology difference (effective fee-rate definition,
RBF/ancestor-fee handling) that no test tolerance should hide."""
import pytest

View File

@@ -45,3 +45,30 @@ def test_mining_blocks_fees_malformed(brk, bad):
assert exc_info.value.status == 400, (
f"expected status=400 for {bad!r}, got {exc_info.value.status}"
)
@pytest.mark.parametrize("period", PERIODS)
def test_mining_blocks_fees_values_match(brk, mempool, period):
"""For shared buckets (keyed by timestamp), avgHeight and avgFees must equal mempool.space.
USD is sourced from different price oracles and is intentionally not compared."""
path = f"/api/v1/mining/blocks/fees/{period}"
b = brk.get_block_fees(period)
m = mempool.get_json(path)
show("GET", path, summary(b), summary(m))
m_by_ts = {e["timestamp"]: e for e in m}
matched = 0
for be in b:
me = m_by_ts.get(be["timestamp"])
if me is None:
continue
matched += 1
assert be["avgHeight"] == me["avgHeight"], (
f"avgHeight drift at timestamp {be['timestamp']}: "
f"brk={be['avgHeight']} mempool={me['avgHeight']}"
)
assert be["avgFees"] == me["avgFees"], (
f"avgFees mismatch at timestamp {be['timestamp']}: "
f"brk={be['avgFees']} mempool={me['avgFees']}"
)
assert matched > 0, "no overlapping bucket timestamps between brk and mempool"

View File

@@ -45,3 +45,29 @@ def test_mining_blocks_rewards_malformed(brk, bad):
assert exc_info.value.status == 400, (
f"expected status=400 for {bad!r}, got {exc_info.value.status}"
)
@pytest.mark.parametrize("period", PERIODS)
def test_mining_blocks_rewards_values_match(brk, mempool, period):
"""For shared buckets (keyed by timestamp), avgHeight and avgRewards must equal mempool.space."""
path = f"/api/v1/mining/blocks/rewards/{period}"
b = brk.get_block_rewards(period)
m = mempool.get_json(path)
show("GET", path, summary(b), summary(m))
m_by_ts = {e["timestamp"]: e for e in m}
matched = 0
for be in b:
me = m_by_ts.get(be["timestamp"])
if me is None:
continue
matched += 1
assert be["avgHeight"] == me["avgHeight"], (
f"avgHeight drift at timestamp {be['timestamp']}: "
f"brk={be['avgHeight']} mempool={me['avgHeight']}"
)
assert be["avgRewards"] == me["avgRewards"], (
f"avgRewards mismatch at timestamp {be['timestamp']}: "
f"brk={be['avgRewards']} mempool={me['avgRewards']}"
)
assert matched > 0, "no overlapping bucket timestamps between brk and mempool"

View File

@@ -59,3 +59,34 @@ def test_mining_blocks_sizes_weights_malformed(brk, bad):
assert exc_info.value.status == 400, (
f"expected status=400 for {bad!r}, got {exc_info.value.status}"
)
@pytest.mark.parametrize("period", PERIODS)
def test_mining_blocks_sizes_weights_values_match(brk, mempool, period):
"""For shared buckets (keyed by timestamp), avgSize and avgWeight must equal mempool.space."""
path = f"/api/v1/mining/blocks/sizes-weights/{period}"
b = brk.get_block_sizes_weights(period)
m = mempool.get_json(path)
show("GET", path, summary(b), summary(m))
sizes_by_ts = {e["timestamp"]: e for e in m["sizes"]}
weights_by_ts = {e["timestamp"]: e for e in m["weights"]}
matched = 0
for s, w in zip(b["sizes"], b["weights"]):
ts = s["timestamp"]
ms = sizes_by_ts.get(ts)
mw = weights_by_ts.get(ts)
if ms is None or mw is None:
continue
matched += 1
assert s["avgHeight"] == ms["avgHeight"], (
f"size avgHeight drift at timestamp {ts}: brk={s['avgHeight']} mempool={ms['avgHeight']}"
)
assert s["avgSize"] == ms["avgSize"], (
f"avgSize mismatch at timestamp {ts}: brk={s['avgSize']} mempool={ms['avgSize']}"
)
assert w["avgWeight"] == mw["avgWeight"], (
f"avgWeight mismatch at timestamp {ts}: brk={w['avgWeight']} mempool={mw['avgWeight']}"
)
assert matched > 0, "no overlapping bucket timestamps between brk and mempool"

View File

@@ -52,3 +52,30 @@ def test_mining_difficulty_adjustments_malformed(brk, bad):
assert exc_info.value.status == 400, (
f"expected status=400 for {bad!r}, got {exc_info.value.status}"
)
# `all`: mempool.space's `difficulty_adjustments` table begins from when their tracker
# started, not genesis, so series length and earliest entries diverge by construction.
@pytest.mark.parametrize("period", [p for p in PERIODS if p != "all"])
def test_mining_difficulty_adjustments_values_match(brk, mempool, period):
"""For every bounded period, every retarget entry must match mempool.space:
same height, same timestamp, and difficulty/change-ratio within float tolerance."""
path = f"/api/v1/mining/difficulty-adjustments/{period}"
b = brk.get_difficulty_adjustments_by_period(period)
m = mempool.get_json(path)
show("GET", path, summary(b), summary(m))
assert len(b) == len(m), f"length mismatch: brk={len(b)} mempool={len(m)}"
for be, me in zip(b, m):
bt, bh, bd, br = be
mt, mh, md, mr = me
assert bh == mh, f"height mismatch at retarget: brk={bh} mempool={mh}"
assert bt == mt, f"timestamp mismatch at height {bh}: brk={bt} mempool={mt}"
# mempool.space serializes difficulty/change_ratio with limited precision,
# so only require parity within mempool.space's ~6-decimal rounding window.
assert abs(bd - md) / max(md, 1.0) < 1e-5, (
f"difficulty drift at height {bh}: brk={bd} mempool={md}"
)
assert abs(br - mr) < 1e-5, (
f"change_ratio drift at height {bh}: brk={br} mempool={mr}"
)

View File

@@ -1,10 +1,15 @@
"""GET /api/v1/mining/reward-stats/{block_count}"""
"""GET /api/v1/mining/reward-stats/{block_count}
Note: there is no values_match test here. mempool.space's reward-stats endpoint
serves results anchored to a cached/precomputed block that lags real-time tip
non-deterministically across counts, so any direct numeric comparison is flaky.
The invariants test below covers structural correctness."""
import pytest
from brk_client import BrkError
from _lib import assert_same_structure, assert_same_values, show
from _lib import assert_same_structure, show
COUNTS = [1, 10, 100, 500, 1000]
@@ -20,16 +25,6 @@ def test_mining_reward_stats_structure(brk, mempool, count):
assert_same_structure(b, m)
@pytest.mark.parametrize("count", [100, 1000])
def test_mining_reward_stats_values_match(brk, mempool, count):
"""brk and mempool must agree exactly on aggregated stats."""
path = f"/api/v1/mining/reward-stats/{count}"
b = brk.get_reward_stats(count)
m = mempool.get_json(path)
show("GET", path, b, m)
assert_same_values(b, m)
def test_mining_reward_stats_invariants(brk):
"""Range alignment, reward >= fee, totalTx >= block count (count=1000)."""
count = 1000