qa: bitcoind: add a helper for sending a P2P message to bitcoind

This commit is contained in:
Antoine Poinsot 2023-08-09 12:14:18 +02:00
parent df396cccc8
commit b96db71a4d
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304

View File

@ -1,5 +1,8 @@
import hashlib
import logging
import os
import socket
import time
from decimal import Decimal
from ephemeral_port_reserve import reserve
@ -53,11 +56,13 @@ class Bitcoind(TailableProc):
"-datadir={}".format(bitcoin_dir),
"-printtoconsole",
"-server",
"-debug=1",
"-debugexclude=libevent",
"-debugexclude=tor",
]
bitcoind_conf = {
"port": self.p2pport,
"rpcport": rpcport,
"debug": 1,
"fallbackfee": Decimal(1000) / COIN,
"rpcthreads": 32,
}
@ -179,6 +184,76 @@ class Bitcoind(TailableProc):
self.generate_block(1 + final_len - (height + shift), memp)
self.wait_for_log(r"UpdateTip: new best=.* height={}".format(final_len))
def send_p2p_message(self, s, command, payload):
magic_bytes = b"\xfa\xbf\xb5\xda"
checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4]
payload_len = len(payload).to_bytes(4, "little")
message = (
magic_bytes
+ command.encode("ascii")
+ bytes(12 - len(command))
+ payload_len
+ checksum
+ payload
)
s.sendall(message)
logging.debug(f"Sent message to bitcoind: {command}")
def connect_p2p(self, cur_height):
version = int(70016).to_bytes(4, "little")
services = int((1 << 0) | (1 << 3)).to_bytes(8, "little")
timestamp = int(time.time()).to_bytes(8, "little")
addr_recv = services + bytes(16) + self.p2pport.to_bytes(2, "little")
addr_from = addr_recv
nonce = os.urandom(8)
user_agent = b"\x00"
start_height = cur_height.to_bytes(4, "little")
relay = b"\x00"
ver_payload = (
version
+ services
+ timestamp
+ addr_recv
+ addr_from
+ nonce
+ user_agent
+ start_height
+ relay
)
logging.debug("Connecting to bitcoind p2p port")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", self.p2pport))
self.send_p2p_message(s, "version", ver_payload)
s.recv(102 + 24) # Recv version
s.recv(0 + 24) # Recv wtxidrelay
s.recv(0 + 24) # Recv sendaddrv2
s.recv(0 + 24) # Recv verack
self.send_p2p_message(s, "verack", b"")
s.recv(9 + 24) # Recv sendcmpct
ping = s.recv(8 + 24) # Recv ping, reply with pong
assert ping[4:8].decode("ascii") == "ping", ping
self.send_p2p_message(s, "pong", ping[-8:])
s.recv(613 + 24) # Recv getheaders (ignore it)
s.recv(8 + 24) # Recv feefilter
logging.debug("Handshake to bitcoind complete")
return s
def submit_block(self, cur_height, block_hex):
"""Submit a block through the P2P interface."""
s = self.connect_p2p(cur_height)
self.send_p2p_message(s, "block", bytes.fromhex(block_hex))
# Make sure the block was received by waiting for the inv.
inv = s.recv(37 + 24)
assert (
inv[4:7].decode("ascii") == "inv"
and int.from_bytes(inv[24 + 1 : 24 + 1 + 4], "little") == 2
), inv
s.close()
def startup(self):
try:
self.start()