0

I am trying to implement an SPV client in python (for my own learning). I want to start by making a simple TCP connection to a full node, but I either cannot get a response to my version message, or to my verack message if the version message goes through. I have referenced this and this is the code that I am trying to run:

#Import dependencies
import socket
import time
import random
import struct
import hashlib
import binascii
import ssl
import socks

# Binary encode the sub-version
def create_sub_version():
    sub_version = "/Satoshi:0.7.2/"
    return b'\x0F' + sub_version.encode()

# Binary encode the network addresses
def create_network_address(ip_address, port):
    network_address = struct.pack('>8s16sH', b'\x01', 
        bytearray.fromhex("00000000000000000000ffff") + socket.inet_aton(ip_address), port)
    return(network_address)

# Create the TCP request object
def create_message(magic, command, payload):
    checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[0:4]
    return(struct.pack('L12sL4s', magic, command.encode(), len(payload), checksum) + payload)

# Create the "version" request payload
def create_payload_version(peer_ip_address):
    version = 60002
    services = 1
    timestamp = int(time.time())
    addr_local = create_network_address("127.0.0.1", 8333)
    addr_peer = create_network_address(peer_ip_address, 8333)
    nonce = random.getrandbits(64)
    start_height = 645953
    payload = struct.pack('<LQQ26s26sQ16sL', version, services, timestamp, addr_peer,
                          addr_local, nonce, create_sub_version(), start_height)
    return(payload)

# Create the "verack" request message
def create_message_verack():
    return bytearray.fromhex("f9beb4d976657261636b000000000000000000005df6e0e2")


# Create the "getdata" request payload
def create_payload_getdata(tx_id):
    count = 1
    type = 1
    hash = bytearray.fromhex(tx_id)
    payload = struct.pack('<bb32s', count, type, hash)
    return(payload)

# Print request/response data
def print_response(command, request_data, response_data):
    print("")
    print("Command: " + command)
    print("Request:")
    print(binascii.hexlify(request_data))
    print("Response:")
    print(binascii.hexlify(response_data))

if __name__ == '__main__':
    # Set constants
    magic_value = 0xd9b4bef9
    tx_id = "a13e7979d3607e187d2ce81ca9fdadfe967b2f3fc1109ce47d46cc09ebd41c50"
    peer_ip_address = socket.gethostbyname('bitcoin.aranguren.org') 
    peer_tcp_port =  50002
    buffer_size = 1024

    # Create Request Objects
    version_payload = create_payload_version(peer_ip_address)
    version_message = create_message(magic_value, 'version', version_payload)
    verack_message = create_message_verack()
    getdata_payload = create_payload_getdata(tx_id)
    getdata_message = create_message(magic_value, 'getdata', getdata_payload)

    # Establish TCP Connection
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(10)
    ws = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1)
    ws.connect((peer_ip_address, peer_tcp_port))

    # Send message "version"
    ws.send(version_message)
    response_data = ws.recv(buffer_size)
    print_response("version", version_message, response_data)

    # Send message "verack"
    ws.send(verack_message)
    response_data = ws.recv(buffer_size)
    print_response("verack", verack_message, response_data)

    # Send message "getdata"
    ws.send(getdata_message)
    response_data = ws.recv(buffer_size)
    print_response("getdata", getdata_message, response_data)

    # Close the TCP connection
    ws.close()

The server I connect to in the code is among the only ones that would respond to the version message, returning b'7b226a736f6e727063223a2022322e30222c20226572726f72223a207b22636f6465223a202d33323730302c20226d657373616765223a20226d65737361676573206d75737420626520656e636f64656420696e205554462d38227d2c20226964223a206e756c6c7d0a'.

I also tried referencing: this stack exchange question and Ken Shirriff's github code, but none seem to work, either because I am using python3 or from other causes. I am a beginner in this, so if somebody could help me understand why I cannot get my messages across in the above code or has another implementation in python3, I would be grateful.

3
  • 1
    It is probably helpful to run a full node with debugging enabled (e.g. Bitcoin Core with -debug=net), and connect to that. The output in debug.log may tell you what you're doing wrong. – Pieter Wuille Aug 30 '20 at 16:09
  • Have you tried with magic = f9beb4d9? – Oscar Serna Aug 30 '20 at 19:03
  • This is the message you are getting back: {"code": -32700, "message": "messages must be encoded in UTF-8"}, "id": null – Oscar Serna Aug 30 '20 at 19:37
0

I think you have your magic byte order backwards. It should be 0xf9beb4d9.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.