Update qa from Bitcoin Core 0.13.2
This commit is contained in:
64
qa/rpc-tests/test_framework/address.py
Normal file
64
qa/rpc-tests/test_framework/address.py
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# address.py
|
||||
#
|
||||
# This file encodes and decodes BASE58 P2PKH and P2SH addresses
|
||||
#
|
||||
|
||||
from .script import hash256, hash160, sha256, CScript, OP_0
|
||||
from .util import bytes_to_hex_str, hex_str_to_bytes
|
||||
|
||||
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
|
||||
def byte_to_base58(b, version):
|
||||
result = ''
|
||||
str = bytes_to_hex_str(b)
|
||||
str = bytes_to_hex_str(chr(version).encode('latin-1')) + str
|
||||
checksum = bytes_to_hex_str(hash256(hex_str_to_bytes(str)))
|
||||
str += checksum[:8]
|
||||
value = int('0x'+str,0)
|
||||
while value > 0:
|
||||
result = chars[value % 58] + result
|
||||
value //= 58
|
||||
while (str[:2] == '00'):
|
||||
result = chars[0] + result
|
||||
str = str[2:]
|
||||
return result
|
||||
|
||||
# TODO: def base58_decode
|
||||
|
||||
def keyhash_to_p2pkh(hash, main = False):
|
||||
assert (len(hash) == 20)
|
||||
version = 0 if main else 111
|
||||
return byte_to_base58(hash, version)
|
||||
|
||||
def scripthash_to_p2sh(hash, main = False):
|
||||
assert (len(hash) == 20)
|
||||
version = 5 if main else 196
|
||||
return byte_to_base58(hash, version)
|
||||
|
||||
def key_to_p2pkh(key, main = False):
|
||||
key = check_key(key)
|
||||
return keyhash_to_p2pkh(hash160(key), main)
|
||||
|
||||
def script_to_p2sh(script, main = False):
|
||||
script = check_script(script)
|
||||
return scripthash_to_p2sh(hash160(script), main)
|
||||
|
||||
def check_key(key):
|
||||
if (type(key) is str):
|
||||
key = hex_str_to_bytes(key) # Assuming this is hex string
|
||||
if (type(key) is bytes and (len(key) == 33 or len(key) == 65)):
|
||||
return key
|
||||
assert(False)
|
||||
|
||||
def check_script(script):
|
||||
if (type(script) is str):
|
||||
script = hex_str_to_bytes(script) # Assuming this is hex string
|
||||
if (type(script) is bytes or type(script) is CScript):
|
||||
return script
|
||||
assert(False)
|
||||
@@ -42,6 +42,7 @@ import base64
|
||||
import decimal
|
||||
import json
|
||||
import logging
|
||||
import socket
|
||||
try:
|
||||
import urllib.parse as urlparse
|
||||
except ImportError:
|
||||
@@ -126,6 +127,12 @@ class AuthServiceProxy(object):
|
||||
return self._get_response()
|
||||
else:
|
||||
raise
|
||||
except (BrokenPipeError,ConnectionResetError):
|
||||
# Python 3.5+ raises BrokenPipeError instead of BadStatusLine when the connection was reset
|
||||
# ConnectionResetError happens on FreeBSD with Python 3.4
|
||||
self.__conn.close()
|
||||
self.__conn.request(method, path, postdata, headers)
|
||||
return self._get_response()
|
||||
|
||||
def __call__(self, *args):
|
||||
AuthServiceProxy.__id_count += 1
|
||||
@@ -151,7 +158,15 @@ class AuthServiceProxy(object):
|
||||
return self._request('POST', self.__url.path, postdata.encode('utf-8'))
|
||||
|
||||
def _get_response(self):
|
||||
http_response = self.__conn.getresponse()
|
||||
try:
|
||||
http_response = self.__conn.getresponse()
|
||||
except socket.timeout as e:
|
||||
raise JSONRPCException({
|
||||
'code': -344,
|
||||
'message': '%r RPC took longer than %f seconds. Consider '
|
||||
'using larger timeout for calls that take '
|
||||
'longer to return.' % (self._service_name,
|
||||
self.__conn.timeout)})
|
||||
if http_response is None:
|
||||
raise JSONRPCException({
|
||||
'code': -342, 'message': 'missing HTTP response from server'})
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from .mininode import *
|
||||
from .script import CScript, OP_TRUE, OP_CHECKSIG
|
||||
from .script import CScript, OP_TRUE, OP_CHECKSIG, OP_RETURN
|
||||
|
||||
# Create a block (with regtest difficulty)
|
||||
def create_block(hashprev, coinbase, nTime=None):
|
||||
@@ -56,12 +56,27 @@ def create_coinbase(height, pubkey = None):
|
||||
coinbase.calc_sha256()
|
||||
return coinbase
|
||||
|
||||
# Create a transaction with an anyone-can-spend output, that spends the
|
||||
# nth output of prevtx.
|
||||
def create_transaction(prevtx, n, sig, value):
|
||||
# Create a transaction.
|
||||
# If the scriptPubKey is not specified, make it anyone-can-spend.
|
||||
def create_transaction(prevtx, n, sig, value, scriptPubKey=CScript()):
|
||||
tx = CTransaction()
|
||||
assert(n < len(prevtx.vout))
|
||||
tx.vin.append(CTxIn(COutPoint(prevtx.sha256, n), sig, 0xffffffff))
|
||||
tx.vout.append(CTxOut(value, b""))
|
||||
tx.vout.append(CTxOut(value, scriptPubKey))
|
||||
tx.calc_sha256()
|
||||
return tx
|
||||
|
||||
def get_legacy_sigopcount_block(block, fAccurate=True):
|
||||
count = 0
|
||||
for tx in block.vtx:
|
||||
count += get_legacy_sigopcount_tx(tx, fAccurate)
|
||||
return count
|
||||
|
||||
def get_legacy_sigopcount_tx(tx, fAccurate=True):
|
||||
count = 0
|
||||
for i in tx.vout:
|
||||
count += i.scriptPubKey.GetSigOpCount(fAccurate)
|
||||
for j in tx.vin:
|
||||
# scriptSig might be of type bytes, so convert to CScript for the moment
|
||||
count += CScript(j.scriptSig).GetSigOpCount(fAccurate)
|
||||
return count
|
||||
|
||||
@@ -50,7 +50,7 @@ class AuthServiceProxyWrapper(object):
|
||||
rpc_method = self.auth_service_proxy_instance._service_name
|
||||
|
||||
if self.coverage_logfile:
|
||||
with open(self.coverage_logfile, 'a+') as f:
|
||||
with open(self.coverage_logfile, 'a+', encoding='utf8') as f:
|
||||
f.write("%s\n" % rpc_method)
|
||||
|
||||
return return_val
|
||||
@@ -100,7 +100,7 @@ def write_all_rpc_commands(dirname, node):
|
||||
if line and not line.startswith('='):
|
||||
commands.add("%s\n" % line.split()[0])
|
||||
|
||||
with open(filename, 'w') as f:
|
||||
with open(filename, 'w', encoding='utf8') as f:
|
||||
f.writelines(list(commands))
|
||||
|
||||
return True
|
||||
|
||||
@@ -75,6 +75,9 @@ ssl.EC_POINT_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p,
|
||||
# this specifies the curve used with ECDSA.
|
||||
NID_secp256k1 = 714 # from openssl/obj_mac.h
|
||||
|
||||
SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
|
||||
SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2
|
||||
|
||||
# Thx to Sam Devlin for the ctypes magic 64-bit fix.
|
||||
def _check_result(val, func, args):
|
||||
if val == 0:
|
||||
@@ -147,7 +150,7 @@ class CECKey(object):
|
||||
r = self.get_raw_ecdh_key(other_pubkey)
|
||||
return kdf(r)
|
||||
|
||||
def sign(self, hash):
|
||||
def sign(self, hash, low_s = True):
|
||||
# FIXME: need unit tests for below cases
|
||||
if not isinstance(hash, bytes):
|
||||
raise TypeError('Hash must be bytes instance; got %r' % hash.__class__)
|
||||
@@ -159,7 +162,25 @@ class CECKey(object):
|
||||
mb_sig = ctypes.create_string_buffer(sig_size0.value)
|
||||
result = ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k)
|
||||
assert 1 == result
|
||||
return mb_sig.raw[:sig_size0.value]
|
||||
assert mb_sig.raw[0] == 0x30
|
||||
assert mb_sig.raw[1] == sig_size0.value - 2
|
||||
total_size = mb_sig.raw[1]
|
||||
assert mb_sig.raw[2] == 2
|
||||
r_size = mb_sig.raw[3]
|
||||
assert mb_sig.raw[4 + r_size] == 2
|
||||
s_size = mb_sig.raw[5 + r_size]
|
||||
s_value = int.from_bytes(mb_sig.raw[6+r_size:6+r_size+s_size], byteorder='big')
|
||||
if (not low_s) or s_value <= SECP256K1_ORDER_HALF:
|
||||
return mb_sig.raw[:sig_size0.value]
|
||||
else:
|
||||
low_s_value = SECP256K1_ORDER - s_value
|
||||
low_s_bytes = (low_s_value).to_bytes(33, byteorder='big')
|
||||
while len(low_s_bytes) > 1 and low_s_bytes[0] == 0 and low_s_bytes[1] < 0x80:
|
||||
low_s_bytes = low_s_bytes[1:]
|
||||
new_s_size = len(low_s_bytes)
|
||||
new_total_size_byte = (total_size + new_s_size - s_size).to_bytes(1,byteorder='big')
|
||||
new_s_size_byte = (new_s_size).to_bytes(1,byteorder='big')
|
||||
return b'\x30' + new_total_size_byte + mb_sig.raw[2:5+r_size] + new_s_size_byte + low_s_bytes
|
||||
|
||||
def verify(self, hash, sig):
|
||||
"""Verify a DER signature"""
|
||||
|
||||
@@ -28,7 +28,7 @@ import asyncore
|
||||
import time
|
||||
import sys
|
||||
import random
|
||||
from binascii import hexlify, unhexlify
|
||||
from .util import hex_str_to_bytes, bytes_to_hex_str
|
||||
from io import BytesIO
|
||||
from codecs import encode
|
||||
import hashlib
|
||||
@@ -36,9 +36,10 @@ from threading import RLock
|
||||
from threading import Thread
|
||||
import logging
|
||||
import copy
|
||||
from test_framework.siphash import siphash256
|
||||
|
||||
BIP0031_VERSION = 60000
|
||||
MY_VERSION = 60001 # past bip-31 for ping/pong
|
||||
MY_VERSION = 70014 # past bip-31 for ping/pong
|
||||
MY_SUBVERSION = b"/python-mininode-tester:0.0.3/"
|
||||
|
||||
MAX_INV_SZ = 50000
|
||||
@@ -46,8 +47,12 @@ MAX_BLOCK_SIZE = 1000000
|
||||
|
||||
COIN = 100000000 # 1 btc in satoshis
|
||||
|
||||
NODE_NETWORK = (1 << 0)
|
||||
NODE_GETUTXO = (1 << 1)
|
||||
NODE_BLOOM = (1 << 2)
|
||||
|
||||
# Keep our own socket map for asyncore, so that we can track disconnects
|
||||
# ourselves (to workaround an issue with closing an asyncore socket when
|
||||
# ourselves (to workaround an issue with closing an asyncore socket when
|
||||
# using select)
|
||||
mininode_socket_map = dict()
|
||||
|
||||
@@ -63,12 +68,25 @@ mininode_lock = RLock()
|
||||
def sha256(s):
|
||||
return hashlib.new('sha256', s).digest()
|
||||
|
||||
def ripemd160(s):
|
||||
return hashlib.new('ripemd160', s).digest()
|
||||
|
||||
def hash256(s):
|
||||
return sha256(sha256(s))
|
||||
|
||||
def ser_compact_size(l):
|
||||
r = b""
|
||||
if l < 253:
|
||||
r = struct.pack("B", l)
|
||||
elif l < 0x10000:
|
||||
r = struct.pack("<BH", 253, l)
|
||||
elif l < 0x100000000:
|
||||
r = struct.pack("<BI", 254, l)
|
||||
else:
|
||||
r = struct.pack("<BQ", 255, l)
|
||||
return r
|
||||
|
||||
def deser_string(f):
|
||||
def deser_compact_size(f):
|
||||
nit = struct.unpack("<B", f.read(1))[0]
|
||||
if nit == 253:
|
||||
nit = struct.unpack("<H", f.read(2))[0]
|
||||
@@ -76,16 +94,14 @@ def deser_string(f):
|
||||
nit = struct.unpack("<I", f.read(4))[0]
|
||||
elif nit == 255:
|
||||
nit = struct.unpack("<Q", f.read(8))[0]
|
||||
return nit
|
||||
|
||||
def deser_string(f):
|
||||
nit = deser_compact_size(f)
|
||||
return f.read(nit)
|
||||
|
||||
def ser_string(s):
|
||||
if len(s) < 253:
|
||||
return struct.pack("B", len(s)) + s
|
||||
elif len(s) < 0x10000:
|
||||
return struct.pack("<BH", 253, len(s)) + s
|
||||
elif len(s) < 0x100000000:
|
||||
return struct.pack("<BI", 254, len(s)) + s
|
||||
return struct.pack("<BQ", 255, len(s)) + s
|
||||
return ser_compact_size(len(s)) + s
|
||||
|
||||
def deser_uint256(f):
|
||||
r = 0
|
||||
@@ -118,13 +134,7 @@ def uint256_from_compact(c):
|
||||
|
||||
|
||||
def deser_vector(f, c):
|
||||
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]
|
||||
nit = deser_compact_size(f)
|
||||
r = []
|
||||
for i in range(nit):
|
||||
t = c()
|
||||
@@ -133,29 +143,20 @@ def deser_vector(f, c):
|
||||
return r
|
||||
|
||||
|
||||
def ser_vector(l):
|
||||
r = b""
|
||||
if len(l) < 253:
|
||||
r = struct.pack("B", len(l))
|
||||
elif len(l) < 0x10000:
|
||||
r = struct.pack("<BH", 253, len(l))
|
||||
elif len(l) < 0x100000000:
|
||||
r = struct.pack("<BI", 254, len(l))
|
||||
else:
|
||||
r = struct.pack("<BQ", 255, len(l))
|
||||
# ser_function_name: Allow for an alternate serialization function on the
|
||||
# entries in the vector.
|
||||
def ser_vector(l, ser_function_name=None):
|
||||
r = ser_compact_size(len(l))
|
||||
for i in l:
|
||||
r += i.serialize()
|
||||
if ser_function_name:
|
||||
r += getattr(i, ser_function_name)()
|
||||
else:
|
||||
r += i.serialize()
|
||||
return r
|
||||
|
||||
|
||||
def deser_uint256_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]
|
||||
nit = deser_compact_size(f)
|
||||
r = []
|
||||
for i in range(nit):
|
||||
t = deser_uint256(f)
|
||||
@@ -164,28 +165,14 @@ def deser_uint256_vector(f):
|
||||
|
||||
|
||||
def ser_uint256_vector(l):
|
||||
r = b""
|
||||
if len(l) < 253:
|
||||
r = struct.pack("B", len(l))
|
||||
elif len(l) < 0x10000:
|
||||
r = struct.pack("<BH", 253, len(l))
|
||||
elif len(l) < 0x100000000:
|
||||
r = struct.pack("<BI", 254, len(l))
|
||||
else:
|
||||
r = struct.pack("<BQ", 255, len(l))
|
||||
r = ser_compact_size(len(l))
|
||||
for i in l:
|
||||
r += ser_uint256(i)
|
||||
return r
|
||||
|
||||
|
||||
def deser_string_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]
|
||||
nit = deser_compact_size(f)
|
||||
r = []
|
||||
for i in range(nit):
|
||||
t = deser_string(f)
|
||||
@@ -194,28 +181,14 @@ def deser_string_vector(f):
|
||||
|
||||
|
||||
def ser_string_vector(l):
|
||||
r = b""
|
||||
if len(l) < 253:
|
||||
r = struct.pack("B", len(l))
|
||||
elif len(l) < 0x10000:
|
||||
r = struct.pack("<BH", 253, len(l))
|
||||
elif len(l) < 0x100000000:
|
||||
r = struct.pack("<BI", 254, len(l))
|
||||
else:
|
||||
r = struct.pack("<BQ", 255, len(l))
|
||||
r = ser_compact_size(len(l))
|
||||
for sv in l:
|
||||
r += ser_string(sv)
|
||||
return r
|
||||
|
||||
|
||||
def deser_int_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]
|
||||
nit = deser_compact_size(f)
|
||||
r = []
|
||||
for i in range(nit):
|
||||
t = struct.unpack("<i", f.read(4))[0]
|
||||
@@ -224,27 +197,19 @@ def deser_int_vector(f):
|
||||
|
||||
|
||||
def ser_int_vector(l):
|
||||
r = b""
|
||||
if len(l) < 253:
|
||||
r = struct.pack("B", len(l))
|
||||
elif len(l) < 0x10000:
|
||||
r = struct.pack("<BH", 253, len(l))
|
||||
elif len(l) < 0x100000000:
|
||||
r = struct.pack("<BI", 254, len(l))
|
||||
else:
|
||||
r = struct.pack("<BQ", 255, len(l))
|
||||
r = ser_compact_size(len(l))
|
||||
for i in l:
|
||||
r += struct.pack("<i", i)
|
||||
return r
|
||||
|
||||
# Deserialize from a hex string representation (eg from RPC)
|
||||
def FromHex(obj, hex_string):
|
||||
obj.deserialize(BytesIO(unhexlify(hex_string.encode('ascii'))))
|
||||
obj.deserialize(BytesIO(hex_str_to_bytes(hex_string)))
|
||||
return obj
|
||||
|
||||
# Convert a binary-serializable object to hex (eg for submission via RPC)
|
||||
def ToHex(obj):
|
||||
return hexlify(obj.serialize()).decode('ascii')
|
||||
return bytes_to_hex_str(obj.serialize())
|
||||
|
||||
# Objects that map to bitcoind objects, which can be serialized/deserialized
|
||||
|
||||
@@ -274,31 +239,6 @@ class CAddress(object):
|
||||
self.ip, self.port)
|
||||
|
||||
|
||||
class CInv(object):
|
||||
typemap = {
|
||||
0: "Error",
|
||||
1: "TX",
|
||||
2: "Block"}
|
||||
|
||||
def __init__(self, t=0, h=0):
|
||||
self.type = t
|
||||
self.hash = h
|
||||
|
||||
def deserialize(self, f):
|
||||
self.type = struct.unpack("<i", f.read(4))[0]
|
||||
self.hash = deser_uint256(f)
|
||||
|
||||
def serialize(self):
|
||||
r = b""
|
||||
r += struct.pack("<i", self.type)
|
||||
r += ser_uint256(self.hash)
|
||||
return r
|
||||
|
||||
def __repr__(self):
|
||||
return "CInv(type=%s hash=%064x)" \
|
||||
% (self.typemap[self.type], self.hash)
|
||||
|
||||
|
||||
class CBlockLocator(object):
|
||||
def __init__(self):
|
||||
self.nVersion = MY_VERSION
|
||||
@@ -362,7 +302,7 @@ class CTxIn(object):
|
||||
|
||||
def __repr__(self):
|
||||
return "CTxIn(prevout=%s scriptSig=%s nSequence=%i)" \
|
||||
% (repr(self.prevout), hexlify(self.scriptSig),
|
||||
% (repr(self.prevout), bytes_to_hex_str(self.scriptSig),
|
||||
self.nSequence)
|
||||
|
||||
|
||||
@@ -384,7 +324,7 @@ class CTxOut(object):
|
||||
def __repr__(self):
|
||||
return "CTxOut(nValue=%i.%08i scriptPubKey=%s)" \
|
||||
% (self.nValue // COIN, self.nValue % COIN,
|
||||
hexlify(self.scriptPubKey))
|
||||
bytes_to_hex_str(self.scriptPubKey))
|
||||
|
||||
|
||||
class CTransaction(object):
|
||||
@@ -401,8 +341,8 @@ class CTransaction(object):
|
||||
self.vin = copy.deepcopy(tx.vin)
|
||||
self.vout = copy.deepcopy(tx.vout)
|
||||
self.nLockTime = tx.nLockTime
|
||||
self.sha256 = None
|
||||
self.hash = None
|
||||
self.sha256 = tx.sha256
|
||||
self.hash = tx.hash
|
||||
|
||||
def deserialize(self, f):
|
||||
self.nVersion = struct.unpack("<i", f.read(4))[0]
|
||||
@@ -420,13 +360,15 @@ class CTransaction(object):
|
||||
r += struct.pack("<I", self.nLockTime)
|
||||
return r
|
||||
|
||||
# Recalculate the txid
|
||||
def rehash(self):
|
||||
self.sha256 = None
|
||||
self.calc_sha256()
|
||||
|
||||
# self.sha256 and self.hash -- those are expected to be the txid.
|
||||
def calc_sha256(self):
|
||||
if self.sha256 is None:
|
||||
self.sha256 = uint256_from_str(hash256(self.serialize()))
|
||||
self.sha256 = uint256_from_str(hash256(self.serialize))
|
||||
self.hash = encode(hash256(self.serialize())[::-1], 'hex_codec').decode('ascii')
|
||||
|
||||
def is_valid(self):
|
||||
@@ -524,11 +466,8 @@ class CBlock(CBlockHeader):
|
||||
r += ser_vector(self.vtx)
|
||||
return r
|
||||
|
||||
def calc_merkle_root(self):
|
||||
hashes = []
|
||||
for tx in self.vtx:
|
||||
tx.calc_sha256()
|
||||
hashes.append(ser_uint256(tx.sha256))
|
||||
# Calculate the merkle root given a vector of transaction hashes
|
||||
def get_merkle_root(self, hashes):
|
||||
while len(hashes) > 1:
|
||||
newhashes = []
|
||||
for i in range(0, len(hashes), 2):
|
||||
@@ -537,6 +476,13 @@ class CBlock(CBlockHeader):
|
||||
hashes = newhashes
|
||||
return uint256_from_str(hashes[0])
|
||||
|
||||
def calc_merkle_root(self):
|
||||
hashes = []
|
||||
for tx in self.vtx:
|
||||
tx.calc_sha256()
|
||||
hashes.append(ser_uint256(tx.sha256))
|
||||
return self.get_merkle_root(hashes)
|
||||
|
||||
def is_valid(self):
|
||||
self.calc_sha256()
|
||||
target = uint256_from_compact(self.nBits)
|
||||
@@ -556,7 +502,6 @@ class CBlock(CBlockHeader):
|
||||
self.nNonce += 1
|
||||
self.rehash()
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" \
|
||||
% (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
|
||||
@@ -638,6 +583,182 @@ class CAlert(object):
|
||||
% (len(self.vchMsg), len(self.vchSig))
|
||||
|
||||
|
||||
class PrefilledTransaction(object):
|
||||
def __init__(self, index=0, tx = None):
|
||||
self.index = index
|
||||
self.tx = tx
|
||||
|
||||
def deserialize(self, f):
|
||||
self.index = deser_compact_size(f)
|
||||
self.tx = CTransaction()
|
||||
self.tx.deserialize(f)
|
||||
|
||||
def serialize(self):
|
||||
r = b""
|
||||
r += ser_compact_size(self.index)
|
||||
r += self.tx.serialize()
|
||||
return r
|
||||
|
||||
def __repr__(self):
|
||||
return "PrefilledTransaction(index=%d, tx=%s)" % (self.index, repr(self.tx))
|
||||
|
||||
# This is what we send on the wire, in a cmpctblock message.
|
||||
class P2PHeaderAndShortIDs(object):
|
||||
def __init__(self):
|
||||
self.header = CBlockHeader()
|
||||
self.nonce = 0
|
||||
self.shortids_length = 0
|
||||
self.shortids = []
|
||||
self.prefilled_txn_length = 0
|
||||
self.prefilled_txn = []
|
||||
|
||||
def deserialize(self, f):
|
||||
self.header.deserialize(f)
|
||||
self.nonce = struct.unpack("<Q", f.read(8))[0]
|
||||
self.shortids_length = deser_compact_size(f)
|
||||
for i in range(self.shortids_length):
|
||||
# shortids are defined to be 6 bytes in the spec, so append
|
||||
# two zero bytes and read it in as an 8-byte number
|
||||
self.shortids.append(struct.unpack("<Q", f.read(6) + b'\x00\x00')[0])
|
||||
self.prefilled_txn = deser_vector(f, PrefilledTransaction)
|
||||
self.prefilled_txn_length = len(self.prefilled_txn)
|
||||
|
||||
def serialize(self):
|
||||
r = b""
|
||||
r += self.header.serialize()
|
||||
r += struct.pack("<Q", self.nonce)
|
||||
r += ser_compact_size(self.shortids_length)
|
||||
for x in self.shortids:
|
||||
# We only want the first 6 bytes
|
||||
r += struct.pack("<Q", x)[0:6]
|
||||
r += ser_vector(self.prefilled_txn)
|
||||
return r
|
||||
|
||||
def __repr__(self):
|
||||
return "P2PHeaderAndShortIDs(header=%s, nonce=%d, shortids_length=%d, shortids=%s, prefilled_txn_length=%d, prefilledtxn=%s" % (repr(self.header), self.nonce, self.shortids_length, repr(self.shortids), self.prefilled_txn_length, repr(self.prefilled_txn))
|
||||
|
||||
# Calculate the BIP 152-compact blocks shortid for a given transaction hash
|
||||
def calculate_shortid(k0, k1, tx_hash):
|
||||
expected_shortid = siphash256(k0, k1, tx_hash)
|
||||
expected_shortid &= 0x0000ffffffffffff
|
||||
return expected_shortid
|
||||
|
||||
# This version gets rid of the array lengths, and reinterprets the differential
|
||||
# encoding into indices that can be used for lookup.
|
||||
class HeaderAndShortIDs(object):
|
||||
def __init__(self, p2pheaders_and_shortids = None):
|
||||
self.header = CBlockHeader()
|
||||
self.nonce = 0
|
||||
self.shortids = []
|
||||
self.prefilled_txn = []
|
||||
|
||||
if p2pheaders_and_shortids != None:
|
||||
self.header = p2pheaders_and_shortids.header
|
||||
self.nonce = p2pheaders_and_shortids.nonce
|
||||
self.shortids = p2pheaders_and_shortids.shortids
|
||||
last_index = -1
|
||||
for x in p2pheaders_and_shortids.prefilled_txn:
|
||||
self.prefilled_txn.append(PrefilledTransaction(x.index + last_index + 1, x.tx))
|
||||
last_index = self.prefilled_txn[-1].index
|
||||
|
||||
def to_p2p(self):
|
||||
ret = P2PHeaderAndShortIDs()
|
||||
ret.header = self.header
|
||||
ret.nonce = self.nonce
|
||||
ret.shortids_length = len(self.shortids)
|
||||
ret.shortids = self.shortids
|
||||
ret.prefilled_txn_length = len(self.prefilled_txn)
|
||||
ret.prefilled_txn = []
|
||||
last_index = -1
|
||||
for x in self.prefilled_txn:
|
||||
ret.prefilled_txn.append(PrefilledTransaction(x.index - last_index - 1, x.tx))
|
||||
last_index = x.index
|
||||
return ret
|
||||
|
||||
def get_siphash_keys(self):
|
||||
header_nonce = self.header.serialize()
|
||||
header_nonce += struct.pack("<Q", self.nonce)
|
||||
hash_header_nonce_as_str = sha256(header_nonce)
|
||||
key0 = struct.unpack("<Q", hash_header_nonce_as_str[0:8])[0]
|
||||
key1 = struct.unpack("<Q", hash_header_nonce_as_str[8:16])[0]
|
||||
return [ key0, key1 ]
|
||||
|
||||
# Version 2 compact blocks use wtxid in shortids (rather than txid)
|
||||
def initialize_from_block(self, block, nonce=0, prefill_list = [0]):
|
||||
self.header = CBlockHeader(block)
|
||||
self.nonce = nonce
|
||||
self.prefilled_txn = [ PrefilledTransaction(i, block.vtx[i]) for i in prefill_list ]
|
||||
self.shortids = []
|
||||
[k0, k1] = self.get_siphash_keys()
|
||||
for i in range(len(block.vtx)):
|
||||
if i not in prefill_list:
|
||||
tx_hash = block.vtx[i].sha256
|
||||
self.shortids.append(calculate_shortid(k0, k1, tx_hash))
|
||||
|
||||
def __repr__(self):
|
||||
return "HeaderAndShortIDs(header=%s, nonce=%d, shortids=%s, prefilledtxn=%s" % (repr(self.header), self.nonce, repr(self.shortids), repr(self.prefilled_txn))
|
||||
|
||||
|
||||
class BlockTransactionsRequest(object):
|
||||
|
||||
def __init__(self, blockhash=0, indexes = None):
|
||||
self.blockhash = blockhash
|
||||
self.indexes = indexes if indexes != None else []
|
||||
|
||||
def deserialize(self, f):
|
||||
self.blockhash = deser_uint256(f)
|
||||
indexes_length = deser_compact_size(f)
|
||||
for i in range(indexes_length):
|
||||
self.indexes.append(deser_compact_size(f))
|
||||
|
||||
def serialize(self):
|
||||
r = b""
|
||||
r += ser_uint256(self.blockhash)
|
||||
r += ser_compact_size(len(self.indexes))
|
||||
for x in self.indexes:
|
||||
r += ser_compact_size(x)
|
||||
return r
|
||||
|
||||
# helper to set the differentially encoded indexes from absolute ones
|
||||
def from_absolute(self, absolute_indexes):
|
||||
self.indexes = []
|
||||
last_index = -1
|
||||
for x in absolute_indexes:
|
||||
self.indexes.append(x-last_index-1)
|
||||
last_index = x
|
||||
|
||||
def to_absolute(self):
|
||||
absolute_indexes = []
|
||||
last_index = -1
|
||||
for x in self.indexes:
|
||||
absolute_indexes.append(x+last_index+1)
|
||||
last_index = absolute_indexes[-1]
|
||||
return absolute_indexes
|
||||
|
||||
def __repr__(self):
|
||||
return "BlockTransactionsRequest(hash=%064x indexes=%s)" % (self.blockhash, repr(self.indexes))
|
||||
|
||||
|
||||
class BlockTransactions(object):
|
||||
|
||||
def __init__(self, blockhash=0, transactions = None):
|
||||
self.blockhash = blockhash
|
||||
self.transactions = transactions if transactions != None else []
|
||||
|
||||
def deserialize(self, f):
|
||||
self.blockhash = deser_uint256(f)
|
||||
self.transactions = deser_vector(f, CTransaction)
|
||||
|
||||
def serialize(self):
|
||||
r = b""
|
||||
r += ser_uint256(self.blockhash)
|
||||
r += ser_vector(self.transactions)
|
||||
return r
|
||||
|
||||
def __repr__(self):
|
||||
return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions))
|
||||
|
||||
|
||||
# Objects that correspond to messages on the wire
|
||||
class msg_version(object):
|
||||
command = b"version"
|
||||
@@ -850,6 +971,7 @@ class msg_generic(object):
|
||||
def __repr__(self):
|
||||
return "msg_generic()"
|
||||
|
||||
|
||||
class msg_getaddr(object):
|
||||
command = b"getaddr"
|
||||
|
||||
@@ -1030,7 +1152,7 @@ class msg_reject(object):
|
||||
% (self.message, self.code, self.reason, self.data)
|
||||
|
||||
# Helper function
|
||||
def wait_until(predicate, attempts=float('inf'), timeout=float('inf')):
|
||||
def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf')):
|
||||
attempt = 0
|
||||
elapsed = 0
|
||||
|
||||
@@ -1045,22 +1167,95 @@ def wait_until(predicate, attempts=float('inf'), timeout=float('inf')):
|
||||
return False
|
||||
|
||||
class msg_feefilter(object):
|
||||
command = "feefilter"
|
||||
command = b"feefilter"
|
||||
|
||||
def __init__(self, feerate=0L):
|
||||
def __init__(self, feerate=0):
|
||||
self.feerate = feerate
|
||||
|
||||
def deserialize(self, f):
|
||||
self.feerate = struct.unpack("<Q", f.read(8))[0]
|
||||
|
||||
def serialize(self):
|
||||
r = ""
|
||||
r = b""
|
||||
r += struct.pack("<Q", self.feerate)
|
||||
return r
|
||||
|
||||
def __repr__(self):
|
||||
return "msg_feefilter(feerate=%08x)" % self.feerate
|
||||
|
||||
class msg_sendcmpct(object):
|
||||
command = b"sendcmpct"
|
||||
|
||||
def __init__(self):
|
||||
self.announce = False
|
||||
self.version = 1
|
||||
|
||||
def deserialize(self, f):
|
||||
self.announce = struct.unpack("<?", f.read(1))[0]
|
||||
self.version = struct.unpack("<Q", f.read(8))[0]
|
||||
|
||||
def serialize(self):
|
||||
r = b""
|
||||
r += struct.pack("<?", self.announce)
|
||||
r += struct.pack("<Q", self.version)
|
||||
return r
|
||||
|
||||
def __repr__(self):
|
||||
return "msg_sendcmpct(announce=%s, version=%lu)" % (self.announce, self.version)
|
||||
|
||||
class msg_cmpctblock(object):
|
||||
command = b"cmpctblock"
|
||||
|
||||
def __init__(self, header_and_shortids = None):
|
||||
self.header_and_shortids = header_and_shortids
|
||||
|
||||
def deserialize(self, f):
|
||||
self.header_and_shortids = P2PHeaderAndShortIDs()
|
||||
self.header_and_shortids.deserialize(f)
|
||||
|
||||
def serialize(self):
|
||||
r = b""
|
||||
r += self.header_and_shortids.serialize()
|
||||
return r
|
||||
|
||||
def __repr__(self):
|
||||
return "msg_cmpctblock(HeaderAndShortIDs=%s)" % repr(self.header_and_shortids)
|
||||
|
||||
class msg_getblocktxn(object):
|
||||
command = b"getblocktxn"
|
||||
|
||||
def __init__(self):
|
||||
self.block_txn_request = None
|
||||
|
||||
def deserialize(self, f):
|
||||
self.block_txn_request = BlockTransactionsRequest()
|
||||
self.block_txn_request.deserialize(f)
|
||||
|
||||
def serialize(self):
|
||||
r = b""
|
||||
r += self.block_txn_request.serialize()
|
||||
return r
|
||||
|
||||
def __repr__(self):
|
||||
return "msg_getblocktxn(block_txn_request=%s)" % (repr(self.block_txn_request))
|
||||
|
||||
class msg_blocktxn(object):
|
||||
command = b"blocktxn"
|
||||
|
||||
def __init__(self):
|
||||
self.block_transactions = BlockTransactions()
|
||||
|
||||
def deserialize(self, f):
|
||||
self.block_transactions.deserialize(f)
|
||||
|
||||
def serialize(self):
|
||||
r = b""
|
||||
r += self.block_transactions.serialize()
|
||||
return r
|
||||
|
||||
def __repr__(self):
|
||||
return "msg_blocktxn(block_transactions=%s)" % (repr(self.block_transactions))
|
||||
|
||||
# This is what a callback should look like for NodeConn
|
||||
# Reimplement the on_* functions to provide handling for events
|
||||
class NodeConnCB(object):
|
||||
@@ -1070,6 +1265,8 @@ class NodeConnCB(object):
|
||||
# tests; it causes message delivery to sleep for the specified time
|
||||
# before acquiring the global lock and delivering the next message.
|
||||
self.deliver_sleep_time = None
|
||||
# Remember the services our peer has advertised
|
||||
self.peer_services = None
|
||||
|
||||
def set_deliver_sleep_time(self, value):
|
||||
with mininode_lock:
|
||||
@@ -1107,6 +1304,7 @@ class NodeConnCB(object):
|
||||
conn.ver_send = min(MY_VERSION, message.nVersion)
|
||||
if message.nVersion < 209:
|
||||
conn.ver_recv = conn.ver_send
|
||||
conn.nServices = message.nServices
|
||||
|
||||
def on_verack(self, conn, message):
|
||||
conn.ver_recv = conn.ver_send
|
||||
@@ -1138,6 +1336,10 @@ class NodeConnCB(object):
|
||||
def on_pong(self, conn, message): pass
|
||||
def on_feefilter(self, conn, message): pass
|
||||
def on_sendheaders(self, conn, message): pass
|
||||
def on_sendcmpct(self, conn, message): pass
|
||||
def on_cmpctblock(self, conn, message): pass
|
||||
def on_getblocktxn(self, conn, message): pass
|
||||
def on_blocktxn(self, conn, message): pass
|
||||
|
||||
# More useful callbacks and functions for NodeConnCB's which have a single NodeConn
|
||||
class SingleNodeConnCB(NodeConnCB):
|
||||
@@ -1154,6 +1356,10 @@ class SingleNodeConnCB(NodeConnCB):
|
||||
def send_message(self, message):
|
||||
self.connection.send_message(message)
|
||||
|
||||
def send_and_ping(self, message):
|
||||
self.send_message(message)
|
||||
self.sync_with_ping()
|
||||
|
||||
def on_pong(self, conn, message):
|
||||
self.last_pong = message
|
||||
|
||||
@@ -1187,15 +1393,19 @@ class NodeConn(asyncore.dispatcher):
|
||||
b"reject": msg_reject,
|
||||
b"mempool": msg_mempool,
|
||||
b"feefilter": msg_feefilter,
|
||||
b"sendheaders": msg_sendheaders
|
||||
b"sendheaders": msg_sendheaders,
|
||||
b"sendcmpct": msg_sendcmpct,
|
||||
b"cmpctblock": msg_cmpctblock,
|
||||
b"getblocktxn": msg_getblocktxn,
|
||||
b"blocktxn": msg_blocktxn
|
||||
}
|
||||
MAGIC_BYTES = {
|
||||
"mainnet": b"\xf9\xbe\xb4\xd9", # mainnet
|
||||
"testnet3": b"\x0b\x11\x09\x07", # testnet3
|
||||
"regtest": b"\xfa\xbf\xb5\xda" # regtest
|
||||
"regtest": b"\xfa\xbf\xb5\xda", # regtest
|
||||
}
|
||||
|
||||
def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=1):
|
||||
def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=NODE_NETWORK):
|
||||
asyncore.dispatcher.__init__(self, map=mininode_socket_map)
|
||||
self.log = logging.getLogger("NodeConn(%s:%d)" % (dstaddr, dstport))
|
||||
self.dstaddr = dstaddr
|
||||
@@ -1210,6 +1420,7 @@ class NodeConn(asyncore.dispatcher):
|
||||
self.network = net
|
||||
self.cb = callback
|
||||
self.disconnect = False
|
||||
self.nServices = 0
|
||||
|
||||
# stuff version msg into sendbuf
|
||||
vt = msg_version()
|
||||
@@ -1319,7 +1530,7 @@ class NodeConn(asyncore.dispatcher):
|
||||
|
||||
def send_message(self, message, pushbuf=False):
|
||||
if self.state != "connected" and not pushbuf:
|
||||
return
|
||||
raise IOError('Not connected, no pushbuf')
|
||||
self.show_debug_msg("Send %s" % repr(message))
|
||||
command = message.command
|
||||
data = message.serialize()
|
||||
|
||||
@@ -58,7 +58,7 @@ def netstat(typ='tcp'):
|
||||
To get pid of all network process running on system, you must run this script
|
||||
as superuser
|
||||
'''
|
||||
with open('/proc/net/'+typ,'r') as f:
|
||||
with open('/proc/net/'+typ,'r',encoding='utf8') as f:
|
||||
content = f.readlines()
|
||||
content.pop(0)
|
||||
result = []
|
||||
@@ -135,7 +135,6 @@ def addr_to_hex(addr):
|
||||
nullbytes = 16 - len(sub[0]) - len(sub[1])
|
||||
assert((x == 0 and nullbytes == 0) or (x == 1 and nullbytes > 0))
|
||||
addr = sub[0] + ([0] * nullbytes) + sub[1]
|
||||
else:
|
||||
else:
|
||||
raise ValueError('Could not parse address %s' % addr)
|
||||
return hexlify(bytearray(addr)).decode('ascii')
|
||||
|
||||
@@ -15,8 +15,9 @@ Functionality to build scripts, as well as SignatureHash().
|
||||
"""
|
||||
|
||||
|
||||
from .mininode import CTransaction, CTxOut, hash256
|
||||
from .mininode import CTransaction, CTxOut, sha256, hash256, uint256_from_str, ser_uint256, ser_string
|
||||
from binascii import hexlify
|
||||
import hashlib
|
||||
|
||||
import sys
|
||||
bchr = chr
|
||||
@@ -36,6 +37,10 @@ MAX_SCRIPT_OPCODES = 201
|
||||
|
||||
OPCODE_NAMES = {}
|
||||
|
||||
def hash160(s):
|
||||
return hashlib.new('ripemd160', sha256(s)).digest()
|
||||
|
||||
|
||||
_opcode_instances = []
|
||||
class CScriptOp(int):
|
||||
"""A single script opcode"""
|
||||
@@ -877,7 +882,7 @@ def SignatureHash(script, txTo, inIdx, hashtype):
|
||||
tmp = txtmp.vout[outIdx]
|
||||
txtmp.vout = []
|
||||
for i in range(outIdx):
|
||||
txtmp.vout.append(CTxOut())
|
||||
txtmp.vout.append(CTxOut(-1))
|
||||
txtmp.vout.append(tmp)
|
||||
|
||||
for i in range(len(txtmp.vin)):
|
||||
|
||||
64
qa/rpc-tests/test_framework/siphash.py
Normal file
64
qa/rpc-tests/test_framework/siphash.py
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# siphash.py - Specialized SipHash-2-4 implementations
|
||||
#
|
||||
# This implements SipHash-2-4 for 256-bit integers.
|
||||
|
||||
def rotl64(n, b):
|
||||
return n >> (64 - b) | (n & ((1 << (64 - b)) - 1)) << b
|
||||
|
||||
def siphash_round(v0, v1, v2, v3):
|
||||
v0 = (v0 + v1) & ((1 << 64) - 1)
|
||||
v1 = rotl64(v1, 13)
|
||||
v1 ^= v0
|
||||
v0 = rotl64(v0, 32)
|
||||
v2 = (v2 + v3) & ((1 << 64) - 1)
|
||||
v3 = rotl64(v3, 16)
|
||||
v3 ^= v2
|
||||
v0 = (v0 + v3) & ((1 << 64) - 1)
|
||||
v3 = rotl64(v3, 21)
|
||||
v3 ^= v0
|
||||
v2 = (v2 + v1) & ((1 << 64) - 1)
|
||||
v1 = rotl64(v1, 17)
|
||||
v1 ^= v2
|
||||
v2 = rotl64(v2, 32)
|
||||
return (v0, v1, v2, v3)
|
||||
|
||||
def siphash256(k0, k1, h):
|
||||
n0 = h & ((1 << 64) - 1)
|
||||
n1 = (h >> 64) & ((1 << 64) - 1)
|
||||
n2 = (h >> 128) & ((1 << 64) - 1)
|
||||
n3 = (h >> 192) & ((1 << 64) - 1)
|
||||
v0 = 0x736f6d6570736575 ^ k0
|
||||
v1 = 0x646f72616e646f6d ^ k1
|
||||
v2 = 0x6c7967656e657261 ^ k0
|
||||
v3 = 0x7465646279746573 ^ k1 ^ n0
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0 ^= n0
|
||||
v3 ^= n1
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0 ^= n1
|
||||
v3 ^= n2
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0 ^= n2
|
||||
v3 ^= n3
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0 ^= n3
|
||||
v3 ^= 0x2000000000000000
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0 ^= 0x2000000000000000
|
||||
v2 ^= 0xFF
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
return v0 ^ v1 ^ v2 ^ v3
|
||||
@@ -5,51 +5,55 @@
|
||||
|
||||
# Base class for RPC testing
|
||||
|
||||
# Add python-bitcoinrpc to module search path:
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
import shutil
|
||||
import tempfile
|
||||
import traceback
|
||||
|
||||
from .util import (
|
||||
initialize_chain,
|
||||
assert_equal,
|
||||
start_nodes,
|
||||
connect_nodes_bi,
|
||||
sync_blocks,
|
||||
sync_mempools,
|
||||
stop_nodes,
|
||||
stop_node,
|
||||
wait_bitcoinds,
|
||||
enable_coverage,
|
||||
check_json_precision,
|
||||
initialize_chain_clean,
|
||||
PortSeed,
|
||||
)
|
||||
from .authproxy import AuthServiceProxy, JSONRPCException
|
||||
from .authproxy import JSONRPCException
|
||||
|
||||
|
||||
class BitcoinTestFramework(object):
|
||||
|
||||
# These may be over-ridden by subclasses:
|
||||
def __init__(self):
|
||||
self.num_nodes = 4
|
||||
self.setup_clean_chain = False
|
||||
self.nodes = None
|
||||
|
||||
def run_test(self):
|
||||
for node in self.nodes:
|
||||
assert_equal(node.getblockcount(), 200)
|
||||
assert_equal(node.getbalance(), 25*50)
|
||||
raise NotImplementedError
|
||||
|
||||
def add_options(self, parser):
|
||||
pass
|
||||
|
||||
def setup_chain(self):
|
||||
print("Initializing test directory "+self.options.tmpdir)
|
||||
initialize_chain(self.options.tmpdir)
|
||||
if self.setup_clean_chain:
|
||||
initialize_chain_clean(self.options.tmpdir, self.num_nodes)
|
||||
else:
|
||||
initialize_chain(self.options.tmpdir, self.num_nodes)
|
||||
|
||||
def stop_node(self, num_node):
|
||||
stop_node(self.nodes[num_node], num_node)
|
||||
|
||||
|
||||
def setup_nodes(self):
|
||||
return start_nodes(4, self.options.tmpdir)
|
||||
return start_nodes(self.num_nodes, self.options.tmpdir)
|
||||
|
||||
def setup_network(self, split = False):
|
||||
self.nodes = self.setup_nodes()
|
||||
@@ -76,7 +80,6 @@ class BitcoinTestFramework(object):
|
||||
"""
|
||||
assert not self.is_network_split
|
||||
stop_nodes(self.nodes)
|
||||
wait_bitcoinds()
|
||||
self.setup_network(True)
|
||||
|
||||
def sync_all(self):
|
||||
@@ -95,71 +98,78 @@ class BitcoinTestFramework(object):
|
||||
"""
|
||||
assert self.is_network_split
|
||||
stop_nodes(self.nodes)
|
||||
wait_bitcoinds()
|
||||
self.setup_network(False)
|
||||
|
||||
def main(self):
|
||||
import optparse
|
||||
|
||||
parser = optparse.OptionParser(usage="%prog [options]")
|
||||
parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true",
|
||||
help="Leave bitcoinds and test.* datadir on exit or error")
|
||||
parser.add_option("--noshutdown", dest="noshutdown", default=False, action="store_true",
|
||||
help="Don't stop bitcoinds after the test execution")
|
||||
parser.add_option("--srcdir", dest="srcdir", default="../../src",
|
||||
parser.add_option("--srcdir", dest="srcdir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__))+"/../../../src"),
|
||||
help="Source directory containing bitcoind/bitcoin-cli (default: %default)")
|
||||
parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
|
||||
help="Root directory for datadirs")
|
||||
parser.add_option("--tracerpc", dest="trace_rpc", default=False, action="store_true",
|
||||
help="Print out all RPC calls as they are made")
|
||||
parser.add_option("--portseed", dest="port_seed", default=os.getpid(), type='int',
|
||||
help="The seed to use for assigning port numbers (default: current process id)")
|
||||
parser.add_option("--coveragedir", dest="coveragedir",
|
||||
help="Write tested RPC commands into this directory")
|
||||
self.add_options(parser)
|
||||
(self.options, self.args) = parser.parse_args()
|
||||
|
||||
# backup dir variable for removal at cleanup
|
||||
self.options.root, self.options.tmpdir = self.options.tmpdir, self.options.tmpdir + '/' + str(self.options.port_seed)
|
||||
|
||||
if self.options.trace_rpc:
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
|
||||
|
||||
if self.options.coveragedir:
|
||||
enable_coverage(self.options.coveragedir)
|
||||
|
||||
PortSeed.n = self.options.port_seed
|
||||
|
||||
os.environ['PATH'] = self.options.srcdir+":"+self.options.srcdir+"/qt:"+os.environ['PATH']
|
||||
|
||||
check_json_precision()
|
||||
|
||||
success = False
|
||||
try:
|
||||
if not os.path.isdir(self.options.tmpdir):
|
||||
os.makedirs(self.options.tmpdir)
|
||||
os.makedirs(self.options.tmpdir, exist_ok=False)
|
||||
self.setup_chain()
|
||||
|
||||
self.setup_network()
|
||||
|
||||
self.run_test()
|
||||
|
||||
success = True
|
||||
|
||||
except JSONRPCException as e:
|
||||
print("JSONRPC error: "+e.error['message'])
|
||||
traceback.print_tb(sys.exc_info()[2])
|
||||
except AssertionError as e:
|
||||
print("Assertion failed: " + str(e))
|
||||
traceback.print_tb(sys.exc_info()[2])
|
||||
except KeyError as e:
|
||||
print("key not found: "+ str(e))
|
||||
traceback.print_tb(sys.exc_info()[2])
|
||||
except Exception as e:
|
||||
print("Unexpected exception caught during testing: " + repr(e))
|
||||
traceback.print_tb(sys.exc_info()[2])
|
||||
except KeyboardInterrupt as e:
|
||||
print("Exiting after " + repr(e))
|
||||
|
||||
if not self.options.noshutdown:
|
||||
print("Stopping nodes")
|
||||
stop_nodes(self.nodes)
|
||||
wait_bitcoinds()
|
||||
else:
|
||||
print("Note: bitcoinds were not stopped and may still be running")
|
||||
|
||||
if not self.options.nocleanup and not self.options.noshutdown:
|
||||
if not self.options.nocleanup and not self.options.noshutdown and success:
|
||||
print("Cleaning up")
|
||||
shutil.rmtree(self.options.tmpdir)
|
||||
if not os.listdir(self.options.root):
|
||||
os.rmdir(self.options.root)
|
||||
else:
|
||||
print("Not cleaning up dir %s" % self.options.tmpdir)
|
||||
|
||||
if success:
|
||||
print("Tests successful")
|
||||
@@ -177,9 +187,10 @@ class BitcoinTestFramework(object):
|
||||
|
||||
class ComparisonTestFramework(BitcoinTestFramework):
|
||||
|
||||
# Can override the num_nodes variable to indicate how many nodes to run.
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 2
|
||||
self.setup_clean_chain = True
|
||||
|
||||
def add_options(self, parser):
|
||||
parser.add_option("--testbinary", dest="testbinary",
|
||||
@@ -189,10 +200,6 @@ class ComparisonTestFramework(BitcoinTestFramework):
|
||||
default=os.getenv("BITCOIND", "bitcoind"),
|
||||
help="bitcoind binary to use for reference nodes (if any)")
|
||||
|
||||
def setup_chain(self):
|
||||
print("Initializing test directory "+self.options.tmpdir)
|
||||
initialize_chain_clean(self.options.tmpdir, self.num_nodes)
|
||||
|
||||
def setup_network(self):
|
||||
self.nodes = start_nodes(
|
||||
self.num_nodes, self.options.tmpdir,
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
# Helpful routines for regression testing
|
||||
#
|
||||
|
||||
# Add python-bitcoinrpc to module search path:
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -16,6 +15,7 @@ from binascii import hexlify, unhexlify
|
||||
from base64 import b64encode
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
import json
|
||||
import http.client
|
||||
import random
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -28,6 +28,20 @@ from .authproxy import AuthServiceProxy, JSONRPCException
|
||||
|
||||
COVERAGE_DIR = None
|
||||
|
||||
# The maximum number of nodes a single test can spawn
|
||||
MAX_NODES = 8
|
||||
# Don't assign rpc or p2p ports lower than this
|
||||
PORT_MIN = 11000
|
||||
# The number of ports to "reserve" for p2p and rpc, each
|
||||
PORT_RANGE = 5000
|
||||
|
||||
BITCOIND_PROC_WAIT_TIMEOUT = 60
|
||||
|
||||
|
||||
class PortSeed:
|
||||
# Must be initialized with a unique integer for each process
|
||||
n = None
|
||||
|
||||
#Set Mocktime default to OFF.
|
||||
#MOCKTIME is only needed for scripts that use the
|
||||
#cached version of the blockchain. If the cached
|
||||
@@ -82,9 +96,11 @@ def get_rpc_proxy(url, node_number, timeout=None):
|
||||
|
||||
|
||||
def p2p_port(n):
|
||||
return 11000 + n + os.getpid()%999
|
||||
assert(n <= MAX_NODES)
|
||||
return PORT_MIN + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
|
||||
|
||||
def rpc_port(n):
|
||||
return 12000 + n + os.getpid()%999
|
||||
return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
|
||||
|
||||
def check_json_precision():
|
||||
"""Make sure json library being used does not lose precision converting BTC values"""
|
||||
@@ -105,30 +121,34 @@ def hex_str_to_bytes(hex_str):
|
||||
def str_to_b64str(string):
|
||||
return b64encode(string.encode('utf-8')).decode('ascii')
|
||||
|
||||
def sync_blocks(rpc_connections, wait=1):
|
||||
def sync_blocks(rpc_connections, wait=1, timeout=60):
|
||||
"""
|
||||
Wait until everybody has the same block count
|
||||
Wait until everybody has the same tip
|
||||
"""
|
||||
while True:
|
||||
counts = [ x.getblockcount() for x in rpc_connections ]
|
||||
if counts == [ counts[0] ]*len(counts):
|
||||
break
|
||||
while timeout > 0:
|
||||
tips = [ x.getbestblockhash() for x in rpc_connections ]
|
||||
if tips == [ tips[0] ]*len(tips):
|
||||
return True
|
||||
time.sleep(wait)
|
||||
timeout -= wait
|
||||
raise AssertionError("Block sync failed")
|
||||
|
||||
def sync_mempools(rpc_connections, wait=1):
|
||||
def sync_mempools(rpc_connections, wait=1, timeout=60):
|
||||
"""
|
||||
Wait until everybody has the same transactions in their memory
|
||||
pools
|
||||
"""
|
||||
while True:
|
||||
while timeout > 0:
|
||||
pool = set(rpc_connections[0].getrawmempool())
|
||||
num_match = 1
|
||||
for i in range(1, len(rpc_connections)):
|
||||
if set(rpc_connections[i].getrawmempool()) == pool:
|
||||
num_match = num_match+1
|
||||
if num_match == len(rpc_connections):
|
||||
break
|
||||
return True
|
||||
time.sleep(wait)
|
||||
timeout -= wait
|
||||
raise AssertionError("Mempool sync failed")
|
||||
|
||||
bitcoind_processes = {}
|
||||
|
||||
@@ -136,17 +156,30 @@ def initialize_datadir(dirname, n):
|
||||
datadir = os.path.join(dirname, "node"+str(n))
|
||||
if not os.path.isdir(datadir):
|
||||
os.makedirs(datadir)
|
||||
with open(os.path.join(datadir, "bitcoin.conf"), 'w') as f:
|
||||
rpc_u, rpc_p = rpc_auth_pair(n)
|
||||
with open(os.path.join(datadir, "bitcoin.conf"), 'w', encoding='utf8') as f:
|
||||
f.write("regtest=1\n")
|
||||
f.write("rpcuser=rt\n")
|
||||
f.write("rpcpassword=rt\n")
|
||||
f.write("rpcuser=" + rpc_u + "\n")
|
||||
f.write("rpcpassword=" + rpc_p + "\n")
|
||||
f.write("port="+str(p2p_port(n))+"\n")
|
||||
f.write("rpcport="+str(rpc_port(n))+"\n")
|
||||
f.write("listenonion=0\n")
|
||||
return datadir
|
||||
|
||||
def rpc_auth_pair(n):
|
||||
return 'rpcuser💻' + str(n), 'rpcpass🔑' + str(n)
|
||||
|
||||
def rpc_url(i, rpchost=None):
|
||||
return "http://rt:rt@%s:%d" % (rpchost or '127.0.0.1', rpc_port(i))
|
||||
rpc_u, rpc_p = rpc_auth_pair(i)
|
||||
host = '127.0.0.1'
|
||||
port = rpc_port(i)
|
||||
if rpchost:
|
||||
parts = rpchost.split(':')
|
||||
if len(parts) == 2:
|
||||
host, port = parts
|
||||
else:
|
||||
host = rpchost
|
||||
return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, host, int(port))
|
||||
|
||||
def wait_for_bitcoind_start(process, url, i):
|
||||
'''
|
||||
@@ -168,24 +201,28 @@ def wait_for_bitcoind_start(process, url, i):
|
||||
raise # unkown JSON RPC exception
|
||||
time.sleep(0.25)
|
||||
|
||||
def initialize_chain(test_dir):
|
||||
def initialize_chain(test_dir, num_nodes):
|
||||
"""
|
||||
Create (or copy from cache) a 200-block-long chain and
|
||||
4 wallets.
|
||||
Create a cache of a 200-block-long chain (with wallet) for MAX_NODES
|
||||
Afterward, create num_nodes copies from the cache
|
||||
"""
|
||||
|
||||
if (not os.path.isdir(os.path.join("cache","node0"))
|
||||
or not os.path.isdir(os.path.join("cache","node1"))
|
||||
or not os.path.isdir(os.path.join("cache","node2"))
|
||||
or not os.path.isdir(os.path.join("cache","node3"))):
|
||||
assert num_nodes <= MAX_NODES
|
||||
create_cache = False
|
||||
for i in range(MAX_NODES):
|
||||
if not os.path.isdir(os.path.join('cache', 'node'+str(i))):
|
||||
create_cache = True
|
||||
break
|
||||
|
||||
if create_cache:
|
||||
|
||||
#find and delete old cache directories if any exist
|
||||
for i in range(4):
|
||||
for i in range(MAX_NODES):
|
||||
if os.path.isdir(os.path.join("cache","node"+str(i))):
|
||||
shutil.rmtree(os.path.join("cache","node"+str(i)))
|
||||
|
||||
# Create cache directories, run bitcoinds:
|
||||
for i in range(4):
|
||||
for i in range(MAX_NODES):
|
||||
datadir=initialize_datadir("cache", i)
|
||||
args = [ os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir="+datadir, "-discover=0" ]
|
||||
if i > 0:
|
||||
@@ -198,15 +235,18 @@ def initialize_chain(test_dir):
|
||||
print("initialize_chain: RPC succesfully started")
|
||||
|
||||
rpcs = []
|
||||
for i in range(4):
|
||||
for i in range(MAX_NODES):
|
||||
try:
|
||||
rpcs.append(get_rpc_proxy(rpc_url(i), i))
|
||||
except:
|
||||
sys.stderr.write("Error connecting to "+url+"\n")
|
||||
sys.exit(1)
|
||||
|
||||
# Create a 200-block-long chain; each of the 4 nodes
|
||||
# Create a 200-block-long chain; each of the 4 first nodes
|
||||
# gets 25 mature blocks and 25 immature.
|
||||
# Note: To preserve compatibility with older versions of
|
||||
# initialize_chain, only 4 nodes will generate coins.
|
||||
#
|
||||
# blocks are created with timestamps 10 minutes apart
|
||||
# starting from 2010 minutes in the past
|
||||
enable_mocktime()
|
||||
@@ -222,15 +262,14 @@ def initialize_chain(test_dir):
|
||||
|
||||
# Shut them down, and clean up cache directories:
|
||||
stop_nodes(rpcs)
|
||||
wait_bitcoinds()
|
||||
disable_mocktime()
|
||||
for i in range(4):
|
||||
for i in range(MAX_NODES):
|
||||
os.remove(log_filename("cache", i, "debug.log"))
|
||||
os.remove(log_filename("cache", i, "db.log"))
|
||||
os.remove(log_filename("cache", i, "peers.dat"))
|
||||
os.remove(log_filename("cache", i, "fee_estimates.dat"))
|
||||
|
||||
for i in range(4):
|
||||
for i in range(num_nodes):
|
||||
from_dir = os.path.join("cache", "node"+str(i))
|
||||
to_dir = os.path.join(test_dir, "node"+str(i))
|
||||
shutil.copytree(from_dir, to_dir)
|
||||
@@ -272,8 +311,7 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
|
||||
datadir = os.path.join(dirname, "node"+str(i))
|
||||
if binary is None:
|
||||
binary = os.getenv("BITCOIND", "bitcoind")
|
||||
# RPC tests still depend on free transactions
|
||||
args = [ binary, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-blockprioritysize=50000", "-mocktime="+str(get_mocktime()) ]
|
||||
args = [ binary, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-mocktime="+str(get_mocktime()) ]
|
||||
if extra_args is not None: args.extend(extra_args)
|
||||
bitcoind_processes[i] = subprocess.Popen(args)
|
||||
if os.getenv("PYTHON_DEBUG", ""):
|
||||
@@ -289,16 +327,16 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
|
||||
|
||||
return proxy
|
||||
|
||||
def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None):
|
||||
def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None):
|
||||
"""
|
||||
Start multiple bitcoinds, return RPC connections to them
|
||||
"""
|
||||
if extra_args is None: extra_args = [ None for i in range(num_nodes) ]
|
||||
if binary is None: binary = [ None for i in range(num_nodes) ]
|
||||
if extra_args is None: extra_args = [ None for _ in range(num_nodes) ]
|
||||
if binary is None: binary = [ None for _ in range(num_nodes) ]
|
||||
rpcs = []
|
||||
try:
|
||||
for i in range(num_nodes):
|
||||
rpcs.append(start_node(i, dirname, extra_args[i], rpchost, binary=binary[i]))
|
||||
rpcs.append(start_node(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i]))
|
||||
except: # If one node failed to start, stop the others
|
||||
stop_nodes(rpcs)
|
||||
raise
|
||||
@@ -308,14 +346,21 @@ def log_filename(dirname, n_node, logname):
|
||||
return os.path.join(dirname, "node"+str(n_node), "regtest", logname)
|
||||
|
||||
def stop_node(node, i):
|
||||
node.stop()
|
||||
bitcoind_processes[i].wait()
|
||||
try:
|
||||
node.stop()
|
||||
except http.client.CannotSendRequest as e:
|
||||
print("WARN: Unable to stop node: " + repr(e))
|
||||
bitcoind_processes[i].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT)
|
||||
del bitcoind_processes[i]
|
||||
|
||||
def stop_nodes(nodes):
|
||||
for node in nodes:
|
||||
node.stop()
|
||||
try:
|
||||
node.stop()
|
||||
except http.client.CannotSendRequest as e:
|
||||
print("WARN: Unable to stop node: " + repr(e))
|
||||
del nodes[:] # Emptying array closes connections as a side effect
|
||||
wait_bitcoinds()
|
||||
|
||||
def set_node_times(nodes, t):
|
||||
for node in nodes:
|
||||
@@ -324,7 +369,7 @@ def set_node_times(nodes, t):
|
||||
def wait_bitcoinds():
|
||||
# Wait for all bitcoinds to cleanly exit
|
||||
for bitcoind in bitcoind_processes.values():
|
||||
bitcoind.wait()
|
||||
bitcoind.wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT)
|
||||
bitcoind_processes.clear()
|
||||
|
||||
def connect_nodes(from_connection, node_num):
|
||||
@@ -463,10 +508,14 @@ def assert_greater_than(thing1, thing2):
|
||||
raise AssertionError("%s <= %s"%(str(thing1),str(thing2)))
|
||||
|
||||
def assert_raises(exc, fun, *args, **kwds):
|
||||
assert_raises_message(exc, None, fun, *args, **kwds)
|
||||
|
||||
def assert_raises_message(exc, message, fun, *args, **kwds):
|
||||
try:
|
||||
fun(*args, **kwds)
|
||||
except exc:
|
||||
pass
|
||||
except exc as e:
|
||||
if message is not None and message not in e.error['message']:
|
||||
raise AssertionError("Expected substring not found:"+e.error['message'])
|
||||
except Exception as e:
|
||||
raise AssertionError("Unexpected exception raised: "+type(e).__name__)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user