add configurable checkpoint to blockchain verification; use genesis as default
This commit is contained in:
parent
85f2f667c3
commit
dd0b018a35
|
@ -45,11 +45,13 @@ ADDRTYPE_P2WPKH = 6
|
|||
XPRV_HEADER = 0x0488ade4
|
||||
XPUB_HEADER = 0x0488b21e
|
||||
HEADERS_URL = "https://headers.electrum.org/blockchain_headers"
|
||||
GENESIS = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
|
||||
|
||||
def set_testnet():
|
||||
global ADDRTYPE_P2PKH, ADDRTYPE_P2SH, ADDRTYPE_P2WPKH
|
||||
global XPRV_HEADER, XPUB_HEADER
|
||||
global TESTNET, HEADERS_URL
|
||||
global GENESIS
|
||||
TESTNET = True
|
||||
ADDRTYPE_P2PKH = 111
|
||||
ADDRTYPE_P2SH = 196
|
||||
|
@ -57,18 +59,21 @@ def set_testnet():
|
|||
XPRV_HEADER = 0x04358394
|
||||
XPUB_HEADER = 0x043587cf
|
||||
HEADERS_URL = "https://headers.electrum.org/testnet_headers"
|
||||
GENESIS = "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
|
||||
|
||||
def set_nolnet():
|
||||
global ADDRTYPE_P2PKH, ADDRTYPE_P2SH, ADDRTYPE_P2WPKH
|
||||
global XPRV_HEADER, XPUB_HEADER
|
||||
global NOLNET, HEADERS_URL
|
||||
NOLNET = True
|
||||
global GENESIS
|
||||
TESTNET = True
|
||||
ADDRTYPE_P2PKH = 0
|
||||
ADDRTYPE_P2SH = 5
|
||||
ADDRTYPE_P2WPKH = 6
|
||||
XPRV_HEADER = 0x0488ade4
|
||||
XPUB_HEADER = 0x0488b21e
|
||||
HEADERS_URL = "https://headers.electrum.org/nolnet_headers"
|
||||
GENESIS = "663c88be18d07c45f87f910b93a1a71ed9ef1946cad50eb6a6f3af4c424625c6"
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -37,7 +37,9 @@ class Blockchain(util.PrintError):
|
|||
def __init__(self, config, network):
|
||||
self.config = config
|
||||
self.network = network
|
||||
self.local_height = 0
|
||||
self.checkpoint_height = self.config.get('checkpoint_height', 0)
|
||||
self.checkpoint_hash = self.config.get('checkpoint_value', bitcoin.GENESIS)
|
||||
self.check_truncate_headers()
|
||||
self.set_local_height()
|
||||
|
||||
def height(self):
|
||||
|
@ -55,11 +57,19 @@ class Blockchain(util.PrintError):
|
|||
|
||||
def verify_header(self, header, prev_header, bits, target):
|
||||
prev_hash = self.hash_header(prev_header)
|
||||
assert prev_hash == header.get('prev_block_hash'), "prev hash mismatch: %s vs %s" % (prev_hash, header.get('prev_block_hash'))
|
||||
if bitcoin.TESTNET or bitcoin.NOLNET: return
|
||||
assert bits == header.get('bits'), "bits mismatch: %s vs %s" % (bits, header.get('bits'))
|
||||
_hash = self.hash_header(header)
|
||||
assert int('0x' + _hash, 16) <= target, "insufficient proof of work: %s vs target %s" % (int('0x' + _hash, 16), target)
|
||||
if prev_hash != header.get('prev_block_hash'):
|
||||
raise BaseException("prev hash mismatch: %s vs %s" % (prev_hash, header.get('prev_block_hash')))
|
||||
if self.checkpoint_height == header.get('block_height') and self.checkpoint_hash != _hash:
|
||||
raise BaseException('failed checkpoint')
|
||||
if self.checkpoint_height == header.get('block_height'):
|
||||
self.print_error("validated checkpoint", self.checkpoint_height)
|
||||
if bitcoin.TESTNET:
|
||||
return
|
||||
if bits != header.get('bits'):
|
||||
raise BaseException("bits mismatch: %s vs %s" % (bits, header.get('bits')))
|
||||
if int('0x' + _hash, 16) > target:
|
||||
raise BaseException("insufficient proof of work: %s vs target %s" % (int('0x' + _hash, 16), target))
|
||||
|
||||
def verify_chain(self, chain):
|
||||
first_header = chain[0]
|
||||
|
@ -78,7 +88,7 @@ class Blockchain(util.PrintError):
|
|||
bits, target = self.get_target(index)
|
||||
for i in range(num):
|
||||
raw_header = data[i*80:(i+1) * 80]
|
||||
header = self.deserialize_header(raw_header)
|
||||
header = self.deserialize_header(raw_header, index*2016 + i)
|
||||
self.verify_header(header, prev_header, bits, target)
|
||||
prev_header = header
|
||||
|
||||
|
@ -91,7 +101,7 @@ class Blockchain(util.PrintError):
|
|||
+ int_to_hex(int(res.get('nonce')), 4)
|
||||
return s
|
||||
|
||||
def deserialize_header(self, s):
|
||||
def deserialize_header(self, s, height):
|
||||
hex_to_int = lambda s: int('0x' + s[::-1].encode('hex'), 16)
|
||||
h = {}
|
||||
h['version'] = hex_to_int(s[0:4])
|
||||
|
@ -100,6 +110,7 @@ class Blockchain(util.PrintError):
|
|||
h['timestamp'] = hex_to_int(s[68:72])
|
||||
h['bits'] = hex_to_int(s[72:76])
|
||||
h['nonce'] = hex_to_int(s[76:80])
|
||||
h['block_height'] = height
|
||||
return h
|
||||
|
||||
def hash_header(self, header):
|
||||
|
@ -146,6 +157,7 @@ class Blockchain(util.PrintError):
|
|||
self.set_local_height()
|
||||
|
||||
def set_local_height(self):
|
||||
self.local_height = 0
|
||||
name = self.path()
|
||||
if os.path.exists(name):
|
||||
h = os.path.getsize(name)/80 - 1
|
||||
|
@ -160,10 +172,25 @@ class Blockchain(util.PrintError):
|
|||
h = f.read(80)
|
||||
f.close()
|
||||
if len(h) == 80:
|
||||
h = self.deserialize_header(h)
|
||||
h = self.deserialize_header(h, block_height)
|
||||
return h
|
||||
|
||||
def check_truncate_headers(self):
|
||||
checkpoint = self.read_header(self.checkpoint_height)
|
||||
if checkpoint is None:
|
||||
return
|
||||
if self.hash_header(checkpoint) == self.checkpoint_hash:
|
||||
return
|
||||
self.print_error('Truncating headers file at height %d'%self.checkpoint_height)
|
||||
name = self.path()
|
||||
f = open(name, 'rwb+')
|
||||
f.seek(self.checkpoint_height * 80)
|
||||
f.truncate()
|
||||
f.close()
|
||||
|
||||
def get_target(self, index, chain=None):
|
||||
if bitcoin.TESTNET:
|
||||
return 0, 0
|
||||
if index == 0:
|
||||
return 0x1d00ffff, MAX_TARGET
|
||||
first = self.read_header((index-1) * 2016)
|
||||
|
@ -176,9 +203,11 @@ class Blockchain(util.PrintError):
|
|||
# bits to target
|
||||
bits = last.get('bits')
|
||||
bitsN = (bits >> 24) & 0xff
|
||||
assert bitsN >= 0x03 and bitsN <= 0x1d, "First part of bits should be in [0x03, 0x1d]"
|
||||
if not (bitsN >= 0x03 and bitsN <= 0x1d):
|
||||
raise BaseException("First part of bits should be in [0x03, 0x1d]")
|
||||
bitsBase = bits & 0xffffff
|
||||
assert bitsBase >= 0x8000 and bitsBase <= 0x7fffff, "Second part of bits should be in [0x8000, 0x7fffff]"
|
||||
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')
|
||||
|
|
|
@ -737,6 +737,9 @@ class Network(util.DaemonThread):
|
|||
|
||||
def on_get_chunk(self, interface, response):
|
||||
'''Handle receiving a chunk of block headers'''
|
||||
if response.get('error'):
|
||||
interface.print_error(response.get('error'))
|
||||
return
|
||||
if self.bc_requests:
|
||||
req_if, data = self.bc_requests[0]
|
||||
req_idx = data.get('chunk_idx')
|
||||
|
|
Loading…
Reference in New Issue