163 lines
5.2 KiB
JavaScript
163 lines
5.2 KiB
JavaScript
var net = require('net');
|
|
var crypto = require('crypto');
|
|
var events = require('events');
|
|
|
|
var util = require('./util.js');
|
|
|
|
|
|
var Peer = module.exports = function(options){
|
|
|
|
var _this = this;
|
|
var client;
|
|
var magic = new Buffer(options.magic, 'hex');
|
|
var magicInt = magic.readUInt32LE(0);
|
|
|
|
//https://en.bitcoin.it/wiki/Protocol_specification#Inventory_Vectors
|
|
var invVectMsgBlock = 2; //Hash from inventory message is related to a data block
|
|
|
|
var networkServices = new Buffer('0100000000000000', 'hex'); //NODE_NETWORK services (value 1 packed as uint64)
|
|
var emptyNetAddress = new Buffer('010000000000000000000000000000000000ffff000000000000', 'hex');
|
|
var userAgent = util.varStringBuffer('/node-stratum/');
|
|
var blockStartHeight = new Buffer('00000000', 'hex'); //block start_height, can be empty
|
|
var relayTransactions = options.protocolVersion >= 70001 ? new Buffer([false]) : new Buffer([]);
|
|
|
|
//Command strings found at: https://en.bitcoin.it/wiki/Protocol_specification
|
|
var commands = {
|
|
version: new Buffer('76657273696F6E0000000000', 'hex')
|
|
};
|
|
|
|
|
|
(function init(){
|
|
Connect();
|
|
})();
|
|
|
|
|
|
function Connect(){
|
|
|
|
client = net.connect(options.host, options.port, function(){
|
|
_this.emit('connected');
|
|
SendVersion();
|
|
});
|
|
client.on('end', function(){
|
|
_this.emit('disconnected');
|
|
Connect();
|
|
});
|
|
client.on('error', function(){
|
|
_this.emit('connectionFailed');
|
|
});
|
|
|
|
|
|
SetupMessageParser(client);
|
|
|
|
|
|
}
|
|
|
|
function SetupMessageParser(client){
|
|
var buff = new Buffer([]);
|
|
var msgCommand, msgLength, msgChecksum, readingPayload = false;
|
|
|
|
var readData = function(data){
|
|
buff = Buffer.concat([buff, data], 2);
|
|
if (!readingPayload){
|
|
if (buff.length < 24) return;
|
|
var msgMagic = buff.readUInt32LE(0);
|
|
msgCommand = buff.slice(4, 16).toString().split("\0", 1)[0];
|
|
msgLength = buff.readUInt32LE(16);
|
|
msgChecksum = buff.readUInt32LE(20);
|
|
if (msgMagic !== magicInt){
|
|
buff = new Buffer([]);
|
|
_this.emit('error', 'bad magic number from peer');
|
|
return;
|
|
}
|
|
readingPayload = true;
|
|
if (buff.length > 24){
|
|
var d = buff.slice(24);
|
|
buff = new Buffer([]);
|
|
readData(d);
|
|
}
|
|
else
|
|
buff = new Buffer([]);
|
|
}
|
|
else{
|
|
if (buff.length >= msgLength){
|
|
|
|
var msgPayload = buff.slice(0, msgLength);
|
|
if (util.doublesha(msgPayload).readUInt32LE(0) !== msgChecksum){
|
|
_this.emit('error', 'bad payload checksum from peer');
|
|
}
|
|
else{
|
|
HandleMessage(msgCommand, msgPayload);
|
|
}
|
|
|
|
if (buff.length > msgLength){
|
|
var d = buff.slice(msgLength);
|
|
buff = new Buffer([]);
|
|
readingPayload = false;
|
|
readData(d);
|
|
}
|
|
else{
|
|
buff = new Buffer([]);
|
|
readingPayload = false;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
client.on('data', readData);
|
|
|
|
}
|
|
|
|
function HandleMessage(command, payload){
|
|
|
|
//Parsing inv message https://en.bitcoin.it/wiki/Protocol_specification#inv
|
|
if (command === 'inv'){
|
|
//sloppy varint decoding
|
|
var count = payload.readUInt8(0);
|
|
payload = payload.slice(1);
|
|
if (count >= 0xfd)
|
|
{
|
|
count = payload.readUInt16LE(0);
|
|
payload = payload.slice(2);
|
|
}
|
|
while (count--)
|
|
{
|
|
if (payload.readUInt32LE(0) === invVectMsgBlock){
|
|
var block = payload.slice(4, 36).toString('hex');
|
|
console.log('block found ' + block);
|
|
_this.emit('blockFound', block);
|
|
}
|
|
payload = payload.slice(36);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Message structure defined at: https://en.bitcoin.it/wiki/Protocol_specification#Message_structure
|
|
function SendMessage(command, payload){
|
|
var message = Buffer.concat([
|
|
magic,
|
|
command,
|
|
util.packUInt32LE(payload.length),
|
|
util.doublesha(payload).slice(0, 4),
|
|
payload
|
|
]);
|
|
client.write(message);
|
|
}
|
|
|
|
function SendVersion(){
|
|
var payload = Buffer.concat([
|
|
util.packUInt32LE(options.protocolVersion),
|
|
networkServices,
|
|
util.packUInt32LE(Date.now() / 1000 | 0),
|
|
emptyNetAddress, //addr_recv, can be empty
|
|
emptyNetAddress, //addr_from, can be empty
|
|
crypto.pseudoRandomBytes(8), //nonce, random unique ID
|
|
userAgent,
|
|
blockStartHeight,
|
|
relayTransactions
|
|
]);
|
|
SendMessage(commands.version, payload);
|
|
}
|
|
|
|
};
|
|
|
|
Peer.prototype.__proto__ = events.EventEmitter.prototype; |