Skip to content

Commit

Permalink
Merge pull request #48 from braydonf/feature/extensibility
Browse files Browse the repository at this point in the history
Extensible Messages
  • Loading branch information
maraoz committed Mar 17, 2015
2 parents 826d6c4 + 34c3846 commit 487b5f0
Show file tree
Hide file tree
Showing 51 changed files with 3,004 additions and 1,388 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<img src="http://bitcore.io/css/images/module-p2p.png" alt="bitcore payment protocol" height="35">
P2P Networking capabilities for bitcore
<img src="http://bitcore.io/css/images/bitcore-p2p.svg" alt="bitcore payment protocol" height="35" width="102">
Bitcore P2P
=======

[![NPM Package](https://img.shields.io/npm/v/bitcore-p2p.svg?style=flat-square)](https://www.npmjs.org/package/bitcore-p2p)
[![Build Status](https://img.shields.io/travis/bitpay/bitcore-p2p.svg?branch=master&style=flat-square)](https://travis-ci.org/bitpay/bitcore-p2p)
[![Coverage Status](https://img.shields.io/coveralls/bitpay/bitcore-p2p.svg?style=flat-square)](https://coveralls.io/r/bitpay/bitcore-p2p?branch=master)

bitcore-p2p adds support for connecting to the bitcoin p2p network in [Node.js](http://nodejs.org/).
`bitcore-p2p` adds [Bitcoin protocol](https://en.bitcoin.it/wiki/Protocol_documentation) support for Bitcore.

See [the main bitcore repo](https://github.com/bitpay/bitcore) for more information.

Expand All @@ -15,12 +15,12 @@ See [the main bitcore repo](https://github.com/bitpay/bitcore) for more informat
```sh
npm install bitcore-p2p
```
In order to connect to the bitcore network, you'll need to know the IP address of at least one node of the network. You can do that by using the known DNS servers. Then, you can connect to it:
In order to connect to the Bitcoin network, you'll need to know the IP address of at least one node of the network, or use [Pool](/docs/pool.md) to discover peers using a DNS seed.

```javascript
var Peer = require('bitcore-p2p').Peer;

var peer = new Peer('0.0.0.0');
var peer = new Peer({host: '127.0.0.1'});

peer.on('ready', function() {
// peer info
Expand Down Expand Up @@ -55,4 +55,3 @@ See [CONTRIBUTING.md](https://github.com/bitpay/bitcore) on the main bitcore rep
Code released under [the MIT license](https://github.com/bitpay/bitcore/blob/master/LICENSE).

Copyright 2013-2015 BitPay, Inc. Bitcore is a trademark maintained by BitPay, Inc.

37 changes: 37 additions & 0 deletions docs/messages.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,47 @@
---
title: Messages
description: A superclass for the messages of the bitcoin network
---
# Messages

The bitcoin protocol specifies a set of [messages](https://en.bitcoin.it/wiki/Protocol_specification) that can be sent from peer to peer. `bitcore-p2p` provides support for some of these messages.

To create a message, you can use any of the message constructors, here is a simple example:

```javascript
var messages = new Messages();
var message = messages.Ping();
```

There are also several convenient helpers for inventory based messages:

```javascript
message = messages.GetData.forTransaction(txHash);
message = messages.GetData.forBlock(blockHash);
message = messages.Inventory.forTransaction(txHash);
```

As well as sending "tx" and "block" messages with Bitcore instances:

```javascript
message = messages.Block(block);
message = messages.Transaction(transaction);
```

Note: A list of further messages is available below.

For advanced usage, you can also customize which constructor is used for Block and Transaction messages by passing it as an argument to Messages, for example:

```javascript
var messages = new Messages({Block: MyBlock, Transaction: MyTransaction});
```

And additionally custom network magic:

```javascript
var messages = new Messages({magicNumber: 0x0b120907});
```

## List of Messages

### Version
Expand Down
17 changes: 9 additions & 8 deletions docs/peer.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
title: Peer
description: The Peer class provides a simple interface for connecting to a node in the bitcoin network.
---
Expand All @@ -15,15 +16,15 @@ The code to create a new peer looks like this:
var Peer = require('bitcore-p2p').Peer;

// default port
var livenetPeer = new Peer('5.9.85.34');
var testnetPeer = new Peer('5.9.85.34', bitcore.testnet);
var livenetPeer = new Peer({host: '5.9.85.34'});
var testnetPeer = new Peer({host: '5.9.85.34', network: Networks.testnet});

// custom port
var livenetPeer = new Peer('5.9.85.34', 8334);
var testnetPeer = new Peer('5.9.85.34', 18334, bitcore.testnet);
var livenetPeer = new Peer({host: '5.9.85.34', port: 8334});
var testnetPeer = new Peer({host: '5.9.85.34', port: 18334, network: Networks.testnet});

// use sock5 proxy (Tor)
var peer = new Peer('5.9.85.34').setProxy('localhost', 9050);
var peer = new Peer({host: '5.9.85.34'}).setProxy('localhost', 9050);
```

## States
Expand All @@ -40,7 +41,7 @@ You can subscribe to the change of those states as follows:
```javascript
var Peer = require('bitcore-p2p').Peer;

var peer = new Peer('5.9.85.34');
var peer = new Peer({host: '5.9.85.34'});

peer.on('ready', function() {
// peer info
Expand All @@ -60,7 +61,7 @@ Once connected, a peer instance can send and receive messages. Every time a mess

```javascript
var Peer = require('bitcore-p2p').Peer;
var peer = new Peer('5.9.85.34');
var peer = new Peer({host: '5.9.85.34'});

// handle events
peer.on('inv', function(message) {
Expand Down Expand Up @@ -88,7 +89,7 @@ An example for requesting other connected nodes to a peers looks like this:
var p2p = require('bitcore-p2p')
var Peer = p2p.Peer;
var Messages = p2p.Messages;
var peer = new Peer('5.9.85.34');
var peer = new Peer({host: '5.9.85.34'});

peer.on('ready', function() {
var message = new Messages.GetAddresses();
Expand Down
17 changes: 15 additions & 2 deletions docs/pool.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
title: Pool
description: A simple interface to create and maintain a set of connections to bitcoin nodes.
---
Expand All @@ -12,7 +13,7 @@ The quickest way to get connected is to run the following:
var Pool = require('bitcore-p2p').Pool;
var Networks = require('bitcore').Networks;

var pool = new Pool(Networks.livenet);
var pool = new Pool({network: Networks.livenet});

// connect to the network
pool.connect();
Expand All @@ -35,7 +36,8 @@ By default, peers will be added via DNS discovery and as peers are announced in

```javascript

var pool = new Pool(Networks.livenet, {
var pool = new Pool({
network: Networks.livenet, // the network object
dnsSeed: false, // prevent seeding with DNS discovered known peers upon connecting
listenAddr: false, // prevent new peers being added from addr messages
addrs: [ // initial peers to connect to
Expand All @@ -50,3 +52,14 @@ var pool = new Pool(Networks.livenet, {
pool.connect();

```

## Listening for Peers

It's also possible to listen to incoming socket connections to add peers to the pool. To enable this capability, you can do the following:

```javascript
var pool = new Pool({network: Networks.livenet});
pool.listen();
```

When there are incoming connections the peer will be added to the pool.
59 changes: 32 additions & 27 deletions integration/bitcoind.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ var Peer = p2p.Peer;
var Pool = p2p.Pool;
var Networks = bitcore.Networks;
var Messages = p2p.Messages;
var Inventory = p2p.Inventory;
var messages = new Messages();
var Block = bitcore.Block;
var Transaction = bitcore.Transaction;

Expand Down Expand Up @@ -45,7 +47,7 @@ describe('Integration with ' + network.name + ' bitcoind', function() {
m.services.toString().should.equal('1');
Math.abs(new Date() - m.timestamp).should.be.below(10000); // less than 10 seconds of time difference
m.nonce.length.should.equal(8);
m.start_height.should.be.above(300000);
m.startHeight.should.be.above(300000);
cb();
});
peer.once('verack', function(m) {
Expand Down Expand Up @@ -92,7 +94,7 @@ describe('Integration with ' + network.name + ' bitcoind', function() {
});
cb();
});
var message = new Messages.GetAddresses();
var message = messages.GetAddr();
peer.sendMessage(message);
});
});
Expand All @@ -106,25 +108,24 @@ describe('Integration with ' + network.name + ' bitcoind', function() {
should.exist(message.transaction);
cb();
});
peer.once('inv', function(m) {
var message = new Messages.GetData(m.inventory);
peer.sendMessage(message);
peer.once('inv', function(message) {
var get = messages.GetData(message.inventory);
peer.sendMessage(get);
});
});
});
it('sends tx inv and receives getdata for that tx', function(cb) {
connect(function(peer) {
var type = Messages.Inventory.TYPE.TX;
var type = Inventory.TYPE.TX;
var inv = [{
type: type,
typeName: Messages.Inventory.TYPE_NAME[type],
hash: Random.getRandomBuffer(32) // needs to be random for repeatability
hash: new Buffer(Random.getRandomBuffer(32)) // needs to be random for repeatability
}];
peer.once('getdata', function(message) {
message.inventory.should.deep.equal(inv);
cb();
});
var message = new Messages.Inventory(inv);
var message = messages.Inventory(inv);
message.inventory[0].hash.length.should.equal(32);
peer.sendMessage(message);
});
Expand All @@ -135,20 +136,22 @@ describe('Integration with ' + network.name + ' bitcoind', function() {
(message.block instanceof Block).should.equal(true);
cb();
});
var message = Messages.GetData.forBlock(blockHash[network.name]);
var message = messages.GetData.forBlock(blockHash[network.name]);
peer.sendMessage(message);
});
});
var fakeHash = 'e2dfb8afe1575bfacae1a0b4afc49af7ddda69285857267bae0e22be15f74a3a';
it('handles request tx data not found', function(cb) {
connect(function(peer) {
var expected = Messages.NotFound.forTransaction(fakeHash);
var expected = messages.NotFound.forTransaction(fakeHash);
peer.once('notfound', function(message) {
(message instanceof Messages.NotFound).should.equal(true);
message.should.deep.equal(expected);
(message instanceof messages.NotFound).should.equal(true);
message.inventory[0].type.should.equal(Inventory.TYPE.TX);
var expectedHash = expected.inventory[0].hash.toString('hex');
message.inventory[0].hash.toString('hex').should.equal(expectedHash);
cb();
});
var message = Messages.GetData.forTransaction(fakeHash);
var message = messages.GetData.forTransaction(fakeHash);
peer.sendMessage(message);
});
});
Expand All @@ -157,47 +160,49 @@ describe('Integration with ' + network.name + ' bitcoind', function() {
it('gets headers', function(cb) {
connect(function(peer) {
peer.once('headers', function(message) {
(message instanceof Messages.Headers).should.equal(true);
(message instanceof messages.Headers).should.equal(true);
message.headers.length.should.equal(3);
cb();
});
var message = new Messages.GetHeaders(from, stop);
var message = messages.GetHeaders({starts: from, stop: stop});
peer.sendMessage(message);
});
});
it('gets blocks', function(cb) {
connect(function(peer) {
peer.once('inv', function(message) {
(message instanceof Messages.Inventory).should.equal(true);
message.command.should.equal('inv');
if (message.inventory.length === 2) {
message.inventory[0].type.should.equal(Messages.Inventory.TYPE.BLOCK);
message.inventory[0].type.should.equal(Inventory.TYPE.BLOCK);
cb();
}
});
var message = new Messages.GetBlocks(from, stop);
var message = messages.GetBlocks({starts: from, stop: stop});
peer.sendMessage(message);
});
});
var testInvGetData = function(expected, message, cb) {
connect(function(peer) {
peer.once('getdata', function(message) {
(message instanceof Messages.GetData).should.equal(true);
message.should.deep.equal(expected);
message.command.should.equal('getdata');
message.inventory[0].type.should.equal(expected.inventory[0].type);
var expectedHash = expected.inventory[0].hash.toString('hex');
message.inventory[0].hash.toString('hex').should.equal(expectedHash);
cb();
});
peer.sendMessage(message);
});
};
it('sends block inv and receives getdata', function(cb) {
var randomHash = Random.getRandomBuffer(32); // needs to be random for repeatability
var expected = Messages.GetData.forBlock(randomHash);
var message = Messages.Inventory.forBlock(randomHash);
var randomHash = new Buffer(Random.getRandomBuffer(32)); // slow buffer
var expected = messages.GetData.forBlock(randomHash);
var message = messages.Inventory.forBlock(randomHash);
testInvGetData(expected, message, cb);
});
it('sends tx inv and receives getdata', function(cb) {
var randomHash = Random.getRandomBuffer(32); // needs to be random for repeatability
var expected = Messages.GetData.forTransaction(randomHash);
var message = Messages.Inventory.forTransaction(randomHash);
var randomHash = new Buffer(Random.getRandomBuffer(32)); // slow buffer
var expected = messages.GetData.forTransaction(randomHash);
var message = messages.Inventory.forTransaction(randomHash);
testInvGetData(expected, message, cb);
});
});
41 changes: 18 additions & 23 deletions lib/bloomfilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,29 @@ var bitcore = require('bitcore');
var BloomFilter = require('bloom-filter');
var BufferReader = bitcore.encoding.BufferReader;
var BufferWriter = bitcore.encoding.BufferWriter;
var $ = bitcore.util.preconditions;


/**
* A constructor for Bloom Filters
* @see https://github.com/bitpay/bloom-filter
* @param {Buffer} - payload
*/
BloomFilter.fromBuffer = function fromBuffer(payload) {
var obj = {};
var parser = new BufferReader(payload);
var data = parser.readVarLengthBuffer();
$.checkState(data.length <= BloomFilter.MAX_BLOOM_FILTER_SIZE,
'Filter data must be <= MAX_BLOOM_FILTER_SIZE bytes');
var nHashFuncs = parser.readUInt32LE();
$.checkState(nHashFuncs <= BloomFilter.MAX_HASH_FUNCS,
'Filter nHashFuncs must be <= MAX_HASH_FUNCS');
var nTweak = parser.readUInt32LE();
var nFlags = parser.readUInt8();

var vData = [];
var dataParser = new BufferReader(data);
for(var i = 0; i < data.length; i++) {
vData.push(dataParser.readUInt8());
var length = parser.readUInt8();
obj.vData = [];
for(var i = 0; i < length; i++) {
obj.vData.push(parser.readUInt8());
}
obj.nHashFuncs = parser.readUInt32LE();
obj.nTweak = parser.readUInt32LE();
obj.nFlags = parser.readUInt8();
return new BloomFilter(obj);
};

return new BloomFilter({
vData: vData,
nHashFuncs: nHashFuncs,
nTweak: nTweak,
nFlags: nFlags
});
}

/**
* @returns {Buffer}
*/
BloomFilter.prototype.toBuffer = function toBuffer() {
var bw = new BufferWriter();
bw.writeVarintNum(this.vData.length);
Expand Down
Loading

0 comments on commit 487b5f0

Please sign in to comment.