diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index ed2bfca0a..5631f894f 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -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} ---" diff --git a/qa/rpc-tests/bip65-cltv-p2p.py b/qa/rpc-tests/bip65-cltv-p2p.py index 6f31c7663..cfd2df01e 100755 --- a/qa/rpc-tests/bip65-cltv-p2p.py +++ b/qa/rpc-tests/bip65-cltv-p2p.py @@ -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() diff --git a/qa/rpc-tests/bipdersig-p2p.py b/qa/rpc-tests/bipdersig-p2p.py index 9604cdbdd..f254843f1 100755 --- a/qa/rpc-tests/bipdersig-p2p.py +++ b/qa/rpc-tests/bipdersig-p2p.py @@ -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]]) diff --git a/qa/rpc-tests/test_framework/blocktools.py b/qa/rpc-tests/test_framework/blocktools.py index 0f8147253..1fe2a5dda 100644 --- a/qa/rpc-tests/test_framework/blocktools.py +++ b/qa/rpc-tests/test_framework/blocktools.py @@ -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 diff --git a/qa/rpc-tests/test_framework/equihash.py b/qa/rpc-tests/test_framework/equihash.py new file mode 100755 index 000000000..e40451978 --- /dev/null +++ b/qa/rpc-tests/test_framework/equihash.py @@ -0,0 +1,293 @@ +from operator import itemgetter +import struct + +DEBUG = False +VERBOSE = False + + +word_size = 32 +word_mask = (1<= 8 and word_size >= 7+bit_len + bit_len_mask = (1<= 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('> (32*i))) + +def hash_xi(digest, xi): + digest.update(struct.pack(' 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('= 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') diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index c39637073..c74d3f931 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -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("= 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("= 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(" 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"): diff --git a/qa/rpc-tests/test_framework/script.py b/qa/rpc-tests/test_framework/script.py index ab7f4ec74..55a7f8e51 100644 --- a/qa/rpc-tests/test_framework/script.py +++ b/qa/rpc-tests/test_framework/script.py @@ -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):