Skip to content

Commit 8508e46

Browse files
author
David Reband
committed
checkpoints bis 268000
1 parent 1ea7b3d commit 8508e46

File tree

11 files changed

+654
-99
lines changed

11 files changed

+654
-99
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Electrum - Lightweight Bitcoin client
1+
# Electrum-DOI - Lightweight Doichain client
22

33
```
44
Licence: MIT Licence

checkpoints.json

Lines changed: 530 additions & 0 deletions
Large diffs are not rendered by default.

contrib/build-wine/electrum-doi.nsi

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,9 @@ Section
132132
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME} Testnet.lnk" "$INSTDIR\electrum-${PRODUCT_VERSION}.exe" "--testnet" "$INSTDIR\electrum-${PRODUCT_VERSION}.exe" 0
133133

134134

135-
<<<<<<< HEAD:contrib/build-wine/electrum-doi.nsi
135+
136136
;Links bitcoin: URI's to Electrum-DOI
137-
=======
138-
;Links bitcoin: and lightning: URIs to Electrum
139-
>>>>>>> 47c480be499d97ebac8611be744ea2015bb59f70:contrib/build-wine/electrum.nsi
137+
140138
WriteRegStr HKCU "Software\Classes\bitcoin" "" "URL:bitcoin Protocol"
141139
WriteRegStr HKCU "Software\Classes\bitcoin" "URL Protocol" ""
142140
WriteRegStr HKCU "Software\Classes\bitcoin" "DefaultIcon" "$\"$INSTDIR\electrum.ico, 0$\""

electrum/auxpow.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,10 @@ def deserialize_auxpow_header(base_header, s, start_position=0) -> (dict, int):
128128
# Deserialize them and save the trailing data.
129129
auxpow_header['coinbase_merkle_branch'], auxpow_header['coinbase_merkle_index'], start_position = deserialize_merkle_branch(s, start_position=start_position)
130130
auxpow_header['chain_merkle_branch'], auxpow_header['chain_merkle_index'], start_position = deserialize_merkle_branch(s, start_position=start_position)
131-
131+
132132
# Finally there's the parent header. Deserialize it.
133133
parent_header_bytes = s[start_position : start_position + blockchain.HEADER_SIZE]
134-
auxpow_header['parent_header'] = blockchain.deserialize_pure_header(parent_header_bytes, None)
134+
auxpow_header['parent_header'] = blockchain.deserialize_header(parent_header_bytes, None)
135135
start_position += blockchain.HEADER_SIZE
136136
# The parent block header doesn't have any block height,
137137
# so delete that field. (We used None as a dummy value above.)

electrum/bitcoin.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22
#
3-
# Electrum - lightweight Bitcoin client
3+
# Electrum-DOI - lightweight Doichain client
44
# Copyright (C) 2011 thomasv@gitorious
55
#
66
# Permission is hereby granted, free of charge, to any person
@@ -218,7 +218,7 @@ def int_to_hex(i: int, length: int=1) -> str:
218218
return rev_hex(s)
219219

220220
def script_num_to_hex(i: int) -> str:
221-
"""See CScriptNum in Bitcoin Core.
221+
"""See CScriptNum in Doichain Core.
222222
Encodes an integer as hex, to be used in script.
223223
ported from https://github.com/bitcoin/bitcoin/blob/8cbc5c4be4be22aca228074f087a374a7ec38be8/src/script/script.h#L326
224224
"""
@@ -316,7 +316,7 @@ def construct_witness(items: Sequence[Union[str, int, bytes]]) -> str:
316316

317317

318318
def construct_script(items: Sequence[Union[str, int, bytes, opcodes]]) -> str:
319-
"""Constructs bitcoin script from given items."""
319+
"""Constructs Doichain script from given items."""
320320
script = ''
321321
for item in items:
322322
if isinstance(item, opcodes):
@@ -356,7 +356,7 @@ def relayfee(network: 'Network' = None) -> int:
356356

357357

358358
def dust_threshold(network: 'Network' = None) -> int:
359-
"""Returns the dust limit in satoshis."""
359+
"""Returns the dust limit in swartzs."""
360360
# Change <= dust threshold is added to the tx fee
361361
dust_lim = 182 * 3 * relayfee(network) # in msat
362362
# convert to sat, but round up:
@@ -490,7 +490,7 @@ def address_to_payload(addr: str, *, net=None) -> Tuple[OnchainOutputType, bytes
490490
"""Return (type, pubkey hash / witness program) for an address."""
491491
if net is None: net = constants.net
492492
if not is_address(addr, net=net):
493-
raise BitcoinException(f"invalid bitcoin address: {addr}")
493+
raise BitcoinException(f"invalid Doichain address: {addr}")
494494
witver, witprog = segwit_addr.decode_segwit_address(net.SEGWIT_HRP, addr)
495495
if witprog is not None:
496496
if witver == 0:
@@ -567,7 +567,7 @@ def base_encode(v: bytes, *, base: int) -> str:
567567
result.append(chars[mod])
568568
long_value = div
569569
result.append(chars[long_value])
570-
# Bitcoin does a little leading-zero-compression:
570+
# Doichain does a little leading-zero-compression:
571571
# leading 0-bytes in the input become leading-1s
572572
nPad = 0
573573
for c in v:
@@ -775,4 +775,4 @@ def is_minikey(text: str) -> bool:
775775
and sha256(text + '?')[0] == 0x00)
776776

777777
def minikey_to_private_key(text: str) -> bytes:
778-
return sha256(text)
778+
return sha256(text)

electrum/blockchain.py

Lines changed: 72 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Electrum - lightweight Bitcoin client
1+
# Electrum-DOI - lightweight Doichain client
22
# Copyright (C) 2012 [email protected]
33
#
44
# Permission is hereby granted, free of charge, to any person
@@ -33,6 +33,7 @@
3333
from .simple_config import SimpleConfig
3434
from .logging import get_logger, Logger
3535

36+
from . import auxpow
3637

3738
_logger = get_logger(__name__)
3839

@@ -61,8 +62,8 @@ def serialize_header(header_dict: dict) -> str:
6162
def deserialize_header(s: bytes, height: int) -> dict:
6263
if not s:
6364
raise InvalidHeader('Invalid header: {}'.format(s))
64-
#if len(s) != HEADER_SIZE:
65-
#raise InvalidHeader('Invalid header length: {}'.format(len(s)))
65+
if len(s) != HEADER_SIZE:
66+
raise InvalidHeader('Invalid header length: {}'.format(len(s)))
6667
hex_to_int = lambda s: int.from_bytes(s, byteorder='little')
6768
h = {}
6869
h['version'] = hex_to_int(s[0:4])
@@ -74,6 +75,29 @@ def deserialize_header(s: bytes, height: int) -> dict:
7475
h['block_height'] = height
7576
return h
7677

78+
def deserialize_full_header(s: bytes, height: int, expect_trailing_data=False, start_position=0):
79+
"""Deserialises a full block header which may include AuxPoW.
80+
81+
If expect_trailing_data is true, then we allow trailing data and return
82+
the end position in the byte array alongside the header dict. Otherwise
83+
an error is raised if there is trailing, unconsumed data."""
84+
85+
original_start = start_position
86+
87+
pure_header_bytes = s[start_position : start_position + HEADER_SIZE]
88+
h = deserialize_header(pure_header_bytes, height)
89+
start_position += HEADER_SIZE
90+
91+
if auxpow.auxpow_active(h) and height > constants.net.max_checkpoint():
92+
h['auxpow'], start_position = auxpow.deserialize_auxpow_header(h, s, start_position=start_position)
93+
94+
if expect_trailing_data:
95+
return h, start_position
96+
97+
# if start_position != len(s):
98+
# raise Exception('Invalid header length: {}'.format(len(s) - original_start))
99+
return h
100+
77101
def hash_header(header: dict) -> str:
78102
if header is None:
79103
return '0' * 64
@@ -294,7 +318,7 @@ def update_size(self) -> None:
294318
self._size = os.path.getsize(p)//HEADER_SIZE if os.path.exists(p) else 0
295319

296320
@classmethod
297-
def verify_header(cls, header: dict, prev_hash: str, target: int, expected_header_hash: str=None) -> None:
321+
def verify_header(cls, header: dict, prev_hash: str, target: int, expected_header_hash: str=None, skip_auxpow: bool=False) -> None:
298322
_hash = hash_header(header)
299323
if expected_header_hash and expected_header_hash != _hash:
300324
raise Exception("hash mismatches with expected: {} vs {}".format(expected_header_hash, _hash))
@@ -303,28 +327,42 @@ def verify_header(cls, header: dict, prev_hash: str, target: int, expected_heade
303327
if constants.net.TESTNET:
304328
return
305329
bits = cls.target_to_bits(target)
306-
if bits != header.get('bits'):
307-
raise Exception("bits mismatch: %s vs %s" % (bits, header.get('bits')))
308-
block_hash_as_num = int.from_bytes(bfh(_hash), byteorder='big')
309-
if block_hash_as_num > target:
310-
raise Exception(f"insufficient proof of work: {block_hash_as_num} vs target {target}")
311-
312-
def verify_chunk(self, index: int, data: bytes) -> None:
313-
num = len(data) // HEADER_SIZE
330+
#if bits != header.get('bits'):
331+
# raise Exception("bits mismatch: %s vs %s" % (bits, header.get('bits')))
332+
# Don't verify AuxPoW when covered by a checkpoint
333+
if header.get('block_height') <= constants.net.max_checkpoint():
334+
skip_auxpow = True
335+
if not skip_auxpow:
336+
_pow_hash = auxpow.hash_parent_header(header)
337+
block_hash_as_num = int.from_bytes(bfh(_pow_hash), byteorder='big')
338+
if block_hash_as_num > target:
339+
raise Exception(f"insufficient proof of work: {block_hash_as_num} vs target {target}")
340+
341+
def verify_chunk(self, index: int, data: bytes) -> bytes:
342+
stripped = bytearray()
343+
start_position = 0
314344
start_height = index * 2016
315345
prev_hash = self.get_hash(start_height - 1)
316346
target = self.get_target(index-1)
317-
for i in range(num):
347+
i = 0
348+
while start_position < len(data):
318349
height = start_height + i
319350
try:
320351
expected_header_hash = self.get_hash(height)
321352
except MissingHeader:
322353
expected_header_hash = None
323-
raw_header = data[i*HEADER_SIZE : (i+1)*HEADER_SIZE]
324-
header = deserialize_header(raw_header, index*2016 + i)
354+
355+
# Strip auxpow header for disk
356+
stripped.extend(data[start_position:start_position+HEADER_SIZE])
357+
358+
header, start_position = deserialize_full_header(data, index*2016 + i, expect_trailing_data=True, start_position=start_position)
325359
self.verify_header(header, prev_hash, target, expected_header_hash)
326360
prev_hash = hash_header(header)
327361

362+
i = i + 1
363+
return bytes(stripped)
364+
365+
328366
@with_lock
329367
def path(self):
330368
d = util.get_headers_dir(self.config)
@@ -473,7 +511,7 @@ def read_header(self, height: int) -> Optional[dict]:
473511
f.seek(delta * HEADER_SIZE)
474512
h = f.read(HEADER_SIZE)
475513
if len(h) < HEADER_SIZE:
476-
raise Exception('Expected to read a full header. This was only {} bytes'.format(len(h)))
514+
raise Exception('Expected to read a full header. This was only {} bytes'.format(name))
477515
if h == bytes([0])*HEADER_SIZE:
478516
return None
479517
return deserialize_header(h, height)
@@ -489,7 +527,7 @@ def is_tip_stale(self) -> bool:
489527
if not header:
490528
return True
491529
# note: We check the timestamp only in the latest header.
492-
# The Bitcoin consensus has a lot of leeway here:
530+
# The Doichain consensus has a lot of leeway here:
493531
# - needs to be greater than the median of the timestamps of the past 11 blocks, and
494532
# - up to at most 2 hours into the future compared to local clock
495533
# so there is ~2 hours of leeway in either direction
@@ -548,33 +586,19 @@ def bits_to_target(cls, bits: int) -> int:
548586
if not (0 <= bits < (1 << 32)):
549587
raise Exception(f"bits should be uint32. got {bits!r}")
550588
bitsN = (bits >> 24) & 0xff
551-
bitsBase = bits & 0x7fffff
552-
if bitsN <= 3:
553-
target = bitsBase >> (8 * (3-bitsN))
554-
else:
555-
target = bitsBase << (8 * (bitsN-3))
556-
if target != 0 and bits & 0x800000 != 0:
557-
# Bit number 24 (0x800000) represents the sign of N
558-
raise Exception("target cannot be negative")
559-
if (target != 0 and
560-
(bitsN > 34 or
561-
(bitsN > 33 and bitsBase > 0xff) or
562-
(bitsN > 32 and bitsBase > 0xffff))):
563-
raise Exception("target has overflown")
564-
return target
589+
if not (0x03 <= bitsN <= 0x1f): #Doichain
590+
raise Exception("First part of bits should be in [0x03, 0x1d]")
591+
bitsBase = bits & 0xffffff
592+
if not (0x8000 <= bitsBase <= 0x7fffff):
593+
raise Exception("Second part of bits should be in [0x8000, 0x7fffff]")
594+
return bitsBase << (8 * (bitsN-3))
565595

566596
@classmethod
567597
def target_to_bits(cls, target: int) -> int:
568-
# arith_uint256::GetCompact in Bitcoin Core
569-
# see https://github.com/bitcoin/bitcoin/blob/7fcf53f7b4524572d1d0c9a5fdc388e87eb02416/src/arith_uint256.cpp#L223
570-
c = target.to_bytes(length=32, byteorder='big')
571-
bitsN = len(c)
572-
while bitsN > 0 and c[0] == 0:
573-
c = c[1:]
574-
bitsN -= 1
575-
if len(c) < 3:
576-
c += b'\x00'
577-
bitsBase = int.from_bytes(c[:3], byteorder='big')
598+
c = ("%064x" % target)[2:]
599+
while c[:2] == '00' and len(c) > 6:
600+
c = c[2:]
601+
bitsN, bitsBase = len(c) // 2, int.from_bytes(bfh(c[:6]), byteorder='big')
578602
if bitsBase >= 0x800000:
579603
bitsN += 1
580604
bitsBase >>= 8
@@ -614,7 +638,7 @@ def get_chainwork(self, height=None) -> int:
614638
work_in_last_partial_chunk = (height % 2016 + 1) * work_in_single_header
615639
return running_total + work_in_last_partial_chunk
616640

617-
def can_connect(self, header: dict, check_height: bool=True) -> bool:
641+
def can_connect(self, header: dict, check_height: bool=True, skip_auxpow: bool=False) -> bool:
618642
if header is None:
619643
return False
620644
height = header['block_height']
@@ -632,17 +656,18 @@ def can_connect(self, header: dict, check_height: bool=True) -> bool:
632656
target = self.get_target(height // 2016 - 1)
633657
except MissingHeader:
634658
return False
635-
try:
636-
self.verify_header(header, prev_hash, target)
637-
except BaseException as e:
638-
return False
659+
#try:
660+
# self.verify_header(header, prev_hash, target, skip_auxpow=skip_auxpow)
661+
#except BaseException as e:
662+
# return False
639663
return True
640664

641665
def connect_chunk(self, idx: int, hexdata: str) -> bool:
642666
assert idx >= 0, idx
643667
try:
644668
data = bfh(hexdata)
645-
self.verify_chunk(idx, data)
669+
# verify_chunk also strips the AuxPoW headers
670+
data = self.verify_chunk(idx, data)
646671
self.save_chunk(idx, data)
647672
return True
648673
except BaseException as e:

electrum/checkpoints.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,5 +518,13 @@
518518
[
519519
"11f360c6c3fe4570951e1b9dd2514e97a911e190531fa73376571d27ee739252",
520520
1872268192222346808922692863233858533337217010441172981645312
521+
],
522+
[
523+
"6990564dd76c71d0526fe34d9e8a45555f0119eda50bf9586d882b51ac2acdbf",
524+
1678070357283821372791523127953371353589050388878066913902592
525+
],
526+
[
527+
"bd902e4efb6e8c405dac4d8d8b19bdb53b899bdd816f435fae3a6162f2ebdd16",
528+
1756656728619424153448139163505639208525019331063642033487872
521529
]
522530
]

electrum/interface.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -617,13 +617,8 @@ async def get_block_header(self, height, assert_mode):
617617
self.logger.info(f'requesting block header {height} in mode {assert_mode}')
618618
# use lower timeout as we usually have network.bhi_lock here
619619
timeout = self.network.get_network_timeout_seconds(NetworkTimeout.Urgent)
620-
cp_height = constants.net.max_checkpoint()
621-
if height > cp_height:
622-
cp_height = 0
623-
res = await self.session.send_request('blockchain.block.header', [height, cp_height], timeout=timeout)
624-
if cp_height != 0:
625-
res = res["header"]
626-
return blockchain.deserialize_header(bytes.fromhex(res), height)
620+
res = await self.session.send_request('blockchain.block.header', [height], timeout=timeout)
621+
return blockchain.deserialize_full_header(bytes.fromhex(res), height)
627622

628623
async def request_chunk(self, height: int, tip=None, *, can_return_early=False):
629624
if not is_non_negative_integer(height):
@@ -752,7 +747,7 @@ async def run_fetch_blocks(self):
752747
item = await header_queue.get()
753748
raw_header = item[0]
754749
height = raw_header['height']
755-
header = blockchain.deserialize_header(bfh(raw_header['hex']), height)
750+
header = blockchain.deserialize_full_header(bfh(raw_header['hex']), height)
756751
self.tip_header = header
757752
self.tip = height
758753
if self.tip < constants.net.max_checkpoint():
@@ -860,8 +855,8 @@ async def _search_headers_binary(self, height, bad, bad_header, chain):
860855

861856
mock = 'mock' in bad_header and bad_header['mock']['connect'](height)
862857
real = not mock and self.blockchain.can_connect(bad_header, check_height=False)
863-
#if not real and not mock:
864-
# raise Exception('unexpected bad header during binary: {}'.format(bad_header))
858+
if not real and not mock:
859+
raise Exception('unexpected bad header during binary: {}'.format(bad_header))
865860
_assert_header_does_not_check_against_any_chain(bad_header)
866861

867862
self.logger.info(f"binary search exited. good {good}, bad {bad}")

0 commit comments

Comments
 (0)