Many comments

This commit is contained in:
Vitalik Buterin 2015-01-12 21:32:26 -05:00
parent 5549c48500
commit de2c4bffa3
1 changed files with 71 additions and 42 deletions

View File

@ -2,98 +2,124 @@ import math, random
hashpower = [float(x) for x in open('hashpower.csv').readlines()] hashpower = [float(x) for x in open('hashpower.csv').readlines()]
target = 12 # Target block time
seconds_in_day = 86400 TARGET = 12
ema_factor = 0.01 # Should be 86400, but can reduce for a quicker sim
f = 20 SECONDS_IN_DAY = 86400
sqrf = 3 # Look at the 1/x day exponential moving average
threshold = 1.3 EMA_FACTOR = 0.01
adj_factor = 0.01 # Damping factor for simple difficulty adjustment
maxadjust = 0.5 SIMPLE_ADJUST_DAMPING_FACTOR = 20
blks_back = 10 # Maximum per-block diff adjustment (as fraction of current diff)
SIMPLE_ADJUST_MAX = 0.5
# Damping factor for quadratic difficulty adjustment
QUADRATIC_ADJUST_DAMPING_FACTOR = 3
# Maximum per-block diff adjustment (as fraction of current diff)
QUADRATIC_ADJUST_MAX = 0.5
# Threshold for bounded adjustor
BOUNDED_ADJUST_THRESHOLD = 1.3
# Bounded adjustment factor
BOUNDED_ADJUST_FACTOR = 0.01
# How many blocks back to look
BLKS_BACK = 10
# Produces a value according to the exponential distribution; used
# to determine the time until the next block given an average block
# time of t
def expdiff(t): def expdiff(t):
return -math.log(random.random()) * t return -math.log(random.random()) * t
def calc_threshold_time(p, t): # abs_sqr(3) = 9, abs_sqr(-7) = -49, etc
return t * -math.log(1 - p)
def abs_sqr(x): def abs_sqr(x):
return -(x**2) if x < 0 else x**2 return -(x**2) if x < 0 else x**2
# Given an array of the most recent timestamps, and the most recent
# difficulties, compute the next difficulty
def simple_adjust(timestamps, diffs): def simple_adjust(timestamps, diffs):
if len(timestamps) < blks_back + 2: if len(timestamps) < BLKS_BACK + 2:
return diffs[-1] return diffs[-1]
# Total interval between previous block and block a bit further back # Total interval between previous block and block a bit further back
delta = timestamps[-2] - timestamps[-2-blks_back] + 0.0 delta = timestamps[-2] - timestamps[-2-BLKS_BACK] + 0.0
# Expected interval # Expected interval
expected = target * blks_back expected = TARGET * BLKS_BACK
fac = max(min(1 - (delta / expected - 1) / f, 1+maxadjust), 1-maxadjust) # Compute adjustment factor
fac = 1 - (delta / expected - 1) / SIMPLE_ADJUST_DAMPING_FACTOR
fac = max(min(fac, 1 + SIMPLE_ADJUST_MAX), 1 - SIMPLE_ADJUST_MAX)
return diffs[-1] * fac return diffs[-1] * fac
# Alternative adjustment algorithm
def quadratic_adjust(timestamps, diffs): def quadratic_adjust(timestamps, diffs):
if len(timestamps) < blks_back + 2: if len(timestamps) < BLKS_BACK + 2:
return diffs[-1] return diffs[-1]
# Total interval between previous block and block a bit further back # Total interval between previous block and block a bit further back
delta = timestamps[-2] - timestamps[-2-blks_back] + 0.0 delta = timestamps[-2] - timestamps[-2-BLKS_BACK] + 0.0
# Expected interval # Expected interval
expected = target * blks_back expected = TARGET * BLKS_BACK
fac = max(min(1 - abs_sqr(delta / expected - 1) / sqrf, # Compute adjustment factor
1+maxadjust), 1-maxadjust) fac = 1 - abs_sqr(delta / expected - 1) / QUADRATIC_ADJUST_DAMPING_FACTOR
fac = max(min(fac, 1 + QUADRATIC_ADJUST_MAX), 1 - QUADRATIC_ADJUST_MAX)
return diffs[-1] * fac return diffs[-1] * fac
# Alternative adjustment algorithm
def bounded_adjust(timestamps, diffs): def bounded_adjust(timestamps, diffs):
if len(timestamps) < blks_back + 2: if len(timestamps) < BLKS_BACK + 2:
return diffs[-1] return diffs[-1]
# Total interval between previous block and block a bit further back # Total interval between previous block and block a bit further back
delta = timestamps[-2] - timestamps[-2-blks_back] + 0.0 delta = timestamps[-2] - timestamps[-2-BLKS_BACK] + 0.0
# Expected interval # Expected interval
expected = target * blks_back expected = TARGET * BLKS_BACK
if delta / expected > threshold: if delta / expected > BOUNDED_ADJUST_THRESHOLD:
fac = (1 - adj_factor) fac = (1 - BOUNDED_ADJUST_FACTOR)
elif delta / expected < 1 / threshold: elif delta / expected < 1 / BOUNDED_ADJUST_THRESHOLD:
fac = (1 + adj_factor) ** (delta / expected) fac = (1 + BOUNDED_ADJUST_FACTOR) ** (delta / expected)
else: else:
fac = 1 fac = 1
return diffs[-1] * fac return diffs[-1] * fac
def test(source, adjust): def test(source, adjust):
ema = maxema = minema = target # Variables to keep track of for stats purposes
ema = maxema = minema = TARGET
lthalf, gtdouble, lttq, gtft = 0, 0, 0, 0 lthalf, gtdouble, lttq, gtft = 0, 0, 0, 0
times = [0]
diffs = [source[0]]
nextprint = 10**6
count = 0 count = 0
while times[-1] < len(source) * seconds_in_day: # Block times
times = [0]
# Block difficulty values
diffs = [source[0]]
# Next time to print status update
nextprint = 10**6
# Main loop
while times[-1] < len(source) * SECONDS_IN_DAY:
# Print status update every 10**6 seconds
if times[-1] > nextprint: if times[-1] > nextprint:
print '%d out of %d processed' % \ print '%d out of %d processed, ema %f' % \
(times[-1], len(source) * seconds_in_day) (times[-1], len(source) * SECONDS_IN_DAY, ema)
nextprint += 10**6 nextprint += 10**6
# Grab hashpower from data source # Grab hashpower from data source
hashpower = source[int(times[-1] // seconds_in_day)] hashpower = source[int(times[-1] // SECONDS_IN_DAY)]
# Calculate new difficulty # Calculate new difficulty
diffs.append(adjust(times, diffs)) diffs.append(adjust(times, diffs))
# Calculate next block time # Calculate next block time
times.append(times[-1] + expdiff(diffs[-1] / hashpower)) times.append(times[-1] + expdiff(diffs[-1] / hashpower))
# Calculate min and max ema # Calculate min and max ema
ema = ema * (1 - ema_factor) + (times[-1] - times[-2]) * ema_factor ema = ema * (1 - EMA_FACTOR) + (times[-1] - times[-2]) * EMA_FACTOR
minema = min(minema, ema) minema = min(minema, ema)
maxema = max(maxema, ema) maxema = max(maxema, ema)
count += 1 count += 1
if ema < target * 0.75: # Keep track of number of blocks we are below 75/50% or above
# 133/200% of target
if ema < TARGET * 0.75:
lttq += 1 lttq += 1
if ema < target * 0.5: if ema < TARGET * 0.5:
lthalf += 1 lthalf += 1
elif ema > target * 1.33333: elif ema > TARGET * 1.33333:
gtft += 1 gtft += 1
if ema > target * 2: if ema > TARGET * 2:
gtdouble += 1 gtdouble += 1
# Pop items to save memory # Pop items to save memory
if len(times) > 2000: if len(times) > 2000:
@ -104,3 +130,6 @@ def test(source, adjust):
'ema > double', gtdouble * 1.0 / count, \ 'ema > double', gtdouble * 1.0 / count, \
'ema < 3/4', lttq * 1.0 / count, \ 'ema < 3/4', lttq * 1.0 / count, \
'ema > 4/3', gtft * 1.0 / count 'ema > 4/3', gtft * 1.0 / count
# Example usage
# blkdiff.test(blkdiff.hashpower, blkdiff.simple_adjust)