aboutsummaryrefslogtreecommitdiff
path: root/test/functional/test_framework
diff options
context:
space:
mode:
authorstratospher <44024636+stratospher@users.noreply.github.com>2022-02-05 23:08:49 +0530
committerstratospher <44024636+stratospher@users.noreply.github.com>2024-01-25 11:09:52 +0530
commit05bddb20f5cc9036fd680500bde8ece70dbf0646 (patch)
tree78356190c1fd4e7a89dfb8eb6c8379c734e249ff /test/functional/test_framework
parenta049d1bd08c8cdb3b693520f24f8a82572dcaab1 (diff)
downloadbitcoin-05bddb20f5cc9036fd680500bde8ece70dbf0646.tar.xz
[test] Perform initial v2 handshake
Diffstat (limited to 'test/functional/test_framework')
-rwxr-xr-xtest/functional/test_framework/p2p.py51
1 files changed, 50 insertions, 1 deletions
diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py
index 004a9edc1c..308cefdbc2 100755
--- a/test/functional/test_framework/p2p.py
+++ b/test/functional/test_framework/p2p.py
@@ -232,13 +232,62 @@ class P2PConnection(asyncio.Protocol):
self.recvbuf = b""
self.on_close()
+ # v2 handshake method
+ def v2_handshake(self):
+ """v2 handshake performed before P2P messages are exchanged (see BIP324). P2PConnection is the initiator
+ (in inbound connections to TestNode) and the responder (in outbound connections from TestNode).
+ Performed by:
+ * initiator using `initiate_v2_handshake()`, `complete_handshake()` and `authenticate_handshake()`
+ * responder using `respond_v2_handshake()`, `complete_handshake()` and `authenticate_handshake()`
+
+ `initiate_v2_handshake()` is immediately done by the initiator when the connection is established in
+ `connection_made()`. The rest of the initial v2 handshake functions are handled here.
+ """
+ if not self.v2_state.peer:
+ if not self.v2_state.initiating and not self.v2_state.sent_garbage:
+ # if the responder hasn't sent garbage yet, the responder is still reading ellswift bytes
+ # reads ellswift bytes till the first mismatch from 12 bytes V1_PREFIX
+ length, send_handshake_bytes = self.v2_state.respond_v2_handshake(BytesIO(self.recvbuf))
+ self.recvbuf = self.recvbuf[length:]
+ if send_handshake_bytes == -1:
+ self.v2_state = None
+ return
+ elif send_handshake_bytes:
+ self.send_raw_message(send_handshake_bytes)
+ elif send_handshake_bytes == b"":
+ return # only after send_handshake_bytes are sent can `complete_handshake()` be done
+
+ # `complete_handshake()` reads the remaining ellswift bytes from recvbuf
+ # and sends response after deriving shared ECDH secret using received ellswift bytes
+ length, response = self.v2_state.complete_handshake(BytesIO(self.recvbuf))
+ self.recvbuf = self.recvbuf[length:]
+ if response:
+ self.send_raw_message(response)
+ else:
+ return # only after response is sent can `authenticate_handshake()` be done
+
+ # `self.v2_state.peer` is instantiated only after shared ECDH secret/BIP324 derived keys and ciphers
+ # is derived in `complete_handshake()`.
+ # so `authenticate_handshake()` which uses the BIP324 derived ciphers gets called after `complete_handshake()`.
+ assert self.v2_state.peer
+ length, is_mac_auth = self.v2_state.authenticate_handshake(self.recvbuf)
+ if not is_mac_auth:
+ raise ValueError("invalid v2 mac tag in handshake authentication")
+ self.recvbuf = self.recvbuf[length:]
+ while self.v2_state.tried_v2_handshake and self.queue_messages:
+ message = self.queue_messages.pop(0)
+ self.send_message(message)
+
# Socket read methods
def data_received(self, t):
"""asyncio callback when data is read from the socket."""
if len(t) > 0:
self.recvbuf += t
- self._on_data()
+ if self.supports_v2_p2p and not self.v2_state.tried_v2_handshake:
+ self.v2_handshake()
+ else:
+ self._on_data()
def _on_data(self):
"""Try to read P2P messages from the recv buffer.