Replace initial headers download with hardcoded checkpoints
This commit is contained in:
@@ -60,8 +60,8 @@ def hash_header(header):
|
||||
|
||||
blockchains = {}
|
||||
|
||||
def read_blockchains(config):
|
||||
blockchains[0] = Blockchain(config, 0, None)
|
||||
def read_blockchains(config, checkpoints):
|
||||
blockchains[0] = Blockchain(config, checkpoints, 0, None)
|
||||
fdir = os.path.join(util.get_headers_dir(config), 'forks')
|
||||
if not os.path.exists(fdir):
|
||||
os.mkdir(fdir)
|
||||
@@ -70,7 +70,7 @@ def read_blockchains(config):
|
||||
for filename in l:
|
||||
checkpoint = int(filename.split('_')[2])
|
||||
parent_id = int(filename.split('_')[1])
|
||||
b = Blockchain(config, checkpoint, parent_id)
|
||||
b = Blockchain(config, checkpoints, checkpoint, parent_id)
|
||||
blockchains[b.checkpoint] = b
|
||||
return blockchains
|
||||
|
||||
@@ -94,10 +94,11 @@ class Blockchain(util.PrintError):
|
||||
Manages blockchain headers and their verification
|
||||
"""
|
||||
|
||||
def __init__(self, config, checkpoint, parent_id):
|
||||
def __init__(self, config, checkpoints, checkpoint, parent_id):
|
||||
self.config = config
|
||||
self.catch_up = None # interface catching up
|
||||
self.checkpoint = checkpoint
|
||||
self.checkpoints = checkpoints
|
||||
self.parent_id = parent_id
|
||||
self.lock = threading.Lock()
|
||||
with self.lock:
|
||||
@@ -127,7 +128,7 @@ class Blockchain(util.PrintError):
|
||||
|
||||
def fork(parent, header):
|
||||
checkpoint = header.get('block_height')
|
||||
self = Blockchain(parent.config, checkpoint, parent.checkpoint)
|
||||
self = Blockchain(parent.config, parent.checkpoints, checkpoint, parent.checkpoint)
|
||||
open(self.path(), 'w+').close()
|
||||
self.save_header(header)
|
||||
return self
|
||||
@@ -143,13 +144,13 @@ class Blockchain(util.PrintError):
|
||||
p = self.path()
|
||||
self._size = os.path.getsize(p)//80 if os.path.exists(p) else 0
|
||||
|
||||
def verify_header(self, header, prev_header, bits, target):
|
||||
prev_hash = hash_header(prev_header)
|
||||
def verify_header(self, header, prev_hash, target):
|
||||
_hash = hash_header(header)
|
||||
if prev_hash != header.get('prev_block_hash'):
|
||||
raise BaseException("prev hash mismatch: %s vs %s" % (prev_hash, header.get('prev_block_hash')))
|
||||
if bitcoin.NetworkConstants.TESTNET:
|
||||
return
|
||||
bits = self.target_to_bits(target)
|
||||
if bits != header.get('bits'):
|
||||
raise BaseException("bits mismatch: %s vs %s" % (bits, header.get('bits')))
|
||||
if int('0x' + _hash, 16) > target:
|
||||
@@ -157,15 +158,13 @@ class Blockchain(util.PrintError):
|
||||
|
||||
def verify_chunk(self, index, data):
|
||||
num = len(data) // 80
|
||||
prev_header = None
|
||||
if index != 0:
|
||||
prev_header = self.read_header(index * 2016 - 1)
|
||||
bits, target = self.get_target(index)
|
||||
prev_hash = self.get_hash(index * 2016 - 1)
|
||||
target = self.get_target(index-1)
|
||||
for i in range(num):
|
||||
raw_header = data[i*80:(i+1) * 80]
|
||||
header = deserialize_header(raw_header, index*2016 + i)
|
||||
self.verify_header(header, prev_header, bits, target)
|
||||
prev_header = header
|
||||
self.verify_header(header, prev_hash, target)
|
||||
prev_hash = hash_header(header)
|
||||
|
||||
def path(self):
|
||||
d = util.get_headers_dir(self.config)
|
||||
@@ -250,69 +249,82 @@ class Blockchain(util.PrintError):
|
||||
with open(name, 'rb') as f:
|
||||
f.seek(delta * 80)
|
||||
h = f.read(80)
|
||||
if h == bytes([0])*80:
|
||||
return None
|
||||
return deserialize_header(h, height)
|
||||
|
||||
def get_hash(self, height):
|
||||
return hash_header(self.read_header(height))
|
||||
|
||||
def BIP9(self, height, flag):
|
||||
v = self.read_header(height)['version']
|
||||
return ((v & 0xE0000000) == 0x20000000) and ((v & flag) == flag)
|
||||
|
||||
def segwit_support(self, N=144):
|
||||
h = self.local_height
|
||||
return sum([self.BIP9(h-i, 2) for i in range(N)])*10000/N/100.
|
||||
if height == -1:
|
||||
return '0000000000000000000000000000000000000000000000000000000000000000'
|
||||
elif height == 0:
|
||||
return bitcoin.NetworkConstants.GENESIS
|
||||
elif height < len(self.checkpoints) * 2016:
|
||||
assert (height+1) % 2016 == 0
|
||||
index = height // 2016
|
||||
h, t = self.checkpoints[index]
|
||||
return h
|
||||
else:
|
||||
return hash_header(self.read_header(height))
|
||||
|
||||
def get_target(self, index):
|
||||
# compute target from chunk x, used in chunk x+1
|
||||
if bitcoin.NetworkConstants.TESTNET:
|
||||
return 0, 0
|
||||
if index == 0:
|
||||
if index == -1:
|
||||
return 0x1d00ffff, MAX_TARGET
|
||||
first = self.read_header((index-1) * 2016)
|
||||
last = self.read_header(index*2016 - 1)
|
||||
# bits to target
|
||||
if index < len(self.checkpoints):
|
||||
h, t = self.checkpoints[index]
|
||||
return t
|
||||
# new target
|
||||
first = self.read_header(index * 2016)
|
||||
last = self.read_header(index * 2016 + 2015)
|
||||
bits = last.get('bits')
|
||||
target = self.bits_to_target(bits)
|
||||
nActualTimespan = last.get('timestamp') - first.get('timestamp')
|
||||
nTargetTimespan = 14 * 24 * 60 * 60
|
||||
nActualTimespan = max(nActualTimespan, nTargetTimespan // 4)
|
||||
nActualTimespan = min(nActualTimespan, nTargetTimespan * 4)
|
||||
new_target = min(MAX_TARGET, (target * nActualTimespan) // nTargetTimespan)
|
||||
return new_target
|
||||
|
||||
def bits_to_target(self, bits):
|
||||
bitsN = (bits >> 24) & 0xff
|
||||
if not (bitsN >= 0x03 and bitsN <= 0x1d):
|
||||
raise BaseException("First part of bits should be in [0x03, 0x1d]")
|
||||
bitsBase = bits & 0xffffff
|
||||
if not (bitsBase >= 0x8000 and bitsBase <= 0x7fffff):
|
||||
raise BaseException("Second part of bits should be in [0x8000, 0x7fffff]")
|
||||
target = bitsBase << (8 * (bitsN-3))
|
||||
# new target
|
||||
nActualTimespan = last.get('timestamp') - first.get('timestamp')
|
||||
nTargetTimespan = 14 * 24 * 60 * 60
|
||||
nActualTimespan = max(nActualTimespan, nTargetTimespan // 4)
|
||||
nActualTimespan = min(nActualTimespan, nTargetTimespan * 4)
|
||||
new_target = min(MAX_TARGET, (target * nActualTimespan) // nTargetTimespan)
|
||||
# convert new target to bits
|
||||
c = ("%064x" % new_target)[2:]
|
||||
return bitsBase << (8 * (bitsN-3))
|
||||
|
||||
def target_to_bits(self, target):
|
||||
c = ("%064x" % target)[2:]
|
||||
while c[:2] == '00' and len(c) > 6:
|
||||
c = c[2:]
|
||||
bitsN, bitsBase = len(c) // 2, int('0x' + c[:6], 16)
|
||||
if bitsBase >= 0x800000:
|
||||
bitsN += 1
|
||||
bitsBase >>= 8
|
||||
new_bits = bitsN << 24 | bitsBase
|
||||
return new_bits, bitsBase << (8 * (bitsN - 3))
|
||||
return bitsN << 24 | bitsBase
|
||||
|
||||
def can_connect(self, header, check_height=True):
|
||||
height = header['block_height']
|
||||
if check_height and self.height() != height - 1:
|
||||
#self.print_error("cannot connect at height", height)
|
||||
return False
|
||||
if height == 0:
|
||||
return hash_header(header) == bitcoin.NetworkConstants.GENESIS
|
||||
previous_header = self.read_header(height -1)
|
||||
if not previous_header:
|
||||
return False
|
||||
prev_hash = hash_header(previous_header)
|
||||
if prev_hash != header.get('prev_block_hash'):
|
||||
return False
|
||||
bits, target = self.get_target(height // 2016)
|
||||
try:
|
||||
self.verify_header(header, previous_header, bits, target)
|
||||
prev_hash = self.get_hash(height - 1)
|
||||
except:
|
||||
return False
|
||||
if prev_hash != header.get('prev_block_hash'):
|
||||
self.print_error("bad hash", height, prev_hash, header.get('prev_block_hash'))
|
||||
return False
|
||||
target = self.get_target(height // 2016 - 1)
|
||||
try:
|
||||
self.verify_header(header, prev_hash, target)
|
||||
except BaseException as e:
|
||||
return False
|
||||
return True
|
||||
|
||||
def connect_chunk(self, idx, hexdata):
|
||||
@@ -325,3 +337,13 @@ class Blockchain(util.PrintError):
|
||||
except BaseException as e:
|
||||
self.print_error('verify_chunk failed', str(e))
|
||||
return False
|
||||
|
||||
def get_checkpoints(self):
|
||||
# for each chunk, store the hash of the last block and the target after the chunk
|
||||
cp = []
|
||||
n = self.height() // 2016
|
||||
for index in range(n):
|
||||
h = self.get_hash((index+1) * 2016 -1)
|
||||
target = self.get_target(index)
|
||||
cp.append((h, target))
|
||||
return cp
|
||||
|
||||
Reference in New Issue
Block a user