⬡ BSAHI

Verification Appendix

Verification Appendix — UTXO Cost Model

Parameter Sources & Methodology

1. Hardware Cost: $500/3yr ($167/yr)

Sources:

Methodology:

2. Bandwidth: $50/mo ($600/yr)

Sources:

Methodology:

3. Electricity: $158/yr

Sources:

Methodology:

4. Inscription Size: 400 bytes UTXO data

Sources:

Methodology:

5. Inscription Volume: 100K/month

Sources:

Methodology:


Verification Scripts

Script 1: Fetch Real Inscription Stats

# 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()

Script 2: Verify Single Inscription Transaction

# 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()

Sensitivity Analysis

ParameterLow EstimateBase EstimateHigh EstimateImpact 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 draw30W (mini PC)150W250W (desktop)±80%
Inscription size200 bytes400 bytes1,000 bytes±60%
Inscription volume/mo50,000100,000300,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.


How to Reproduce Every Number

# 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

Open Verification Questions

  1. What fraction of nodes use high-power vs low-power hardware? — Bitnodes.io surveys could tell us. Currently assume 150W average. If 80% run on mini PCs (60W), the annual node cost drops to ~$700/yr.
  2. What is the actual UTXO set contribution per inscription? — Some inscriptions create multiple UTXOs. The envelope may be 400 bytes but the total UTXO footprint could be larger. Need to analyze a sample.
  3. How long do inscription UTXOs actually live? — Assume 10yr, but if inscription UTXOs are spent quickly (e.g., trading), the storage cost is lower. If they're never spent (collector behavior), the cost is higher (permanent).
  4. How many node operators are there? — Estimates range from 10,000 to 100,000 reachable nodes. The aggregate storage cost is $9.24K/yr. Per node: $0.09 to $0.92/yr. Is this economically significant?

Model Reconciliation (v2.0.0)

Aspectstorage-ratio.js (SCCR)utxo_cost_model.py (inscription externality)
Quantity estimatedcb for Storage Cost Coverage Ratio (all block data)cb for marginal inscription burden
Annual node cost C925924.35 (component sum, rounded)
DenominatorB_block × blocks/yr = 7.8894e10 bytes/yr (all block bytes)400 bytes × 100K/mo × 12 = 4.8e8 bytes/yr (inscription-only)
cb current1.172459e-71.925729e-6
cb corrected1.172459e-8 (horizon-free)unchanged
Attributionblock-averagemarginal
Horizon Ttwice (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).


Reproducibility

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 through L = 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).

← All research · ← Back to Learn