commit d1b8a6fae6d44e78c8093b01321f8d430442b687
parent b4e43754e0f60d8438b5fd412de816a466344401
Author: ThomasV <thomasv@electrum.org>
Date: Tue, 5 Dec 2017 18:03:07 +0100
Replace initial headers download with hardcoded checkpoints
Diffstat:
M | lib/blockchain.py | | | 108 | +++++++++++++++++++++++++++++++++++++++++++++++-------------------------------- |
A | lib/checkpoints.json | | | 991 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | lib/network.py | | | 73 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------- |
M | lib/verifier.py | | | 25 | +++++++++++++++++++------ |
4 files changed, 1119 insertions(+), 78 deletions(-)
diff --git a/lib/blockchain.py b/lib/blockchain.py
@@ -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,68 +249,81 @@ 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:
+ try:
+ prev_hash = self.get_hash(height - 1)
+ except:
return False
- prev_hash = hash_header(previous_header)
if prev_hash != header.get('prev_block_hash'):
+ self.print_error("bad hash", height, prev_hash, header.get('prev_block_hash'))
return False
- bits, target = self.get_target(height // 2016)
+ target = self.get_target(height // 2016 - 1)
try:
- self.verify_header(header, previous_header, bits, target)
- except:
+ self.verify_header(header, prev_hash, target)
+ except BaseException as e:
return False
return True
@@ -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
diff --git a/lib/checkpoints.json b/lib/checkpoints.json
@@ -0,0 +1,990 @@
+[
+ [
+ "00000000693067b0e6b440bc51450b9f3850561b07f6d3c021c54fbd6abb9763",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "00000000f037ad09d0b05ee66b8c1da83030abaf909d2b1bf519c3c7d2cd3fdf",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "000000006ce8b5f16fcedde13acbc9641baa1c67734f177d770a4069c06c9de8",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "00000000563298de120522b5ae17da21aaae02eee2d7fcb5be65d9224dbd601c",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "000000009b0a4b2833b4a0aa61171ee75b8eb301ac45a18713795a72e461a946",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "00000000fa8a7363e8f6fdc88ec55edf264c9c7b31268c26e497a4587c750584",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "000000008ac55b5cd76a5c176f2457f0e9df5ff1c719d939f1022712b1ba2092",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "000000007f0c796631f00f542c0b402d638d3518bc208f8c9e5d29d2f169c084",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "00000000ffb062296c9d4eb5f87bbf905d30669d26eab6bced341bd3f1dba5fd",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "0000000074c108842c3ec2252bba62db4050bf0dddfee3ddaa5f847076b8822f",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "0000000067dc2f84a73fbf5d3c70678ce4a1496ef3a62c557bc79cbdd1d49f22",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "00000000dbf06f47c0624262ecb197bccf6bdaaabc2d973708ac401ac8955acc",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "000000009260fe30ec89ef367122f429dcc59f61735760f2b2288f2e854f04ac",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "00000000f9f1a700898c4e0671af6efd441eaf339ba075a5c5c7b0949473c80b",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "000000005107662c86452e7365f32f8ffdc70d8d87aa6f78630a79f7d77fbfe6",
+ 26959535291011309493156476344723991336010898738574164086137773096960
+ ],
+ [
+ "00000000984f962134a7291e3693075ae03e521f0ee33378ec30a334d860034b",
+ 22791193517536179595645637622052884930882401463536451358196587084939
+ ],
+ [
+ "000000005e36047e39452a7beaaa6721048ac408a3e75bb60a8b0008713653ce",
+ 20657752757787447339704949573503817091559730029795210136290380062967
+ ],
+ [
+ "00000000128d789579ffbec00203a371cbb39cee27df35d951fd66e62ed59258",
+ 20055900442607493419304231885070612388653052033693203212369143515380
+ ],
+ [
+ "000000008dde642fb80481bb5e1671cb04c6716de5b7f783aa3388456d5c8a85",
+ 14823964236757567892443379740509603561300486961438335652879209691748
+ ],
+ [
+ "000000008135b689ad1557d4e148a8b9e58e2c4a67240fc87962abb69710231a",
+ 10665478642773714533594093039651282002301533435475036254747899885223
+ ],
+ [
+ "00000000308496ef3e4f9fa542a772df637b4aaf1dcce404424611feacfc09e7",
+ 7129928201274994723790235748908587989251132236328748923672922318604
+ ],
+ [
+ "000000001a2e0c63d7d012003c9173acfd04ccd6372027718979228c461b5ed5",
+ 5949911830998722926292643443014583571932577723103865087785236463581
+ ],
+ [
+ "000000002e0c0ac26ccde91b51ab018576b3a126b413e9f6f787b36637f1b174",
+ 5905493731356012500002445562241380310188483401887904088185399375735
+ ],
+ [
+ "00000000103226f85fe2b68795f087dcec345e523363f18017e60b5c94175355",
+ 4430144627991408624040948791361640318006240855899368474057439916851
+ ],
+ [
+ "000000001ae6f66fd4de47f8d6f357e798943bbfc4f39ebf14b0975fab059173",
+ 3447600873975070077932488290376750731396138937686397230467460081722
+ ],
+ [
+ "000000000a3f22690162744d3bc0b674c92e661a25afb3d2ac8b39b27ac14373",
+ 2351604930866654632766829472567920383958332390561025111996712740267
+ ],
+ [
+ "0000000006dc436c3c515a97446af858c1203a501c85d26c4a30afa380aba4a1",
+ 2098151743855439919137531366951071713579837678345159724749870973527
+ ],
+ [
+ "000000000943fe1680ffcc498ce50790ff8e842a8af2c157664e4fbc1cb7cb46",
+ 2275792073644785018721128646741518076327875870388847727099387795022
+ ],
+ [
+ "000000000847b2144376c1fb057ea1d5a027d5a6004277ed4c72422e93df04e9",
+ 1622204498754365521718764766072378227544231556364276849425436764228
+ ],
+ [
+ "00000000094505954deb1d31382b86d0510fd280a34143400b1856a4d52b4c93",
+ 1551050141962082184940599235022157265046848054947355206102386866143
+ ],
+ [
+ "000000000109272cecb3f7e98ac12cf149fa8a1b2aaab248e1b006b0dc595a3a",
+ 1389323441362281405504133894690662702230469716601985716313296951861
+ ],
+ [
+ "0000000009e6aa0fe39b790625ffeb18a2d6ff5060a5bd14e699e83c54109977",
+ 1147154217026336014073920869620380692430705543951348139504758384216
+ ],
+ [
+ "0000000000d14af55c4eae0121184919baba2deb8bf89c3af6b8e4c4f35c8e4e",
+ 594008212391331743177258641174232971084553374243271275697110908234
+ ],
+ [
+ "0000000003dfbfa2b33707e691ab2ab7cda7503be2c2cce43d1b21cd1cc757fb",
+ 148501965484106068333659342839523859586884345264449234288706060288
+ ],
+ [
+ "0000000000c169d181d66d242901f70d006f3e088c1ae9cacb88b94b8266e9c3",
+ 110393704409292953137636253955510629068475916699790368077242928142
+ ],
+ [
+ "000000000009f7d1439d6a2fc1a456db8e843674275bf0133fc7b43b5f45b96e",
+ 76555780966028280774274008956877300222068246662708272689770207398
+ ],
+ [
+ "000000000011b8a8fad7973548b50f6d4b2ba1690f7487c374e43248c576354f",
+ 52679970922643127683947083904801524368866887307161543562595547363
+ ],
+ [
+ "000000000077e856b6cc475d9cf784119811214c9cac8d7b674ec24faa7c2c0c",
+ 43246875121342569218488803557695204365585581295709263857216301849
+ ],
+ [
+ "00000000004cbb474f2cbf3a65f690efa09804512af3351ba3a0888c806c6625",
+ 37817522176947171595261355763110820847417850236612020028828535138
+ ],
+ [
+ "0000000000235b1ec6656d8e91f3dde3b6ab9ad7e75b332e4da9355ce60d860e",
+ 29373105354589651513503064535568195122478342070358205617825458296
+ ],
+ [
+ "00000000002a153a2c95a8e5493db93086b0e3fe590b636a5871ace57523ef93",
+ 20444489530085161064085987129079503334049188267661948259198215487
+ ],
+ [
+ "00000000000e9550e084908cf91a4e8b74f9f1315d1bc4020709f9e7f261bb18",
+ 19563851393374294635996921207472450463857223702361327968607284610
+ ],
+ [
+ "00000000002c2cfef3bb85b463d3fcd39b73a6d3d5ae11c1e2a8113e3794f28d",
+ 12545027206560661467344001226069385793869578030934168709550533072
+ ],
+ [
+ "00000000000fa92b757ee29674aa97e98a49ba3ad340d2baa94155d71648dfe1",
+ 8719871918647905191685831001181973300414533694245757905046274783
+ ],
+ [
+ "0000000000030571601dbc8e13d00d45004eee6ea8b6ab3cdfb38d2546fee21c",
+ 5942997561411541711563156602531385577600077786198627208704997014
+ ],
+ [
+ "00000000000bb6adef42e63082b20fd2b1dc1b324c51973512a4c31f29a9986e",
+ 3926018509229572344313816286588613965571477415700629866143917555
+ ],
+ [
+ "000000000000765094788a98dbb8adac30d248b7129b59b1441ee2b7ef9e332f",
+ 3337325505332425700040650320729095537310516946108490809993884103
+ ],
+ [
+ "00000000000431a0aa9625f82975709f3c6f4f64d04c559512af051599872084",
+ 2200422254731939804709388022233205762025354383380152145148334197
+ ],
+ [
+ "00000000000292b850b8f8578e6b4d03cbb4a78ada44afbb4d2f80a16490e8f9",
+ 1861317049673577272902795125376526066826651733332976503154178702
+ ],
+ [
+ "0000000000025afe84e27423011af25f777e5a94545dbd00fd04bebe9050f7dd",
+ 1653210985697702096268217038408538100642620147117674184232799239
+ ],
+ [
+ "0000000000000e389cccae2a40437be574fd806909e24136711e7f8bce671d65",
+ 1462202160246170142640486657710301628879951515428353771159991652
+ ],
+ [
+ "0000000000030510bf6bc1649726cf2e6e4010c64a2c8fd3fde5dc92535ca40e",
+ 1224747759897877506274637367000463834699323352769332185408382770
+ ],
+ [
+ "00000000000082648057f14fc835779c6ce46a407bafb2e5c2ac1d20d9f4e822",
+ 1036993586117576703268996282150397585765576605730719362190807632
+ ],
+ [
+ "000000000000f38accd6b22959010471a6d9f159d43bf2a9d4c53c220201254e",
+ 739430452756642306146040915925451887239493960335784687377022899
+ ],
+ [
+ "0000000000004ed7a73133678b5eb883cd8882bf14dfb26c104ae0c3f94cf4ee",
+ 484980150867459464772730739859302095672636271057575743647282522
+ ],
+ [
+ "00000000000037bb3ff4cf649a1757d4028ecc10f893529b4a2214792c981f96",
+ 353834202080594446847490995785168095798368734611949601937470709
+ ],
+ [
+ "0000000000008008f46559fe7f181e9dc0648f213472a1e576e8bf506b88f22f",
+ 390846686979010943280302753017141998917705716027679590623447523
+ ],
+ [
+ "000000000000691d0c2444db713bf6c088844cc95a37cdc55cc269bb0a31d8c8",
+ 327399809810416473497219170054754564569687652741316499001410264
+ ],
+ [
+ "00000000000071153b0afcc64a425f8442c29749610797119e732dd4b723f675",
+ 291937852278662838074813817696277197987476923260730675453803937
+ ],
+ [
+ "000000000000a384acb522e4e5935ad2bc31366ecf1f16f1f11023e967ef033d",
+ 245829147781851502645710488124949429684812753873220896184598139
+ ],
+ [
+ "0000000000002e532093d43e901292121fb7c6583caf2d13b666fe7e194b4a97",
+ 171262571764606989041741296999128813297927395580615685573053946
+ ],
+ [
+ "00000000000033e435c4bbddc7eb255146aa7f18e61a832983af3a9ee5dd144d",
+ 110439004522135981410957929709803254805947931106765020063637821
+ ],
+ [
+ "00000000000028ff4b0bd45f0e3e713f91fa1821d28a276a1a1f32f786662f13",
+ 61993466854134149454140006024796140857619052825495269156061184
+ ],
+ [
+ "0000000000001ef9c75318e116a607af4de68fb4f67c788677ee6779fb5fa0d5",
+ 47525095027499967685539085016596651791271838150303471592202567
+ ],
+ [
+ "0000000000000e6e98694ccb8247aad63aaa1e2bec5a7be14329407e4cea6223",
+ 30742242324775075538370115437091356458943450412845263377277862
+ ],
+ [
+ "000000000000000a2153574b2523a6d1844c3cb82d085e2575846dd8c5d4ebb4",
+ 19547340168280248765311813293333293631817970001494998481269884
+ ],
+ [
+ "00000000000002a92c1b1ffb2a8388979cf30798e312335ae2a1b922927ee83d",
+ 17248294060755457364687620800167145237577978222086136949668577
+ ],
+ [
+ "00000000000004d54b1422ce733922e7672a4e2ecc86dcf96c0de06565cddaa6",
+ 15943944661534740097945584046599407470739618287604834836788345
+ ],
+ [
+ "00000000000009dd91ae96cbbf67af42340b0bc715b3606aa725f630b470262d",
+ 14273487520109069190865495135324295912393888045891964854360837
+ ],
+ [
+ "00000000000007d33d78522fa95bdcd4a25072aeac844cbe9b6bc5d0cc885d0a",
+ 14930240326912220437232591181374307607822146395712988852898063
+ ],
+ [
+ "00000000000003dd57f5dd1228f68390b586700063225d26bac972bd120546d2",
+ 15164777495018002532932947047554711971850359981358394796619712
+ ],
+ [
+ "000000000000076bdeca878b47c392f51fbda543b1e69612cf7d305deb537604",
+ 15357836661363254148000422860842573817259062733233058353910518
+ ],
+ [
+ "00000000000008eb1bb7e18d9dfe62210d761cbf114d59ca08e4f638b8563e30",
+ 15958691676650473757098043151847631737628132481844875166319930
+ ],
+ [
+ "00000000000001b0d8d885e4d77d7c51e8f1fdaba68f229ac04d191915845f09",
+ 18362382113291570192217962968958993778167022285180280072455374
+ ],
+ [
+ "000000000000081baa3a716d5f9ab072c9fc3b798900234c9be23ab02a287c30",
+ 22401656061533210580918575951901358551917227873474367195418168
+ ],
+ [
+ "00000000000005b88d0224b9b0d4b65d3de9a61d93609bb91c9297440f1c4657",
+ 22607630170339665188190152183146632918104515553204180801386220
+ ],
+ [
+ "000000000000027d6a6870403fa43a650b7d9a6e61243f375a79ea935ad9ef1f",
+ 24717308784096979165831027254372357786209337057535982141051915
+ ],
+ [
+ "0000000000000810a3490b86e4f302f6557f9621c5c8620c2b09ec8f0cf72794",
+ 23340837323532611728563455098354667083079032543420012677249737
+ ],
+ [
+ "000000000000073833bca8d0ea909fde717e251576b7b3ccaaa58ad5d39eed60",
+ 23242403153239567181248045649858932694926499996163845297462125
+ ],
+ [
+ "000000000000031b7fd2ed1f28ff74e969aa891297706c38bd2e1d3bc48183c4",
+ 21554570223215082708991040006621195807471559921461022664387024
+ ],
+ [
+ "0000000000000b0738bcba382983811d40b531f2e68cd57126092755f1be4ba6",
+ 20615559358036706401988446678345142325284830029403352655769482
+ ],
+ [
+ "000000000000000664cbfd5e3fa497c07614c33a0934b83e01fbe980634a9aa4",
+ 19540900118929245469513784022598005389554682908250308721002538
+ ],
+ [
+ "000000000000021eb520df39289a70e40c59822a8c47924dc4940e7d0c1455c4",
+ 19588382633956678748738987427134971684150657954263472331193639
+ ],
+ [
+ "0000000000000275e0c41b11bc250fe887c5e60c8ebaaa449f5c28c67133d496",
+ 18009312093826905807996061071987479121278814437031313100845126
+ ],
+ [
+ "000000000000097fb0fdbeee0cee7e8f4e1a4ef8fad49f3d549624b0d47abed0",
+ 17993487382135493395314550202532083574115934981151443202421804
+ ],
+ [
+ "000000000000053f199ae19d34365277e534f978ea2f6c69cd4757a4fc099af5",
+ 16574652347477707606538518827054821354422596190208356086094719
+ ],
+ [
+ "0000000000000217b2e7b4f61682d24b9357d62ad29f27ed45ea2a32dc1f32f6",
+ 17085566110414426392074980811822124799183310889195548936089857
+ ],
+ [
+ "000000000000039c1d77acd4702393f48ca61983c64fc0209ade141c694b2359",
+ 17870696125576904989516147458864032514115346444088781066283239
+ ],
+ [
+ "0000000000000ae53f0c78330f6c2fbece2752909bc3742823e4fab29c5fd2b0",
+ 15554723035590620381978382489682684584827446061258013409024347
+ ],
+ [
+ "00000000000004b4d72b8631a85ec7d226dc696f1913ba1bf735b7c8dec207b8",
+ 16944240402989056240270048857919858304172512515419325535711617
+ ],
+ [
+ "00000000000006e06735bffb7d2f215dcadd8311fc33f4a46661fdca3dc0560e",
+ 17028766006301915583302001014128348187011555103613522799474256
+ ],
+ [
+ "000000000000055fc0110d4a38ffb338eabc30c8b0aef355d4643d21b5b6a860",
+ 15614541816377627606833566623846498830327983334155710863946027
+ ],
+ [
+ "000000000000081b69cb4de006c14084c4861f0e4a140c37200117a738733fe8",
+ 15392658582903619517884239396883829533752908215468116311928350
+ ],
+ [
+ "00000000000009920770f2d40b5b6a8aba33d969b855c91b0f56e3db9c27e41a",
+ 14444739202621038642296525467957270513966223272539123613709315
+ ],
+ [
+ "0000000000000791dd1cb7a684a54c72ccde51f459fff0fc3e6e051641b1e941",
+ 13237069982010980053565410157895773782534548540484990599728904
+ ],
+ [
+ "000000000000019da474a1a598b5cf28534b7fd9b214eed0f36c67c203a9b449",
+ 12305441845260052457400411036992507599992679866354285875870526
+ ],
+ [
+ "000000000000074333e888bac730f9772b65e4cc9d07edb122c6e3c6606bc8ab",
+ 11046102047853392984991332456419807063224677592114743703633836
+ ],
+ [
+ "000000000000067080669115c445f378f3dec19787558d0e03263b9dec5d7720",
+ 10007086165511791816771124848728462094811571795311807624126594
+ ],
+ [
+ "0000000000000304760bf583f4ac241c5ffe77312fa213634eba252c720530f1",
+ 9412804029559050886132126846183090289448911866201243978830721
+ ],
+ [
+ "000000000000041fb61665c8a31b8b5c3ae8fe81903ea81530c979d5094e6f9d",
+ 8825807680257895657479991196220989276506275995152177228848553
+ ],
+ [
+ "000000000000022fc7f2a5c87b2bab742d71c4eb662d572df33f18193d6abf0e",
+ 8774981337152660993121733114298631263731662998207194412401974
+ ],
+ [
+ "000000000000013c6d43ba38bc5f24e699515b9d78602694112fefdc64606640",
+ 8158793708965770005321748925786317683564827171691288121295309
+ ],
+ [
+ "00000000000001665176b9a810fddf27cca60dfcfd80bf113289fcc8ffed0284",
+ 8002813558257060656072356380146767001272597020026124199745768
+ ],
+ [
+ "00000000000002dc6ef80f56a00f1091471d942ce9bfb656ebdab4ea0b77eb0b",
+ 7839578136187174365862370390163660393786299729896106652527867
+ ],
+ [
+ "00000000000002a1fa5546ec48ca88b9e5710e2c6d895bb3675004fdacd6ab13",
+ 7999436853933517849738304697453936802516675338771116464559736
+ ],
+ [
+ "00000000000000f517517c11e649b98feca7da84ae44fb643de5a86798fe3c31",
+ 9047933968943662429055854851798411859479270438104123361452456
+ ],
+ [
+ "0000000000000299cab92a923348acf9251f656bcbacdb641fd0a66d895a6e8f",
+ 8296401729498848716200066027575181804609215798824798623774115
+ ],
+ [
+ "000000000000027508b977f72c3a0f06f1f36e311ad079536630661880934501",
+ 9081043763417525999805054818818176389840193708186237826596038
+ ],
+ [
+ "00000000000001925959229452cc6fbfef0104ebed7ccd6f584f2439c5dd1f1b",
+ 8230756222604082728916412296377630357556635887892965869189316
+ ],
+ [
+ "00000000000003b34ca89509da5f558af468c194afaa8d458bbeb07c50cc7c74",
+ 7384132762576773456261468151764493698188252321818593178380086
+ ],
+ [
+ "0000000000000076559e314ab0c86cc552e34fd79488415d3d17f6ea3c01adb3",
+ 6172235633712067451972497618887145940241016806561805162089236
+ ],
+ [
+ "000000000000003a58043252cdc30ed2f37fb17e6ef1658324b1478f16c1463b",
+ 5561375174290806544537887055854541186367445945410171525594428
+ ],
+ [
+ "000000000000011babf767e60240658195b693711c217d7da0d9215ccab45333",
+ 4026320687087602082485484360946232153393536063582206994825059
+ ],
+ [
+ "000000000000027579d28fb480ccad8e2516d1219d4c1919e3fd4fc0c882955d",
+ 3513562835129894943437236119628516496362458327482173263945837
+ ],
+ [
+ "0000000000000074546fe07f80ba15fc81897ec56a5535de727df9fda9dab500",
+ 3004086841873755151847218915251583968757589997419002536446958
+ ],
+ [
+ "00000000000000b6c55833b80c07894f4c4d3bb686e5ddbc1b1d162e22752ca3",
+ 2675564091736135973597987074403776057837198839748912144832848
+ ],
+ [
+ "00000000000001326f2f970753122e35bfdf3358d046ddf5ea22e57f5d82b00d",
+ 2409853811740497723006216754124060157774336072925654369402748
+ ],
+ [
+ "00000000000000641084745613912464ff73c974bafd0bf6dd306295f019d306",
+ 2218270940716371747904935551989691447849649677886077648624174
+ ],
+ [
+ "000000000000011ae105ddb1a5bbac6931a6578d95c201525f3a945276a64559",
+ 1727570438327407251342043828017904756815782584333725141104066
+ ],
+ [
+ "00000000000000d9b66fee19af89eaaf3f3933d1acd2617924c107f0abbe0a41",
+ 1394050998377933499722472690026032322818492088393319462766728
+ ],
+ [
+ "0000000000000011956d42670c2f75eeb344ac0657a806775998e2c58fa4b157",
+ 1263613033940095470462619539828531085609177044392029609988618
+ ],
+ [
+ "00000000000000959b1ea990368fd16d494e68ee13bd7245ddd9cdfba3330100",
+ 1030471032625362817908252078771570487808270046919474202776261
+ ],
+ [
+ "0000000000000091f86b1e423e24fe358c72db181cfcc2738c85f2f51871a960",
+ 862536742724199235179104073167840532858949484653681168904647
+ ],
+ [
+ "0000000000000055e146e473b49fe656a1f2f4b8c33e72b80acc18f84d9fcc26",
+ 720982725653754866133106184196823339064064188411714396293721
+ ],
+ [
+ "000000000000004f6a191a3261274735292bc30a1f79f23a143e4ee7dd2f64c1",
+ 530591605956209005375408931042036763612094286954585940489028
+ ],
+ [
+ "000000000000005327c8e714272803c60277333362e74ec88b9ffab5410c2358",
+ 410030655694725315191023225682702558843537088229871225194892
+ ],
+ [
+ "0000000000000002e2a62b8705564c38d6a746fc8e971a450a69989152b5ee97",
+ 310118507134852270764417655876559284597214440570539833833949
+ ],
+ [
+ "00000000000000202bf3ff30109538bfd9b5075c6438ab5ef64ebe2cf9b61404",
+ 239366804613626989118705458454015500681551595998816410136871
+ ],
+ [
+ "000000000000001c997105893f5991cb45765ff856b6e503f8466cb22cdd330a",
+ 181156300891423147840813581996669801683959668074714341556907
+ ],
+ [
+ "0000000000000010c13ce182a3d8fc6748b75640447eb360d7739a5fe984ffc1",
+ 142431143903518058663503832095902619444236806543928975891292
+ ],
+ [
+ "000000000000000bbb49db68b79ecc8393376d78272d237bb612288af64c1de8",
+ 100696286705944192804288311731154032278221074156374274573154
+ ],
+ [
+ "0000000000000001bbfd0973c367d30eef2416d9e94bdddea53bccf541a4858f",
+ 68962785458117760598328072539715155134139124175836033018875
+ ],
+ [
+ "0000000000000004ee5b6ace996ab746f1e6dd952cdbc74c0b4f8b9ac51c7335",
+ 52765647417137724306257751915372504293019655403366801103482
+ ],
+ [
+ "0000000000000002f2f23b515085d0c9f37a2824304ccb7ca1546a48548d0dac",
+ 44233494692117781485772218913793271750746093635349642503033
+ ],
+ [
+ "00000000000000045590c3fdeca1753d148a87614a70fa0897a17f90bb321654",
+ 38110303308616451367971130315102755539751527244002747835354
+ ],
+ [
+ "0000000000000002b704edc0bf1435fe2116040b547adb1bc2d196eb81779834",
+ 29679712134953944285822600537404275892101515173751373902643
+ ],
+ [
+ "00000000000000038cc59dc6dd68ae0fbe2ded8a3de65dbd9a2f9a36d26772df",
+ 22829284162675848134182694598477416531051323480214451851537
+ ],
+ [
+ "0000000000000000a979bc50075e7cdf0da5274f7314910b2d798b1aeaf6543f",
+ 19005972021752888554737867279515830726136655207276613952446
+ ],
+ [
+ "0000000000000001dd8e548c8cf5b77cde6e5631cd542e39f42c41952e5e7085",
+ 15065030752662243106668159124876133476723125447787423397009
+ ],
+ [
+ "0000000000000002513542a461de351a5a94f96b4bcd3e324a48d2d71b403fe0",
+ 12288777851891587151373320769563000373599628572350950946294
+ ],
+ [
+ "000000000000000150cc07163e78d599a7e56c0d1040641bffb382705ac17df0",
+ 10284450072667651845630380921900049634274231900711580829901
+ ],
+ [
+ "00000000000000009051d83d276dad5c547612f67c2907acf6a143039bddb1bb",
+ 8614457133517962240383077577277860009688882364333357498735
+ ],
+ [
+ "00000000000000000b83d3947d2790ab0bcbbb61eba1eb8d8f0f0eb3e9d461e0",
+ 7065404376960081064548050202734411051432779994036264291865
+ ],
+ [
+ "00000000000000005a4fbbaeffee6d52fa329dd8c559f90c9b30264c46ad33fd",
+ 6343128691613752139911564815777925738673759990853012864417
+ ],
+ [
+ "00000000000000006b6834bae83e895a78c5026a8c8141388040d90506cf3148",
+ 5384566985902468539838947745491317290501351277582100625895
+ ],
+ [
+ "0000000000000000bf3c066c9acdb008e7fff3672f1391b35c8877b76b9e295e",
+ 4405445424268587912774001698765643657938467054813941696357
+ ],
+ [
+ "00000000000000006bcf448b771c8f4db4e2ca653474e3b29504ec08422b3fba",
+ 3863116091606416844204395924633339211949472882692642434091
+ ],
+ [
+ "000000000000000098686ab04cc22fec77e4fa2d76d5a3cc0eb8cbf4ed800cdc",
+ 3369644874471976788888364569461031006144821186115339704344
+ ],
+ [
+ "000000000000000036cc637d80982595b1fa30f877efe8904965e6fd70aeae1a",
+ 3045099804940836864917455634208357232827311736852711219052
+ ],
+ [
+ "00000000000000000ee9b585e0a707347d7c80f3a905f48fa32d448917335366",
+ 2578448738892556035161639572550297683334908085589209042124
+ ],
+ [
+ "00000000000000000401800189014bad6a3ca1af029e19b362d6ef3c5425a8dc",
+ 2293150027595934059742111263510686973492486336734191444857
+ ],
+ [
+ "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2",
+ 2002553394643609738890838973561169711471353898661293921361
+ ],
+ [
+ "00000000000000003ff2a53152ee98910d7383c0177459ad258c4b2d2c4d4610",
+ 1602973121906621623499825176001242504910089450561449296745
+ ],
+ [
+ "00000000000000001bb242c9463b511b9e6a99a6d48bd783acb070ca27861c2b",
+ 1555090301026128543569302441423333574769288057539276771351
+ ],
+ [
+ "000000000000000019d43247356b848a7ef8b1c786d8c833b76e382608cb59e9",
+ 1438882618901096676077751337424466243540231648216042671672
+ ],
+ [
+ "00000000000000003711b624fbde8c77d4c7e25334cfa8bc176b7248ca67b24b",
+ 1366448148696423482270218240630565379904190231445288559686
+ ],
+ [
+ "0000000000000000092c1f996e0b6d07fd0e73dfe6409a5c2adc1206e997c3a2",
+ 1130631792721554272454999472203133803635779505498977249380
+ ],
+ [
+ "000000000000000020ce180d66df9d3c28aee9fcec7896071ec67091a9753283",
+ 982897902661444504749094486748895114762769275663213548760
+ ],
+ [
+ "000000000000000018d37d53ae02e13634eefb8d9246253e99c1bdf65ac293ea",
+ 903780674822307262725136466127288858430591999464421319774
+ ],
+ [
+ "00000000000000001607d1a21507dea1c0e5f398daf94d35fb7e0a3238f96a0f",
+ 777796786715545142990933608995805126717575855757223448283
+ ],
+ [
+ "00000000000000001acae244523061f650ddab9c3271d13c0cd86071ae6e8a5f",
+ 770217857427240993023051315984564139215374347389780685886
+ ],
+ [
+ "0000000000000000104430189dba1219b0e3dd90824e8c2271609aca5b71250f",
+ 749175002550855564826315453191856424408132088739667533908
+ ],
+ [
+ "00000000000000001aa260733b6d8f8faa2092af35e55973278bb17f8eaeca6b",
+ 680733332917879088904702563202563546480869669564659182916
+ ],
+ [
+ "000000000000000009925ad5866a9cb3a1d83d9399137bccc7b5470b38b1db2b",
+ 668970749931191589798031473561994304229010598616526068121
+ ],
+ [
+ "00000000000000001133acacb92e43e24af63a487923361a4a98c87a5550dffe",
+ 673862885517789065391946314370719009092913047398806257816
+ ],
+ [
+ "000000000000000018c66b4a76ca69204e24ee069da9368c7a9883adb36c24af",
+ 683252375980679323816587400004061743952674823748550569728
+ ],
+ [
+ "000000000000000010b13aed220b96c35ccd5f07125b51308db976eefcd718f9",
+ 663358898259210531333699235628449595078182768956016850932
+ ],
+ [
+ "0000000000000000031b14ece1cfda0e23774e473cd2676834f73155e4f46a2b",
+ 613111677421249032126095464155766633549817788831841702233
+ ],
+ [
+ "000000000000000010bfa427c8d305d861ab5ee4776d87d6d911f5fb3045c754",
+ 653202571346946874804858789924935228771775905822751784751
+ ],
+ [
+ "000000000000000005d1e9e192a43a19e2fbd933ffb27df2623187ad5ce10adc",
+ 606440210473080582646260971729051700700295823810315465086
+ ],
+ [
+ "00000000000000000f9e30784bd647e91f6923263a674c9c5c18084fe79a41f8",
+ 577485545195557219124205162278233745767078209386685370301
+ ],
+ [
+ "00000000000000000036d3e1c36e4b959a3e4ad6376ce9ae65961e60350c86e8",
+ 568436189899844976161013318161470010900802307864463999350
+ ],
+ [
+ "00000000000000000b3ec9df7aebc319bb12491ba651337f9b3541e78446eca8",
+ 577075446183156083131210077122535091982277790261940376730
+ ],
+ [
+ "000000000000000012d24ce222e3c81d4c148f2bce88f752c0dba184c3bc6844",
+ 545227685810993878908530774661151072647124692119579479626
+ ],
+ [
+ "000000000000000000c4ccbdd98c267bd16bda12b63b648c47af3ac51c1cc574",
+ 566251462633192796874293710752184671013063323002614261298
+ ],
+ [
+ "00000000000000000056bfec1dca8e82710f411af64b1d3b04a2d2364a81993f",
+ 565861163013726292152715860908846169118213713027013549266
+ ],
+ [
+ "00000000000000001275d1cadce690546f74f77f6d4a6190e2137a8a819946f6",
+ 552365082628398268882484833076555675653086455208105645421
+ ],
+ [
+ "000000000000000003816ae80c6413b84cbee2f639ba497ab5872ec9711eb256",
+ 566500826506537696689556913703962485638366020240431987761
+ ],
+ [
+ "00000000000000000d92953224570f521b09553194da1ca3c4b31a09a238f4f6",
+ 542528831070582225190358970054175523872885764221168055524
+ ],
+ [
+ "000000000000000006721943f23cfacf20c17c2ad6ea4e902af36b01f92e3c06",
+ 545717458684443426657861963694104795617022469075593560376
+ ],
+ [
+ "0000000000000000031d9af2fe38cc02410361fb213181fdb667c74e210d54c4",
+ 527828116295419256939747768525818422990809696098687485908
+ ],
+ [
+ "0000000000000000142e8a13ef6994961655c8e86aece3f0abebd2ee05473e75",
+ 515692649961651115318501607126660466594771968970128733915
+ ],
+ [
+ "00000000000000000c7a8db37a746d6637ef6a6eab28735608fd715ee2f394e7",
+ 511567833081612605062932845380344111401319750691048028647
+ ],
+ [
+ "000000000000000007854877c66c71a49af40d20f2d6f817becfe4d66d5e5a81",
+ 496889275651173623472900330204902534352929519684753746862
+ ],
+ [
+ "000000000000000005ce1d2d10aeb9def4d38233e859d98a4a168ea3fa36687a",
+ 473326016878892721329791660926511941983191613711888666872
+ ],
+ [
+ "000000000000000007c71decfe74855ad99dc2aa4a2e713165db5a8d6da5f32a",
+ 454358905739145490120646206475613103265889121292141221496
+ ],
+ [
+ "000000000000000008ce4f34161be6760569877c685e37ebebce3546ea42a767",
+ 443317174350997401226699663083830316501226707336190868827
+ ],
+ [
+ "0000000000000000086233f4843682eb47bacb58930a5577fbfd5c9ebd57ddf9",
+ 442803156296231091698861521258691618419467911445974398697
+ ],
+ [
+ "000000000000000010a904eee4fc763c6b88d378884f368fd652f63c1af71580",
+ 433057295538880306866830023102486508102611067408810729986
+ ],
+ [
+ "00000000000000000c114754749d622d4fa2f78c84d7147c345b2b99a8e83d2e",
+ 409419135913169127551416754586994781281659818649795994250
+ ],
+ [
+ "000000000000000000a5039e32cc9a89aeffbde1391e8bc9ae9724127904f01d",
+ 370716565562591807409073645534324134138902968133741824826
+ ],
+ [
+ "000000000000000003b0b73d9b3259c318cca48a6335b5d64545583f7f3773fa",
+ 340818601652590375722654926010534269909167221015231774473
+ ],
+ [
+ "00000000000000000198bcc5bd65fd0ccd1c7e3b49e0170ea80296cbfee05042",
+ 288495776454828940814130957501183806179235220269688957284
+ ],
+ [
+ "00000000000000000a60f379d3dc1413491f360809a97cbb02c81442c613dce7",
+ 259524927038954052049842432960406271327041356520946780931
+ ],
+ [
+ "0000000000000000038973a5f8ba8cdc7e371dcc8f4b24337ef695f24b962907",
+ 237834533496394499560421837048697627284447080833665891069
+ ],
+ [
+ "000000000000000004b8ec471974913d052a3af7dc2a8c6f01c2ac2f3d1f7b19",
+ 224600594221399775791208366807237501899705336368643295004
+ ],
+ [
+ "0000000000000000075d572eef1c4210adc7abf4e40986d7f0a80003853bfec4",
+ 187068024570118295326670137055767916260683809649859998591
+ ],
+ [
+ "0000000000000000074f9edbfc07648dc74392ba8248f0983ffea63431b3bc20",
+ 164898586657174446766450284432249324933473312757247241703
+ ],
+ [
+ "000000000000000003c4a4d9c62b3a7f4893afe14eef8a6a377229d23ad4b1ea",
+ 170169949941312779383320359289276524103458774855674537695
+ ],
+ [
+ "00000000000000000404b6939e6c35a5448386e5d58f318c82ce2fefb7d73e47",
+ 162900642628594452312926252009782198966469183066378413701
+ ],
+ [
+ "0000000000000000034656c96781091b5fbc799c881ea85b41cba0b88128eff7",
+ 161578253985639514393501040432436419806938319938347383115
+ ],
+ [
+ "0000000000000000045645e2acd740a88d2b3a09369e9f0f80d5376e4b6c5189",
+ 150883217088565412406283744917586302541065882485692466643
+ ],
+ [
+ "00000000000000000381e6a138308c6547d6fe3eb3437250ffefdebbf71eefd1",
+ 150899431314054665651533974629900879951167127567886958331
+ ],
+ [
+ "0000000000000000012100ddbb2102e65fb1ebbf104ead754a4110abffc4b8bc",
+ 138784704342716220538434620238263807017514526920482840730
+ ],
+ [
+ "0000000000000000046f56e59b9b1293b5e7c1587aa6d29c4f3f79b98cf22ee6",
+ 135263027158857483473983812897618462696878980167989570177
+ ],
+ [
+ "000000000000000001bd1c291e91f4476f93454d4542d2ed7e44fc86902c93bb",
+ 137505575960473580232190762314053902119220761315057010096
+ ],
+ [
+ "000000000000000001c37a483375ff6fd6ed7c5b79d80167b027a8fdb0721dcd",
+ 128714000003724620550017796842876174875520737762229396938
+ ],
+ [
+ "0000000000000000051804b4c2da5298c4573386bf1d4242bf0e26a49ec32e42",
+ 126334257597368896694079008874105899845411447996852366067
+ ],
+ [
+ "0000000000000000034bff7888f1f7294311f0199322f77c1457018c875bd9e1",
+ 126278728489740292169183109579386034099056145098127681816
+ ],
+ [
+ "00000000000000000506b43c9283ccbc40f583e0c734e4a8af2ce6a4262c6221",
+ 133533674521328301805375468020445677637867523414815983180
+ ],
+ [
+ "000000000000000003937068e19a0750a33978050f019d2b60f430e3da707db9",
+ 124023231761354306172598997090326962528984683316222123922
+ ],
+ [
+ "000000000000000002e2f6ec3c9eb965aa706c788da7dede201b6b4b8fae3971",
+ 122123890689597169329897975011373560881532793639713851004
+ ],
+ [
+ "000000000000000000b3076636b13562bb4315f895bcb324e0c962763c2196b1",
+ 119378471659813172166584350643745606396975629669615648535
+ ],
+ [
+ "00000000000000000025b8961d1d0cfba33b0205ec10b3ce541618e352b0bbd5",
+ 111760099061575845238587552104542233599456594020708180600
+ ],
+ [
+ "00000000000000000421d58b78b9f063a4b20e181d55c9c79082f9e4b8b30925",
+ 104283398725864083874296861096497976441886465506877958948
+ ],
+ [
+ "0000000000000000027fd968d41741f31c73c4a3b304472da0165245278e2ea3",
+ 106299891835047816880570816560226555729378855394467112113
+ ],
+ [
+ "00000000000000000364a23184b8a2c009d13172094421c22e4d9bc85dcf90a5",
+ 105881534387569087602448606393026827269357803018613746024
+ ],
+ [
+ "0000000000000000042a2ed4a504424060407825d774a54f2e148fa769ee72ff",
+ 95668758377605096786059344838386233938948428360571473100
+ ],
+ [
+ "0000000000000000025f769f13f2806fed19d9948b1a7ef19048177789afc5d3",
+ 94012478943487551583874745631213709785208280748731165788
+ ],
+ [
+ "000000000000000000b3ff31d54e9e83515ee18360c7dc59e30697d083c745ff",
+ 86923144448447518913809103136679872784564523201770836515
+ ],
+ [
+ "0000000000000000021ecdcb2368ce66c23efd8bd8ab6a88a8bb70571c6e67f0",
+ 84861696667064232085350895302379622169877065200841464945
+ ],
+ [
+ "000000000000000001972cb33b862b27c1dc3f3a723f7d1cfd69aebe0409126c",
+ 80022436630974307725804284020086214397285337936510125904
+ ],
+ [
+ "000000000000000000cb26d2b1018d80670ccc41d89c7da92175bd6b00f27a3e",
+ 68605895635350324123887563889758158648405285708846995220
+ ],
+ [
+ "00000000000000000276deb4022f66cacd929c690cd6b4f7e740836b614b21f4",
+ 63859488458993656960329361157926368758742149072401957675
+ ],
+ [
+ "000000000000000000587912ced677698c86eec8b1d70144dccb1c6b0bad0f17",
+ 61163588147080336562860372542789363550797760125590468374
+ ],
+ [
+ "0000000000000000009f989a246ac4221ebdced8ccebae9b8d5c83b69bb5e7c8",
+ 58509968837817799412963215131374851975666125194369450244
+ ],
+ [
+ "000000000000000000038bed8b89c4e82c13076dd64dc5f7a349c39d3921d607",
+ 56672978024443644437306289406994921596646228103740151166
+ ],
+ [
+ "00000000000000000122f47d580700a3a5b4b6cb46669a36e4fa974c720ab6cd",
+ 53958706289281806789111061412993899806784528297928389354
+ ],
+ [
+ "00000000000000000172ad9ea56a90bdfed0f364a902500e9ff4d74f000ced99",
+ 51765097045688608012424287693701763884232488530834902033
+ ],
+ [
+ "00000000000000000201d7429db233c7055e9699c5bfb57b167ca8d0c710dc71",
+ 51649247587912518226490987244672765779747315777961084943
+ ],
+ [
+ "000000000000000000c0549b2a8adbefbf6c909f61fdc4d6087c44a549cf8201",
+ 48144761676638685568393252844604229390549310101321306353
+ ],
+ [
+ "0000000000000000015b6789cdc5dc13766f58b38f16d5b35bf79ce4b040f7fd",
+ 45240056525891956455575817517143990421796325617308336169
+ ],
+ [
+ "0000000000000000013a31b29f845d97465bff53f901027f8ab4b1a2f59118a8",
+ 39719085345888042233262788103506269388987831055953076236
+ ],
+ [
+ "00000000000000000088cdeaa7389a7de9f09e3a28b3647630fea3bd1b107134",
+ 37880653743061241847157755785329340895782894371522587986
+ ],
+ [
+ "000000000000000001389446206ebcd378c32cd00b4920a8a1ba7b540ca7d699",
+ 38043253251243498799796359449649225329347481521269202959
+ ],
+ [
+ "000000000000000000f41e2b7f056b6edef47477d0d0f5833d5d4a047151f2dc",
+ 33510049713200839962002052974605137446441531580345905745
+ ],
+ [
+ "0000000000000000010e0373719b7538e713e47d8d7189826dce4264d85a79b8",
+ 31340511093499215382498875631096178729473407545556119324
+ ],
+ [
+ "00000000000000000053e2d10bd703ad5b7787614965711d6170b69b133aa366",
+ 29201554221106481014362444600779904393001928219662824381
+ ],
+ [
+ "000000000000000000cbeff0b533f8e1189cf09dfbebf57a8ebe349362811b80",
+ 30354232589320643409720162249214362116926806095467115096
+ ],
+ [
+ "000000000000000000d0ad638ad61e7c4c3113618b8b26b2044347c00c042278",
+ 29217445580005453044145144287633722880237231025559536344
+ ],
+ [
+ "000000000000000000a7bda943639876a2d7a8caf4cac45678fb237d59c28ba1",
+ 24433315186493117547015353728839494165411420867297244659
+ ],
+ [
+ "000000000000000000fb6c6a307c8363e923873499ba6299597769c10a438e61",
+ 23988337581966024451862874735374376736823985966238572778
+ ],
+ [
+ "0000000000000000006f408147ffbcaa0fb1dcf1f199c527ffdaf159d86e5cd9",
+ 22526603255015707503680924025827203599625190615869254262
+ ],
+ [
+ "000000000000000000e3be3cf7343d7792c0d47d3c39ddb9ceaf19961e9eeab4",
+ 18556473167918062248854389700869820348727762534776424137
+ ],
+ [
+ "000000000000000000b3fb09d6def197657e20f9c1d5e9680cfcac1e1f9aa269",
+ 19759157687224108664379003516351943599373215433413919905
+ ],
+ [
+ "000000000000000000bfe71f044145e1b42fdfb3a523ee2a215e80fa6afc2a98",
+ 20014601621424565995143800336070874732337755340431658220
+ ],
+ [
+ "000000000000000000cee3bff56ee49c0f96d1cbd17fa17dc6f84b3f48aed765",
+ 16946223147907286639275870228581142863500004051737247938
+ ]
+]+
\ No newline at end of file
diff --git a/lib/network.py b/lib/network.py
@@ -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)
diff --git a/lib/verifier.py b/lib/verifier.py
@@ -34,18 +34,31 @@ class SPV(ThreadJob):
# Keyed by tx hash. Value is None if the merkle branch was
# requested, and the merkle root once it has been verified
self.merkle_roots = {}
+ self.requested_chunks = {}
def run(self):
lh = self.network.get_local_height()
unverified = self.wallet.get_unverified_txs()
for tx_hash, tx_height in unverified.items():
# do not request merkle branch before headers are available
- if (tx_height > 0) and (tx_hash not in self.merkle_roots) and (tx_height <= lh):
- request = ('blockchain.transaction.get_merkle',
- [tx_hash, tx_height])
- self.network.send([request], self.verify_merkle)
- self.print_error('requested merkle', tx_hash)
- self.merkle_roots[tx_hash] = None
+ if (tx_height > 0) and (tx_height <= lh):
+ header = self.network.blockchain().read_header(tx_height)
+ index = tx_height // 2016
+ #print(index, header)
+ if header is None:
+ if index not in self.requested_chunks and self.network.interface:
+ print("requesting chunk", index)
+ #request = ('blockchain.block.get_chunk', [index])
+ #self.network.send([request], self.verify_merkle)
+ self.requested_chunks[index] = None
+ self.network.request_chunk(self.network.interface, index)
+ else:
+ if tx_hash not in self.merkle_roots:
+ request = ('blockchain.transaction.get_merkle',
+ [tx_hash, tx_height])
+ self.network.send([request], self.verify_merkle)
+ self.print_error('requested merkle', tx_hash)
+ self.merkle_roots[tx_hash] = None
if self.network.blockchain() != self.blockchain:
self.blockchain = self.network.blockchain()