From 387c77b185ed95e69ebe45c65ead79334ce0a283 Mon Sep 17 00:00:00 2001 From: zebra-lucky Date: Tue, 20 Mar 2018 22:27:48 +0200 Subject: [PATCH] add Zcash specific to lib/(bitocin|blockchain|network).py --- lib/bitcoin.py | 14 ++++---- lib/blockchain.py | 50 +++++++++++++++++------------ lib/network.py | 69 ++++++++++++++++++++++++++-------------- lib/servers.json | 4 +-- lib/servers_testnet.json | 4 +-- lib/version.py | 2 +- 6 files changed, 87 insertions(+), 56 deletions(-) diff --git a/lib/bitcoin.py b/lib/bitcoin.py index 05730280..dfc6ff69 100644 --- a/lib/bitcoin.py +++ b/lib/bitcoin.py @@ -64,8 +64,8 @@ class NetworkConstants: def set_mainnet(cls): cls.TESTNET = False cls.WIF_PREFIX = 0x80 - cls.ADDRTYPE_P2PKH = 0x1CB8 - cls.ADDRTYPE_P2SH = 0x1CBD + cls.ADDRTYPE_P2PKH = bytes.fromhex('1CB8') + cls.ADDRTYPE_P2SH = bytes.fromhex('1CBD') cls.HEADERS_URL = '' # TODO headers bootstrap cls.GENESIS = '00040fe8ec8471911baa1db1266ea15dd06b4a8a5c453883c000b031973dce08' cls.DEFAULT_PORTS = {'t': '50001', 's': '50002'} @@ -75,8 +75,8 @@ class NetworkConstants: def set_testnet(cls): cls.TESTNET = True cls.WIF_PREFIX = 0xEF - cls.ADDRTYPE_P2PKH = 0x1D25 - cls.ADDRTYPE_P2SH = 0x1CBA + cls.ADDRTYPE_P2PKH = bytes.fromhex('1D25') + cls.ADDRTYPE_P2SH = bytes.fromhex('1CBA') cls.HEADERS_URL = '' # TODO headers bootstrap cls.GENESIS = '05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38' cls.DEFAULT_PORTS = {'t':'51001', 's':'51002'} @@ -309,15 +309,15 @@ def hash_160(public_key): def hash160_to_b58_address(h160, addrtype): - s = bytes([addrtype]) + s = addrtype s += h160 return base_encode(s+Hash(s)[0:4], base=58) def b58_address_to_hash160(addr): addr = to_bytes(addr, 'ascii') - _bytes = base_decode(addr, 25, base=58) - return _bytes[0], _bytes[1:21] + _bytes = base_decode(addr, 26, base=58) + return _bytes[0:2], _bytes[2:22] def hash160_to_p2pkh(h160): diff --git a/lib/blockchain.py b/lib/blockchain.py index 00f82b84..61efc3ac 100644 --- a/lib/blockchain.py +++ b/lib/blockchain.py @@ -27,15 +27,20 @@ from . import util from . import bitcoin from .bitcoin import * +HDR_LEN = 1487 +CHUNK_LEN = 100 MAX_TARGET = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 def serialize_header(res): s = int_to_hex(res.get('version'), 4) \ + rev_hex(res.get('prev_block_hash')) \ + rev_hex(res.get('merkle_root')) \ + + rev_hex(res.get('reserved_hash')) \ + int_to_hex(int(res.get('timestamp')), 4) \ + int_to_hex(int(res.get('bits')), 4) \ - + int_to_hex(int(res.get('nonce')), 4) + + rev_hex(res.get('nonce')) \ + + rev_hex(res.get('sol_size')) \ + + rev_hex(res.get('solution')) return s def deserialize_header(s, height): @@ -44,9 +49,12 @@ def deserialize_header(s, height): h['version'] = hex_to_int(s[0:4]) h['prev_block_hash'] = hash_encode(s[4:36]) h['merkle_root'] = hash_encode(s[36:68]) - h['timestamp'] = hex_to_int(s[68:72]) - h['bits'] = hex_to_int(s[72:76]) - h['nonce'] = hex_to_int(s[76:80]) + h['reserved_hash'] = hash_encode(s[68:100]) + h['timestamp'] = hex_to_int(s[100:104]) + h['bits'] = hex_to_int(s[104:108]) + h['nonce'] = hash_encode(s[108:140]) + h['sol_size'] = hash_encode(s[140:143]) + h['solution'] = hash_encode(s[143:1487]) h['block_height'] = height return h @@ -141,7 +149,7 @@ class Blockchain(util.PrintError): def update_size(self): p = self.path() - self._size = os.path.getsize(p)//80 if os.path.exists(p) else 0 + self._size = os.path.getsize(p)//HDR_LEN if os.path.exists(p) else 0 def verify_header(self, header, prev_header, bits, target): prev_hash = hash_header(prev_header) @@ -156,14 +164,14 @@ class Blockchain(util.PrintError): raise BaseException("insufficient proof of work: %s vs target %s" % (int('0x' + _hash, 16), target)) def verify_chunk(self, index, data): - num = len(data) // 80 + num = len(data) // HDR_LEN prev_header = None if index != 0: - prev_header = self.read_header(index * 2016 - 1) + prev_header = self.read_header(index * CHUNK_LEN - 1) bits, target = self.get_target(index) for i in range(num): - raw_header = data[i*80:(i+1) * 80] - header = deserialize_header(raw_header, index*2016 + i) + raw_header = data[i*HDR_LEN:(i+1) * HDR_LEN] + header = deserialize_header(raw_header, index*CHUNK_LEN + i) self.verify_header(header, prev_header, bits, target) prev_header = header @@ -174,7 +182,7 @@ class Blockchain(util.PrintError): def save_chunk(self, index, chunk): filename = self.path() - d = (index * 2016 - self.checkpoint) * 80 + d = (index * CHUNK_LEN - self.checkpoint) * HDR_LEN if d < 0: chunk = chunk[-d:] d = 0 @@ -194,10 +202,10 @@ class Blockchain(util.PrintError): with open(self.path(), 'rb') as f: my_data = f.read() with open(parent.path(), 'rb') as f: - f.seek((checkpoint - parent.checkpoint)*80) - parent_data = f.read(parent_branch_size*80) + f.seek((checkpoint - parent.checkpoint)*HDR_LEN) + parent_data = f.read(parent_branch_size*HDR_LEN) self.write(parent_data, 0) - parent.write(my_data, (checkpoint - parent.checkpoint)*80) + parent.write(my_data, (checkpoint - parent.checkpoint)*HDR_LEN) # store file path for b in blockchains.values(): b.old_path = b.path() @@ -219,7 +227,7 @@ class Blockchain(util.PrintError): filename = self.path() with self.lock: with open(filename, 'rb+') as f: - if offset != self._size*80: + if offset != self._size*HDR_LEN: f.seek(offset) f.truncate() f.seek(offset) @@ -232,8 +240,8 @@ class Blockchain(util.PrintError): delta = header.get('block_height') - self.checkpoint data = bfh(serialize_header(header)) assert delta == self.size() - assert len(data) == 80 - self.write(data, delta*80) + assert len(data) == HDR_LEN + self.write(data, delta*HDR_LEN) self.swap_with_parent() def read_header(self, height): @@ -248,8 +256,8 @@ class Blockchain(util.PrintError): name = self.path() if os.path.exists(name): with open(name, 'rb') as f: - f.seek(delta * 80) - h = f.read(80) + f.seek(delta * HDR_LEN) + h = f.read(HDR_LEN) return deserialize_header(h, height) def get_hash(self, height): @@ -260,8 +268,8 @@ class Blockchain(util.PrintError): return 0, 0 if index == 0: return 0x1d00ffff, MAX_TARGET - first = self.read_header((index-1) * 2016) - last = self.read_header(index*2016 - 1) + first = self.read_header((index-1) * CHUNK_LEN) + last = self.read_header(index*CHUNK_LEN - 1) # bits to target bits = last.get('bits') bitsN = (bits >> 24) & 0xff @@ -300,7 +308,7 @@ class Blockchain(util.PrintError): prev_hash = hash_header(previous_header) if prev_hash != header.get('prev_block_hash'): return False - bits, target = self.get_target(height // 2016) + bits, target = self.get_target(height // CHUNK_LEN) try: self.verify_header(header, previous_header, bits, target) except: diff --git a/lib/network.py b/lib/network.py index 739aa535..bb5c047a 100644 --- a/lib/network.py +++ b/lib/network.py @@ -38,6 +38,7 @@ import socks from . import util from . import bitcoin from .bitcoin import * +from .blockchain import HDR_LEN, CHUNK_LEN from .interface import Connection, Interface from . import blockchain from .version import ELECTRUM_VERSION, PROTOCOL_VERSION @@ -553,10 +554,12 @@ class Network(util.DaemonThread): if error is None: self.relay_fee = int(result * COIN) self.print_error("relayfee", self.relay_fee) - elif method == 'blockchain.block.get_chunk': - self.on_get_chunk(interface, response) - elif method == 'blockchain.block.get_header': - self.on_get_header(interface, response) + elif method == 'blockchain.block.headers': + height, count = params + if count == 1: + self.on_get_header(interface, response, height) + elif count == CHUNK_LEN: + self.on_get_chunk(interface, response, height) for callback in callbacks: callback(response) @@ -704,7 +707,7 @@ class Network(util.DaemonThread): interface.mode = 'default' interface.request = None self.interfaces[server] = interface - self.queue_request('blockchain.headers.subscribe', [], interface) + self.queue_request('blockchain.headers.subscribe', [True], interface) if server == self.default_server: self.switch_to_interface(server) #self.notify('interfaces') @@ -757,23 +760,26 @@ class Network(util.DaemonThread): def request_chunk(self, interface, idx): interface.print_error("requesting chunk %d" % idx) - self.queue_request('blockchain.block.get_chunk', [idx], interface) + self.queue_request('blockchain.block.headers', + [CHUNK_LEN*idx, CHUNK_LEN], interface) interface.request = idx interface.req_time = time.time() - def on_get_chunk(self, interface, response): + def on_get_chunk(self, interface, response, height): '''Handle receiving a chunk of block headers''' error = response.get('error') result = response.get('result') - params = response.get('params') - if result is None or params is None or error is not None: + if result is None or error is not None: interface.print_error(error or 'bad response') return + + hex_chunk = result.get('hex', None) # Ignore unsolicited chunks - index = params[0] - if interface.request != index: + index = height // CHUNK_LEN + if interface.request != index or height / CHUNK_LEN != index: return - connect = interface.blockchain.connect_chunk(index, result) + + connect = interface.blockchain.connect_chunk(index, hex_chunk) # If not finished, get the next chunk if not connect: self.connection_down(interface.server) @@ -789,23 +795,32 @@ class Network(util.DaemonThread): def request_header(self, interface, height): #interface.print_error("requesting header %d" % height) - self.queue_request('blockchain.block.get_header', [height], interface) + self.queue_request('blockchain.block.headers', [height, 1], interface) interface.request = height interface.req_time = time.time() - def on_get_header(self, interface, response): + def on_get_header(self, interface, response, height): '''Handle receiving a single block header''' - header = response.get('result') - if not header: - interface.print_error(response) - self.connection_down(interface.server) - return - height = header.get('block_height') + result = response.get('result', {}) + hex_header = result.get('hex', None) + if interface.request != height: interface.print_error("unsolicited header",interface.request, height) self.connection_down(interface.server) return + if not hex_header: + interface.print_error(response) + self.connection_down(interface.server) + return + + if len(hex_header) != HDR_LEN*2: + interface.print_error('wrong header length', interface.request) + self.connection_down(interface.server) + return + + header = blockchain.deserialize_header(bfh(hex_header), height) + chain = blockchain.check_header(header) if interface.mode == 'backward': if chain: @@ -901,7 +916,7 @@ class Network(util.DaemonThread): # If not finished, get the next header if next_height: if interface.mode == 'catch_up' and interface.tip > next_height + 50: - self.request_chunk(interface, next_height // 2016) + self.request_chunk(interface, next_height // CHUNK_LEN) else: self.request_header(interface, next_height) else: @@ -979,9 +994,17 @@ class Network(util.DaemonThread): self.on_stop() def on_notify_header(self, interface, header): - height = header.get('block_height') - if not height: + height = header.get('height') + hex_header = header.get('hex') + if not height or not hex_header: return + + if len(hex_header) != HDR_LEN*2: + interface.print_error('wrong header length', interface.request) + self.connection_down(interface.server) + return + + header = blockchain.deserialize_header(bfh(hex_header), height) interface.tip_header = header interface.tip = height if interface.mode != 'default': diff --git a/lib/servers.json b/lib/servers.json index 4096b464..0da91d86 100644 --- a/lib/servers.json +++ b/lib/servers.json @@ -1,8 +1,8 @@ { "localhost": { "pruning": "-", - "s": "50002", - "t": "50001", + "s": "50022", + "t": "50021", "version": "1.1" } } diff --git a/lib/servers_testnet.json b/lib/servers_testnet.json index 4096b464..b44b0988 100644 --- a/lib/servers_testnet.json +++ b/lib/servers_testnet.json @@ -1,8 +1,8 @@ { "localhost": { "pruning": "-", - "s": "50002", - "t": "50001", + "s": "51022", + "t": "51021", "version": "1.1" } } diff --git a/lib/version.py b/lib/version.py index d842284f..15ad3699 100644 --- a/lib/version.py +++ b/lib/version.py @@ -1,5 +1,5 @@ ELECTRUM_VERSION = '3.0.5' # version of the client package -PROTOCOL_VERSION = '1.1' # protocol version requested +PROTOCOL_VERSION = '1.2' # protocol version requested # The hash of the mnemonic seed must begin with this SEED_PREFIX = '01' # Standard wallet