forked from PiRK/ElectrumABC
/
asert_daa.py
183 lines (153 loc) · 6.23 KB
/
asert_daa.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# Electrum ABC - lightweight eCash client
# Copyright (C) 2020 The Electron Cash developers
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
from collections import namedtuple
from typing import Optional, Union
from .printerror import print_error
def bits_to_target(bits: int) -> int:
size = bits >> 24
assert size <= 0x1D
word = bits & 0x00FFFFFF
assert 0x8000 <= word <= 0x7FFFFF
if size <= 3:
return word >> (8 * (3 - size))
else:
return word << (8 * (size - 3))
def _get_asert_activation_mtp():
"""Returns 1605441600 (Nov 15, 2020 12:00:00 UTC) or whatever override may
be set by the env variable ASERT_MTP"""
default_mtp = 1605441600 # Nov 15, 2020 12:00:00 UTC
mtp = os.environ.get("ASERT_MTP", default_mtp)
try:
mtp = int(mtp)
except Exception:
pass
if not isinstance(mtp, int) or mtp <= 1510600000:
print_error(
"Error: Environment variable ASERT_MTP ignored because it is invalid: {}".format(
str(mtp)
)
)
mtp = default_mtp
if mtp != default_mtp:
print_error("ASERT_MTP of {} will be used".format(mtp))
return mtp
class Anchor(namedtuple("Anchor", "height bits prev_time")):
pass
class ASERTDaa:
"""Parameters and methods for the ASERT DAA. Instances of these live in
networks.TestNet, networks.MainNet as part of the chain params."""
MTP_ACTIVATION_TIME = (
_get_asert_activation_mtp()
) # Normally Nov. 15th, 2020 UTC 12:00:00
IDEAL_BLOCK_TIME = 10 * 60 # 10 mins
HALF_LIFE = 2 * 24 * 3600
# Integer implementation uses these for fixed point math
RBITS = 16 # number of bits after the radix for fixed-point math
RADIX = 1 << RBITS
# POW Limit
MAX_BITS = 0x1D00FFFF
MAX_TARGET = bits_to_target(MAX_BITS)
# If left as none, blockchain.py will calculate this at runtime as we read headers.
anchor: Optional[Anchor] = None
@staticmethod
def bits_to_target(bits: int) -> int:
return bits_to_target(bits)
def target_to_bits(self, target: int) -> int:
assert target > 0
if target > self.MAX_TARGET:
print_error(
"Warning: target went above maximum ({} > {})".format(
target, self.MAX_TARGET
)
)
target = self.MAX_TARGET
size = (target.bit_length() + 7) // 8
mask64 = 0xFFFFFFFFFFFFFFFF
if size <= 3:
compact = (target & mask64) << (8 * (3 - size))
else:
compact = (target >> (8 * (size - 3))) & mask64
if compact & 0x00800000:
compact >>= 8
size += 1
assert compact == (compact & 0x007FFFFF)
assert size < 256
return compact | size << 24
@staticmethod
def bits_to_work(bits: int) -> int:
return (2 << 255) // (bits_to_target(bits) + 1)
@staticmethod
def target_to_hex(target: int) -> str:
h = hex(target)[2:]
return "0" * (64 - len(h)) + h
def next_bits_aserti3_2d(
self, anchor_bits: int, time_diff: Union[float, int], height_diff: int
) -> int:
"""Integer ASERTI algorithm, based on Jonathan Toomim's
`next_bits_aserti` implementation in mining.py (see
https://github.com/jtoomim/difficulty)"""
target = self.bits_to_target(anchor_bits)
# Ultimately, we want to approximate the following ASERT formula, using
# only integer (fixed-point) math:
# new_target = old_target * 2^((time_diff -
# IDEAL_BLOCK_TIME*(height_diff+1)) / HALF_LIFE)
# First, we'll calculate the exponent, using floor division. The
# assertion checks a type constraint of the C++ implementation which
# uses a 64-bit signed integer for the exponent. If inputs violate that,
# then the implementation will diverge.
assert abs(time_diff - self.IDEAL_BLOCK_TIME * (height_diff + 1)) < (
1 << (63 - self.RBITS)
)
exponent = int(
((time_diff - self.IDEAL_BLOCK_TIME * (height_diff + 1)) * self.RADIX)
/ self.HALF_LIFE
)
# Next, we use the 2^x = 2 * 2^(x-1) identity to shift our exponent into the (0, 1] interval.
shifts = exponent >> self.RBITS
exponent -= shifts * self.RADIX
assert exponent >= 0 and exponent < 65536
# Now we compute an approximated target * 2^(fractional part) * 65536
# target * 2^x ~= target * (1 + 0.695502049*x + 0.2262698*x**2 + 0.0782318*x**3)
target *= self.RADIX + (
(
195766423245049 * exponent
+ 971821376 * exponent**2
+ 5127 * exponent**3
+ 2**47
)
>> (self.RBITS * 3)
)
# Next, we shift to multiply by 2^(integer part). Python doesn't allow
# shifting by negative integers, so:
if shifts < 0:
target >>= -shifts
else:
target <<= shifts
# Remove the 65536 multiplier we got earlier
target >>= self.RBITS
if target == 0:
return self.target_to_bits(1)
if target > self.MAX_TARGET:
return self.MAX_BITS
return self.target_to_bits(target)