add Zcash specific to lib/(bitocin|blockchain|network).py
This commit is contained in:
parent
fe602c2499
commit
387c77b185
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"localhost": {
|
||||
"pruning": "-",
|
||||
"s": "50002",
|
||||
"t": "50001",
|
||||
"s": "50022",
|
||||
"t": "50021",
|
||||
"version": "1.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"localhost": {
|
||||
"pruning": "-",
|
||||
"s": "50002",
|
||||
"t": "50001",
|
||||
"s": "51022",
|
||||
"t": "51021",
|
||||
"version": "1.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue