Skip to content

Instantly share code, notes, and snippets.

@hkalodner
Last active February 1, 2024 15:33
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save hkalodner/b389a5009969de6b6141031829c9640b to your computer and use it in GitHub Desktop.
Save hkalodner/b389a5009969de6b6141031829c9640b to your computer and use it in GitHub Desktop.
BlockSci UTXO age distribution
import blocksci
import pandas as pd
import numpy as np
chain = blocksci.Blockchain("/blocksci/bitcoin")
block_day = 6 * 24
block_year = 365 * block_day
block_week = 7 * block_day
block_month = block_year // 12
block_3month = block_year // 4
block_6month = block_year // 2
cutoffs = [0,
block_day,
block_week,
block_month,
block_3month,
block_6month,
block_year,
block_year + block_6month,
block_year * 2,
block_year * 3,
block_year * 5,
block_year * 20
]
buckets = list(zip(cutoffs[:-1], cutoffs[1:]))
bucket_names = [
"<1d",
"1d-1w",
"1w-1m",
"1-3m",
"3-6m",
"6-12m",
"12-18m",
"18-24m",
"2-3y",
"3-5y",
">5y"
]
def calculateImpl(blocks):
totals = np.zeros((len(chain), len(buckets)))
for block in blocks:
never_spent = np.sum(block.outputs.unspent.value)
spent_outputs = block.outputs.spending_tx.has_value
output_heights = block.outputs.spending_tx.block_height.with_value
output_ages = output_heights - block.height
output_values = block.outputs.value[spent_outputs]
age_sort = np.argsort(output_ages)
sorted_ages = output_ages[age_sort]
if len(output_values) > 0:
sorted_values = output_values[age_sort]
cuts = np.searchsorted(sorted_ages, cutoffs, side="left")
out = np.add.reduceat(np.concatenate((sorted_values, np.zeros(1, dtype=int))), cuts)
stop_points = np.empty_like(cuts)
stop_points[:-1] = cuts[1:]
stop_points[-1] = len(sorted_values)
out[cuts == stop_points] = 0
for i, cut in enumerate(zip(cuts[:-1], cuts[1:])):
np.subtract.at(totals, (output_heights[age_sort][cut[0]:cut[1]], i), sorted_values[cut[0]:cut[1]])
else:
out = np.zeros(len(cutoffs))
out[-1] = never_spent
out = out[::-1].cumsum()[::-1]
for i, (start, end) in enumerate(buckets):
if block.height + start < len(chain):
totals[block.height + start, i] += out[i]
if i > 0:
totals[block.height + start, i - 1] -= out[i]
return totals
def calculateNet(blocks):
return calculateImpl(blocks).cumsum(axis=0)
def calculateNetMulti(chain):
def mapFunc(blocks):
return [calculateImpl(blocks)]
def reduceFunc(accum, new_val):
accum.extend(new_val)
return accum
parts = chain.mapreduce_block_ranges(mapFunc, reduceFunc)
return sum(parts).cumsum(axis=0)
data = calculateNetMulti(chain)
df = pd.DataFrame(data, index=chain.blocks.time)
total = df.sum(axis=1)
for i in range(11):
df[i] /= total
df.columns = bucket_names
df.plot.area()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment