Changeset View
Changeset View
Standalone View
Standalone View
qa/rpc-tests/test_framework/mininode.py
Show First 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
NODE_BLOOM = (1 << 2) | NODE_BLOOM = (1 << 2) | ||||
NODE_WITNESS = (1 << 3) | NODE_WITNESS = (1 << 3) | ||||
NODE_XTHIN = (1 << 4) | NODE_XTHIN = (1 << 4) | ||||
NODE_BITCOIN_CASH = (1 << 5) | NODE_BITCOIN_CASH = (1 << 5) | ||||
# Howmuch data will be read from the network at once | # Howmuch data will be read from the network at once | ||||
READ_BUFFER_SIZE = 8192 | READ_BUFFER_SIZE = 8192 | ||||
logger = logging.getLogger("TestFramework.mininode") | |||||
# Keep our own socket map for asyncore, so that we can track disconnects | # 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) | # using select) | ||||
mininode_socket_map = dict() | mininode_socket_map = dict() | ||||
# One lock for synchronizing all data access between the networking thread (see | # One lock for synchronizing all data access between the networking thread (see | ||||
# NetworkThread below) and the thread running the test logic. For simplicity, | # NetworkThread below) and the thread running the test logic. For simplicity, | ||||
# NodeConn acquires this lock whenever delivering a message to to a NodeConnCB, | # NodeConn acquires this lock whenever delivering a message to to a NodeConnCB, | ||||
▲ Show 20 Lines • Show All 1,501 Lines • ▼ Show 20 Lines | def deliver(self, conn, message): | ||||
deliver_sleep = self.get_deliver_sleep_time() | deliver_sleep = self.get_deliver_sleep_time() | ||||
if deliver_sleep is not None: | if deliver_sleep is not None: | ||||
time.sleep(deliver_sleep) | time.sleep(deliver_sleep) | ||||
with mininode_lock: | with mininode_lock: | ||||
try: | try: | ||||
getattr(self, 'on_' + message.command.decode('ascii'))( | getattr(self, 'on_' + message.command.decode('ascii'))( | ||||
conn, message) | conn, message) | ||||
except: | except: | ||||
print("ERROR delivering %s (%s)" % (repr(message), | logger.exception("ERROR delivering %s" % repr(message)) | ||||
sys.exc_info()[0])) | |||||
def on_version(self, conn, message): | def on_version(self, conn, message): | ||||
if message.nVersion >= 209: | if message.nVersion >= 209: | ||||
conn.send_message(msg_verack()) | conn.send_message(msg_verack()) | ||||
conn.ver_send = min(MY_VERSION, message.nVersion) | conn.ver_send = min(MY_VERSION, message.nVersion) | ||||
if message.nVersion < 209: | if message.nVersion < 209: | ||||
conn.ver_recv = conn.ver_send | conn.ver_recv = conn.ver_send | ||||
conn.nServices = message.nServices | conn.nServices = message.nServices | ||||
▲ Show 20 Lines • Show All 122 Lines • ▼ Show 20 Lines | class NodeConn(asyncore.dispatcher): | ||||
MAGIC_BYTES = { | MAGIC_BYTES = { | ||||
"mainnet": b"\xe3\xe1\xf3\xe8", | "mainnet": b"\xe3\xe1\xf3\xe8", | ||||
"testnet3": b"\xf4\xe5\xf3\xf4", | "testnet3": b"\xf4\xe5\xf3\xf4", | ||||
"regtest": b"\xda\xb5\xbf\xfa", | "regtest": b"\xda\xb5\xbf\xfa", | ||||
} | } | ||||
def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=NODE_NETWORK, send_version=True): | def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=NODE_NETWORK, send_version=True): | ||||
asyncore.dispatcher.__init__(self, map=mininode_socket_map) | asyncore.dispatcher.__init__(self, map=mininode_socket_map) | ||||
self.log = logging.getLogger("NodeConn(%s:%d)" % (dstaddr, dstport)) | |||||
self.dstaddr = dstaddr | self.dstaddr = dstaddr | ||||
self.dstport = dstport | self.dstport = dstport | ||||
self.create_socket(socket.AF_INET, socket.SOCK_STREAM) | self.create_socket(socket.AF_INET, socket.SOCK_STREAM) | ||||
self.sendbuf = b"" | self.sendbuf = b"" | ||||
self.recvbuf = b"" | self.recvbuf = b"" | ||||
self.ver_send = 209 | self.ver_send = 209 | ||||
self.ver_recv = 209 | self.ver_recv = 209 | ||||
self.last_sent = 0 | self.last_sent = 0 | ||||
self.state = "connecting" | self.state = "connecting" | ||||
self.network = net | self.network = net | ||||
self.cb = callback | self.cb = callback | ||||
self.disconnect = False | self.disconnect = False | ||||
self.nServices = 0 | self.nServices = 0 | ||||
if send_version: | if send_version: | ||||
# stuff version msg into sendbuf | # stuff version msg into sendbuf | ||||
vt = msg_version() | vt = msg_version() | ||||
vt.nServices = services | vt.nServices = services | ||||
vt.addrTo.ip = self.dstaddr | vt.addrTo.ip = self.dstaddr | ||||
vt.addrTo.port = self.dstport | vt.addrTo.port = self.dstport | ||||
vt.addrFrom.ip = "0.0.0.0" | vt.addrFrom.ip = "0.0.0.0" | ||||
vt.addrFrom.port = 0 | vt.addrFrom.port = 0 | ||||
self.send_message(vt, True) | self.send_message(vt, True) | ||||
print('MiniNode: Connecting to Bitcoin Node IP # ' + dstaddr + ':' | logger.info('Connecting to Bitcoin Node: %s:%d' % | ||||
+ str(dstport)) | (self.dstaddr, self.dstport)) | ||||
try: | try: | ||||
self.connect((dstaddr, dstport)) | self.connect((dstaddr, dstport)) | ||||
except: | except: | ||||
self.handle_close() | self.handle_close() | ||||
self.rpc = rpc | self.rpc = rpc | ||||
def show_debug_msg(self, msg): | |||||
self.log.debug(msg) | |||||
def handle_connect(self): | def handle_connect(self): | ||||
if self.state != "connected": | if self.state != "connected": | ||||
self.show_debug_msg("MiniNode: Connected & Listening: \n") | logger.debug("Connected & Listening: \n") | ||||
self.state = "connected" | self.state = "connected" | ||||
self.cb.on_open(self) | self.cb.on_open(self) | ||||
def handle_close(self): | def handle_close(self): | ||||
self.show_debug_msg("MiniNode: Closing Connection to %s:%d... " | logger.debug("Closing Connection to %s:%d... " % | ||||
% (self.dstaddr, self.dstport)) | (self.dstaddr, self.dstport)) | ||||
self.state = "closed" | self.state = "closed" | ||||
self.recvbuf = b"" | self.recvbuf = b"" | ||||
self.sendbuf = b"" | self.sendbuf = b"" | ||||
try: | try: | ||||
self.close() | self.close() | ||||
except: | except: | ||||
pass | pass | ||||
self.cb.on_close(self) | self.cb.on_close(self) | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | def got_data(self): | ||||
return None | return None | ||||
msg = self.recvbuf[4 + 12 + 4 + 4:4 + 12 + 4 + 4 + msglen] | msg = self.recvbuf[4 + 12 + 4 + 4:4 + 12 + 4 + 4 + msglen] | ||||
h = sha256(sha256(msg)) | h = sha256(sha256(msg)) | ||||
if checksum != h[:4]: | if checksum != h[:4]: | ||||
raise ValueError( | raise ValueError( | ||||
"got bad checksum " + repr(self.recvbuf)) | "got bad checksum " + repr(self.recvbuf)) | ||||
self.recvbuf = self.recvbuf[4 + 12 + 4 + 4 + msglen:] | self.recvbuf = self.recvbuf[4 + 12 + 4 + 4 + msglen:] | ||||
if command not in self.messagemap: | if command not in self.messagemap: | ||||
self.show_debug_msg("Unknown command: '" + command + "' " + | logger.warning("Received unknown command from %s:%d: '%s' %s" % | ||||
repr(msg)) | (self.dstaddr, self.dstport, command, repr(msg))) | ||||
return None | return None | ||||
f = BytesIO(msg) | f = BytesIO(msg) | ||||
m = self.messagemap[command]() | m = self.messagemap[command]() | ||||
m.deserialize(f) | m.deserialize(f) | ||||
return m | return m | ||||
except Exception as e: | except Exception as e: | ||||
print('got_data:', repr(e)) | logger.exception('got_data:', repr(e)) | ||||
# import traceback | |||||
# traceback.print_tb(sys.exc_info()[2]) | |||||
def send_message(self, message, pushbuf=False): | def send_message(self, message, pushbuf=False): | ||||
if self.state != "connected" and not pushbuf: | if self.state != "connected" and not pushbuf: | ||||
raise IOError('Not connected, no pushbuf') | raise IOError('Not connected, no pushbuf') | ||||
self.show_debug_msg("Send %s" % repr(message)) | logger.debug("Send message to %s:%d: %s" % | ||||
(self.dstaddr, self.dstport, repr(message))) | |||||
command = message.command | command = message.command | ||||
data = message.serialize() | data = message.serialize() | ||||
tmsg = self.MAGIC_BYTES[self.network] | tmsg = self.MAGIC_BYTES[self.network] | ||||
tmsg += command | tmsg += command | ||||
tmsg += b"\x00" * (12 - len(command)) | tmsg += b"\x00" * (12 - len(command)) | ||||
tmsg += struct.pack("<I", len(data)) | tmsg += struct.pack("<I", len(data)) | ||||
if self.ver_send >= 209: | if self.ver_send >= 209: | ||||
th = sha256(data) | th = sha256(data) | ||||
h = sha256(th) | h = sha256(th) | ||||
tmsg += h[:4] | tmsg += h[:4] | ||||
tmsg += data | tmsg += data | ||||
with mininode_lock: | with mininode_lock: | ||||
self.sendbuf += tmsg | self.sendbuf += tmsg | ||||
self.last_sent = time.time() | self.last_sent = time.time() | ||||
def got_message(self, message): | def got_message(self, message): | ||||
if message.command == b"version": | if message.command == b"version": | ||||
if message.nVersion <= BIP0031_VERSION: | if message.nVersion <= BIP0031_VERSION: | ||||
self.messagemap[b'ping'] = msg_ping_prebip31 | self.messagemap[b'ping'] = msg_ping_prebip31 | ||||
if self.last_sent + 30 * 60 < time.time(): | if self.last_sent + 30 * 60 < time.time(): | ||||
self.send_message(self.messagemap[b'ping']()) | self.send_message(self.messagemap[b'ping']()) | ||||
self.show_debug_msg("Recv %s" % repr(message)) | logger.debug("Received message from %s:%d: %s" % | ||||
(self.dstaddr, self.dstport, repr(message))) | |||||
self.cb.deliver(self, message) | self.cb.deliver(self, message) | ||||
def disconnect_node(self): | def disconnect_node(self): | ||||
self.disconnect = True | self.disconnect = True | ||||
class NetworkThread(Thread): | class NetworkThread(Thread): | ||||
Show All 22 Lines |