15 from binascii
import hexlify, unhexlify
16 from base64
import b64encode
17 from decimal
import Decimal, ROUND_DOWN
26 from .
import coverage
27 from .authproxy
import AuthServiceProxy, JSONRPCException
43 MOCKTIME = 1417713337 + (201 * 156)
53 """Maintain a log of which RPC calls are made during testing.""" 55 COVERAGE_DIR = dirname
61 url (str): URL of the RPC server to call 62 node_number (int): the node number (or id) that this calls to 65 timeout (int): HTTP timeout in seconds 68 AuthServiceProxy. convenience object for making RPC calls. 72 if timeout
is not None:
73 proxy_kwargs[
'timeout'] = timeout
78 coverage_logfile = coverage.get_filename(
79 COVERAGE_DIR, node_number)
if COVERAGE_DIR
else None 84 result = node.mnsync(
"status")
85 return result[
'IsSynced']
94 return 11000 + n + os.getpid()%999
96 return 12000 + n + os.getpid()%999
99 """Make sure json library being used does not lose precision converting BTC values""" 100 n = Decimal(
"20000000.00000003")
101 satoshis = int(json.loads(json.dumps(float(n)))*1.0e8)
102 if satoshis != 2000000000000003:
103 raise RuntimeError(
"JSON encode/decode loses precision")
106 return len(bytearray.fromhex(hex_string))
109 return hexlify(byte_str).decode(
'ascii')
112 return unhexlify(hex_str.encode(
'ascii'))
115 return b64encode(string.encode(
'utf-8')).decode(
'ascii')
119 Wait until everybody has the same block count 122 counts = [ x.getblockcount()
for x
in rpc_connections ]
123 if counts == [ counts[0] ]*len(counts):
129 Wait until everybody has the same transactions in their memory 135 for i
in range(1, len(rpc_connections)):
137 num_match = num_match+1
138 if num_match == len(rpc_connections):
143 for node
in rpc_connections:
146 bitcoind_processes = {}
149 datadir = os.path.join(dirname,
"node"+str(n))
150 if not os.path.isdir(datadir):
152 with open(os.path.join(datadir,
"dash.conf"),
'w')
as f:
153 f.write(
"regtest=1\n")
154 f.write(
"rpcuser=rt\n")
155 f.write(
"rpcpassword=rt\n")
156 f.write(
"port="+str(
p2p_port(n))+
"\n")
157 f.write(
"rpcport="+str(
rpc_port(n))+
"\n")
158 f.write(
"listenonion=0\n")
162 return "http://rt:rt@%s:%d" % (rpchost
or '127.0.0.1',
rpc_port(i))
166 Wait for dashd to start. This means that RPC is accessible and fully initialized. 167 Raise an exception if dashd exits during initialization. 170 if process.poll()
is not None:
171 raise Exception(
'dashd exited with status %i during initialization' % process.returncode)
174 blocks = rpc.getblockcount()
177 if e.errno != errno.ECONNREFUSED:
179 except JSONRPCException
as e:
180 if e.error[
'code'] != -28:
186 Create (or copy from cache) a 200-block-long chain and 190 if (
not os.path.isdir(os.path.join(
"cache",
"node0"))
191 or not os.path.isdir(os.path.join(
"cache",
"node1"))
192 or not os.path.isdir(os.path.join(
"cache",
"node2"))
193 or not os.path.isdir(os.path.join(
"cache",
"node3"))):
197 if os.path.isdir(os.path.join(
"cache",
"node"+str(i))):
198 shutil.rmtree(os.path.join(
"cache",
"node"+str(i)))
203 args = [ os.getenv(
"DASHD",
"dashd"),
"-server",
"-keypool=1",
"-datadir="+datadir,
"-discover=0" ]
205 args.append(
"-connect=127.0.0.1:"+str(
p2p_port(0)))
206 bitcoind_processes[i] = subprocess.Popen(args)
207 if os.getenv(
"PYTHON_DEBUG",
""):
208 print "initialize_chain: dashd started, waiting for RPC to come up" 210 if os.getenv(
"PYTHON_DEBUG",
""):
211 print "initialize_chain: RPC succesfully started" 218 sys.stderr.write(
"Error connecting to "+url+
"\n")
228 for peer
in range(4):
244 os.remove(
log_filename(
"cache", i,
"fee_estimates.dat"))
247 from_dir = os.path.join(
"cache",
"node"+str(i))
248 to_dir = os.path.join(test_dir,
"node"+str(i))
249 shutil.copytree(from_dir, to_dir)
254 Create an empty blockchain and num_nodes wallets. 255 Useful if a test case wants complete control over initialization. 257 for i
in range(num_nodes):
262 '''Convert optional IP:port spec to rpcconnect/rpcport args''' 266 match = re.match(
'(\[[0-9a-fA-f:]+\]|[^:]+)(?::([0-9]+))?$', rpchost)
268 raise ValueError(
'Invalid RPC host spec ' + rpchost)
270 rpcconnect = match.group(1)
271 rpcport = match.group(2)
273 if rpcconnect.startswith(
'['):
274 rpcconnect = rpcconnect[1:-1]
276 rv = [
'-rpcconnect=' + rpcconnect]
278 rv += [
'-rpcport=' + rpcport]
281 def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None):
283 Start a dashd and return RPC connection to it 285 datadir = os.path.join(dirname,
"node"+str(i))
287 binary = os.getenv(
"DASHD",
"dashd")
289 args = [ binary,
"-datadir="+datadir,
"-server",
"-keypool=1",
"-discover=0",
"-rest",
"-blockprioritysize=50000",
"-mocktime="+str(
get_mocktime()) ]
290 if extra_args
is not None: args.extend(extra_args)
291 bitcoind_processes[i] = subprocess.Popen(args)
292 if os.getenv(
"PYTHON_DEBUG",
""):
293 print "start_node: dashd started, waiting for RPC to come up" 296 if os.getenv(
"PYTHON_DEBUG",
""):
297 print "start_node: RPC succesfully started" 301 coverage.write_all_rpc_commands(COVERAGE_DIR, proxy)
305 def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None):
307 Start multiple dashds, return RPC connections to them 309 if extra_args
is None: extra_args = [
None for i
in range(num_nodes) ]
310 if binary
is None: binary = [
None for i
in range(num_nodes) ]
313 for i
in range(num_nodes):
314 rpcs.append(
start_node(i, dirname, extra_args[i], rpchost, binary=binary[i]))
321 return os.path.join(dirname,
"node"+str(n_node),
"regtest", logname)
325 bitcoind_processes[i].wait()
326 del bitcoind_processes[i]
339 for bitcoind
in bitcoind_processes.values():
341 bitcoind_processes.clear()
344 ip_port =
"127.0.0.1:"+str(
p2p_port(node_num))
345 from_connection.addnode(ip_port,
"onetry")
348 while any(peer[
'version'] == 0
for peer
in from_connection.getpeerinfo()):
357 Return index to output of txid with value amount 358 Raises exception if there is none. 360 txdata = node.getrawtransaction(txid, 1)
361 for i
in range(len(txdata[
"vout"])):
362 if txdata[
"vout"][i][
"value"] == amount:
364 raise RuntimeError(
"find_output txid %s : %s not found"%(txid,str(amount)))
369 Return a random set of unspent txouts that are enough to pay amount_needed 371 assert(confirmations_required >=0)
372 utxo = from_node.listunspent(confirmations_required)
375 total_in = Decimal(
"0.00000000")
376 while total_in < amount_needed
and len(utxo) > 0:
378 total_in += t[
"amount"]
379 inputs.append({
"txid" : t[
"txid"],
"vout" : t[
"vout"],
"address" : t[
"address"] } )
380 if total_in < amount_needed:
381 raise RuntimeError(
"Insufficient funds: need %d, have %d"%(amount_needed, total_in))
382 return (total_in, inputs)
386 Create change output(s), return them 389 amount = amount_out+fee
390 change = amount_in - amount
391 if change > amount*2:
393 change_address = from_node.getnewaddress()
395 outputs[change_address] = Decimal(change/2).quantize(Decimal(
'0.00000001'), rounding=ROUND_DOWN)
396 change = amount_in - amount - outputs[change_address]
398 outputs[from_node.getnewaddress()] = change
403 Create&broadcast a zero-priority transaction. 404 Returns (txid, hex-encoded-txdata) 405 Ensures transaction is zero-priority by first creating a send-to-self, 406 then using its output 410 self_address = from_node.getnewaddress()
412 outputs =
make_change(from_node, total_in, amount+fee, fee)
413 outputs[self_address] = float(amount+fee)
415 self_rawtx = from_node.createrawtransaction(inputs, outputs)
416 self_signresult = from_node.signrawtransaction(self_rawtx)
417 self_txid = from_node.sendrawtransaction(self_signresult[
"hex"],
True)
419 vout =
find_output(from_node, self_txid, amount+fee)
422 inputs = [ {
"txid" : self_txid,
"vout" : vout } ]
423 outputs = { to_node.getnewaddress() : float(amount) }
425 rawtx = from_node.createrawtransaction(inputs, outputs)
426 signresult = from_node.signrawtransaction(rawtx)
427 txid = from_node.sendrawtransaction(signresult[
"hex"],
True)
429 return (txid, signresult[
"hex"])
433 Create a random zero-priority transaction. 434 Returns (txid, hex-encoded-transaction-data, fee) 436 from_node = random.choice(nodes)
437 to_node = random.choice(nodes)
438 fee = min_fee + fee_increment*random.randint(0,fee_variants)
440 return (txid, txhex, fee)
444 Create a random transaction. 445 Returns (txid, hex-encoded-transaction-data, fee) 447 from_node = random.choice(nodes)
448 to_node = random.choice(nodes)
449 fee = min_fee + fee_increment*random.randint(0,fee_variants)
452 outputs =
make_change(from_node, total_in, amount, fee)
453 outputs[to_node.getnewaddress()] = float(amount)
455 rawtx = from_node.createrawtransaction(inputs, outputs)
456 signresult = from_node.signrawtransaction(rawtx)
457 txid = from_node.sendrawtransaction(signresult[
"hex"],
True)
459 return (txid, signresult[
"hex"], fee)
463 raise AssertionError(
"%s != %s"%(str(thing1),str(thing2)))
467 raise AssertionError(
"%s <= %s"%(str(thing1),str(thing2)))
474 except Exception
as e:
475 raise AssertionError(
"Unexpected exception raised: "+type(e).__name__)
477 raise AssertionError(
"No exception raised")
482 except Exception
as e:
483 raise AssertionError(
484 "Couldn't interpret %r as hexadecimal; raised: %s" % (string, e))
487 if not isinstance(string, basestring):
488 raise AssertionError(
"Expected a string, got type %r" % type(string))
489 elif length
and len(string) != length:
490 raise AssertionError(
491 "String of length %d expected; got %d" % (length, len(string)))
492 elif not re.match(
'[abcdef0-9]+$', string):
493 raise AssertionError(
494 "String %r contains invalid characters for a hash." % string)
498 Pass in array of JSON objects, a dictionary with key/value pairs 499 to match against, and another dictionary with expected key/value 501 If the should_not_find flag is true, to_match should not be found 504 if should_not_find ==
True:
507 for item
in object_array:
509 for key,value
in to_match.items():
510 if item[key] != value:
514 elif should_not_find ==
True:
515 num_matched = num_matched+1
516 for key,value
in expected.items():
517 if item[key] != value:
518 raise AssertionError(
"%s : expected %s=%s"%(str(item), str(key), str(value)))
519 num_matched = num_matched+1
520 if num_matched == 0
and should_not_find !=
True:
521 raise AssertionError(
"No objects matched %s"%(str(to_match)))
522 if num_matched > 0
and should_not_find ==
True:
523 raise AssertionError(
"Objects were found %s"%(str(to_match)))
526 return Decimal(amount).quantize(Decimal(
'0.00000001'), rounding=ROUND_DOWN)
531 node.generate(int(0.5*count)+101)
532 utxos = node.listunspent()
533 iterations = count - len(utxos)
534 addr1 = node.getnewaddress()
535 addr2 = node.getnewaddress()
538 for i
in xrange(iterations):
541 inputs.append({
"txid" : t[
"txid"],
"vout" : t[
"vout"]})
543 send_value = t[
'amount'] - fee
546 raw_tx = node.createrawtransaction(inputs, outputs)
547 signed_tx = node.signrawtransaction(raw_tx)[
"hex"]
548 txid = node.sendrawtransaction(signed_tx)
550 while (node.getmempoolinfo()[
'size'] > 0):
553 utxos = node.listunspent()
554 assert(len(utxos) >= count)
563 script_pubkey =
"6a4d0200" 564 for i
in xrange (512):
565 script_pubkey = script_pubkey +
"01" 568 for k
in xrange(128):
570 txouts = txouts +
"0000000000000000" 572 txouts = txouts +
"fd0402" 574 txouts = txouts + script_pubkey
578 inputs = [{
"txid" : coinbase,
"vout" : 0}]
579 outputs = { to_address : amount }
580 rawtx = node.createrawtransaction(inputs, outputs)
581 signresult = node.signrawtransaction(rawtx)
583 return signresult[
"hex"]
588 addr = node.getnewaddress()
590 for i
in xrange(len(utxos)):
593 inputs.append({
"txid" : t[
"txid"],
"vout" : t[
"vout"]})
595 send_value = t[
'amount'] - fee
597 rawtx = node.createrawtransaction(inputs, outputs)
599 newtx = newtx + txouts
600 newtx = newtx + rawtx[94:]
601 signresult = node.signrawtransaction(newtx,
None,
None,
"NONE")
602 txid = node.sendrawtransaction(signresult[
"hex"],
True)
607 info = node.getblockchaininfo()
608 for row
in info[
'bip9_softforks']:
611 raise IndexError (
'key:"%s" not found' % key)
def check_json_precision()
def get_rpc_proxy(url, node_number, timeout=None)
def initialize_chain(test_dir)
def get_bip9_status(node, key)
def random_transaction(nodes, amount, min_fee, fee_increment, fee_variants)
def count_bytes(hex_string)
def hex_str_to_bytes(hex_str)
def assert_array_result(object_array, to_match, expected, should_not_find=False)
def assert_greater_than(thing1, thing2)
def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None)
def assert_raises(exc, fun, args, kwds)
def make_change(from_node, amount_in, amount_out, fee)
def connect_nodes(from_connection, node_num)
def sync_mempools(rpc_connections, wait=1)
def sync_masternodes(rpc_connections)
def send_zeropri_transaction(from_node, to_node, amount, fee)
def get_mnsync_status(node)
def rpc_url(i, rpchost=None)
def initialize_chain_clean(test_dir, num_nodes)
def find_output(node, txid, amount)
def satoshi_round(amount)
def gather_inputs(from_node, amount_needed, confirmations_required=1)
def random_zeropri_transaction(nodes, amount, min_fee, fee_increment, fee_variants)
def create_tx(node, coinbase, to_address, amount)
def set_node_times(nodes, t)
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None)
def assert_is_hex_string(string)
UniValue generate(const UniValue ¶ms, bool fHelp)
def sync_blocks(rpc_connections, wait=1)
def enable_coverage(dirname)
def wait_for_bitcoind_start(process, url, i)
def log_filename(dirname, n_node, logname)
UniValue getrawmempool(const UniValue ¶ms, bool fHelp)
def initialize_datadir(dirname, n)
def create_confirmed_utxos(fee, node, count)
def _rpchost_to_args(rpchost)
def assert_is_hash_string(string, length=64)
def str_to_b64str(string)
def assert_equal(thing1, thing2)
def connect_nodes_bi(nodes, a, b)
def create_lots_of_big_transactions(node, txouts, utxos, fee)
def bytes_to_hex_str(byte_str)