Replace initial headers download with hardcoded checkpoints
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user