Haciendo conexión entre pares en Python

Estoy tratando de hacer una conexión simple entre pares usando Python.

Si entiendo correctamente, la comunicación comienza enviando un paquete de "versión" al nodo receptor. Luego, el nodo devuelve un paquete "verack", luego puede comenzar a consultar datos/empujar txs.

El blog de Ken Shirriff ha demostrado ser invaluable para llevarme tan lejos, pero no puedo hacer que sus ejemplos de código funcionen, ¿posiblemente porque están fechados?

En particular, cuando se ejecuta:

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

el zócalo se cierra inmediatamente. No puedo recibir un "verack" y no puedo continuar con el envío de mi tx. Tenga en cuenta que he reemplazado su IP codificada con un nodo actualmente operativo (124.248.237.178:8333).

Intenté construir un paquete de versión más "actualizado" (70002) que hace referencia a estos documentos , pero me encontré con el mismo problema:

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

¿Alguien puede señalarme en la dirección correcta?

Respuestas (1)

Lo tengo funcionando.

El blog de Ken asume un entorno de Windows, en Linux de 64 bits, los recuentos de bytes son diferentes para ciertos tipos de datos, por lo que tuve que cambiar algunos de los formatos de "paquete".

Finalmente, había estado haciendo todo en hexadecimal y convirtiendo a binario solo para el envío de socket. Sin embargo, al calcular la suma de verificación sha256 (dos veces), debe estar en la carga útil BINARIA.

Código de trabajo (al menos para Linux de 64 bits):

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")