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

@@ -1,4 +1,3 @@
# Electrum - Lightweight Bitcoin Client
# Copyright (c) 2011-2016 Thomas Voegtlin
#
@@ -166,7 +165,8 @@ class Network(util.DaemonThread):
util.DaemonThread.__init__(self)
self.config = SimpleConfig(config) if isinstance(config, dict) else config
self.num_server = 10 if not self.config.get('oneserver') else 0
self.blockchains = blockchain.read_blockchains(self.config)
self.read_checkpoints()
self.blockchains = blockchain.read_blockchains(self.config, self.checkpoints)
self.print_error("blockchains", self.blockchains.keys())
self.blockchain_index = config.get('blockchain_index', 0)
if self.blockchain_index not in self.blockchains.keys():
@@ -804,15 +804,21 @@ class Network(util.DaemonThread):
interface.print_error("unsolicited header",interface.request, height)
self.connection_down(interface.server)
return
can_connect = blockchain.can_connect(header)
chain = blockchain.check_header(header)
if interface.mode == 'backward':
if chain:
if can_connect and can_connect.catch_up is None:
interface.mode = 'catch_up'
interface.blockchain = can_connect
next_height = height + 1
interface.blockchain.catch_up = interface.server
elif chain:
interface.print_error("binary search")
interface.mode = 'binary'
interface.blockchain = chain
interface.good = height
next_height = (interface.bad + interface.good) // 2
assert next_height >= self.max_checkpoint(), (interface.bad, interface.good)
else:
if height == 0:
self.connection_down(interface.server)
@@ -821,7 +827,7 @@ class Network(util.DaemonThread):
interface.bad = height
interface.bad_header = header
delta = interface.tip - height
next_height = max(0, interface.tip - 2 * delta)
next_height = max(self.max_checkpoint(), interface.tip - 2 * delta)
elif interface.mode == 'binary':
if chain:
@@ -832,6 +838,7 @@ class Network(util.DaemonThread):
interface.bad_header = header
if interface.bad != interface.good + 1:
next_height = (interface.bad + interface.good) // 2
assert next_height >= self.max_checkpoint()
elif not interface.blockchain.can_connect(interface.bad_header, check_height=False):
self.connection_down(interface.server)
next_height = None
@@ -941,33 +948,18 @@ class Network(util.DaemonThread):
def init_headers_file(self):
b = self.blockchains[0]
if b.get_hash(0) == bitcoin.NetworkConstants.GENESIS:
self.downloading_headers = False
return
filename = b.path()
def download_thread():
try:
import urllib.request, socket
socket.setdefaulttimeout(30)
self.print_error("downloading ", bitcoin.NetworkConstants.HEADERS_URL)
urllib.request.urlretrieve(bitcoin.NetworkConstants.HEADERS_URL, filename + '.tmp')
os.rename(filename + '.tmp', filename)
self.print_error("done.")
except Exception:
self.print_error("download failed. creating file", filename)
open(filename, 'wb+').close()
b = self.blockchains[0]
with b.lock: b.update_size()
self.downloading_headers = False
self.downloading_headers = True
t = threading.Thread(target = download_thread)
t.daemon = True
t.start()
if not os.path.exists(filename):
length = 80 * len(self.checkpoints) * 2016
with open(filename, 'wb') as f:
if length>0:
f.seek(length-1)
f.write(b'\x00')
with b.lock:
b.update_size()
def run(self):
self.init_headers_file()
while self.is_running() and self.downloading_headers:
time.sleep(1)
while self.is_running():
self.maintain_sockets()
self.wait_on_sockets()
@@ -1005,14 +997,17 @@ class Network(util.DaemonThread):
interface.mode = 'backward'
interface.bad = height
interface.bad_header = header
self.request_header(interface, min(tip, height - 1))
self.request_header(interface, min(tip +1, height - 1))
else:
chain = self.blockchains[0]
if chain.catch_up is None:
chain.catch_up = interface
interface.mode = 'catch_up'
interface.blockchain = chain
self.print_error("switching to catchup mode", tip, self.blockchains)
self.request_header(interface, 0)
else:
self.print_error("chain already catching up with", chain.catch_up.server)
def blockchain(self):
if self.interface and self.interface.blockchain is not None:
@@ -1068,3 +1063,23 @@ class Network(util.DaemonThread):
if out != tx_hash:
return False, "error: " + out
return True, out
def checkpoints_path(self):
return os.path.join(os.path.dirname(__file__), 'checkpoints.json')
def export_checkpoints(self):
# for each chunk, store the hash of the last block and the target after the chunk
cp = self.blockchain().get_checkpoints()
with open(self.checkpoints_path(), 'w') as f:
f.write(json.dumps(cp, indent=4))
def read_checkpoints(self):
try:
with open(self.checkpoints_path(), 'r') as f:
self.checkpoints = json.loads(f.read())
self.print_error("Read %d checkpoints" % len(self.checkpoints))
except:
self.checkpoints = []
def max_checkpoint(self):
return max(0, len(self.checkpoints) * 2016 - 1)