Skip to content

Commit 1fc8c92

Browse files
Update peer.js
1 parent 2379631 commit 1fc8c92

File tree

1 file changed

+127
-193
lines changed

1 file changed

+127
-193
lines changed

lib/peer.js

+127-193
Original file line numberDiff line numberDiff line change
@@ -1,230 +1,164 @@
1-
var net = require('net');
2-
var crypto = require('crypto');
3-
var events = require('events');
4-
5-
var util = require('./util.js');
6-
7-
8-
//Example of p2p in node from TheSeven: http://paste.pm/e54.js
9-
10-
11-
var fixedLenStringBuffer = function(s, len) {
12-
var buff = new Buffer(len);
13-
buff.fill(0);
14-
buff.write(s);
15-
return buff;
16-
};
17-
18-
var commandStringBuffer = function (s) {
19-
return fixedLenStringBuffer(s, 12);
20-
};
1+
const net = require('net');
2+
const crypto = require('crypto');
3+
const { EventEmitter } = require('events');
4+
const util = require('./util.js');
5+
6+
class Peer extends EventEmitter {
7+
constructor(options) {
8+
super();
9+
this.options = options;
10+
this.client = null;
11+
this.verack = false;
12+
this.validConnectionConfig = true;
13+
this.magic = Buffer.from(options.testnet ? options.coin.peerMagicTestnet : options.coin.peerMagic, 'hex');
14+
this.magicInt = this.magic.readUInt32LE(0);
15+
this.commands = {
16+
version: this.commandStringBuffer('version'),
17+
inv: this.commandStringBuffer('inv'),
18+
verack: this.commandStringBuffer('verack'),
19+
addr: this.commandStringBuffer('addr'),
20+
getblocks: this.commandStringBuffer('getblocks')
21+
};
22+
this.networkServices = Buffer.from('0100000000000000', 'hex'); // NODE_NETWORK services (value 1 packed as uint64)
23+
this.emptyNetAddress = Buffer.from('010000000000000000000000000000000000ffff000000000000', 'hex');
24+
this.userAgent = util.varStringBuffer('/node-stratum/');
25+
this.blockStartHeight = Buffer.from('00000000', 'hex'); // block start_height, can be empty
26+
this.relayTransactions = options.p2p.disableTransactions ? Buffer.from([0]) : Buffer.from([1]);
2127

22-
/* Reads a set amount of bytes from a flowing stream, argument descriptions:
23-
- stream to read from, must have data emitter
24-
- amount of bytes to read
25-
- preRead argument can be used to set start with an existing data buffer
26-
- callback returns 1) data buffer and 2) lopped/over-read data */
27-
var readFlowingBytes = function (stream, amount, preRead, callback) {
28+
this.connect();
29+
}
2830

29-
var buff = preRead ? preRead : new Buffer([]);
31+
commandStringBuffer(s) {
32+
const buffer = Buffer.alloc(12);
33+
buffer.write(s);
34+
return buffer;
35+
}
3036

31-
var readData = function (data) {
32-
buff = Buffer.concat([buff, data]);
33-
if (buff.length >= amount) {
34-
var returnData = buff.slice(0, amount);
35-
var lopped = buff.length > amount ? buff.slice(amount) : null;
36-
callback(returnData, lopped);
37-
}
38-
else
39-
stream.once('data', readData);
40-
};
41-
42-
readData(new Buffer([]));
43-
};
44-
45-
var Peer = module.exports = function (options) {
46-
47-
var _this = this;
48-
var client;
49-
var magic = new Buffer(options.testnet ? options.coin.peerMagicTestnet : options.coin.peerMagic, 'hex');
50-
var magicInt = magic.readUInt32LE(0);
51-
var verack = false;
52-
var validConnectionConfig = true;
53-
54-
//https://en.bitcoin.it/wiki/Protocol_specification#Inventory_Vectors
55-
var invCodes = {
56-
error: 0,
57-
tx: 1,
58-
block: 2
59-
};
60-
61-
var networkServices = new Buffer('0100000000000000', 'hex'); //NODE_NETWORK services (value 1 packed as uint64)
62-
var emptyNetAddress = new Buffer('010000000000000000000000000000000000ffff000000000000', 'hex');
63-
var userAgent = util.varStringBuffer('/node-stratum/');
64-
var blockStartHeight = new Buffer('00000000', 'hex'); //block start_height, can be empty
65-
66-
//If protocol version is new enough, add do not relay transactions flag byte, outlined in BIP37
67-
//https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#extensions-to-existing-messages
68-
var relayTransactions = options.p2p.disableTransactions === true ? new Buffer([false]) : new Buffer([]);
69-
70-
var commands = {
71-
version: commandStringBuffer('version'),
72-
inv: commandStringBuffer('inv'),
73-
verack: commandStringBuffer('verack'),
74-
addr: commandStringBuffer('addr'),
75-
getblocks: commandStringBuffer('getblocks')
76-
};
77-
78-
79-
(function init() {
80-
Connect();
81-
})();
82-
83-
84-
function Connect() {
85-
86-
client = net.connect({
87-
host: options.p2p.host,
88-
port: options.p2p.port
89-
}, function () {
90-
SendVersion();
37+
async connect() {
38+
this.client = net.createConnection({
39+
host: this.options.p2p.host,
40+
port: this.options.p2p.port
41+
}, () => {
42+
this.sendVersion();
9143
});
92-
client.on('close', function () {
93-
if (verack) {
94-
_this.emit('disconnected');
95-
verack = false;
96-
Connect();
97-
}
98-
else if (validConnectionConfig)
99-
_this.emit('connectionRejected');
10044

45+
this.client.on('close', () => {
46+
if (this.verack) {
47+
this.emit('disconnected');
48+
this.verack = false;
49+
this.connect();
50+
} else if (this.validConnectionConfig) {
51+
this.emit('connectionRejected');
52+
}
10153
});
102-
client.on('error', function (e) {
103-
if (e.code === 'ECONNREFUSED') {
104-
validConnectionConfig = false;
105-
_this.emit('connectionFailed');
54+
55+
this.client.on('error', (err) => {
56+
if (err.code === 'ECONNREFUSED') {
57+
this.validConnectionConfig = false;
58+
this.emit('connectionFailed');
59+
} else {
60+
this.emit('socketError', err);
10661
}
107-
else
108-
_this.emit('socketError', e);
10962
});
11063

64+
this.setupMessageParser();
65+
}
11166

112-
SetupMessageParser(client);
67+
setupMessageParser() {
68+
let buffer = Buffer.alloc(0);
11369

114-
}
70+
this.client.on('data', (chunk) => {
71+
buffer = Buffer.concat([buffer, chunk]);
11572

116-
function SetupMessageParser(client) {
117-
118-
var beginReadingMessage = function (preRead) {
119-
120-
readFlowingBytes(client, 24, preRead, function (header, lopped) {
121-
var msgMagic = header.readUInt32LE(0);
122-
if (msgMagic !== magicInt) {
123-
_this.emit('error', 'bad magic number from peer');
124-
while (header.readUInt32LE(0) !== magicInt && header.length >= 4) {
125-
header = header.slice(1);
126-
}
127-
if (header.readUInt32LE(0) === magicInt) {
128-
beginReadingMessage(header);
129-
} else {
130-
beginReadingMessage(new Buffer([]));
131-
}
132-
return;
73+
while (buffer.length >= 24) {
74+
const msgMagic = buffer.readUInt32LE(0);
75+
if (msgMagic !== this.magicInt) {
76+
this.emit('error', 'bad magic number from peer');
77+
buffer = buffer.slice(1);
78+
continue;
13379
}
134-
var msgCommand = header.slice(4, 16).toString();
135-
var msgLength = header.readUInt32LE(16);
136-
var msgChecksum = header.readUInt32LE(20);
137-
readFlowingBytes(client, msgLength, lopped, function (payload, lopped) {
138-
if (util.sha256d(payload).readUInt32LE(0) !== msgChecksum) {
139-
_this.emit('error', 'bad payload - failed checksum');
140-
beginReadingMessage(null);
141-
return;
142-
}
143-
HandleMessage(msgCommand, payload);
144-
beginReadingMessage(lopped);
145-
});
146-
});
147-
};
14880

149-
beginReadingMessage(null);
150-
}
81+
const msgCommand = buffer.slice(4, 16).toString().replace(/\0+$/, '');
82+
const msgLength = buffer.readUInt32LE(16);
83+
const msgChecksum = buffer.readUInt32LE(20);
15184

85+
if (buffer.length < 24 + msgLength) break; // Check if the full message is buffered
15286

153-
//Parsing inv message https://en.bitcoin.it/wiki/Protocol_specification#inv
154-
function HandleInv(payload) {
155-
//sloppy varint decoding
156-
var count = payload.readUInt8(0);
157-
payload = payload.slice(1);
158-
if (count >= 0xfd)
159-
{
160-
count = payload.readUInt16LE(0);
161-
payload = payload.slice(2);
162-
}
163-
while (count--) {
164-
switch(payload.readUInt32LE(0)) {
165-
case invCodes.error:
166-
break;
167-
case invCodes.tx:
168-
var tx = payload.slice(4, 36).toString('hex');
169-
break;
170-
case invCodes.block:
171-
var block = payload.slice(4, 36).toString('hex');
172-
_this.emit('blockFound', block);
173-
break;
87+
const payload = buffer.slice(24, 24 + msgLength);
88+
buffer = buffer.slice(24 + msgLength);
89+
90+
if (util.sha256d(payload).readUInt32LE(0) !== msgChecksum) {
91+
this.emit('error', 'bad payload - failed checksum');
92+
continue;
93+
}
94+
95+
this.handleMessage(msgCommand, payload);
17496
}
175-
payload = payload.slice(36);
176-
}
97+
});
17798
}
17899

179-
function HandleMessage(command, payload) {
180-
_this.emit('peerMessage', {command: command, payload: payload});
100+
handleMessage(command, payload) {
101+
this.emit('peerMessage', { command, payload });
102+
181103
switch (command) {
182-
case commands.inv.toString():
183-
HandleInv(payload);
104+
case 'inv':
105+
this.handleInv(payload);
184106
break;
185-
case commands.verack.toString():
186-
if(!verack) {
187-
verack = true;
188-
_this.emit('connected');
189-
}
107+
case 'verack':
108+
this.verack = true;
109+
this.emit('connected');
190110
break;
191-
case commands.version.toString():
192-
SendMessage(commands.verack, Buffer.alloc(0));
193-
break;
194-
default:
111+
case 'version':
112+
this.sendMessage(this.commands.verack, Buffer.alloc(0));
195113
break;
196114
}
115+
}
197116

117+
handleInv(payload) {
118+
const count = payload.readUInt8(0);
119+
payload = payload.slice(1);
120+
for (let i = 0; i < count; i++) {
121+
const type = payload.readUInt32LE(i * 36);
122+
const hash = payload.slice(i * 36 + 4, i * 36 + 36).toString('hex');
123+
switch (type) {
124+
case 0: // ERROR
125+
break;
126+
case 1: // TX
127+
this.emit('txFound', hash);
128+
break;
129+
case 2: // BLOCK
130+
this.emit('blockFound', hash);
131+
break;
132+
}
133+
}
198134
}
199135

200-
//Message structure defined at: https://en.bitcoin.it/wiki/Protocol_specification#Message_structure
201-
function SendMessage(command, payload) {
202-
var message = Buffer.concat([
203-
magic,
136+
sendMessage(command, payload) {
137+
const message = Buffer.concat([
138+
this.magic,
204139
command,
205140
util.packUInt32LE(payload.length),
206141
util.sha256d(payload).slice(0, 4),
207142
payload
208143
]);
209-
client.write(message);
210-
_this.emit('sentMessage', message);
144+
this.client.write(message);
145+
this.emit('sentMessage', message);
211146
}
212147

213-
function SendVersion() {
214-
var payload = Buffer.concat([
215-
util.packUInt32LE(options.protocolVersion),
216-
networkServices,
217-
util.packInt64LE(Date.now() / 1000 | 0),
218-
emptyNetAddress, //addr_recv, can be empty
219-
emptyNetAddress, //addr_from, can be empty
220-
crypto.pseudoRandomBytes(8), //nonce, random unique ID
221-
userAgent,
222-
blockStartHeight,
223-
relayTransactions
148+
sendVersion() {
149+
const payload = Buffer.concat([
150+
util.packUInt32LE(this.options.protocolVersion),
151+
this.networkServices,
152+
util.packInt64LE(Math.floor(Date.now() / 1000)),
153+
this.emptyNetAddress,
154+
this.emptyNetAddress,
155+
crypto.randomBytes(8),
156+
this.userAgent,
157+
this.blockStartHeight,
158+
this.relayTransactions
224159
]);
225-
SendMessage(commands.version, payload);
160+
this.sendMessage(this.commands.version, payload);
226161
}
162+
}
227163

228-
};
229-
230-
Peer.prototype.__proto__ = events.EventEmitter.prototype;
164+
module.exports = Peer;

0 commit comments

Comments
 (0)