electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

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:
Mlib/blockchain.py | 108+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Alib/checkpoints.json | 991+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/network.py | 73++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mlib/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()