code drop

This commit is contained in:
nxsofsys 2018-01-14 20:04:59 +03:00
parent 066ce9679b
commit c1601fd749
6 changed files with 149 additions and 100 deletions

View File

@ -92,13 +92,14 @@ class NetworkConstants:
cls.ADDRTYPE_P2PKH = 111
cls.ADDRTYPE_P2SH = 196
cls.SEGWIT_HRP = "tb"
cls.GENESIS = "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
cls.HEADERS_URL = "http://35.224.186.7/headers00" #TODO
cls.GENESIS = "0007104ccda289427919efc39dc9e4d499804b7bebc22df55f8b834301260602"
cls.DEFAULT_PORTS = {'t':'51001', 's':'51002'}
cls.DEFAULT_SERVERS = read_json('servers_testnet.json', {})
cls.CHECKPOINTS = read_json('checkpoints_testnet.json', [])
NetworkConstants.set_mainnet()
NetworkConstants.set_testnet()
################################## transactions
@ -241,20 +242,34 @@ def push_script(x):
# ZCASH specific utils methods
# https://github.com/zcash/zcash/blob/master/qa/rpc-tests/test_framework/mininode.py
BASIC_HEADER_SIZE = 140
hash_to_str = lambda x: bytes(reversed(x)).hex()
str_to_hash = lambda x: bytes(reversed(bytes.fromhex(x)))
def read_vector_size(f):
nit = struct.unpack("<B", f.read(1))[0]
if nit == 253:
return struct.unpack("<H", f.read(2))[0]
elif nit == 254:
return struct.unpack("<I", f.read(4))[0]
elif nit == 255:
return struct.unpack("<Q", f.read(8))[0]
return nit
def ser_char_vector(l):
r = b''
if l is None:
l = ''
l = b''
if len(l) < 253:
r = chr(len(l))
r = struct.pack("<B", len(l))
elif len(l) < 0x10000:
r = chr(253) + struct.pack("<H", len(l))
r = struct.pack("<B", 253) + struct.pack("<H", len(l))
elif len(l) < 0x100000000:
r = chr(254) + struct.pack("<I", len(l))
r = struct.pack("<B", 254) + struct.pack("<I", len(l))
else:
r = chr(255) + struct.pack("<Q", len(l))
for i in l:
r += chr(i)
r = struct.pack("<B", 255) + struct.pack("<Q", len(l))
r += bytes(l)
return r
@ -281,7 +296,7 @@ def deser_uint256(f):
return r
def uint256_from_str(s):
def uint256_from_bytes(s):
r = 0
t = struct.unpack("<IIIIIIII", s[:32])
for i in range(8):
@ -300,30 +315,13 @@ def ser_uint256(u):
u >>= 32
return rs
def uint256_from_str(s):
r = 0
t = struct.unpack("<IIIIIIII", s[:32])
for i in range(8):
r += t[i] << (i * 32)
return r
def sha256(x):
x = to_bytes(x, 'utf8')
return bytes(hashlib.sha256(x).digest())
def Hash(x):
x = to_bytes(x, 'utf8')
out = bytes(sha256(sha256(x)))
return out
hash_encode = lambda x: bh2u(x[::-1])
hash_decode = lambda x: bfh(x)[::-1]
hmac_sha_512 = lambda x, y: hmac.new(x, y, hashlib.sha512).digest()
def is_new_seed(x, prefix=version.SEED_PREFIX):
from . import mnemonic
x = mnemonic.normalize_text(x)

View File

