Auto merge of #2533 - str4d:2530-mininode, r=str4d

Migrate MiniNode to Zcash

This enables various RPC tests that use it (most of them in the extended test suite) to properly test Zcash code.

The PR also fixes bugs in the BIP65 and BIP66 tests that were both masking and masked by the un-migrated MiniNode.

The Python module `pyblake2` is now a requirement for the RPC tests.

Part of #2530.
This commit is contained in:
Homu
2017-10-16 08:43:58 -07:00
7 changed files with 588 additions and 35 deletions

View File

@@ -85,7 +85,7 @@ function runTestScript
echo -e "=== Running testscript ${testName} ==="
if eval "$@" | sed 's/^/ /'
if eval "$@"
then
successCount=$(expr $successCount + 1)
echo "--- Success: ${testName} ---"

View File

@@ -12,7 +12,6 @@ from test_framework.comptool import TestInstance, TestManager
from test_framework.script import CScript, OP_1NEGATE, OP_NOP2, OP_DROP
from binascii import unhexlify
import cStringIO
import time
'''
@@ -38,7 +37,7 @@ class BIP65Test(ComparisonTestFramework):
def run_test(self):
test = TestManager(self, self.options.tmpdir)
# Don't call test.add_all_connections because there is only one node.
test.add_all_connections(self.nodes)
NetworkThread().start() # Start up network handling in another thread
test.run()
@@ -64,9 +63,9 @@ class BIP65Test(ComparisonTestFramework):
def get_tests(self):
self.coinbase_blocks = self.nodes[0].generate(1)
self.nodes[0].generate(100)
self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0)
self.nodeaddress = self.nodes[0].getnewaddress()
self.block_time = time.time() + 1
'''Check that the rules are enforced.'''
for valid in (True, False):
@@ -77,7 +76,12 @@ class BIP65Test(ComparisonTestFramework):
self.invalidate_transaction(spendtx)
spendtx.rehash()
block = create_block(self.tip, create_coinbase(1), self.block_time)
gbt = self.nodes[0].getblocktemplate()
self.block_time = gbt["mintime"] + 1
self.block_bits = int("0x" + gbt["bits"], 0)
block = create_block(self.tip, create_coinbase(101),
self.block_time, self.block_bits)
block.nVersion = 4
block.vtx.append(spendtx)
block.hashMerkleRoot = block.calc_merkle_root()

View File

@@ -12,7 +12,6 @@ from test_framework.comptool import TestInstance, TestManager
from test_framework.script import CScript
from binascii import unhexlify
import cStringIO
import time
'''
@@ -37,7 +36,7 @@ class BIP66Test(ComparisonTestFramework):
def run_test(self):
test = TestManager(self, self.options.tmpdir)
# Don't call test.add_all_connections because there is only one node.
test.add_all_connections(self.nodes)
NetworkThread().start() # Start up network handling in another thread
test.run()
@@ -71,9 +70,9 @@ class BIP66Test(ComparisonTestFramework):
def get_tests(self):
self.coinbase_blocks = self.nodes[0].generate(1)
self.nodes[0].generate(100)
self.tip = int ("0x" + self.nodes[0].getbestblockhash() + "L", 0)
self.nodeaddress = self.nodes[0].getnewaddress()
self.block_time = time.time() + 1
'''Check that the rules are enforced.'''
for valid in (True, False):
@@ -84,13 +83,17 @@ class BIP66Test(ComparisonTestFramework):
self.invalidate_transaction(spendtx)
spendtx.rehash()
block = create_block(self.tip, create_coinbase(1), self.block_time)
gbt = self.nodes[0].getblocktemplate()
self.block_time = gbt["mintime"] + 1
self.block_bits = int("0x" + gbt["bits"], 0)
block = create_block(self.tip, create_coinbase(101),
self.block_time, self.block_bits)
block.nVersion = 4
block.vtx.append(spendtx)
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
block.solve()
self.block_time += 1
self.tip = block.sha256
yield TestInstance([[block, valid]])

View File

@@ -4,10 +4,11 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
from mininode import CBlock, CTransaction, CTxIn, CTxOut, COutPoint, ser_string
from mininode import CBlock, CTransaction, CTxIn, CTxOut, COutPoint
from script import CScript, OP_0, OP_EQUAL, OP_HASH160
# Create a block (with regtest difficulty)
def create_block(hashprev, coinbase, nTime=None):
def create_block(hashprev, coinbase, nTime=None, nBits=None):
block = CBlock()
if nTime is None:
import time
@@ -15,7 +16,10 @@ def create_block(hashprev, coinbase, nTime=None):
else:
block.nTime = nTime
block.hashPrevBlock = hashprev
block.nBits = 0x207fffff # Will break after a difficulty adjustment...
if nBits is None:
block.nBits = 0x200f0f0f # Will break after a difficulty adjustment...
else:
block.nBits = nBits
block.vtx.append(coinbase)
block.hashMerkleRoot = block.calc_merkle_root()
block.calc_sha256()
@@ -42,14 +46,24 @@ def create_coinbase(heightAdjust = 0):
global counter
coinbase = CTransaction()
coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff),
ser_string(serialize_script_num(counter+heightAdjust)), 0xffffffff))
CScript([counter+heightAdjust, OP_0]), 0xffffffff))
counter += 1
coinbaseoutput = CTxOut()
coinbaseoutput.nValue = 50*100000000
coinbaseoutput.nValue = int(12.5*100000000)
halvings = int((counter+heightAdjust)/150) # regtest
coinbaseoutput.nValue >>= halvings
coinbaseoutput.scriptPubKey = ""
coinbase.vout = [ coinbaseoutput ]
if halvings == 0: # regtest
froutput = CTxOut()
froutput.nValue = coinbaseoutput.nValue / 5
# regtest
fraddr = bytearray([0x67, 0x08, 0xe6, 0x67, 0x0d, 0xb0, 0xb9, 0x50,
0xda, 0xc6, 0x80, 0x31, 0x02, 0x5c, 0xc5, 0xb6,
0x32, 0x13, 0xa4, 0x91])
froutput.scriptPubKey = CScript([OP_HASH160, fraddr, OP_EQUAL])
coinbaseoutput.nValue -= froutput.nValue
coinbase.vout = [ coinbaseoutput, froutput ]
coinbase.calc_sha256()
return coinbase

View File

@@ -0,0 +1,293 @@
from operator import itemgetter
import struct
DEBUG = False
VERBOSE = False
word_size = 32
word_mask = (1<<word_size)-1
def expand_array(inp, out_len, bit_len, byte_pad=0):
assert bit_len >= 8 and word_size >= 7+bit_len
bit_len_mask = (1<<bit_len)-1
out_width = (bit_len+7)/8 + byte_pad
assert out_len == 8*out_width*len(inp)/bit_len
out = bytearray(out_len)
bit_len_mask = (1 << bit_len) - 1
# The acc_bits least-significant bits of acc_value represent a bit sequence
# in big-endian order.
acc_bits = 0;
acc_value = 0;
j = 0
for i in xrange(len(inp)):
acc_value = ((acc_value << 8) & word_mask) | inp[i]
acc_bits += 8
# When we have bit_len or more bits in the accumulator, write the next
# output element.
if acc_bits >= bit_len:
acc_bits -= bit_len
for x in xrange(byte_pad, out_width):
out[j+x] = (
# Big-endian
acc_value >> (acc_bits+(8*(out_width-x-1)))
) & (
# Apply bit_len_mask across byte boundaries
(bit_len_mask >> (8*(out_width-x-1))) & 0xFF
)
j += out_width
return out
def compress_array(inp, out_len, bit_len, byte_pad=0):
assert bit_len >= 8 and word_size >= 7+bit_len
in_width = (bit_len+7)/8 + byte_pad
assert out_len == bit_len*len(inp)/(8*in_width)
out = bytearray(out_len)
bit_len_mask = (1 << bit_len) - 1
# The acc_bits least-significant bits of acc_value represent a bit sequence
# in big-endian order.
acc_bits = 0;
acc_value = 0;
j = 0
for i in xrange(out_len):
# When we have fewer than 8 bits left in the accumulator, read the next
# input element.
if acc_bits < 8:
acc_value = ((acc_value << bit_len) & word_mask) | inp[j]
for x in xrange(byte_pad, in_width):
acc_value = acc_value | (
(
# Apply bit_len_mask across byte boundaries
inp[j+x] & ((bit_len_mask >> (8*(in_width-x-1))) & 0xFF)
) << (8*(in_width-x-1))); # Big-endian
j += in_width
acc_bits += bit_len
acc_bits -= 8
out[i] = (acc_value >> acc_bits) & 0xFF
return out
def get_indices_from_minimal(minimal, bit_len):
eh_index_size = 4
assert (bit_len+7)/8 <= eh_index_size
len_indices = 8*eh_index_size*len(minimal)/bit_len
byte_pad = eh_index_size - (bit_len+7)/8
expanded = expand_array(minimal, len_indices, bit_len, byte_pad)
return [struct.unpack('>I', expanded[i:i+4])[0] for i in range(0, len_indices, eh_index_size)]
def get_minimal_from_indices(indices, bit_len):
eh_index_size = 4
assert (bit_len+7)/8 <= eh_index_size
len_indices = len(indices)*eh_index_size
min_len = bit_len*len_indices/(8*eh_index_size)
byte_pad = eh_index_size - (bit_len+7)/8
byte_indices = bytearray(''.join([struct.pack('>I', i) for i in indices]))
return compress_array(byte_indices, min_len, bit_len, byte_pad)
def hash_nonce(digest, nonce):
for i in range(8):
digest.update(struct.pack('<I', nonce >> (32*i)))
def hash_xi(digest, xi):
digest.update(struct.pack('<I', xi))
return digest # For chaining
def count_zeroes(h):
# Convert to binary string
if type(h) == bytearray:
h = ''.join('{0:08b}'.format(x, 'b') for x in h)
else:
h = ''.join('{0:08b}'.format(ord(x), 'b') for x in h)
# Count leading zeroes
return (h+'1').index('1')
def has_collision(ha, hb, i, l):
res = [ha[j] == hb[j] for j in range((i-1)*l/8, i*l/8)]
return reduce(lambda x, y: x and y, res)
def distinct_indices(a, b):
for i in a:
for j in b:
if i == j:
return False
return True
def xor(ha, hb):
return bytearray(a^b for a,b in zip(ha,hb))
def gbp_basic(digest, n, k):
'''Implementation of Basic Wagner's algorithm for the GBP.'''
validate_params(n, k)
collision_length = n/(k+1)
hash_length = (k+1)*((collision_length+7)//8)
indices_per_hash_output = 512/n
# 1) Generate first list
if DEBUG: print 'Generating first list'
X = []
tmp_hash = ''
for i in range(0, 2**(collision_length+1)):
r = i % indices_per_hash_output
if r == 0:
# X_i = H(I||V||x_i)
curr_digest = digest.copy()
hash_xi(curr_digest, i/indices_per_hash_output)
tmp_hash = curr_digest.digest()
X.append((
expand_array(bytearray(tmp_hash[r*n/8:(r+1)*n/8]),
hash_length, collision_length),
(i,)
))
# 3) Repeat step 2 until 2n/(k+1) bits remain
for i in range(1, k):
if DEBUG: print 'Round %d:' % i
# 2a) Sort the list
if DEBUG: print '- Sorting list'
X.sort(key=itemgetter(0))
if DEBUG and VERBOSE:
for Xi in X[-32:]:
print '%s %s' % (print_hash(Xi[0]), Xi[1])
if DEBUG: print '- Finding collisions'
Xc = []
while len(X) > 0:
# 2b) Find next set of unordered pairs with collisions on first n/(k+1) bits
j = 1
while j < len(X):
if not has_collision(X[-1][0], X[-1-j][0], i, collision_length):
break
j += 1
# 2c) Store tuples (X_i ^ X_j, (i, j)) on the table
for l in range(0, j-1):
for m in range(l+1, j):
# Check that there are no duplicate indices in tuples i and j
if distinct_indices(X[-1-l][1], X[-1-m][1]):
if X[-1-l][1][0] < X[-1-m][1][0]:
concat = X[-1-l][1] + X[-1-m][1]
else:
concat = X[-1-m][1] + X[-1-l][1]
Xc.append((xor(X[-1-l][0], X[-1-m][0]), concat))
# 2d) Drop this set
while j > 0:
X.pop(-1)
j -= 1
# 2e) Replace previous list with new list
X = Xc
# k+1) Find a collision on last 2n(k+1) bits
if DEBUG:
print 'Final round:'
print '- Sorting list'
X.sort(key=itemgetter(0))
if DEBUG and VERBOSE:
for Xi in X[-32:]:
print '%s %s' % (print_hash(Xi[0]), Xi[1])
if DEBUG: print '- Finding collisions'
solns = []
while len(X) > 0:
j = 1
while j < len(X):
if not (has_collision(X[-1][0], X[-1-j][0], k, collision_length) and
has_collision(X[-1][0], X[-1-j][0], k+1, collision_length)):
break
j += 1
for l in range(0, j-1):
for m in range(l+1, j):
res = xor(X[-1-l][0], X[-1-m][0])
if count_zeroes(res) == 8*hash_length and distinct_indices(X[-1-l][1], X[-1-m][1]):
if DEBUG and VERBOSE:
print 'Found solution:'
print '- %s %s' % (print_hash(X[-1-l][0]), X[-1-l][1])
print '- %s %s' % (print_hash(X[-1-m][0]), X[-1-m][1])
if X[-1-l][1][0] < X[-1-m][1][0]:
solns.append(list(X[-1-l][1] + X[-1-m][1]))
else:
solns.append(list(X[-1-m][1] + X[-1-l][1]))
# 2d) Drop this set
while j > 0:
X.pop(-1)
j -= 1
return [get_minimal_from_indices(soln, collision_length+1) for soln in solns]
def gbp_validate(digest, minimal, n, k):
validate_params(n, k)
collision_length = n/(k+1)
hash_length = (k+1)*((collision_length+7)//8)
indices_per_hash_output = 512/n
solution_width = (1 << k)*(collision_length+1)//8
if len(minimal) != solution_width:
print 'Invalid solution length: %d (expected %d)' % \
(len(minimal), solution_width)
return False
X = []
for i in get_indices_from_minimal(minimal, collision_length+1):
r = i % indices_per_hash_output
# X_i = H(I||V||x_i)
curr_digest = digest.copy()
hash_xi(curr_digest, i/indices_per_hash_output)
tmp_hash = curr_digest.digest()
X.append((
expand_array(bytearray(tmp_hash[r*n/8:(r+1)*n/8]),
hash_length, collision_length),
(i,)
))
for r in range(1, k+1):
Xc = []
for i in range(0, len(X), 2):
if not has_collision(X[i][0], X[i+1][0], r, collision_length):
print 'Invalid solution: invalid collision length between StepRows'
return False
if X[i+1][1][0] < X[i][1][0]:
print 'Invalid solution: Index tree incorrectly ordered'
return False
if not distinct_indices(X[i][1], X[i+1][1]):
print 'Invalid solution: duplicate indices'
return False
Xc.append((xor(X[i][0], X[i+1][0]), X[i][1] + X[i+1][1]))
X = Xc
if len(X) != 1:
print 'Invalid solution: incorrect length after end of rounds: %d' % len(X)
return False
if count_zeroes(X[0][0]) != 8*hash_length:
print 'Invalid solution: incorrect number of zeroes: %d' % count_zeroes(X[0][0])
return False
return True
def zcash_person(n, k):
return b'ZcashPoW' + struct.pack('<II', n, k)
def print_hash(h):
if type(h) == bytearray:
return ''.join('{0:02x}'.format(x, 'x') for x in h)
else:
return ''.join('{0:02x}'.format(ord(x), 'x') for x in h)
def validate_params(n, k):
if (k >= n):
raise ValueError('n must be larger than k')
if (((n/(k+1))+1) >= 32):
raise ValueError('Parameters must satisfy n/(k+1)+1 < 32')

View File

@@ -30,9 +30,17 @@ from threading import RLock
from threading import Thread
import logging
import copy
from pyblake2 import blake2b
from .equihash import (
gbp_basic,
gbp_validate,
hash_nonce,
zcash_person,
)
BIP0031_VERSION = 60000
MY_VERSION = 60001 # past bip-31 for ping/pong
MY_VERSION = 170002 # past bip-31 for ping/pong
MY_SUBVERSION = "/python-mininode-tester:0.0.1/"
MAX_INV_SZ = 50000
@@ -234,6 +242,36 @@ def ser_int_vector(l):
return r
def deser_char_vector(f):
nit = struct.unpack("<B", f.read(1))[0]
if nit == 253:
nit = struct.unpack("<H", f.read(2))[0]
elif nit == 254:
nit = struct.unpack("<I", f.read(4))[0]
elif nit == 255:
nit = struct.unpack("<Q", f.read(8))[0]
r = []
for i in xrange(nit):
t = struct.unpack("<B", f.read(1))[0]
r.append(t)
return r
def ser_char_vector(l):
r = ""
if len(l) < 253:
r = chr(len(l))
elif len(l) < 0x10000:
r = chr(253) + struct.pack("<H", len(l))
elif len(l) < 0x100000000L:
r = chr(254) + struct.pack("<I", len(l))
else:
r = chr(255) + struct.pack("<Q", len(l))
for i in l:
r += chr(i)
return r
# Objects that map to bitcoind objects, which can be serialized/deserialized
class CAddress(object):
@@ -307,6 +345,154 @@ class CBlockLocator(object):
% (self.nVersion, repr(self.vHave))
G1_PREFIX_MASK = 0x02
G2_PREFIX_MASK = 0x0a
class ZCProof(object):
def __init__(self):
self.g_A = None
self.g_A_prime = None
self.g_B = None
self.g_B_prime = None
self.g_C = None
self.g_C_prime = None
self.g_K = None
self.g_H = None
def deserialize(self, f):
def deser_g1(self, f):
leadingByte = struct.unpack("<B", f.read(1))[0]
return {
'y_lsb': leadingByte & 1,
'x': f.read(32),
}
def deser_g2(self, f):
leadingByte = struct.unpack("<B", f.read(1))[0]
return {
'y_gt': leadingByte & 1,
'x': f.read(64),
}
self.g_A = deser_g1(f)
self.g_A_prime = deser_g1(f)
self.g_B = deser_g2(f)
self.g_B_prime = deser_g1(f)
self.g_C = deser_g1(f)
self.g_C_prime = deser_g1(f)
self.g_K = deser_g1(f)
self.g_H = deser_g1(f)
def serialize(self):
def ser_g1(self, p):
return chr(G1_PREFIX_MASK | p['y_lsb']) + p['x']
def ser_g2(self, p):
return chr(G2_PREFIX_MASK | p['y_gt']) + p['x']
r = ""
r += ser_g1(self.g_A)
r += ser_g1(self.g_A_prime)
r += ser_g2(self.g_B)
r += ser_g1(self.g_B_prime)
r += ser_g1(self.g_C)
r += ser_g1(self.g_C_prime)
r += ser_g1(self.g_K)
r += ser_g1(self.g_H)
return r
def __repr__(self):
return "ZCProof(g_A=%s g_A_prime=%s g_B=%s g_B_prime=%s g_C=%s g_C_prime=%s g_K=%s g_H=%s)" \
% (repr(self.g_A), repr(self.g_A_prime),
repr(self.g_B), repr(self.g_B_prime),
repr(self.g_C), repr(self.g_C_prime),
repr(self.g_K), repr(self.g_H))
ZC_NUM_JS_INPUTS = 2
ZC_NUM_JS_OUTPUTS = 2
ZC_NOTEPLAINTEXT_LEADING = 1
ZC_V_SIZE = 8
ZC_RHO_SIZE = 32
ZC_R_SIZE = 32
ZC_MEMO_SIZE = 512
ZC_NOTEPLAINTEXT_SIZE = (
ZC_NOTEPLAINTEXT_LEADING +
ZC_V_SIZE +
ZC_RHO_SIZE +
ZC_R_SIZE +
ZC_MEMO_SIZE
)
NOTEENCRYPTION_AUTH_BYTES = 16
ZC_NOTECIPHERTEXT_SIZE = (
ZC_NOTEPLAINTEXT_SIZE +
NOTEENCRYPTION_AUTH_BYTES
)
class JSDescription(object):
def __init__(self):
self.vpub_old = 0
self.vpub_new = 0
self.anchor = 0
self.nullifiers = [0] * ZC_NUM_JS_INPUTS
self.commitments = [0] * ZC_NUM_JS_OUTPUTS
self.onetimePubKey = 0
self.randomSeed = 0
self.macs = [0] * ZC_NUM_JS_INPUTS
self.proof = None
self.ciphertexts = [None] * ZC_NUM_JS_OUTPUTS
def deserialize(self, f):
self.vpub_old = struct.unpack("<q", f.read(8))[0]
self.vpub_new = struct.unpack("<q", f.read(8))[0]
self.anchor = deser_uint256(f)
self.nullifiers = []
for i in range(ZC_NUM_JS_INPUTS):
self.nullifiers.append(deser_uint256(f))
self.commitments = []
for i in range(ZC_NUM_JS_OUTPUTS):
self.commitments.append(deser_uint256(f))
self.onetimePubKey = deser_uint256(f)
self.randomSeed = deser_uint256(f)
self.macs = []
for i in range(ZC_NUM_JS_INPUTS):
self.macs.append(deser_uint256(f))
self.proof = ZCProof()
self.proof.deserialize(f)
self.ciphertexts = []
for i in range(ZC_NUM_JS_OUTPUTS):
self.ciphertexts.append(f.read(ZC_NOTECIPHERTEXT_SIZE))
def serialize(self):
r = ""
r += struct.pack("<q", self.vpub_old)
r += struct.pack("<q", self.vpub_new)
r += ser_uint256(self.anchor)
for i in range(ZC_NUM_JS_INPUTS):
r += ser_uint256(self.nullifiers[i])
for i in range(ZC_NUM_JS_OUTPUTS):
r += ser_uint256(self.commitments[i])
r += ser_uint256(self.onetimePubKey)
r += ser_uint256(self.randomSeed)
for i in range(ZC_NUM_JS_INPUTS):
r += ser_uint256(self.macs[i])
r += self.proof.serialize()
for i in range(ZC_NUM_JS_OUTPUTS):
r += ser_uint256(self.ciphertexts[i])
return r
def __repr__(self):
return "JSDescription(vpub_old=%i.%08i vpub_new=%i.%08i anchor=%064x onetimePubKey=%064x randomSeed=%064x proof=%s)" \
% (self.vpub_old, self.vpub_new, self.anchor,
self.onetimePubKey, self.randomSeed, repr(self.proof))
class COutPoint(object):
def __init__(self, hash=0, n=0):
self.hash = hash
@@ -382,6 +568,9 @@ class CTransaction(object):
self.vin = []
self.vout = []
self.nLockTime = 0
self.vjoinsplit = []
self.joinSplitPubKey = None
self.joinSplitSig = None
self.sha256 = None
self.hash = None
else:
@@ -389,6 +578,9 @@ class CTransaction(object):
self.vin = copy.deepcopy(tx.vin)
self.vout = copy.deepcopy(tx.vout)
self.nLockTime = tx.nLockTime
self.vjoinsplit = copy.deepcopy(tx.vjoinsplit)
self.joinSplitPubKey = tx.joinSplitPubKey
self.joinSplitSig = tx.joinSplitSig
self.sha256 = None
self.hash = None
@@ -397,6 +589,11 @@ class CTransaction(object):
self.vin = deser_vector(f, CTxIn)
self.vout = deser_vector(f, CTxOut)
self.nLockTime = struct.unpack("<I", f.read(4))[0]
if self.nVersion >= 2:
self.vjoinsplit = deser_vector(f, JSDescription)
if len(self.vjoinsplit) > 0:
self.joinSplitPubKey = deser_uint256(f)
self.joinSplitSig = f.read(64)
self.sha256 = None
self.hash = None
@@ -406,6 +603,11 @@ class CTransaction(object):
r += ser_vector(self.vin)
r += ser_vector(self.vout)
r += struct.pack("<I", self.nLockTime)
if self.nVersion >= 2:
r += ser_vector(self.vjoinsplit)
if len(self.vjoinsplit) > 0:
r += ser_uint256(self.joinSplitPubKey)
r += self.joinSplitSig
return r
def rehash(self):
@@ -425,8 +627,15 @@ class CTransaction(object):
return True
def __repr__(self):
return "CTransaction(nVersion=%i vin=%s vout=%s nLockTime=%i)" \
r = "CTransaction(nVersion=%i vin=%s vout=%s nLockTime=%i" \
% (self.nVersion, repr(self.vin), repr(self.vout), self.nLockTime)
if self.nVersion >= 2:
r += " vjoinsplit=%s" % repr(self.vjoinsplit)
if len(self.vjoinsplit) > 0:
r += " joinSplitPubKey=%064x joinSplitSig=%064x" \
(self.joinSplitPubKey, self.joinSplitSig)
r += ")"
return r
class CBlockHeader(object):
@@ -437,20 +646,24 @@ class CBlockHeader(object):
self.nVersion = header.nVersion
self.hashPrevBlock = header.hashPrevBlock
self.hashMerkleRoot = header.hashMerkleRoot
self.hashReserved = header.hashReserved
self.nTime = header.nTime
self.nBits = header.nBits
self.nNonce = header.nNonce
self.nSolution = header.nSolution
self.sha256 = header.sha256
self.hash = header.hash
self.calc_sha256()
def set_null(self):
self.nVersion = 1
self.nVersion = 4
self.hashPrevBlock = 0
self.hashMerkleRoot = 0
self.hashReserved = 0
self.nTime = 0
self.nBits = 0
self.nNonce = 0
self.nSolution = []
self.sha256 = None
self.hash = None
@@ -458,9 +671,11 @@ class CBlockHeader(object):
self.nVersion = struct.unpack("<i", f.read(4))[0]
self.hashPrevBlock = deser_uint256(f)
self.hashMerkleRoot = deser_uint256(f)
self.hashReserved = deser_uint256(f)
self.nTime = struct.unpack("<I", f.read(4))[0]
self.nBits = struct.unpack("<I", f.read(4))[0]
self.nNonce = struct.unpack("<I", f.read(4))[0]
self.nNonce = deser_uint256(f)
self.nSolution = deser_char_vector(f)
self.sha256 = None
self.hash = None
@@ -469,9 +684,11 @@ class CBlockHeader(object):
r += struct.pack("<i", self.nVersion)
r += ser_uint256(self.hashPrevBlock)
r += ser_uint256(self.hashMerkleRoot)
r += ser_uint256(self.hashReserved)
r += struct.pack("<I", self.nTime)
r += struct.pack("<I", self.nBits)
r += struct.pack("<I", self.nNonce)
r += ser_uint256(self.nNonce)
r += ser_char_vector(self.nSolution)
return r
def calc_sha256(self):
@@ -480,9 +697,11 @@ class CBlockHeader(object):
r += struct.pack("<i", self.nVersion)
r += ser_uint256(self.hashPrevBlock)
r += ser_uint256(self.hashMerkleRoot)
r += ser_uint256(self.hashReserved)
r += struct.pack("<I", self.nTime)
r += struct.pack("<I", self.nBits)
r += struct.pack("<I", self.nNonce)
r += ser_uint256(self.nNonce)
r += ser_char_vector(self.nSolution)
self.sha256 = uint256_from_str(hash256(r))
self.hash = hash256(r)[::-1].encode('hex_codec')
@@ -492,9 +711,9 @@ class CBlockHeader(object):
return self.sha256
def __repr__(self):
return "CBlockHeader(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x)" \
% (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
time.ctime(self.nTime), self.nBits, self.nNonce)
return "CBlockHeader(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x hashReserved=%064x nTime=%s nBits=%08x nNonce=%064x nSolution=%s)" \
% (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot, self.hashReserved,
time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.nSolution))
class CBlock(CBlockHeader):
@@ -525,7 +744,13 @@ class CBlock(CBlockHeader):
hashes = newhashes
return uint256_from_str(hashes[0])
def is_valid(self):
def is_valid(self, n=48, k=5):
# H(I||...
digest = blake2b(digest_size=(512/n)*n/8, person=zcash_person(n, k))
digest.update(super(CBlock, self).serialize()[:108])
hash_nonce(digest, self.nNonce)
if not gbp_validate(self.nSolution, digest, n, k):
return False
self.calc_sha256()
target = uint256_from_compact(self.nBits)
if self.sha256 > target:
@@ -537,17 +762,31 @@ class CBlock(CBlockHeader):
return False
return True
def solve(self):
self.calc_sha256()
def solve(self, n=48, k=5):
target = uint256_from_compact(self.nBits)
while self.sha256 > target:
# H(I||...
digest = blake2b(digest_size=(512/n)*n/8, person=zcash_person(n, k))
digest.update(super(CBlock, self).serialize()[:108])
self.nNonce = 0
while True:
# H(I||V||...
curr_digest = digest.copy()
hash_nonce(curr_digest, self.nNonce)
# (x_1, x_2, ...) = A(I, V, n, k)
solns = gbp_basic(curr_digest, n, k)
for soln in solns:
assert(gbp_validate(curr_digest, soln, n, k))
self.nSolution = soln
self.rehash()
if self.sha256 <= target:
return
self.nNonce += 1
self.rehash()
def __repr__(self):
return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" \
return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x hashReserved=%064x nTime=%s nBits=%08x nNonce=%064x nSolution=%s vtx=%s)" \
% (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx))
self.hashReserved, time.ctime(self.nTime), self.nBits,
self.nNonce, repr(self.nSolution), repr(self.vtx))
class CUnsignedAlert(object):
@@ -1082,9 +1321,9 @@ class NodeConn(asyncore.dispatcher):
"mempool": msg_mempool
}
MAGIC_BYTES = {
"mainnet": "\xf9\xbe\xb4\xd9", # mainnet
"testnet3": "\x0b\x11\x09\x07", # testnet3
"regtest": "\xfa\xbf\xb5\xda" # regtest
"mainnet": "\x24\xe9\x27\x64", # mainnet
"testnet3": "\xfa\x1a\xf9\xbf", # testnet3
"regtest": "\xaa\xe8\x3f\x5f" # regtest
}
def __init__(self, dstaddr, dstport, rpc, callback, net="regtest"):

View File

@@ -666,7 +666,7 @@ class CScript(bytes):
else:
other = CScriptOp.encode_op_pushdata(bignum.bn2vch(other))
elif isinstance(other, (bytes, bytearray)):
other = CScriptOp.encode_op_pushdata(other)
other = bytes(CScriptOp.encode_op_pushdata(other))
return other
def __add__(self, other):