Skip to content

Commit 0b26f23

Browse files
committed
Further progress on handshake - fixed various bugs in multiple locations
1 parent cd8a886 commit 0b26f23

24 files changed

+405
-193
lines changed

exampels/connectToLocal.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Peer, KeyPair, parseEncode, MessageType } from '../dist';
1+
import { Peer, KeyPair, parseEncode, MessageType, NodeServer } from '../dist';
22

33
(async () => {
44
const node = new Peer(
@@ -9,6 +9,9 @@ import { Peer, KeyPair, parseEncode, MessageType } from '../dist';
99
'0a04fa0107c51d2b9fa4504e220537f1a3aaf287cfcd5a66b8c2c8272fd8029a'
1010
)
1111
);
12+
const server = new NodeServer(
13+
'0a04fa0107c51d2b9fa4504e220537f1a3aaf287cfcd5a66b8c2c8272fd8029a'
14+
);
1215
await node.connect(
1316
parseEncode(
1417
'enode://565201cf682f2e62fc03173098e39e72ca49cb28beef29e956b480763150565be0471c39bccc8ffb4d8684e658034c3e7a93d315f57a42e82506bb29a973273e@localhost:30303'

exampels/connectToPeer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { Peer, enocdes, MessageType, parseEncode } from '../dist';
1212
});
1313
*/
1414
await node.sendMessage({
15-
type: MessageType.AUTH,
15+
type: MessageType.AUTH_EIP_8,
1616
});
1717
})
1818
);

jest.config.int.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// eslint-disable-next-line no-undef
22
module.exports = {
3-
testRegex: ['.*.int.test.js'],
3+
testRegex: [".*.int.test.ts"],
44
transform: {
5-
'\\.(js|ts|jsx|tsx)$': 'babel-jest',
5+
"\\.(js|ts|jsx|tsx)$": "babel-jest",
66
},
7-
// verbose: true,
7+
// verbose: true,
88
};

jest.config.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module.exports = {
2-
testRegex: ['.*.unit.test.js'],
2+
testRegex: [".*.unit.test.js"],
33
transform: {
4-
"\\.(js|ts|jsx|tsx)$": "babel-jest",
4+
'\\.(js|ts|jsx|tsx)$': 'babel-jest',
55
},
66
// verbose: true,
77
};

src/network/Packet.ts

+57-15
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,21 @@ import {
2121
import { ReadOutRlp } from '../rlp/ReadOutRlp';
2222
import ip6addr from 'ip6addr';
2323
import { convertNumberToPadHex } from './convertNumberToPadHex';
24+
import { getBufferFromHex } from './getBufferFromHex';
25+
import BigNumber from 'bignumber.js';
2426

2527
export class Packet {
28+
public parse({ packet }: { packet: Buffer }) {
29+
const packetId = packet[0];
30+
// falsy values are parsed as 0x80 in RLP
31+
const parsedPacketId = packetId === 0x80 ? 0 : packetId;
32+
if (parsedPacketId === PacketTypes.HELLO) {
33+
return this.decodeHello({ input: packet });
34+
} else {
35+
throw new Error(`Unknown packet (${parsedPacketId})`);
36+
}
37+
}
38+
2639
public encodePing(_input: PingPacket): string {
2740
/*
2841
packet-data = [4, from, to, expiration, enr-seq ...]
@@ -50,33 +63,58 @@ export class Packet {
5063
});
5164
}
5265

66+
// TODO Moves this into an encoder / decoder class
5367
public decodeHello({ input }: { input: Buffer }): ParsedHelloPacket {
5468
const data = new RlpDecoder().decode({ input: input.toString('hex') });
5569
const rlpReader = new ReadOutRlp(data);
5670

71+
console.log(`HELLO PACKET ${input.toString('hex')}`);
72+
73+
console.log(data);
74+
75+
// protocol version
76+
const [protocolVersion] = rlpReader.readArray<number>({
77+
length: 1,
78+
isFlat: true,
79+
});
5780
const [client] = rlpReader.readArray<string>({
58-
skip: 1,
5981
length: 1,
82+
isFlat: true,
6083
});
61-
62-
const [version] = rlpReader.readArray<number>({
63-
length: 2,
64-
isNumeric: true,
65-
valueFetcher: (item) => {
66-
if (Array.isArray(item) && Array.isArray(item[1])) {
67-
return [item[1][1] as number];
68-
} else {
69-
throw new Error('Missing data');
70-
}
71-
},
84+
const capabilities = rlpReader.readArray<string[]>({
85+
length: 1,
86+
isFlat: true,
87+
});
88+
const [listenPort] = rlpReader.readArray<number | boolean>({
89+
length: 1,
90+
isFlat: true,
91+
});
92+
const [nodeId] = rlpReader.readArray<string>({
93+
length: 1,
7294
});
73-
7495
return {
96+
protocolVersion,
97+
capabilities,
98+
listenPort: typeof listenPort === 'boolean' ? 0 : listenPort,
7599
userAgent: client,
76-
version,
100+
nodeId,
101+
// version: capabilities[0][0] as unknown as number,
77102
};
78103
}
79104

105+
public encodeHello({ packet }: { packet: ParsedHelloPacket }) {
106+
const helloPacket = new RlpEncoder().encode({
107+
input: [
108+
packet.protocolVersion,
109+
packet.userAgent,
110+
packet.capabilities,
111+
packet.listenPort,
112+
getBufferFromHex(packet.nodeId),
113+
],
114+
});
115+
return Buffer.concat([Buffer.from([0x80]), getBufferFromHex(helloPacket)]);
116+
}
117+
80118
public decodePing({
81119
input,
82120
}: {
@@ -137,6 +175,7 @@ export class Packet {
137175
}
138176

139177
export enum PacketTypes {
178+
HELLO = 0x0,
140179
PING = 1,
141180
PONG = 2,
142181
FIND_NODE = 3,
@@ -145,8 +184,11 @@ export enum PacketTypes {
145184
}
146185

147186
interface ParsedHelloPacket {
148-
version: number;
149187
userAgent: string | number;
188+
capabilities: string[][];
189+
protocolVersion: number;
190+
listenPort: number;
191+
nodeId: string;
150192
}
151193

152194
interface ParsedPacket {

src/network/Packets.unit.test.ts

+42-5
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ import { cleanString } from '../utils';
22
import { getBufferFromHex } from './getBufferFromHex';
33
import { Packet, PacketTypes } from './Packet';
44
import ip6addr from 'ip6addr';
5+
import { decode } from '@ethereumjs/devp2p';
6+
import { HelpOutline } from '@mui/icons-material';
57

68
describe('Packets', () => {
79
// from https://github.com/ethereum/go-ethereum/pull/2091/files#diff-a2488b7a37555bfb5c64327072acdbbf703ab127176956f6b6558067950f8f73R455
810

911
it('should correctly decode hello packet', () => {
1012
// from https://eips.ethereum.org/EIPS/eip-8
11-
const nodeKey = getBufferFromHex(
12-
'b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291'
13-
);
1413
const helloPacket = getBufferFromHex(
1514
cleanString(`
1615
f87137916b6e6574682f76302e39312f706c616e39cdc5836574683dc6846d6f726b1682270fb840
@@ -20,7 +19,33 @@ describe('Packets', () => {
2019
);
2120

2221
const decodedPacket = new Packet().decodeHello({ input: helloPacket });
23-
expect(decodedPacket.version).toBe(22);
22+
//expect(decodedPacket.version).toBe(22);
23+
expect(decodedPacket.nodeId).toBe(
24+
'0xfda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877'
25+
);
26+
});
27+
28+
it('should correctly decode hello packet from geth', () => {
29+
const helloPacket = getBufferFromHex(
30+
cleanString(`
31+
80f88305b0476574682f76312e31302e31372d737461626c652d32356339623439662f6c696e75782d616d6436342f676f312e3138cdc58365746842c684736e61700180b840565201cf682f2e62fc03173098e39e72ca49cb28beef29e956b480763150565be0471c39bccc8ffb4d8684e658034c3e7a93d315f57a42e82506bb29a973273e `)
32+
);
33+
34+
const decodedPacket = new Packet().decodeHello({ input: helloPacket });
35+
expect(decodedPacket.userAgent).toBe(
36+
'Geth/v1.10.17-stable-25c9b49f/linux-amd64/go1.18'
37+
);
38+
expect(decodedPacket.protocolVersion).toBe(5);
39+
expect(decodedPacket.capabilities.toString()).toBe(
40+
[
41+
['eth', 66],
42+
['snap', 1],
43+
].toString()
44+
);
45+
expect(decodedPacket.listenPort).toBe(0);
46+
expect(decodedPacket.nodeId).toBe(
47+
'0x565201cf682f2e62fc03173098e39e72ca49cb28beef29e956b480763150565be0471c39bccc8ffb4d8684e658034c3e7a93d315f57a42e82506bb29a973273e'
48+
);
2449
});
2550

2651
it('should correctly decode a ping packet', () => {
@@ -50,7 +75,7 @@ describe('Packets', () => {
5075
expect(decodedPacket.toTcpPort).toBe(3333);
5176
});
5277

53-
it.only('should correctly encode a ping packet', () => {
78+
it('should correctly encode a ping packet', () => {
5479
// https://github.com/ethereum/devp2p/blob/master/discv4.md#ping-packet-0x01
5580
const packet = new Packet().encodePing({
5681
version: 4,
@@ -166,4 +191,16 @@ dd7fc0c04ad9ebf3919644c91cb247affc82b69bd2ca235c71eab8e49737c937a2c396
166191
expect(decodedPacket.nodes.length).toBe(4);
167192
expect(decodedPacket.nodes[0].ip).toBeTruthy();
168193
});
194+
195+
it('should correctly create an hello packet', () => {
196+
const helloPacket = getBufferFromHex(
197+
cleanString(`
198+
80f88305b0476574682f76312e31302e31372d737461626c652d32356339623439662f6c696e75782d616d6436342f676f312e3138cdc58365746842c684736e61700180b840565201cf682f2e62fc03173098e39e72ca49cb28beef29e956b480763150565be0471c39bccc8ffb4d8684e658034c3e7a93d315f57a42e82506bb29a973273e `)
199+
);
200+
201+
const decodedPacket = new Packet().decodeHello({ input: helloPacket });
202+
203+
const packet = new Packet().encodeHello({ packet: decodedPacket });
204+
expect(packet.toString('hex')).toBe(helloPacket.toString('hex'));
205+
});
169206
});

src/network/Peer.int.test.ts

-23
This file was deleted.

src/network/Peer.ts

+44-21
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
import { encode } from '@ethereumjs/devp2p';
12
import { ThirtyFpsSharp } from '@mui/icons-material';
23
import net from 'net';
4+
import { RlpEncoder } from '../rlp/RlpEncoder';
35
import { KeyPair } from '../signatures/KeyPair';
46
import { FrameCommunication } from './auth/FrameCommunication';
57
import { Auth8Eip } from './AuthEip8';
68
import { getBufferFromHex } from './getBufferFromHex';
79
import { getRandomPeer } from './getRandomPeer';
10+
import { CryptoNonceGenerator } from './nonce-generator/CryptoNonceGenerator';
11+
import { NonceGenerator } from './nonce-generator/NonceGenerator';
12+
import { Packet, PacketTypes } from './Packet';
813
import { Rlpx } from './Rlpx';
914
import { AbstractSocket } from './socket/AbstractSocket';
1015

@@ -30,11 +35,14 @@ export class Peer {
3035
constructor(
3136
private keyPair = new KeyPair(),
3237
private ephemeralKeyPair = new KeyPair(),
33-
private socket: AbstractSocket = new net.Socket() as AbstractSocket
38+
private socket: AbstractSocket = new net.Socket() as AbstractSocket,
39+
private randomNumberGenerator: NonceGenerator = new CryptoNonceGenerator()
3440
) {
3541
this.rlpx = new Rlpx(
3642
this.keyPair,
37-
Buffer.from(this.ephemeralKeyPair.privatekey, 'hex')
43+
Buffer.from(this.ephemeralKeyPair.privatekey, 'hex'),
44+
new RlpEncoder(),
45+
this.randomNumberGenerator
3846
);
3947
this.auth8Eip = new Auth8Eip(this.rlpx);
4048
}
@@ -102,13 +110,9 @@ export class Peer {
102110
}
103111

104112
public async sendMessage(message: MessageOptions) {
105-
if ([MessageType.AUTH, MessageType.AUTH_EIP_8].includes(message.type)) {
106-
const rlpx = new Rlpx(
107-
this.keyPair,
108-
Buffer.from(this.ephemeralKeyPair.privatekey, 'hex')
109-
);
113+
if (MessageType.AUTH_EIP_8 === message.type) {
110114
const { results: authMessage, header } =
111-
await rlpx.createEncryptedAuthMessageEip8({
115+
await this.rlpx.createEncryptedAuthMessageEip8({
112116
ethNodePublicKey: this.nodePublicKey,
113117
});
114118

@@ -121,6 +125,9 @@ export class Peer {
121125
);
122126

123127
await this.connectionWrite(authMessage);
128+
} else if (MessageType.HELLO === message.type) {
129+
console.log('hello ser');
130+
throw new Error('nono, please go in order ser');
124131
} else {
125132
throw new Error(`Unknown message type${message.type}`);
126133
}
@@ -145,26 +152,42 @@ export class Peer {
145152
this._senderNonce,
146153
getBufferFromHex(nonce)
147154
).setup({
148-
secret: this._secret,
149-
150-
remoteNonce: getBufferFromHex(nonce),
151155
remotePacket: message,
152-
153156
initiatorPacket: this.sentPacket,
154-
initiatorNonce: this._senderNonce,
155157
});
156-
console.log(':)');
157158
} else {
158-
console.log('hello ? what is this ser?');
159-
console.log(
160-
this.frameCommunication?.parse({
161-
message,
162-
})
163-
);
159+
if (!this.frameCommunication) {
160+
throw new Error('Missing frame communicator');
161+
}
162+
const body = this.frameCommunication.parse({
163+
message,
164+
});
165+
const packetParser = new Packet();
166+
const hello = packetParser.parse({
167+
packet: body,
168+
});
169+
/** Todo run some validation here maybe ? */
170+
console.log('Trying to say hello');
171+
/*await this.sendMessage({
172+
type: MessageType.HELLO,
173+
});
174+
*/
175+
const heloMessage = new Packet().encodeHello({
176+
packet: {
177+
...hello,
178+
nodeId: `0x${this.keyPair.getPublicKey()}`,
179+
},
180+
});
181+
const encodedMessage = this.frameCommunication.encode({
182+
message: heloMessage,
183+
});
184+
await this.connectionWrite(encodedMessage);
164185
}
165186
}
166187

167188
private async connectionWrite(message: Buffer) {
189+
console.log(`writing on the wire SER, ${message.length}`);
190+
console.log(` ${message.toString('hex')}`);
168191
await new Promise<void>((resolve, reject) => {
169192
this.connection.write(message, (error) => {
170193
if (error) {
@@ -195,7 +218,7 @@ type MessageOptions = BaseMessage;
195218

196219
export enum MessageType {
197220
AUTH_EIP_8,
198-
AUTH,
221+
HELLO,
199222
}
200223
interface BaseMessage {
201224
type: MessageType;

0 commit comments

Comments
 (0)