@ -38,38 +38,37 @@ MAX_TARGET = 0x00000000FFFF0000000000000000000000000000000000000000000000000000
def serialize_header(res):
r = b''
r += struct.pack("<i", res.get('version'))
r += ser_uint256(res.get('prev_block_hash'))
r += ser_uint256(res.get('merkle_root'))
r += ser_uint256(res.get('hash_reserved'))
r += str_to_hash(res.get('prev_block_hash'))
r += str_to_hash(res.get('merkle_root'))
r += str_to_hash(res.get('hash_reserved'))
r += struct.pack("<I", res.get('timestamp'))
r += struct.pack("<I", res.get('bits'))
r += ser_uint256(res.get('nonce'))
r += ser_char_vector(res.get('n_solution')).encode('utf-8')
r += str_to_hash(res.get('nonce'))
r += ser_char_vector(str_to_hash(res.get('n_solution')))
return r
def deserialize_header(f, height):
hex_to_int = lambda s: int('0x' + s[::-1].encode('hex'), 16)
h = {}
h['version'] = struct.unpack("<I", f.read(4))[0]
h['prev_block_hash'] = deser_uint256(f)
h['merkle_root'] = deser_uint256(f)
h['hash_reserved'] = deser_uint256(f)
h['prev_block_hash'] = hash_to_str(f.read(32))
h['merkle_root'] = hash_to_str(f.read(32))
h['hash_reserved'] = hash_to_str(f.read(32))
h['timestamp'] = struct.unpack("<I", f.read(4))[0]
h['bits'] = struct.unpack("<I", f.read(4))[0]
h['nonce'] = struct.unpack("<I", f.read(4))[0]
h['n_solution'] = deser_char_vector(f)
h['nonce'] = hash_to_str(f.read(32))
h['n_solution'] = hash_to_str(deser_char_vector(f))
h['block_height'] = height
return h
def sha256_header(self, header):
return uint256_from_str(Hash(self.serialize_header(header)))
def sha256_header(header):
return uint256_from_bytes(Hash(serialize_header(header)))
def hash_header(header):
if header is None:
return '0' * 64
if header.get('prev_block_hash') is None:
header['prev_block_hash'] = '00'*32
return hash_encode(Hash(serialize_header(header)))
header['prev_block_hash'] = '00'*64
return hash_to_str(Hash(serialize_header(header)))
blockchains = {}
@ -160,7 +159,21 @@ class Blockchain(util.PrintError):
def update_size(self):
p = self.path()
self._size = os.path.getsize(p) / 1484 if os.path.exists(p) else 0
self._size = 0
if os.path.exists(p):
with open(p, 'rb') as f:
f.seek(0, 2)
eof = f.tell()
f.seek(0, 0)
while True:
try:
f.seek(bitcoin.BASIC_HEADER_SIZE, 1)
vs = read_vector_size(f)
f.seek(vs, 1)
if f.tell() < eof:
self._size += 1
except:
break
def verify_header(self, header, prev_header, bits, target, nonce, n_solution):
prev_hash = self.sha256_header(prev_header)
@ -199,7 +212,8 @@ class Blockchain(util.PrintError):
def save_chunk(self, index, chunk):
filename = self.path()
d = (index * 2016 - self.checkpoint) * 1484
with open(filename, 'rb') as f:
d = self._height_to_offset(f, index * 2016 - self.checkpoint)
if d < 0:
chunk = chunk[-d:]
d = 0
@ -219,10 +233,14 @@ 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)*1484)
parent_data = f.read(parent_branch_size*1484)
self._height_to_offset
offset = self._height_to_offset(f, checkpoint - parent.checkpoint)
length = self._height_to_offset(f, parent_branch_size, offset)
f.seek(offset)
parent_data = f.read(length)
self.write(parent_data, 0)
parent.write(my_data, (checkpoint - parent.checkpoint)*1484)
parent.write(my_data, checkpoint - parent.checkpoint)
# store file path
for b in blockchains.values():
b.old_path = b.path()
@ -240,14 +258,30 @@ class Blockchain(util.PrintError):
blockchains[self.checkpoint] = self
blockchains[parent.checkpoint] = parent
def write(self, data, offset, truncate=True):
def _height_to_offset(self, f, height, start=0):
pos = f.tell()
f.seek(0, 2)
eof = f.tell()
f.seek(start, 0)
for i in range(height):
f.seek(bitcoin.BASIC_HEADER_SIZE, 1)
vs = read_vector_size(f)
f.seek(vs, 1)
if f.tell() >= eof:
raise Exception('Out of file')
f.seek(pos, 0)
return f.tell()
def write(self, data, delta, truncate):
filename = self.path()
with self.lock:
with open(filename, 'rb+') as f:
if offset != self._size*1484:
f.seek(offset)
f.truncate()
f.seek(0, 2)
eof = f.tell()
offset = self._height_to_offset(f, delta)
f.seek(offset)
if truncate and offset < eof:
f.truncate()
f.write(data)
f.flush()
os.fsync(f.fileno())
@ -255,10 +289,9 @@ class Blockchain(util.PrintError):
def save_header(self, header):
delta = header.get('block_height') - self.checkpoint
data = bfh(serialize_header(header))
data = serialize_header(header)
assert delta == self.size()
assert len(data) == 1484
self.write(data, delta*1484)
self.write(data, delta)
self.swap_with_parent()
def read_header(self, height):
@ -270,28 +303,24 @@ class Blockchain(util.PrintError):
if height > self.height():
return
idx, h = 0, None
delta = height - self.checkpoint
name = self.path()
if os.path.exists(name):
while idx <= height:
f = open(name, 'rb')
h = deserialize_header(f, height)
idx += 1
f = open(name, 'rb')
for i in range(delta):
f.seek(bitcoin.BASIC_HEADER_SIZE, 1)
vs = read_vector_size(f)
f.seek(vs, 1)
h = deserialize_header(f, height)
f.close()
return h
def get_hash(self, height):
if height == -1:
return '0000000000000000000000000000000000000000000000000000000000000000'
elif height == 0:
return bitcoin.NetworkConstants.GENESIS
elif height < len(self.checkpoints) * 2016:
assert (height+1) % 2016 == 0, height
index = height // 2016
h, t = self.checkpoints[index]
return h
else:
return hash_header(self.read_header(height))
return self.hash_header(self.read_header(height))
def hash_header(self, header):
return hash_header(header)
def get_target(self, index):
# compute target from chunk x, used in chunk x+1
@ -334,9 +363,10 @@ class Blockchain(util.PrintError):
return bitsN << 24 | bitsBase
def can_connect(self, header, check_height=True):
import pdb; pdb.set_trace()
height = header['block_height']
if check_height and self.height() != height - 1:
#self.print_error("cannot connect at height", height)
self.print_error("cannot connect at height", height)
return False
if height == 0:
return hash_header(header) == bitcoin.NetworkConstants.GENESIS

