Sources:
Methodology:
Sources:
Methodology:
Sources:
Methodology:
Sources:
Methodology:
OP_FALSE OP_IF <data> OP_ENDIFresearch/verify_inscriptions.py to decode recent inscription txsSources:
Methodology:
research/fetch_inscription_stats.py to pull live data# research/fetch_inscription_stats.py
"""Fetch live inscription data from public APIs."""
import json
import urllib.request
import sys
def fetch_ordinals_stats():
"""Get total inscription count from ordinals.com API."""
try:
resp = urllib.request.urlopen(
'https://ordinals.com/api/stats',
timeout=10
)
data = json.loads(resp.read())
return data
except Exception as e:
print(f" ⚠ Ordinals API unavailable: {e}")
return None
def fetch_utxo_set_size():
"""Get UTXO set size from blockchain.info."""
try:
resp = urllib.request.urlopen(
'https://blockchain.info/q/utxocount',
timeout=10
)
return int(resp.read().strip())
except Exception as e:
print(f" ⚠ Blockchain.info unavailable: {e}")
return None
def fetch_mempool_fees():
"""Get current fee estimates from mempool.space."""
try:
resp = urllib.request.urlopen(
'https://mempool.space/api/v1/fees/recommended',
timeout=10
)
return json.loads(resp.read())
except Exception as e:
print(f" ⚠ Mempool.space unavailable: {e}")
return None
def main():
print("=" * 62)
print(" Bitcoin Inscription Data — Live Verification")
print("=" * 62)
stats = fetch_ordinals_stats()
if stats:
total = stats.get('total_inscriptions', 0)
print(f"\n Total inscriptions: {total:,}")
utxo = fetch_utxo_set_size()
if utxo:
print(f" UTXO set size: {utxo:,} outputs")
fees = fetch_mempool_fees()
if fees:
print(f"\n Fee estimates (sat/vB):")
print(f" No priority (slow): {fees.get('minimumFee', '?')}")
print(f" Econ priority: {fees.get('economyFee', '?')}")
print(f" Hour priority: {fees.get('hourFee', '?')}")
print(f" Half-hour priority: {fees.get('halfHourFee', '?')}")
print(f" Fastest priority: {fees.get('fastestFee', '?')}")
print(f"\n To estimate daily inscription count, query:")
print(f" https://ordinals.com/api/inscriptions/recent")
print(f" or use Dune Analytics: Ordinals dashboard")
if __name__ == "__main__":
main()
# research/verify_inscription_size.py
"""Decode a single inscription tx to measure witness vs non-witness bytes."""
import json
import urllib.request
import sys
def decode_tx(txid):
"""Fetch and decode a transaction from blockchain.info."""
try:
resp = urllib.request.urlopen(
f'https://blockchain.info/rawtx/{txid}?format=hex',
timeout=10
)
return resp.read().decode().strip()
except Exception as e:
print(f" ⚠ Fetch failed: {e}")
return None
def estimate_inscription_size(tx_hex):
"""Rough estimate of inscription data size from hex."""
if not tx_hex:
return 0
# Very rough: count witness data in the last part of the tx
# A proper implementation would use a Bitcoin library to decode
total_bytes = len(tx_hex) // 2
return total_bytes
def main():
# Known inscription transaction (example — replace with actual)
example_txid = "b8b4c0d3e9f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6"
print("=" * 62)
print(" Inscription Transaction Size Verification")
print("=" * 62)
print(f"\n To verify, run with a real inscription txid:")
print(f" python3 research/verify_inscription_size.py <txid>")
print()
print(f" Example:")
print(f" from mempool.space, find a recent inscription")
print(f" copy its txid and pass as argument")
print()
print(f" Expected result:")
print(f" Total tx size: ~1,500 bytes")
print(f" Witness data: ~400 bytes (inscription envelope)")
print(f" Non-witness: ~1,100 bytes (inputs, outputs, header)")
print(f" Block weight: 400×1 + 1100×4 = 4,800 WU = 1,200 vbytes")
if __name__ == "__main__":
txid = sys.argv[1] if len(sys.argv) > 1 else None
if txid:
tx_hex = decode_tx(txid)
size = estimate_inscription_size(tx_hex)
print(f"\n Transaction {txid}: ~{size} bytes")
else:
main()
| Parameter | Low Estimate | Base Estimate | High Estimate | Impact on Cost/Byte |
|---|---|---|---|---|
| Hardware cost/3yr | $200 (RPi 4 only) | $500 | $1,000 (high-end mini PC) | ±40% |
| Bandwidth/mo | $30 (budget ISP) | $50 | $100 (business grade) | ±50% |
| Electricity/kWh | $0.08 (low cost area) | $0.12 | $0.40 (high cost area) | ±90% |
| Node power draw | 30W (mini PC) | 150W | 250W (desktop) | ±80% |
| Inscription size | 200 bytes | 400 bytes | 1,000 bytes | ±60% |
| Inscription volume/mo | 50,000 | 100,000 | 300,000 | ±60% |
Key finding: Even at the most conservative estimates (lowest hardware, lowest bandwidth, most efficient node), the cost per byte per year is within ~2× of the base estimate. The conclusion — that storage cost is orders of magnitude below current fees — is robust.
# 1. Run the cost model
python3 research/utxo_cost_model.py
# 2. Verify inscription stats (requires internet)
python3 research/fetch_inscription_stats.py
# 3. Verify transaction structure (requires a txid)
python3 research/verify_inscription_size.py <txid>
# 4. Check BIP-141 weight formula
# https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
# 5. Compare with live fee market
# curl https://mempool.space/api/v1/fees/recommended
| Aspect | storage-ratio.js (SCCR) | utxo_cost_model.py (inscription externality) |
|---|---|---|
| Quantity estimated | cb for Storage Cost Coverage Ratio (all block data) | cb for marginal inscription burden |
| Annual node cost C | 925 | 924.35 (component sum, rounded) |
| Denominator | B_block × blocks/yr = 7.8894e10 bytes/yr (all block bytes) | 400 bytes × 100K/mo × 12 = 4.8e8 bytes/yr (inscription-only) |
| cb current | 1.172459e-7 | 1.925729e-6 |
| cb corrected | 1.172459e-8 (horizon-free) | unchanged |
| Attribution | block-average | marginal |
| Horizon T | twice (bug: /T in denominator L48/77 AND ×T in L33) | once (×T in final lifetime only) |
Decomposition: the observed 16.4× gap between the two models = 164× denominator gap ÷ 10× time-horizon bug.
Verdict: Finding 1 is primarily intentional-but-unexplained differing denominators (164×), partially masked by the 10× bug; Finding 2 is a confirmed 10× dimensional error (cb inflated ×10, ratio deflated ×10).
Internal validation note (v2.0.0). Internal validation identified an inconsistency in the Storage Cost Coverage Ratio implementation, traced it to a duplicated time-horizon term, corrected the implementation, regenerated all reported values, and confirmed the qualitative conclusions unchanged. Specifically, methodology.json v1.0.0 and tools/research/storage-ratio.js applied the storage horizon T twice — once dividing the denominator of cost-per-byte-per-year (nodeCostPerYear / (avgBlockSizeBytes × blocksPerYear / yearsOfStorage)) and again in the lifetime-cost product (bytes × cb × years). This inflated modeled storage cost — and deflated the coverage ratio — by exactly 10×. The canonical definition of cb is horizon-free (cb = C / bytes-per-year); T enters only throughL = cb × B × T. After the correction, the per-node lifetime storage cost of an average block falls from $1.76 to $0.176, the network figure from $105.5K to $10.6K, and the average coverage ratio rises from 0.0172 to 0.1719 (canonical capture, 2026-08-01; 0.121–0.218 across the full capture log). All sampled blocks remain below the 1.0 threshold, so the direction of the finding — transaction fees do not cover the estimated storage externality — is unchanged. A second discrepancy, the 16.4× gap between the two cost models, was traced to intentionally different attribution denominators (block-average vs inscription-marginal byte counts; denominator ratio 164×), not to the time-horizon bug; it is documented in the Model Reconciliation table. All quantities in this paper are regenerated from research/model-spec.json; no script redefines a model constant. The correction increased the estimated SCCR by an order of magnitude but did not reverse the paper's qualitative conclusion. This distinction is important: the implementation error affected the estimated magnitude of the measurement, whereas the underlying hypothesis was evaluated against the corrected model and remained supported under the paper's assumptions (working paper §6.5).