devp2p节点发现协议v4不完全实现

Node Discovery Protocol v4

本文包含了Ping Packet、Pong Packet、FindNode Packet、Neighbors Packet的生成及解析,以及Ping包和Pong包的互相发送。代码如下:

eth-keys安装

Windows下安装eth-keys需要提前安装Visual Studio生成工具。

下载地址:Microsoft C++ 生成工具 - Visual Studio

然后在工作负荷中勾选如下项目即可:

packet.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import time
import rlp
import secrets
from eth_hash.auto import keccak
from eth_keys import KeyAPI
from eth_keys.datatypes import PrivateKey


class BasicPacket:
private_key = None
public_key = None
packet = None
packet_header = None
packet_data = None
hash = None
signature = None
packet_type = None
expiration = int(time.time())

def __init__(self):
self.private_key = PrivateKey(secrets.token_bytes(32))
self.public_key = self.private_key.public_key
self.signature = KeyAPI().ecdsa_sign(
keccak(b"".join((self.packet_type, self.packet_data))), self.private_key
).to_bytes()
self.hash = keccak(b"".join((self.signature, self.packet_type, self.packet_data)))
self.packet_header = b"".join((self.hash, self.signature, self.packet_type))
self.packet = b"".join((self.packet_header, self.packet_data))

def to_bytes(self):
return b"".join((self.packet_header, self.packet_data))

@classmethod
def unpack(cls, packet):
if len(packet) < 98:
return False, "packet长度不足"
raw_hash = packet[:32]
new_hash = keccak(packet[32:])
if raw_hash != new_hash:
return False, "packet hash校验失败"
packet_type = packet[97]
if packet_type == 1:
return True, "Ping Packet"
elif packet_type == 2:
return True, "Pong Packet"
elif packet_type == 3:
return True, "FindNode Packet"
elif packet_type == 4:
return True, "Neighbors Packet"
else:
return False, "类型无法解析"


class PingPacket(BasicPacket):
packet_type = b'\x01'
sender_ip = None
sender_port = None
recipient_ip = None
recipient_port = None
version = 4

def __init__(self, sender_ip='localhost', sender_port=30303, recipient_ip='localhost', recipient_port=30303):
self.sender_ip = sender_ip
self.sender_port = sender_port
self.recipient_ip = recipient_ip
self.recipient_port = recipient_port
sender = [self.sender_ip, self.sender_port, 0]
recipient = [self.recipient_ip, self.recipient_port, 0]
before_packet_data = [self.version, sender, recipient, self.expiration]
self.packet_data = rlp.encode(before_packet_data)
super().__init__()


class PongPacket(BasicPacket):
packet_type = b'\x02'
recipient_ip = None
recipient_port = None
ping_hash = None

def __init__(self, ping_hash, recipient_ip='localhost', recipient_port=30303):
self.recipient_ip = recipient_ip
self.recipient_port = recipient_port
self.ping_hash = ping_hash
recipient = [self.recipient_ip, self.recipient_port, 0]
before_packet_data = [recipient, self.expiration]
self.packet_data = rlp.encode(before_packet_data)
super().__init__()


class FindNodePacket(BasicPacket):
packet_type = b'\x03'
target = None

def __init__(self, target):
self.target = target
before_packet_data = [self.target, self.expiration]
self.packet_data = rlp.encode(before_packet_data)
super().__init__()


class NeighborsPacket(BasicPacket):
packet_type = b'\x04'
nodes = None

def __init__(self, nodes=None):
if nodes is None:
nodes = []
before_packet_data = [self.nodes, self.expiration]
self.packet_data = rlp.encode(before_packet_data)
super().__init__()

server.py

1
2
3
4
5
6
7
8
9
10
11
12
from socket import *
from packet import PongPacket

HOST = 'localhost'
PORT = 30303
s = socket(AF_INET, SOCK_DGRAM)
s.bind((HOST, PORT))

data, address = s.recvfrom(1024)
ping_hash = data[:32]
pongPacket = PongPacket(ping_hash)
s.sendto(pongPacket.to_bytes(), address)

client.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from socket import *
from packet import PingPacket, BasicPacket

HOST = 'localhost'
PORT = 30303
address = (HOST, PORT)
s = socket(AF_INET, SOCK_DGRAM)

pingPacket = PingPacket()
s.sendto(pingPacket.to_bytes(), address)
data, _ = s.recvfrom(1024)
print(data)
success, message = BasicPacket.unpack(data)
print(success, message)

运行