Merge branch 'develop'

This commit is contained in:
Jon Layton 2018-01-14 21:25:53 -05:00
commit 615ea50786
6 changed files with 55 additions and 314 deletions

View File

@ -87,8 +87,9 @@ class NetworkConstants:
cls.EQUIHASH_N = 200
cls.EQUIHASH_K = 9
cls.HEADERS_URL = "https://headers.electrum.org/blockchain_headers" #TODO
cls.HEADERS_URL = "http://35.224.186.7/headers00" #TODO
cls.CHUNK_SIZE = 200
# https://github.com/z-classic/zclassic/blob/master/src/chainparams.cpp#L234
@classmethod
@ -104,11 +105,12 @@ class NetworkConstants:
cls.CHECKPOINTS = read_json('checkpoints_testnet.json', [])
cls.EQUIHASH_N = 200
cls.EQUIHASH_K = 9
cls.CHUNK_SIZE = 200
cls.HEADERS_URL = "http://35.224.186.7/headers00" #TODO
NetworkConstants.set_testnet()
NetworkConstants.set_mainnet()
################################## transactions
@ -334,6 +336,10 @@ def Hash(x):
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

@ -117,7 +117,7 @@ class Blockchain(util.PrintError):
self.config = config
self.catch_up = None # interface catching up
self.checkpoint = checkpoint
self.checkpoints = bitcoin.NetworkConstants.CHECKPOINTS
self.checkpoints = NetworkConstants.CHECKPOINTS
self.parent_id = parent_id
self.lock = threading.Lock()
with self.lock:
@ -164,46 +164,48 @@ class Blockchain(util.PrintError):
self._size = 0
if os.path.exists(p):
with open(p, 'rb') as f:
f.seek(0, 2)
eof = f.tell()
eof = f.seek(0, 2)
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:
if f.tell() <= eof:
self._size += 1
if f.tell() >= eof:
break
except:
import traceback
traceback.print_exc()
break
def verify_header(self, header, prev_header, target):
prev_hash = hash_header(prev_header)
def verify_header(self, header, prev_header):
if prev_header:
prev_hash = hash_header(prev_header)
if prev_hash != header.get('prev_block_hash'):
raise BaseException("prev hash mismatch: %s vs %s" % (prev_hash, header.get('prev_block_hash')))
_powhash = sha256_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')))
target = self.bits_to_target(header['bits'])
if _powhash > target:
raise BaseException("insufficient proof of work: %s vs target %s" % (int('0x' + _powhash, 16), target))
nonce = uint256_from_bytes(str_to_hash(header.get('nonce')))
n_solution = vector_from_bytes(base64.b64decode(header.get('n_solution').encode('utf8')))
if not is_gbp_valid(serialize_header(header), nonce, n_solution,
bitcoin.NetworkConstants.EQUIHASH_N, bitcoin.NetworkConstants.EQUIHASH_K):
NetworkConstants.EQUIHASH_N, NetworkConstants.EQUIHASH_K):
raise BaseException("Equihash invalid")
def verify_chunk(self, index, data):
num = len(data) / 1484
data = BytesIO(data)
prev_header = None
if index != 0:
prev_header = self.read_header(index * 2016 - 1)
headers = {}
for i in range(num):
raw_header = data[i * 1484:(i + 1) * 1484]
header = self.deserialize_header(raw_header, index * 2016 + i)
headers[header.get('block_height')] = header
nonce, n_solution = headers.get('nonce'), header.get('n_solution')
bits, target = self.get_target(index * 2016 + i, headers)
self.verify_header(header, prev_header, bits, target, nonce, n_solution)
prev_header = self.read_header(index * NetworkConstants.CHUNK_SIZE - 1)
while True:
try:
header = self.deserialize_header(data, index * NetworkConstants.CHUNK_SIZE + i)
except:
break
self.verify_header(header, prev_header)
prev_header = header
def path(self):
@ -214,7 +216,7 @@ class Blockchain(util.PrintError):
def save_chunk(self, index, chunk):
filename = self.path()
with open(filename, 'rb') as f:
d = self._height_to_offset(f, index * 2016 - self.checkpoint)
d = self._height_to_offset(f, index * NetworkConstants.CHUNK_SIZE - self.checkpoint)
if d < 0:
chunk = chunk[-d:]
d = 0
@ -261,24 +263,25 @@ class Blockchain(util.PrintError):
def _height_to_offset(self, f, height, start=0):
pos = f.tell()
f.seek(0, 2)
eof = f.tell()
eof = f.seek(0, 2)
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:
if f.tell() > eof:
raise Exception('Out of file')
elif f.tell() == eof:
break
result = f.tell()
f.seek(pos, 0)
return f.tell()
return result
def write(self, data, delta, truncate=False):
filename = self.path()
with self.lock:
with open(filename, 'rb+') as f:
f.seek(0, 2)
eof = f.tell()
eof = f.seek(0, 2)
offset = self._height_to_offset(f, delta)
f.seek(offset)
if truncate and offset < eof:
@ -289,6 +292,7 @@ class Blockchain(util.PrintError):
self.update_size()
def save_header(self, header):
self.print_error("save_header", header.get('block_height'))
delta = header.get('block_height') - self.checkpoint
data = serialize_header(header)
assert delta == self.size()
@ -321,27 +325,6 @@ class Blockchain(util.PrintError):
def hash_header(self, header):
return hash_header(header)
def get_target(self, index):
# compute target from chunk x, used in chunk x+1
if bitcoin.NetworkConstants.TESTNET:
return 0
if index == -1:
return MAX_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):
@ -367,7 +350,7 @@ class Blockchain(util.PrintError):
self.print_error("cannot connect at height", height)
return False
if height == 0:
return hash_header(header) == bitcoin.NetworkConstants.GENESIS
return hash_header(header) == NetworkConstants.GENESIS
try:
prev_header = self.read_header(height - 1)
prev_hash = self.hash_header(prev_header)
@ -375,9 +358,8 @@ class Blockchain(util.PrintError):
return False
if prev_hash != header.get('prev_block_hash'):
return False
target = self.get_target(height // 2016 - 1)
try:
self.verify_header(header, prev_header, target)
self.verify_header(header, prev_header)
except BaseException as e:
import traceback
traceback.print_exc()
@ -387,21 +369,13 @@ class Blockchain(util.PrintError):
def connect_chunk(self, idx, hexdata):
try:
data = bfh(hexdata)
data = bytes.fromhex(hexdata)
self.verify_chunk(idx, data)
#self.print_error("validated chunk %d" % idx)
self.print_error("validated chunk %d" % idx)
self.save_chunk(idx, data)
return True
except BaseException as e:
import traceback
traceback.print_exc()
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

View File

@ -59,7 +59,7 @@ def parse_servers(result):
for v in item[2]:
if re.match("[st]\d*", v):
protocol, port = v[0], v[1:]
if port == '': port = bitcoin.NetworkConstants.DEFAULT_PORTS[protocol]
if port == '': port = NetworkConstants.DEFAULT_PORTS[protocol]
out[protocol] = port
elif re.match("v(.?)+", v):
version = v[1:]
@ -93,7 +93,7 @@ def filter_protocol(hostmap, protocol = 's'):
def pick_random_server(hostmap = None, protocol = 's', exclude_set = set()):
if hostmap is None:
hostmap = bitcoin.NetworkConstants.DEFAULT_SERVERS
hostmap = NetworkConstants.DEFAULT_SERVERS
eligible = list(set(filter_protocol(hostmap, protocol)) - exclude_set)
return random.choice(eligible) if eligible else None
@ -358,7 +358,7 @@ class Network(util.DaemonThread):
return list(self.interfaces.keys())
def get_servers(self):
out = bitcoin.NetworkConstants.DEFAULT_SERVERS
out = NetworkConstants.DEFAULT_SERVERS
if self.irc_servers:
out.update(filter_version(self.irc_servers.copy()))
else:
@ -908,7 +908,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 // NetworkConstants.CHUNK_SIZE)
else:
self.request_header(interface, next_height)
else:
@ -949,8 +949,8 @@ 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:
print(b.get_hash(0), NetworkConstants.GENESIS)
if b.get_hash(0) == NetworkConstants.GENESIS:
self.downloading_headers = False
return
filename = b.path()
@ -958,8 +958,8 @@ class Network(util.DaemonThread):
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("downloading ", NetworkConstants.HEADERS_URL)
urllib.request.urlretrieve(NetworkConstants.HEADERS_URL, filename)
self.print_error("done.")
except Exception:
import traceback
@ -1092,9 +1092,3 @@ class Network(util.DaemonThread):
if out != tx_hash:
return False, "error: " + out
return True, out
def export_checkpoints(self, path):
# run manually from the console to generate checkpoints
cp = self.blockchain().get_checkpoints()
with open(path, 'w') as f:
f.write(json.dumps(cp, indent=4))

View File

@ -1,231 +1,3 @@
{
"E-X.not.fyi": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"ELECTRUMX.not.fyi": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"ELEX01.blackpole.online": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"VPS.hsmiths.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"bitcoin.freedomnode.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"btc.smsys.me": {
"pruning": "-",
"s": "995",
"version": "1.1"
},
"currentlane.lovebitco.in": {
"pruning": "-",
"t": "50001",
"version": "1.1"
},
"daedalus.bauerj.eu": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"de01.hamster.science": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"ecdsa.net": {
"pruning": "-",
"s": "110",
"t": "50001",
"version": "1.1"
},
"elec.luggs.co": {
"pruning": "-",
"s": "443",
"version": "1.1"
},
"electrum.akinbo.org": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"electrum.antumbra.se": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"electrum.be": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"electrum.coinucopia.io": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"electrum.cutie.ga": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"electrum.festivaldelhumor.org": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"electrum.hsmiths.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"electrum.qtornado.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"electrum.vom-stausee.de": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"electrum3.hachre.de": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"electrumx.bot.nu": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"electrumx.westeurope.cloudapp.azure.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"elx01.knas.systems": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"ex-btc.server-on.net": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"helicarrier.bauerj.eu": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"mooo.not.fyi": {
"pruning": "-",
"s": "50012",
"t": "50011",
"version": "1.1"
},
"ndnd.selfhost.eu": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"node.arihanc.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"node.xbt.eu": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"node1.volatilevictory.com": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"noserver4u.de": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"qmebr.spdns.org": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"raspi.hsmiths.com": {
"pruning": "-",
"s": "51002",
"t": "51001",
"version": "1.1"
},
"s2.noip.pl": {
"pruning": "-",
"s": "50102",
"version": "1.1"
},
"s5.noip.pl": {
"pruning": "-",
"s": "50105",
"version": "1.1"
},
"songbird.bauerj.eu": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"us.electrum.be": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
},
"us01.hamster.science": {
"pruning": "-",
"s": "50002",
"t": "50001",
"version": "1.1"
}
"localhost": {"t":"51001", "s":"51002"}
}

View File

@ -1,8 +1,3 @@
{
"testnetnode.arihanc.com": {"t":"51001", "s":"51002"},
"testnet1.bauerj.eu": {"t":"51001", "s":"51002"},
"14.3.140.101": {"t":"51001", "s":"51002"},
"testnet.hsmiths.com": {"t":"53011", "s":"53012"},
"electrum.akinbo.org": {"t":"51001", "s":"51002"},
"ELEX05.blackpole.online": {"t":"52011", "s":"52002"}
"localhost": {"t":"51001", "s":"51002"}
}

View File

@ -43,7 +43,7 @@ class SPV(ThreadJob):
if (tx_height > 0) and (tx_height <= lh):
header = self.network.blockchain().read_header(tx_height)
if header is None and self.network.interface:
index = tx_height // 2016
index = tx_height // NetworkConstants.CHUNK_SIZE
self.network.request_chunk(self.network.interface, index)
else:
if tx_hash not in self.merkle_roots: