Создание однорангового соединения в Python

Я пытаюсь установить простое одноранговое соединение с помощью Python.

Если я правильно понимаю, общение начинается с отправки пакета «версия» на принимающий узел. Затем узел отправляет обратно пакет «verack», после чего вы можете начать запрашивать данные/отправлять txs.

Блог Кена Ширриффа оказался неоценимым для меня, но я не могу заставить его примеры кода работать, возможно, потому, что они устарели?

В частности, при запуске:

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

сокет сразу закрывается. Я не могу получить обратно "verack" и не могу продолжить отправку моего tx. Обратите внимание, что я заменил его жестко запрограммированный IP-адрес на текущий работающий узел (124.248.237.178:8333).

Я попытался создать пакет более «актуальной» версии (70002), ссылаясь на эти документы , но столкнулся с той же проблемой:

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

Может кто-то указать мне верное направление?

Ответы (1)

Получил это работает.

В блоге Кена предполагается среда Windows, в 64-разрядной версии Linux количество байтов отличается для определенных типов данных, поэтому мне пришлось изменить некоторые форматы «упаковки».

Наконец, я делал все в шестнадцатеричном формате и преобразовывал в двоичный только для отправки через сокет. Однако при вычислении контрольной суммы sha256 (дважды) это должно быть в BINARY полезной нагрузке.

Рабочий код (по крайней мере, для 64-битной Linux):

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