1
0

Replace initial headers download with hardcoded checkpoints

This commit is contained in:
ThomasV
2017-12-05 18:03:07 +01:00
parent b4e43754e0
commit d1b8a6fae6
4 changed files with 1120 additions and 80 deletions

View File

@@ -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