Changeset View
Changeset View
Standalone View
Standalone View
test/functional/test_framework/mininode.py
Show First 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | def peer_connect(self, dstaddr, dstport, net="regtest"): | ||||
self.create_socket(socket.AF_INET, socket.SOCK_STREAM) | self.create_socket(socket.AF_INET, socket.SOCK_STREAM) | ||||
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) | self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) | ||||
self.sendbuf = b"" | self.sendbuf = b"" | ||||
self.recvbuf = b"" | self.recvbuf = b"" | ||||
self.state = "connecting" | self.state = "connecting" | ||||
self.network = net | self.network = net | ||||
self.disconnect = False | self.disconnect = False | ||||
logger.info('Connecting to Bitcoin Node: %s:%d' % | logger.info('Connecting to Bitcoin Node: {}:{}'.format( | ||||
(self.dstaddr, self.dstport)) | self.dstaddr, self.dstport)) | ||||
try: | try: | ||||
self.connect((dstaddr, dstport)) | self.connect((dstaddr, dstport)) | ||||
except: | except: | ||||
self.handle_close() | self.handle_close() | ||||
def peer_disconnect(self): | def peer_disconnect(self): | ||||
# Connection could have already been closed by other end. | # Connection could have already been closed by other end. | ||||
if self.state == "connected": | if self.state == "connected": | ||||
self.disconnect_node() | self.disconnect_node() | ||||
# Connection and disconnection methods | # Connection and disconnection methods | ||||
def handle_connect(self): | def handle_connect(self): | ||||
"""asyncore callback when a connection is opened.""" | """asyncore callback when a connection is opened.""" | ||||
if self.state != "connected": | if self.state != "connected": | ||||
logger.debug("Connected & Listening: %s:%d" % | logger.debug("Connected & Listening: {}:{}".format( | ||||
(self.dstaddr, self.dstport)) | self.dstaddr, self.dstport)) | ||||
self.state = "connected" | self.state = "connected" | ||||
self.on_open() | self.on_open() | ||||
def handle_close(self): | def handle_close(self): | ||||
"""asyncore callback when a connection is closed.""" | """asyncore callback when a connection is closed.""" | ||||
logger.debug("Closing connection to: %s:%d" % | logger.debug("Closing connection to: {}:{}".format( | ||||
(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.on_close() | self.on_close() | ||||
Show All 26 Lines | def _on_data(self): | ||||
This method reads data from the buffer in a loop. It deserializes, | This method reads data from the buffer in a loop. It deserializes, | ||||
parses and verifies the P2P header, then passes the P2P payload to | parses and verifies the P2P header, then passes the P2P payload to | ||||
the on_message callback for processing.""" | the on_message callback for processing.""" | ||||
try: | try: | ||||
with mininode_lock: | with mininode_lock: | ||||
if len(self.recvbuf) < 4: | if len(self.recvbuf) < 4: | ||||
return None | return None | ||||
if self.recvbuf[:4] != MAGIC_BYTES[self.network]: | if self.recvbuf[:4] != MAGIC_BYTES[self.network]: | ||||
raise ValueError("got garbage %s" % repr(self.recvbuf)) | raise ValueError( | ||||
"got garbage {}".format(repr(self.recvbuf))) | |||||
if len(self.recvbuf) < 4 + 12 + 4 + 4: | if len(self.recvbuf) < 4 + 12 + 4 + 4: | ||||
return | return | ||||
command = self.recvbuf[4:4+12].split(b"\x00", 1)[0] | command = self.recvbuf[4:4+12].split(b"\x00", 1)[0] | ||||
msglen = struct.unpack("<i", self.recvbuf[4+12:4+12+4])[0] | msglen = struct.unpack("<i", self.recvbuf[4+12:4+12+4])[0] | ||||
checksum = self.recvbuf[4+12+4:4+12+4+4] | checksum = self.recvbuf[4+12+4:4+12+4+4] | ||||
if len(self.recvbuf) < 4 + 12 + 4 + 4 + msglen: | if len(self.recvbuf) < 4 + 12 + 4 + 4 + msglen: | ||||
return | return | ||||
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("got bad checksum " + repr(self.recvbuf)) | raise ValueError("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 MESSAGEMAP: | if command not in MESSAGEMAP: | ||||
raise ValueError("Received unknown command from %s:%d: '%s' %s" % ( | raise ValueError("Received unknown command from {}:{}: '{}' {}".format( | ||||
self.dstaddr, self.dstport, command, repr(msg))) | self.dstaddr, self.dstport, command, repr(msg))) | ||||
f = BytesIO(msg) | f = BytesIO(msg) | ||||
m = MESSAGEMAP[command]() | m = MESSAGEMAP[command]() | ||||
m.deserialize(f) | m.deserialize(f) | ||||
self._log_message("receive", m) | self._log_message("receive", m) | ||||
return m | return m | ||||
except Exception as e: | except Exception as e: | ||||
logger.exception('Error reading message:', repr(e)) | logger.exception('Error reading message:', repr(e)) | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | class P2PConnection(asyncore.dispatcher): | ||||
# Class utility methods | # Class utility methods | ||||
def _log_message(self, direction, msg): | def _log_message(self, direction, msg): | ||||
"""Logs a message being sent or received over the connection.""" | """Logs a message being sent or received over the connection.""" | ||||
if direction == "send": | if direction == "send": | ||||
log_message = "Send message to " | log_message = "Send message to " | ||||
elif direction == "receive": | elif direction == "receive": | ||||
log_message = "Received message from " | log_message = "Received message from " | ||||
log_message += "%s:%d: %s" % (self.dstaddr, | log_message += "{}:{}: {}".format( | ||||
self.dstport, repr(msg)[:500]) | self.dstaddr, self.dstport, repr(msg)[:500]) | ||||
if len(log_message) > 500: | if len(log_message) > 500: | ||||
log_message += "... (msg truncated)" | log_message += "... (msg truncated)" | ||||
logger.debug(log_message) | logger.debug(log_message) | ||||
class P2PInterface(P2PConnection): | class P2PInterface(P2PConnection): | ||||
"""A high-level P2P interface class for communicating with a Bitcoin Cash node. | """A high-level P2P interface class for communicating with a Bitcoin Cash node. | ||||
Show All 40 Lines | def on_message(self, message): | ||||
and the most recent message of each type.""" | and the most recent message of each type.""" | ||||
with mininode_lock: | with mininode_lock: | ||||
try: | try: | ||||
command = message.command.decode('ascii') | command = message.command.decode('ascii') | ||||
self.message_count[command] += 1 | self.message_count[command] += 1 | ||||
self.last_message[command] = message | self.last_message[command] = message | ||||
getattr(self, 'on_' + command)(message) | getattr(self, 'on_' + command)(message) | ||||
except: | except: | ||||
print("ERROR delivering %s (%s)" % (repr(message), | print("ERROR delivering {} ({})".format( | ||||
sys.exc_info()[0])) | repr(message), sys.exc_info()[0])) | ||||
raise | raise | ||||
# Callback methods. Can be overridden by subclasses in individual test | # Callback methods. Can be overridden by subclasses in individual test | ||||
# cases to provide custom message handling behaviour. | # cases to provide custom message handling behaviour. | ||||
def on_open(self): | def on_open(self): | ||||
pass | pass | ||||
▲ Show 20 Lines • Show All 166 Lines • Show Last 20 Lines |