aboutsummaryrefslogtreecommitdiff
path: root/qa/rpc-tests/test_framework/mininode.py
diff options
context:
space:
mode:
Diffstat (limited to 'qa/rpc-tests/test_framework/mininode.py')
-rwxr-xr-xqa/rpc-tests/test_framework/mininode.py212
1 files changed, 187 insertions, 25 deletions
diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py
index 6612b99b84..4548e2e7c0 100755
--- a/qa/rpc-tests/test_framework/mininode.py
+++ b/qa/rpc-tests/test_framework/mininode.py
@@ -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
@@ -46,6 +46,11 @@ MAX_BLOCK_SIZE = 1000000
COIN = 100000000 # 1 btc in satoshis
+NODE_NETWORK = (1 << 0)
+NODE_GETUTXO = (1 << 1)
+NODE_BLOOM = (1 << 2)
+NODE_WITNESS = (1 << 3)
+
# Keep our own socket map for asyncore, so that we can track disconnects
# ourselves (to workaround an issue with closing an asyncore socket when
# using select)
@@ -63,6 +68,8 @@ 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))
@@ -133,7 +140,10 @@ def deser_vector(f, c):
return r
-def ser_vector(l):
+# ser_function_name: Allow for an alternate serialization function on the
+# entries in the vector (we use this for serializing the vector of transactions
+# for a witness block).
+def ser_vector(l, ser_function_name=None):
r = b""
if len(l) < 253:
r = struct.pack("B", len(l))
@@ -144,7 +154,10 @@ def ser_vector(l):
else:
r = struct.pack("<BQ", 255, 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
@@ -239,12 +252,12 @@ def ser_int_vector(l):
# 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
@@ -273,12 +286,16 @@ class CAddress(object):
return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices,
self.ip, self.port)
+MSG_WITNESS_FLAG = 1<<30
class CInv(object):
typemap = {
0: "Error",
1: "TX",
- 2: "Block"}
+ 2: "Block",
+ 1|MSG_WITNESS_FLAG: "WitnessTx",
+ 2|MSG_WITNESS_FLAG : "WitnessBlock"
+ }
def __init__(self, t=0, h=0):
self.type = t
@@ -362,7 +379,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 +401,67 @@ 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 CScriptWitness(object):
+ def __init__(self):
+ # stack is a vector of strings
+ self.stack = []
+
+ def __repr__(self):
+ return "CScriptWitness(%s)" % \
+ (",".join([bytes_to_hex_str(x) for x in self.stack]))
+
+ def is_null(self):
+ if self.stack:
+ return False
+ return True
+
+
+class CTxinWitness(object):
+ def __init__(self):
+ self.scriptWitness = CScriptWitness()
+
+ def deserialize(self, f):
+ self.scriptWitness.stack = deser_string_vector(f)
+
+ def serialize(self):
+ return ser_string_vector(self.scriptWitness.stack)
+
+ def __repr__(self):
+ return repr(self.scriptWitness)
+
+ def is_null(self):
+ return self.scriptWitness.is_null()
+
+
+class CTxWitness(object):
+ def __init__(self):
+ self.vtxinwit = []
+
+ def deserialize(self, f):
+ for i in range(len(self.vtxinwit)):
+ self.vtxinwit[i].deserialize(f)
+
+ def serialize(self):
+ r = b""
+ # This is different than the usual vector serialization --
+ # we omit the length of the vector, which is required to be
+ # the same length as the transaction's vin vector.
+ for x in self.vtxinwit:
+ r += x.serialize()
+ return r
+
+ def __repr__(self):
+ return "CTxWitness(%s)" % \
+ (';'.join([repr(x) for x in self.vtxinwit]))
+
+ def is_null(self):
+ for x in self.vtxinwit:
+ if not x.is_null():
+ return False
+ return True
class CTransaction(object):
@@ -393,6 +470,7 @@ class CTransaction(object):
self.nVersion = 1
self.vin = []
self.vout = []
+ self.wit = CTxWitness()
self.nLockTime = 0
self.sha256 = None
self.hash = None
@@ -401,32 +479,80 @@ 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
+ self.wit = copy.deepcopy(tx.wit)
def deserialize(self, f):
self.nVersion = struct.unpack("<i", f.read(4))[0]
self.vin = deser_vector(f, CTxIn)
- self.vout = deser_vector(f, CTxOut)
+ flags = 0
+ if len(self.vin) == 0:
+ flags = struct.unpack("<B", f.read(1))[0]
+ # Not sure why flags can't be zero, but this
+ # matches the implementation in bitcoind
+ if (flags != 0):
+ self.vin = deser_vector(f, CTxIn)
+ self.vout = deser_vector(f, CTxOut)
+ else:
+ self.vout = deser_vector(f, CTxOut)
+ if flags != 0:
+ self.wit.vtxinwit = [CTxinWitness()]*len(self.vin)
+ self.wit.deserialize(f)
self.nLockTime = struct.unpack("<I", f.read(4))[0]
self.sha256 = None
self.hash = None
- def serialize(self):
+ def serialize_without_witness(self):
+ r = b""
+ r += struct.pack("<i", self.nVersion)
+ r += ser_vector(self.vin)
+ r += ser_vector(self.vout)
+ r += struct.pack("<I", self.nLockTime)
+ return r
+
+ # Only serialize with witness when explicitly called for
+ def serialize_with_witness(self):
+ flags = 0
+ if not self.wit.is_null():
+ flags |= 1
r = b""
r += struct.pack("<i", self.nVersion)
+ if flags:
+ dummy = []
+ r += ser_vector(dummy)
+ r += struct.pack("<B", flags)
r += ser_vector(self.vin)
r += ser_vector(self.vout)
+ if flags & 1:
+ if (len(self.wit.vtxinwit) != len(self.vin)):
+ # vtxinwit must have the same length as vin
+ self.wit.vtxinwit = self.wit.vtxinwit[:len(self.vin)]
+ for i in range(len(self.wit.vtxinwit), len(self.vin)):
+ self.wit.vtxinwit.append(CTxinWitness())
+ r += self.wit.serialize()
r += struct.pack("<I", self.nLockTime)
return r
+ # Regular serialization is without witness -- must explicitly
+ # call serialize_with_witness to include witness data.
+ def serialize(self):
+ return self.serialize_without_witness()
+
+ # Recalculate the txid (transaction hash without witness)
def rehash(self):
self.sha256 = None
self.calc_sha256()
- def calc_sha256(self):
+ # We will only cache the serialization without witness in
+ # self.sha256 and self.hash -- those are expected to be the txid.
+ def calc_sha256(self, with_witness=False):
+ if with_witness:
+ # Don't cache the result, just return it
+ return uint256_from_str(hash256(self.serialize_with_witness()))
+
if self.sha256 is None:
- self.sha256 = uint256_from_str(hash256(self.serialize()))
+ self.sha256 = uint256_from_str(hash256(self.serialize_without_witness()))
self.hash = encode(hash256(self.serialize())[::-1], 'hex_codec').decode('ascii')
def is_valid(self):
@@ -518,17 +644,17 @@ class CBlock(CBlockHeader):
super(CBlock, self).deserialize(f)
self.vtx = deser_vector(f, CTransaction)
- def serialize(self):
+ def serialize(self, with_witness=False):
r = b""
r += super(CBlock, self).serialize()
- r += ser_vector(self.vtx)
+ if with_witness:
+ r += ser_vector(self.vtx, "serialize_with_witness")
+ else:
+ 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 +663,24 @@ 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 calc_witness_merkle_root(self):
+ # For witness root purposes, the hash of the
+ # coinbase, with witness, is defined to be 0...0
+ hashes = [ser_uint256(0)]
+
+ for tx in self.vtx[1:]:
+ # Calculate the hashes with witness data
+ hashes.append(ser_uint256(tx.calc_sha256(True)))
+
+ return self.get_merkle_root(hashes)
+
def is_valid(self):
self.calc_sha256()
target = uint256_from_compact(self.nBits)
@@ -812,11 +956,16 @@ class msg_tx(object):
self.tx.deserialize(f)
def serialize(self):
- return self.tx.serialize()
+ return self.tx.serialize_without_witness()
def __repr__(self):
return "msg_tx(tx=%s)" % (repr(self.tx))
+class msg_witness_tx(msg_tx):
+
+ def serialize(self):
+ return self.tx.serialize_with_witness()
+
class msg_block(object):
command = b"block"
@@ -849,6 +998,12 @@ class msg_generic(object):
def __repr__(self):
return "msg_generic()"
+class msg_witness_block(msg_block):
+
+ def serialize(self):
+ r = self.block.serialize(with_witness=True)
+ return r
+
class msg_getaddr(object):
command = b"getaddr"
@@ -947,6 +1102,7 @@ class msg_sendheaders(object):
def __repr__(self):
return "msg_sendheaders()"
+
# getheaders message has
# number of entries
# vector of hashes
@@ -1068,6 +1224,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:
@@ -1105,6 +1263,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
@@ -1135,6 +1294,7 @@ class NodeConnCB(object):
def on_mempool(self, conn): pass
def on_pong(self, conn, message): pass
def on_feefilter(self, conn, message): pass
+ def on_sendheaders(self, conn, message): pass
# More useful callbacks and functions for NodeConnCB's which have a single NodeConn
class SingleNodeConnCB(NodeConnCB):
@@ -1183,15 +1343,16 @@ class NodeConn(asyncore.dispatcher):
b"getheaders": msg_getheaders,
b"reject": msg_reject,
b"mempool": msg_mempool,
- b"feefilter": msg_feefilter
+ b"feefilter": msg_feefilter,
+ b"sendheaders": msg_sendheaders
}
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
@@ -1206,6 +1367,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()