5

I'm trying to make a simple peer connection using Python.

If I understand correctly communication begins by sending a "version" packet to the receiving node. The node then sends back a "verack" packet, afterwards you may begin querying for data/pushing txs.

Ken Shirriff's blog has proved invaluable in getting me this far but I'm unable to get his code examples working, possibly because they're dated?

In particular, when running:

https://github.com/shirriff/bitcoin-code/blob/master/minimalPeerConnection.py

the socket closes immediately. I fail to receive back a "verack" and am unable to proceed with pushing my tx. Note that I've replaced his hard-coded IP with a currently operating node (124.248.237.178:8333).

I've attempted to construct a more "up to date" version packet (70002) referencing these docs but run into the same issue:

import struct
import socket
import time
import hashlib
import binascii

magic = 0xd9b4bef9

def makeMessage(magic,command,payload):
    checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[0:4]
    return struct.pack('L12sL4s',magic,command,len(payload),checksum).encode("hex")+payload
def makeVersionPayload():
    version = 70002
    services = 1
    timestamp = int(time.time())

    adr_u = "::ffff:127.0.0.1"
    services_u = 1
    port_u = 8333

    adr_me = "::ffff:127.0.0.1"
    services_me = 1
    port_me = 8333

    nonce = 0

    user_agent_bytes = 0
    start_height = 0
    relay = 0

    #https://bitcoin.org/en/developer-reference#version
    payload_hex = "";
    payload_hex += struct.pack("<L",version).encode("hex")
    payload_hex += struct.pack("<Q",services).encode("hex")
    payload_hex += struct.pack("<Q",timestamp).encode("hex")
    payload_hex += struct.pack("<Q",services_u).encode("hex")
    payload_hex += struct.pack(">16s",adr_u).encode("hex")
    payload_hex += struct.pack(">H",port_u).encode("hex")
    payload_hex += struct.pack("<Q",services_me).encode("hex")
    payload_hex += struct.pack(">16s",adr_me).encode("hex")
    payload_hex += struct.pack(">H",port_me).encode("hex")
    payload_hex += struct.pack("<Q",nonce).encode("hex")
    payload_hex += struct.pack("<B",user_agent_bytes).encode("hex")
    payload_hex += struct.pack("<L",start_height).encode("hex")
    payload_hex += struct.pack("<B",relay).encode("hex")
    return payload_hex

ip = socket.gethostbyname("124.248.237.178")
port = 8333
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "connected to node..."
sock.connect((ip,port))

hex_msg = makeMessage(magic,"version",makeVersionPayload())
print "sending version packet"
sock.send(binascii.unhexlify(hex_msg))

while 1:
    msg = sock.recv(4096)
    if not msg:
        print "disconnected"
        exit()
    else:
        #expecting verack?
        print "response: ",msg

Can anyone point me in the right direction?

1 Answer 1

3

Got it working.

Ken's blog assumes a Windows environment, in 64-bit Linux the byte counts are different for certain datatypes so I had to change some of the "pack" formats.

Finally, I'd been doing everything in hex and converting to binary only for the socket send. When calculating the sha256 (twice) checksum however this must be on the BINARY payload.

Working code (for 64-bit Linux at least):

import struct
import socket
import time
import hashlib
import binascii

magic = "f9beb4d9"

def makeMessage(magic,command,payload):
    checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[0:4]
    return magic.decode("hex")+struct.pack('12sI4s',command,len(payload),checksum)+payload
def makeVersionPayload():
    version = 70002
    services = 0
    timestamp = int(time.time())

    addr_you = "127.0.0.1"
    services_you = 0
    port_you = 8333

    addr_me = "127.0.0.1"
    services_me = 0
    port_me = 8333

    nonce = 0

    user_agent_bytes = 0
    start_height = 0
    relay = 1

    #https://bitcoin.org/en/developer-reference#version
    payload = "";
    payload += struct.pack("i",version)
    payload += struct.pack("Q",services)
    payload += struct.pack("q",timestamp)
    payload += struct.pack("Q",services_you)
    payload += struct.pack(">16s",addr_you)
    payload += struct.pack(">H",port_you)
    payload += struct.pack("Q",services_me)
    payload += struct.pack(">16s",addr_me)
    payload += struct.pack(">H",port_me)
    payload += struct.pack("Q",nonce)
    payload += struct.pack("B",user_agent_bytes)
    payload += struct.pack("i",start_height)
    payload += struct.pack("B",relay)
    return payload

ip = socket.gethostbyname("124.248.237.178")
port = 8333
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "connected to node..."
sock.connect((ip,port))

msg = makeMessage(magic,"version",makeVersionPayload())
print "sending version packet"
sock.send(msg)

while 1:
    msg = sock.recv(2**10)
    if not msg:
        print "done"
        exit()
    else:
        print msg.encode("hex")

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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