This repository has been archived by the owner on Feb 10, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 52
/
Copy pathblkdiff.py
150 lines (133 loc) · 5 KB
/
blkdiff.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import math, random
hashpower = [float(x) for x in open('hashpower.csv').readlines()]
# Target block time
TARGET = 12
# Should be 86400, but can reduce for a quicker sim
SECONDS_IN_DAY = 86400
# Look at the 1/x day exponential moving average
EMA_FACTOR = 0.01
# Damping factor for simple difficulty adjustment
SIMPLE_ADJUST_DAMPING_FACTOR = 20
# 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
# Naive difficulty adjustment factor
NAIVE_ADJUST_FACTOR = 1/1024.
# 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):
return -math.log(random.random()) * t
# abs_sqr(3) = 9, abs_sqr(-7) = -49, etc
def abs_sqr(x):
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):
if len(timestamps) < BLKS_BACK + 2:
return diffs[-1]
# Total interval between previous block and block a bit further back
delta = timestamps[-2] - timestamps[-2-BLKS_BACK] + 0.0
# Expected interval
expected = TARGET * BLKS_BACK
# 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
# Alternative adjustment algorithm
def quadratic_adjust(timestamps, diffs):
if len(timestamps) < BLKS_BACK + 2:
return diffs[-1]
# Total interval between previous block and block a bit further back
delta = timestamps[-2] - timestamps[-2-BLKS_BACK] + 0.0
# Expected interval
expected = TARGET * BLKS_BACK
# Compute adjustment factor
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
# Alternative adjustment algorithm
def bounded_adjust(timestamps, diffs):
if len(timestamps) < BLKS_BACK + 2:
return diffs[-1]
# Total interval between previous block and block a bit further back
delta = timestamps[-2] - timestamps[-2-BLKS_BACK] + 0.0
# Expected interval
expected = TARGET * BLKS_BACK
if delta / expected > BOUNDED_ADJUST_THRESHOLD:
fac = (1 - BOUNDED_ADJUST_FACTOR)
elif delta / expected < 1 / BOUNDED_ADJUST_THRESHOLD:
fac = (1 + BOUNDED_ADJUST_FACTOR) ** (delta / expected)
else:
fac = 1
return diffs[-1] * fac
# Old Ethereum algorithm
def old_adjust(timestamps, diffs):
if len(timestamps) < 2:
return diffs[-1]
delta = timestamps[-1] - timestamps[-2]
expected = TARGET * 0.693
if delta > expected:
fac = 1 - NAIVE_ADJUST_FACTOR
else:
fac = 1 + NAIVE_ADJUST_FACTOR
return diffs[-1] * fac
def test(source, adjust):
# Variables to keep track of for stats purposes
ema = maxema = minema = TARGET
lthalf, gtdouble, lttq, gtft = 0, 0, 0, 0
count = 0
# 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:
print '%d out of %d processed, ema %f' % \
(times[-1], len(source) * SECONDS_IN_DAY, ema)
nextprint += 10**6
# Grab hashpower from data source
hashpower = source[int(times[-1] // SECONDS_IN_DAY)]
# Calculate new difficulty
diffs.append(adjust(times, diffs))
# Calculate next block time
times.append(times[-1] + expdiff(diffs[-1] / hashpower))
# Calculate min and max ema
ema = ema * (1 - EMA_FACTOR) + (times[-1] - times[-2]) * EMA_FACTOR
minema = min(minema, ema)
maxema = max(maxema, ema)
count += 1
# Keep track of number of blocks we are below 75/50% or above
# 133/200% of target
if ema < TARGET * 0.75:
lttq += 1
if ema < TARGET * 0.5:
lthalf += 1
elif ema > TARGET * 1.33333:
gtft += 1
if ema > TARGET * 2:
gtdouble += 1
# Pop items to save memory
if len(times) > 2000:
times.pop(0)
diffs.pop(0)
print 'min', minema, 'max', maxema, 'avg', times[-1] / count, \
'ema < half', lthalf * 1.0 / count, \
'ema > double', gtdouble * 1.0 / count, \
'ema < 3/4', lttq * 1.0 / count, \
'ema > 4/3', gtft * 1.0 / count
# Example usage
# blkdiff.test(blkdiff.hashpower, blkdiff.simple_adjust)