View File

@ -23,8 +23,8 @@ def expand_array(inp, out_len, bit_len, byte_pad=0):
# The acc_bits least-significant bits of acc_value represent a bit sequence
# in big-endian order.
acc_bits = 0;
acc_value = 0;
acc_bits = 0
acc_value = 0
j = 0
for i in range(len(inp)):

View File

@ -181,7 +181,7 @@ class Network(util.DaemonThread):
except:
self.default_server = None
if not self.default_server:
self.default_server = pick_random_server()
self.default_server = pick_random_server(protocol=self.protocol)
self.lock = threading.Lock()
self.pending_sends = []
self.message_id = 0
@ -789,7 +789,7 @@ class Network(util.DaemonThread):
self.notify('updated')
def request_header(self, interface, height):
#interface.print_error("requesting header %d" % height)
interface.print_error("requesting header %d" % height)
self.queue_request('blockchain.block.get_header', [height], interface)
interface.request = height
interface.req_time = time.time()
@ -806,6 +806,7 @@ class Network(util.DaemonThread):
interface.print_error("unsolicited header",interface.request, height)
self.connection_down(interface.server)
return
interface.print_error("interface.mode %s" % interface.mode)
chain = blockchain.check_header(header)
if interface.mode == 'backward':
can_connect = blockchain.can_connect(header)
@ -821,7 +822,6 @@ class Network(util.DaemonThread):
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)
@ -830,8 +830,7 @@ class Network(util.DaemonThread):
interface.bad = height
interface.bad_header = header
delta = interface.tip - height
next_height = max(self.max_checkpoint(), interface.tip - 2 * delta)
next_height = max(0, interface.tip - 2 * delta)
elif interface.mode == 'binary':
if chain:
interface.good = height
@ -841,7 +840,6 @@ 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
@ -951,18 +949,36 @@ class Network(util.DaemonThread):
def init_headers_file(self):
b = self.blockchains[0]
print(b.get_hash(0), bitcoin.NetworkConstants.GENESIS)
if b.get_hash(0) == bitcoin.NetworkConstants.GENESIS:
self.downloading_headers = False
return
filename = b.path()
length = 80 * len(bitcoin.NetworkConstants.CHECKPOINTS) * 2016
if not os.path.exists(filename) or os.path.getsize(filename) < length:
with open(filename, 'wb') as f:
if length>0:
f.seek(length-1)
f.write(b'\x00')
with b.lock:
b.update_size()
def download_thread():
try:
import urllib, socket
socket.setdefaulttimeout(30)
self.print_error("downloading ", bitcoin.NetworkConstants.HEADERS_URL)
urllib.request.urlretrieve(bitcoin.NetworkConstants.HEADERS_URL, filename)
self.print_error("done.")
except Exception:
import traceback
traceback.print_exc()
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()
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()
@ -974,23 +990,27 @@ class Network(util.DaemonThread):
def on_notify_header(self, interface, header):
height = header.get('block_height')
if not height:
return
if height < self.max_checkpoint():
self.connection_down(interface)
return
interface.tip_header = header
interface.tip = height
if interface.mode != 'default':
return
b = blockchain.check_header(header)
if b:
interface.blockchain = b
self.switch_lagging_interface()
self.notify('updated')
self.notify('interfaces')
return
b = blockchain.can_connect(header)
if b:
interface.blockchain = b
b.save_header(header)
@ -998,7 +1018,9 @@ class Network(util.DaemonThread):
self.notify('updated')
self.notify('interfaces')
return
tip = max([x.height() for x in self.blockchains.values()])
if tip >=0:
interface.mode = 'backward'
interface.bad = height
@ -1006,6 +1028,7 @@ class Network(util.DaemonThread):
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'
@ -1075,6 +1098,3 @@ class Network(util.DaemonThread):
cp = self.blockchain().get_checkpoints()
with open(path, 'w') as f:
f.write(json.dumps(cp, indent=4))
def max_checkpoint(self):
return max(0, len(bitcoin.NetworkConstants.CHECKPOINTS) * 2016 - 1)

View File

@ -323,11 +323,11 @@ def user_dir():
if 'ANDROID_DATA' in os.environ:
return android_check_data_dir()
elif os.name == 'posix':
return os.path.join(os.environ["HOME"], ".electrum")
return os.path.join(os.environ["HOME"], ".electrum-zcl")
elif "APPDATA" in os.environ:
return os.path.join(os.environ["APPDATA"], "Electrum")
return os.path.join(os.environ["APPDATA"], "Electrum-zcl")
elif "LOCALAPPDATA" in os.environ:
return os.path.join(os.environ["LOCALAPPDATA"], "Electrum")
return os.path.join(os.environ["LOCALAPPDATA"], "Electrum-zcl")
else:
#raise Exception("No home directory found in environment variables.")
return

View File

@ -2,9 +2,10 @@
# A simple script that connects to a server and displays block headers
import sys
import time
from electrum import SimpleConfig, Network
from electrum.util import print_msg, json_encode
from electrum_zcl import SimpleConfig, Network
from electrum_zcl.util import print_msg, json_encode
# start network
c = SimpleConfig()