From e496239c3d86102f9a800ac2e322e14d12213936 Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Tue, 18 Mar 2014 16:56:17 -0400 Subject: [PATCH 01/22] added socks5 client dependency --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 1ee9479f3..492a47ca1 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,8 @@ "browserify-bignum": "git://github.com/maraoz/browserify-bignum.git", "browserify-buffertools": "~1.0.2", "brfs": "~1.0.0", - "uglifyify": "~1.2.3" + "uglifyify": "~1.2.3", + "socks5-client": "^0.3.4" }, "devDependencies": { "grunt-contrib-watch": "~0.5.3", From ebec07c050a0dee3587e336a89637b81980d88f0 Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Wed, 19 Mar 2014 16:11:47 -0400 Subject: [PATCH 02/22] added a seedlist class and example for connecting over tor --- SeedList.js | 47 ++++++++++++++++++++++++++++++++++++++++++++ config.js | 2 +- examples/TorProxy.js | 33 +++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 SeedList.js create mode 100644 examples/TorProxy.js diff --git a/SeedList.js b/SeedList.js new file mode 100644 index 000000000..b90ad883c --- /dev/null +++ b/SeedList.js @@ -0,0 +1,47 @@ +'use strict'; +var imports = require('soop').imports(); +var parent = imports.parent || require('events').EventEmitter; +var EventEmitter = require('events').EventEmitter; +var dns = require('dns'); +var inherits = require('util').inherits; + +function SeedList(options) { + this.options = options || {}; + this.sources = [ + 'dnsseed.bluematt.me', + 'dnsseed.bitcoin.dashjr.org', + 'seed.bitcoin.sipa.be', + 'seed.bitcoinstats.com', + 'bitseed.xf2.org' + ]; + this.source = this.options.source || this.sources[0]; + this.seeds = []; + this.find() +}; + +inherits(SeedList, EventEmitter); + +SeedList.prototype.find = function() { + var self = this; + dns.resolve(self.source, function(err, seeds) { + if (err) { + var index = self.sources.indexOf(self.source); + if (index !== -1) { + index++; + if (!self.sources[index]) { + return self.emit('seedsNotFound'); + } + else { + self.source = self.sources[index]; + } + self.find(); + } + return self.emit('error', err); + } + self.seeds = self.seeds.concat(seeds); + self.emit('seedsFound', seeds); + }); + return self; +}; + +module.exports = require('soop')(SeedList); diff --git a/config.js b/config.js index aea295c72..58335365c 100644 --- a/config.js +++ b/config.js @@ -1,4 +1,4 @@ module.exports = { network: 'livenet', - logger: 'normal' // none, normal, debug + logger: 'debug' // none, normal, debug }; diff --git a/examples/TorProxy.js b/examples/TorProxy.js new file mode 100644 index 000000000..2603da9c0 --- /dev/null +++ b/examples/TorProxy.js @@ -0,0 +1,33 @@ +var Socks5Client = require('socks5-client'); +var Peer = require('../Peer'); +var Connection = require('../Connection'); +var SeedList = require('../SeedList') + +// start looking for a seed +var seedlist = new SeedList(); +// create a client socket proxied through +// tor's socks5 proxy +var client = new Socks5Client('127.0.0.1', 9050); + +// when we have a list of seeds... +seedlist.on('seedsFound', function(seeds) { + // use the first seed in list + var peer = new Peer(seeds[0], 8333); + var connection = new Connection(client, peer); + // open the connection to the seed + client.connect(peer.port, peer.host); + // always handle errors + connection.on('error', function(err) { + console.log(err); + }); +}); + +// failboat +seedlist.on('seedsNotFound', function() { + console.log('failed to find seeds :('); +}); + +// double failboat +seedlist.on('error', function(err) { + console.log('error:', err); +}); From 5cc98a459525300d626324ac3c7b264c4d3e66cb Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Wed, 19 Mar 2014 16:13:42 -0400 Subject: [PATCH 03/22] using soop for inheritance --- SeedList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SeedList.js b/SeedList.js index b90ad883c..05ea63a41 100644 --- a/SeedList.js +++ b/SeedList.js @@ -3,9 +3,9 @@ var imports = require('soop').imports(); var parent = imports.parent || require('events').EventEmitter; var EventEmitter = require('events').EventEmitter; var dns = require('dns'); -var inherits = require('util').inherits; function SeedList(options) { + SeedList.super(this, arguments); this.options = options || {}; this.sources = [ 'dnsseed.bluematt.me', @@ -19,7 +19,7 @@ function SeedList(options) { this.find() }; -inherits(SeedList, EventEmitter); +SeedList.parent = imports.parent || EventEmitter; SeedList.prototype.find = function() { var self = this; From ce0a240d6b5a0c90846ec601af2ea9d6f92a8c7d Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Wed, 19 Mar 2014 17:36:28 -0400 Subject: [PATCH 04/22] pointing socks5-client to gordon's fork --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 492a47ca1..363566c2e 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "browserify-buffertools": "~1.0.2", "brfs": "~1.0.0", "uglifyify": "~1.2.3", - "socks5-client": "^0.3.4" + "socks5-client": "git+https://github.com/gordonwritescode/socks5-client.git" }, "devDependencies": { "grunt-contrib-watch": "~0.5.3", From b23f3c808811978753e2ce2617330179419e41fd Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Fri, 4 Apr 2014 11:46:57 -0400 Subject: [PATCH 05/22] add support for connecting over tor, remove seedlist class (for now) --- Connection.js | 19 ++++++++++++++-- PeerManager.js | 4 ++-- SeedList.js | 47 --------------------------------------- examples/ConnectionTor.js | 32 ++++++++++++++++++++++++++ examples/TorProxy.js | 33 --------------------------- 5 files changed, 51 insertions(+), 84 deletions(-) delete mode 100644 SeedList.js create mode 100644 examples/ConnectionTor.js delete mode 100644 examples/TorProxy.js diff --git a/Connection.js b/Connection.js index f273ec3e7..8c6c589b8 100644 --- a/Connection.js +++ b/Connection.js @@ -10,6 +10,7 @@ var PROTOCOL_VERSION = 70000; var Binary = imports.Binary || require('binary'); var Put = imports.Put || require('bufferput'); var Buffers = imports.Buffers || require('buffers'); +var Socks5Client = imports.Socks5Client || require('socks5-client') require('./Buffers.monkey').patch(Buffers); var Block = require('./Block'); @@ -22,11 +23,19 @@ var nonce = util.generateNonce(); var BIP0031_VERSION = 60000; -function Connection(socket, peer) { +function Connection(socket, peer, opts) { Connection.super(this, arguments); + + this.options = opts || {}; + this.socket = socket; this.peer = peer; + // check for socks5 proxy options and construct a proxied socket + if (this.options.proxy) { + this.socket = new Socks5Client(opts.proxy.host, opts.proxy.port); + } + // A connection is considered "active" once we have received verack this.active = false; // The version incoming packages are interpreted as @@ -36,7 +45,7 @@ function Connection(socket, peer) { // The (claimed) height of the remote peer's block chain this.bestHeight = 0; // Is this an inbound connection? - this.inbound = !!socket.server; + this.inbound = !!this.socket.server; // Have we sent a getaddr on this connection? this.getaddr = false; @@ -54,6 +63,12 @@ function Connection(socket, peer) { } Connection.parent = imports.parent || require('events').EventEmitter; +Connection.prototype.open = function(callback) { + if (typeof callback === 'function') this.once('connect', callback); + this.socket.connect(this.peer.port, this.peer.host); + return this; +}; + Connection.prototype.setupHandlers = function () { this.socket.addListener('connect', this.handleConnect.bind(this)); this.socket.addListener('error', this.handleError.bind(this)); diff --git a/PeerManager.js b/PeerManager.js index c6e202990..67b6d43a8 100644 --- a/PeerManager.js +++ b/PeerManager.js @@ -95,8 +95,8 @@ PeerManager.prototype.connectTo = function(peer) { } }; -PeerManager.prototype.addConnection = function(socketConn, peer) { - var conn = new Connection(socketConn, peer); +PeerManager.prototype.addConnection = function(socketConn, peer, opts) { + var conn = new Connection(socketConn, peer, opts); this.connections.push(conn); this.emit('connection', conn); diff --git a/SeedList.js b/SeedList.js deleted file mode 100644 index 05ea63a41..000000000 --- a/SeedList.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict'; -var imports = require('soop').imports(); -var parent = imports.parent || require('events').EventEmitter; -var EventEmitter = require('events').EventEmitter; -var dns = require('dns'); - -function SeedList(options) { - SeedList.super(this, arguments); - this.options = options || {}; - this.sources = [ - 'dnsseed.bluematt.me', - 'dnsseed.bitcoin.dashjr.org', - 'seed.bitcoin.sipa.be', - 'seed.bitcoinstats.com', - 'bitseed.xf2.org' - ]; - this.source = this.options.source || this.sources[0]; - this.seeds = []; - this.find() -}; - -SeedList.parent = imports.parent || EventEmitter; - -SeedList.prototype.find = function() { - var self = this; - dns.resolve(self.source, function(err, seeds) { - if (err) { - var index = self.sources.indexOf(self.source); - if (index !== -1) { - index++; - if (!self.sources[index]) { - return self.emit('seedsNotFound'); - } - else { - self.source = self.sources[index]; - } - self.find(); - } - return self.emit('error', err); - } - self.seeds = self.seeds.concat(seeds); - self.emit('seedsFound', seeds); - }); - return self; -}; - -module.exports = require('soop')(SeedList); diff --git a/examples/ConnectionTor.js b/examples/ConnectionTor.js new file mode 100644 index 000000000..f03bf9e6e --- /dev/null +++ b/examples/ConnectionTor.js @@ -0,0 +1,32 @@ +var Peer = require('../Peer'); +var Connection = require('../Connection'); + +// create a peer instance from a know peer +// (later we can use built-in peer discovery) +// to get a peer to connect to you can run: +// +// ~# dig dnsseed.bluematt.me +// +// (or use a different dns seed) +var peer = new Peer('108.13.10.109', 8333); + +// create a connection without an existing socket +// but specify a socks5 proxy to create a socket +// that's bound to that proxy in it's place +var connection = new Connection(null, peer, { + proxy: { host: '127.0.0.1', port: 9050 } +}); + +// open the connection +connection.open(); + +// you can listen for the connect event +connection.on('connect', function(data) { + // we are connected! + console.log('connected'); +}); + +connection.on('error', function(err) { + // boo! :( + console.log('poop'); +}); diff --git a/examples/TorProxy.js b/examples/TorProxy.js deleted file mode 100644 index 2603da9c0..000000000 --- a/examples/TorProxy.js +++ /dev/null @@ -1,33 +0,0 @@ -var Socks5Client = require('socks5-client'); -var Peer = require('../Peer'); -var Connection = require('../Connection'); -var SeedList = require('../SeedList') - -// start looking for a seed -var seedlist = new SeedList(); -// create a client socket proxied through -// tor's socks5 proxy -var client = new Socks5Client('127.0.0.1', 9050); - -// when we have a list of seeds... -seedlist.on('seedsFound', function(seeds) { - // use the first seed in list - var peer = new Peer(seeds[0], 8333); - var connection = new Connection(client, peer); - // open the connection to the seed - client.connect(peer.port, peer.host); - // always handle errors - connection.on('error', function(err) { - console.log(err); - }); -}); - -// failboat -seedlist.on('seedsNotFound', function() { - console.log('failed to find seeds :('); -}); - -// double failboat -seedlist.on('error', function(err) { - console.log('error:', err); -}); From 27191fc0ece7217c0d1b3a5517b6a88daa87ce82 Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Fri, 4 Apr 2014 11:49:11 -0400 Subject: [PATCH 06/22] point back to socks5-client upstream repo --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 363566c2e..501f3576f 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "browserify-buffertools": "~1.0.2", "brfs": "~1.0.0", "uglifyify": "~1.2.3", - "socks5-client": "git+https://github.com/gordonwritescode/socks5-client.git" + "socks5-client": "~0.3.6" }, "devDependencies": { "grunt-contrib-watch": "~0.5.3", From 591d5fff401202305b37e881c461f85e33a24a57 Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Fri, 4 Apr 2014 11:53:05 -0400 Subject: [PATCH 07/22] set logger back to normal --- config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.js b/config.js index 58335365c..aea295c72 100644 --- a/config.js +++ b/config.js @@ -1,4 +1,4 @@ module.exports = { network: 'livenet', - logger: 'debug' // none, normal, debug + logger: 'normal' // none, normal, debug }; From 78edb3200eeeef5d092d9b44ab1a9a4978669622 Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Fri, 4 Apr 2014 12:45:38 -0400 Subject: [PATCH 08/22] change example to log actual error --- examples/ConnectionTor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ConnectionTor.js b/examples/ConnectionTor.js index f03bf9e6e..e41a77bbf 100644 --- a/examples/ConnectionTor.js +++ b/examples/ConnectionTor.js @@ -28,5 +28,5 @@ connection.on('connect', function(data) { connection.on('error', function(err) { // boo! :( - console.log('poop'); + console.log(err); }); From 529d26e052432cd2e62706d53c882b03b2d4adfa Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Wed, 9 Apr 2014 11:49:46 -0400 Subject: [PATCH 09/22] add missing comma to package.json deps --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 82098a7e5..d021292ac 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "browserify-bignum": "git://github.com/maraoz/browserify-bignum.git", "browserify-buffertools": "git://github.com/maraoz/browserify-buffertools.git", "brfs": "~1.0.0", - "socks5-client": "~0.3.6" + "socks5-client": "~0.3.6", "chai": "~1.9.0", "uglifyify": "~1.2.3" }, From e53a2341c3f00d8813d78f0c82afa737e8cdf1f1 Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Fri, 11 Apr 2014 13:56:37 -0400 Subject: [PATCH 10/22] updated connection tests --- package.json | 4 ++++ test/test.Connection.js | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/package.json b/package.json index d021292ac..3176ced2a 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,10 @@ { "name": "Ryan X. Charles", "email": "ryan@bitpay.com" + }, + { + "name": "Gordon Hall", + "email": "gordon@bitpay.com" } ], "keywords": [ diff --git a/test/test.Connection.js b/test/test.Connection.js index 868297384..f070993d8 100644 --- a/test/test.Connection.js +++ b/test/test.Connection.js @@ -23,6 +23,12 @@ describe('Connection', function() { var c = new Connection(mSocket, mPeer); should.exist(c); }); + it('should create a proxied socket if instructed', function() { + var c = new Connection(null, mPeer, { + proxy: { host: 'localhost', port: 9050 } + }); + should.exist(c.socket); + }); }); From a5ba4577873ac86623eec6432f92e666122ca65a Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Fri, 11 Apr 2014 14:05:53 -0400 Subject: [PATCH 11/22] update example to discover a peer instead of hardcoding arbitrary IP --- examples/ConnectionTor.js | 49 ++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/examples/ConnectionTor.js b/examples/ConnectionTor.js index e41a77bbf..aa006f677 100644 --- a/examples/ConnectionTor.js +++ b/examples/ConnectionTor.js @@ -1,32 +1,27 @@ var Peer = require('../Peer'); var Connection = require('../Connection'); +var dns = require('dns'); -// create a peer instance from a know peer -// (later we can use built-in peer discovery) -// to get a peer to connect to you can run: -// -// ~# dig dnsseed.bluematt.me -// -// (or use a different dns seed) -var peer = new Peer('108.13.10.109', 8333); +// get a peer from dns seed +dns.resolve('dnsseed.bluematt.me', function(err, seeds) { + // use the first peer + var peer = new Peer(seeds[0], 8333); + + // create a connection without an existing socket + // but specify a socks5 proxy to create a socket + // that's bound to that proxy in it's place + var connection = new Connection(null, peer, { + proxy: { host: '127.0.0.1', port: 9050 } + }); + + connection.open(); + + connection.on('connect', function(data) { + console.log('connected through socks5!'); + }); + + connection.on('error', function(err) { + console.log(err); + }); -// create a connection without an existing socket -// but specify a socks5 proxy to create a socket -// that's bound to that proxy in it's place -var connection = new Connection(null, peer, { - proxy: { host: '127.0.0.1', port: 9050 } -}); - -// open the connection -connection.open(); - -// you can listen for the connect event -connection.on('connect', function(data) { - // we are connected! - console.log('connected'); -}); - -connection.on('error', function(err) { - // boo! :( - console.log(err); }); From 7b9e2e7b4911439d8782bf97927219cff918f9ff Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Fri, 11 Apr 2014 14:12:00 -0400 Subject: [PATCH 12/22] remove duplicate dependency --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index c54644d9e..a6917abbd 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,6 @@ "commander": "=2.1.0", "browserify-bignum": "git://github.com/maraoz/browserify-bignum.git", "browserify-buffertools": "git://github.com/maraoz/browserify-buffertools.git", - "brfs": "~1.0.0", "socks5-client": "~0.3.6", "brfs": "=1.0.0", "chai": "=1.9.1", From a89b51d5aab7a2a248194c4f4ae0203a6eb54e67 Mon Sep 17 00:00:00 2001 From: Gordon Hall Date: Tue, 15 Apr 2014 10:30:41 -0400 Subject: [PATCH 13/22] requiring socks5 client conditionally from connection constructor --- Connection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Connection.js b/Connection.js index a9ff23d15..27a4fb618 100644 --- a/Connection.js +++ b/Connection.js @@ -10,7 +10,6 @@ var PROTOCOL_VERSION = 70000; var Binary = imports.Binary || require('binary'); var Put = imports.Put || require('bufferput'); var Buffers = imports.Buffers || require('buffers'); -var Socks5Client = imports.Socks5Client || require('socks5-client') require('./Buffers.monkey').patch(Buffers); var Block = require('./Block'); @@ -33,6 +32,7 @@ function Connection(socket, peer, opts) { // check for socks5 proxy options and construct a proxied socket if (this.options.proxy) { + var Socks5Client = imports.Socks5Client || require('socks5-client'); this.socket = new Socks5Client(opts.proxy.host, opts.proxy.port); } From a749dc9d97cf26208b8dded5948ba8ef83a23485 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Wed, 16 Apr 2014 21:37:46 -0300 Subject: [PATCH 14/22] fix key tests for browser Some tests that were intended for node only were running the browser. This update removes those tests from the browser. --- test/test.Key.js | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/test/test.Key.js b/test/test.Key.js index 9eb988239..3320e4f99 100644 --- a/test/test.Key.js +++ b/test/test.Key.js @@ -16,21 +16,6 @@ describe('Key', function() { var k = new Key(); should.exist(k); }); - it('should not fail when called as Key() without "new"', function() { - var key = Key(); - should.exist(key); - }); - it('should not fail when called as Key() without "new" with some args', function() { - var key = Key(1, 2, 3, 4, 5); - should.exist(key); - }); - it('should have correct properties when called with Key() without "new"', function() { - var key = Key(); - key.compressed.should.equal(true); - should.not.exist(key.public); - should.not.exist(key.private); - should.exist(key); - }); it('should be able to generateSync instance', function() { var k = Key.generateSync(); should.exist(k); @@ -180,6 +165,25 @@ describe('Key', function() { }); }); + + describe('node only Key functionality', function() { + it('should not fail when called as Key() without "new"', function() { + var key = Key(); + should.exist(key); + }); + it('should not fail when called as Key() without "new" with some args', function() { + var key = Key(1, 2, 3, 4, 5); + should.exist(key); + }); + it('should have correct properties when called with Key() without "new"', function() { + var key = Key(); + key.compressed.should.equal(true); + should.not.exist(key.public); + should.not.exist(key.private); + should.exist(key); + }); + + }); } }); From bc8a554536e59f524dd71951beebae0e01f70d65 Mon Sep 17 00:00:00 2001 From: Simon Schoeters Date: Thu, 17 Apr 2014 14:53:14 +0200 Subject: [PATCH 15/22] Fix undefined `Unspent` variable in the P2P transaction readme example --- README.md | 116 +++++++++++++++++++++++++++--------------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 3c71e035d..dbb5bd719 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ A pure, powerful core for your bitcoin project. Bitcore is a complete, native interface to the Bitcoin network, and provides the core functionality needed to develop apps for bitcoin. #Principles + Bitcoin is a powerful new peer-to-peer platform for the next generation of financial technology. The decentralized nature of the Bitcoin network allows for highly resilient bitcoin infrastructure, and the developer community needs reliable, open-source tools to implement bitcoin apps and services. **Bitcore unchains developers from fallible, centralized APIs, and provides the tools to interact with the real Bitcoin network.** @@ -15,11 +16,13 @@ Bitcoin is a powerful new peer-to-peer platform for the next generation of finan #Get Started Bitcore runs on [node](http://nodejs.org/), and can be installed via [npm](https://npmjs.org/): + ``` npm install bitcore ``` It is a collection of objects useful to bitcoin applications; class-like idioms are enabled via [Soop](https://github.com/bitpay/soop). In most cases, a developer will require the object's class directly. For instance: + ``` var bitcore = require('bitcore'); var Address = bitcore.Address; @@ -32,7 +35,9 @@ var PeerManager = bitcore.PeerManager; Some examples are provided at the [examples](/examples) path. Here are some snippets: ## Validating an address + Validating a Bitcoin address: + ```js var bitcore = require('bitcore'); var Address = bitcore.Address; @@ -51,8 +56,11 @@ addrs.forEach(function(addr) { console.log(addr.data + ' is ' + (valid ? '' : 'not ') + 'valid'); }); ``` + ## Monitoring Blocks and Transactions + For this example you need a running bitcoind instance with RPC enabled. + ```js var bitcore = require('bitcore'); var networks = bitcore.networks; @@ -68,7 +76,6 @@ var handleBlock = function(info) { var handleTx = function(info) { var tx = info.message.tx.getStandardizedObject(); - console.log('** TX Received **'); console.log(tx); }; @@ -76,7 +83,6 @@ var handleTx = function(info) { var handleInv = function(info) { console.log('** Inv **'); console.log(info.message); - var invs = info.message.invs; info.conn.sendGetData(invs); }; @@ -96,9 +102,10 @@ peerman.start(); PeerManager will emit the following events: 'version', 'verack', 'addr', 'getaddr', 'error' 'disconnect'; and will relay events like: 'tx', 'block', 'inv'. Please see [PeerManager.js](PeerManager.js), [Peer.js](Peer.js) and [Connection.js](Connection.js) - ## Consuming bitcoind RPC -For this example you need a running bitcoind instance with RPC enabled. + +For this example you need a running bitcoind instance with RPC enabled. + ```js var bitcore = require('bitcore'); var RpcClient = bitcore.RpcClient; @@ -123,6 +130,7 @@ rpc.getBlock(hash, function(err, ret) { console.log(ret); }); ``` + Check the list of all supported RPC call at [RpcClient.js](RpcClient.js) ## Creating and sending a Transaction through P2P @@ -140,69 +148,66 @@ var PeerManager = require('soop').load('../PeerManager', { network: networks.testnet }); -// this can be get from insight.bitcore.io API o blockchain.info - +// Unspent transactions can be found via the insight.bitcore.io or blockchain.info APIs var unspent = [ - { - "address": "n4g2TFaQo8UgedwpkYdcQFF6xE2Ei9Czvy", - "txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1", - "scriptPubKey": "76a914fe021bac469a5c49915b2a8ffa7390a9ce5580f988ac", - "vout": 1, - "amount": 1.0101, - "confirmations":7 - }, - { - "address": "mhNCT9TwZAGF1tLPpZdqfkTmtBkY282YDW", - "txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2", - "scriptPubKey": "76a9141448534cb1a1ec44665b0eb2326e570814afe3f188ac", - "vout": 0, - "confirmations": 1, - "amount": 10 - }, + { + "address": "n4g2TFaQo8UgedwpkYdcQFF6xE2Ei9Czvy", + "txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1", + "scriptPubKey": "76a914fe021bac469a5c49915b2a8ffa7390a9ce5580f988ac", + "vout": 1, + "amount": 1.0101, + "confirmations":7 + }, + { + "address": "mhNCT9TwZAGF1tLPpZdqfkTmtBkY282YDW", + "txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2", + "scriptPubKey": "76a9141448534cb1a1ec44665b0eb2326e570814afe3f188ac", + "vout": 0, + "confirmations": 1, + "amount": 10 + } ]; -//private keys in WIF format (see TransactionBuilder.js for other options) +// Private keys in WIF format (see TransactionBuilder.js for other options) var keys = [ "cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV", "cPa87VgwZfowGZYaEenoQeJgRfKW6PhZ1R65EHTkN1K19cSvc92G", "cPQ9DSbBRLva9av5nqeF5AGrh3dsdW8p2E5jS4P8bDWZAoQTeeKB" ]; - var peerman = new PeerManager(); peerman.addPeer(new Peer('127.0.0.1', 18333)); peerman.on('connect', function() { var conn = peerman.getActiveConnection(); if (conn) { - var outs = [{address:toAddress, amount:amt}]; - var opts = {remainderOut: {address: changeAddressString}}; + var outs = [{address: toAddress, amount: amt}]; + var opts = {remainderOut: {address: changeAddress}}; var Builder = bitcore.TransactionBuilder; var tx = new Builder(opts) - .setUnspent(Unspent) - .setOutputs(outs) - .sign(keys) - .build(); + .setUnspent(unspent) + .setOutputs(outs) + .sign(keys) + .build(); - /* create and signing can be done in multiple steps using: + /* Create and signing can be done in multiple steps: * * var builder = new bitcore.TransactionBuilder(opts) - * .setUnspent(utxos) - * .setOutputs(outs); - * //later - * builder.sign(key1); - * // get partially signed tx - * var tx = builder.build(); + * .setUnspent(utxos) + * .setOutputs(outs); * - * //later + * // Sign with the first key + * builder.sign(key1); + * var tx = builder.build(); // Partially signed transaction + * + * // Sign with the second key * builder.sign(key2); * if (builder.isFullySigned()){ * var tx = builder.build(); * } * - * The selected Unspent Outputs for the transaction can be retrieved with: - * var selectedUnspent = build.getSelectedUnspent(); + * var selectedUnspent = build.getSelectedUnspent(); // Retrieve selected unspent outputs from the transaction */ conn.sendTx(tx.serialize().toString('hex')); } @@ -214,8 +219,7 @@ peerman.on('connect', function() { peerman.start(); ``` - -## Parsing a Script +## Parsing a Script Gets an address strings from a ScriptPubKey Buffer @@ -265,25 +269,26 @@ console.log(getAddrStr(s)[0]); // mkZBYBiq6DNoQEKakpMJegyDbw2YiNQnHT ``` #Security + Please use at your own risk. Bitcore is still under heavy development and not quite ready for "drop-in" production use. If you find a security issue, please email security@bitcore.io. #Contributing + Bitcore needs some developer love. Please send pull requests for bug fixes, code optimization, and ideas for improvement. #Browser support + ## Building the browser bundle -To build bitcore full bundle for the browser: -(this is automatically executed after you run `npm install`) + +To build bitcore full bundle for the browser (this is automatically executed after you run `npm install`): ``` node browser/build.js -a ``` -This will generate a `browser/bundle.js` file which you can include -in your HTML to use bitcore in the browser. -## +This will generate a `browser/bundle.js` file which you can include in your HTML to use bitcore in the browser. ##Example browser usage @@ -303,23 +308,21 @@ From example/simple.html ``` -You can check a more complex usage example at examples/example.html +You can check a more complex usage example at examples/example.html. ## Generating a customized browser bundle -To generate a customized bitcore bundle, you can specify -which submodules you want to include in it with the -s option: + +To generate a customized bitcore bundle, you can specify which submodules you want to include in it with the -s option: ``` node browser/build.js -s Transaction,Address ``` -This will generate a `browser/bundle.js` containing only the Transaction - and Address class, with all their dependencies. -Use this option if you are not using the whole bitcore library, to optimize -the bundle size, script loading time, and general resource usage. + +This will generate a `browser/bundle.js` containing only the Transaction and Address class, with all their dependencies. Use this option if you are not using the whole bitcore library, to optimize the bundle size, script loading time, and general resource usage. ## Tests -Run tests in node: +Run tests in node: ``` mocha @@ -341,13 +344,10 @@ npm run-script coverage And then open coverage/lcov-report/index.html in your browser. - #License **Code released under [the MIT license](https://github.com/bitpay/bitcore/blob/master/LICENSE).** Copyright 2013-2014 BitPay, Inc. Bitcore is a trademark maintained by BitPay, Inc. - [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/bitpay/bitcore/trend.png)](https://bitdeli.com/free "Bitdeli Badge") - From 2a7eba9f6b8bb938e5ee63115c3a8b5eee7f055b Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 17 Apr 2014 09:56:58 -0300 Subject: [PATCH 16/22] Update TransactionBuilder name --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3c71e035d..149185404 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ Check the list of all supported RPC call at [RpcClient.js](RpcClient.js) ## Creating and sending a Transaction through P2P The fee of the transaction can be given in `opts` or it will be determined -by the transaction size. Documentation on the paramets of `TransactionBuilder` +by the transaction size. Documentation on the parameters of `TransactionBuilder` can be found on the source file. ```js @@ -177,9 +177,9 @@ peerman.on('connect', function() { if (conn) { var outs = [{address:toAddress, amount:amt}]; var opts = {remainderOut: {address: changeAddressString}}; - var Builder = bitcore.TransactionBuilder; + var TransactionBuilder = bitcore.TransactionBuilder; - var tx = new Builder(opts) + var tx = new TransactionBuilder(opts) .setUnspent(Unspent) .setOutputs(outs) .sign(keys) From d5153850ad44e344dd9047454967563a30fdd645 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Thu, 17 Apr 2014 11:19:54 -0300 Subject: [PATCH 17/22] make PeerManager and Connection instances configurable --- Connection.js | 35 ++++++++++++++++++----------------- PeerManager.js | 19 ++++++++----------- examples/PeerManager.js | 5 +---- examples/SendTx.js | 4 +--- examples/SimpleP2Pmonitor.js | 2 +- 5 files changed, 29 insertions(+), 36 deletions(-) diff --git a/Connection.js b/Connection.js index 319a723e3..154509319 100644 --- a/Connection.js +++ b/Connection.js @@ -1,19 +1,18 @@ var imports = require('soop').imports(); -var config = imports.config || require('./config'); var log = imports.log || require('./util/log'); -var network = imports.network || require('./networks')[config.network]; var MAX_RECEIVE_BUFFER = 10000000; var PROTOCOL_VERSION = 70000; -var Binary = imports.Binary || require('binary'); var Put = imports.Put || require('bufferput'); var Buffers = imports.Buffers || require('buffers'); require('./Buffers.monkey').patch(Buffers); -var Block = require('./Block'); -var Transaction = require('./Transaction'); +var bitcoreDefaults = imports.config || require('./config'); +var networks = imports.networks || require('./networks'); +var Block = imports.Block || require('./Block'); +var Transaction = imports.Transaction || require('./Transaction'); var util = imports.util || require('./util/util'); var Parser = imports.Parser || require('./util/BinaryParser'); var buffertools = imports.buffertools || require('buffertools'); @@ -22,12 +21,14 @@ var nonce = util.generateNonce(); var BIP0031_VERSION = 60000; -function Connection(socket, peer) { +function Connection(socket, peer, config) { Connection.super(this, arguments); + this.config = config || bitcoreDefaults; + this.network = networks[this.config.network] || networks.testnet; this.socket = socket; this.peer = peer; - // A connection is considered "active" once we have received verack + // A connection is considered 'active' once we have received verack this.active = false; // The version incoming packages are interpreted as this.recvVer = 0; @@ -131,7 +132,7 @@ Connection.prototype.handleMessage = function(message) { this.recvVer = Math.min(message.version, PROTOCOL_VERSION); } else { // We won't start expecting a checksum until after we've received - // the "verack" message. + // the 'verack' message. this.once('verack', (function () { this.recvVer = message.version; }).bind(this)); @@ -145,7 +146,7 @@ Connection.prototype.handleMessage = function(message) { break; case 'ping': - if ("object" === typeof message.nonce) { + if ('object' === typeof message.nonce) { this.sendPong(message.nonce); } break; @@ -285,7 +286,7 @@ Connection.prototype.sendBlock = function (block, txs) { Connection.prototype.sendMessage = function (command, payload) { try { - var magic = network.magic; + var magic = this.network.magic; var commandBuf = new Buffer(command, 'ascii'); if (commandBuf.length > 12) throw 'Command name too long'; @@ -308,13 +309,13 @@ Connection.prototype.sendMessage = function (command, payload) { var buffer = message.buffer(); log.debug('['+this.peer+'] '+ - "Sending message "+command+" ("+payload.length+" bytes)"); + 'Sending message '+command+' ('+payload.length+' bytes)'); this.socket.write(buffer); } catch (err) { // TODO: We should catch this error one level higher in order to better // determine how to react to it. For now though, ignoring it will do. - log.err("Error while sending message to peer "+this.peer+": "+ + log.err('Error while sending message to peer '+this.peer+': '+ (err.stack ? err.stack : err.toString())); } }; @@ -323,7 +324,7 @@ Connection.prototype.handleData = function (data) { this.buffers.push(data); if (this.buffers.length > MAX_RECEIVE_BUFFER) { - log.err("Peer "+this.peer+" exceeded maxreceivebuffer, disconnecting."+ + log.err('Peer '+this.peer+' exceeded maxreceivebuffer, disconnecting.'+ (err.stack ? err.stack : err.toString())); this.socket.destroy(); return; @@ -336,7 +337,7 @@ Connection.prototype.processData = function () { // If there are less than 20 bytes there can't be a message yet. if (this.buffers.length < 20) return; - var magic = network.magic; + var magic = this.network.magic; var i = 0; for (;;) { if (this.buffers.get(i ) === magic[0] && @@ -371,13 +372,13 @@ Connection.prototype.processData = function () { if (this.buffers.length < endPos) return; - var command = this.buffers.slice(4, 16).toString('ascii').replace(/\0+$/,""); + var command = this.buffers.slice(4, 16).toString('ascii').replace(/\0+$/,''); var payload = this.buffers.slice(startPos, endPos); var checksum = (this.recvVer >= 209) ? this.buffers.slice(20, 24) : null; log.debug('['+this.peer+'] ' + - "Received message " + command + - " (" + payloadLen + " bytes)"); + 'Received message ' + command + + ' (' + payloadLen + ' bytes)'); if (checksum !== null) { var checksumConfirm = doubleSha256(payload).slice(0, 4); diff --git a/PeerManager.js b/PeerManager.js index d8229867f..76daa5c20 100644 --- a/PeerManager.js +++ b/PeerManager.js @@ -1,21 +1,18 @@ -var imports = require('soop').imports(); -var config = imports.config || require('./config'); -var log = imports.log || require('./util/log'); -var network = imports.network || require('./networks')[config.network]; +var imports = require('soop').imports(); +var log = imports.log || require('./util/log'); +var bitcoreDefaults = imports.config || require('./config'); +var Connection = imports.Connection || require ('./Connection'); -var Connection = imports.Connection || - require('soop').load('./Connection', {config: config, network: network}) || - require ('./Connection'); - -var Peer = imports.Peer || require('./Peer'); +var Peer = imports.Peer || require('./Peer'); GetAdjustedTime = imports.GetAdjustedTime || function () { // TODO: Implement actual adjustment return Math.floor(new Date().getTime() / 1000); }; -function PeerManager() { +function PeerManager(config) { + this.config = config || bitcoreDefaults; this.active = false; this.timer = null; @@ -96,7 +93,7 @@ PeerManager.prototype.connectTo = function(peer) { }; PeerManager.prototype.addConnection = function(socketConn, peer) { - var conn = new Connection(socketConn, peer); + var conn = new Connection(socketConn, peer, this.config); this.connections.push(conn); this.emit('connection', conn); diff --git a/examples/PeerManager.js b/examples/PeerManager.js index 0d0bdbae5..2cccac9e1 100644 --- a/examples/PeerManager.js +++ b/examples/PeerManager.js @@ -3,11 +3,8 @@ var run = function() { // Replace '../bitcore' with 'bitcore' if you use this code elsewhere. var bitcore = require('../bitcore'); - var networks = bitcore.networks; var Peer = bitcore.Peer; - var PeerManager = require('soop').load('../PeerManager', { - network: networks.testnet - }); + var PeerManager = bitcore.PeerManager; var handleBlock = function(info) { console.log('** Block Received **'); diff --git a/examples/SendTx.js b/examples/SendTx.js index 36646606a..5dddd8010 100644 --- a/examples/SendTx.js +++ b/examples/SendTx.js @@ -9,9 +9,7 @@ var run = function() { var Address = bitcore.Address; var Script = bitcore.Script; var coinUtil = bitcore.util; - var PeerManager = require('soop').load('../PeerManager', { - network: networks.testnet - }); + var PeerManager = require('../PeerManager'); var createTx = function() { var TXIN = 'd05f35e0bbc495f6dcab03e599c8f5e32a07cdb4bc76964de201d06a2a7d8265'; diff --git a/examples/SimpleP2Pmonitor.js b/examples/SimpleP2Pmonitor.js index 1791b9bef..838e57833 100644 --- a/examples/SimpleP2Pmonitor.js +++ b/examples/SimpleP2Pmonitor.js @@ -1,6 +1,6 @@ /** * This is a simple script that will display network messages. - * It users the Peer / Connection classes * directly instead of + * It users the Peer / Connection classes directly instead of * relying on PeerManager. */ From 84ec7bae4c845c20f7b17801775783d4872c0657 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Thu, 17 Apr 2014 11:27:48 -0300 Subject: [PATCH 18/22] fix PeerManager example --- PeerManager.js | 2 +- examples/PeerManager.js | 4 +++- examples/SendTx.js | 3 +-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/PeerManager.js b/PeerManager.js index 76daa5c20..8e90317dc 100644 --- a/PeerManager.js +++ b/PeerManager.js @@ -25,7 +25,7 @@ function PeerManager(config) { this.interval = 5000; this.minConnections = 8; this.minKnownPeers = 10; -}; +} PeerManager.parent = imports.parent || require('events').EventEmitter; PeerManager.Connection = Connection; diff --git a/examples/PeerManager.js b/examples/PeerManager.js index 2cccac9e1..06d2c2648 100644 --- a/examples/PeerManager.js +++ b/examples/PeerManager.js @@ -26,7 +26,9 @@ var run = function() { info.conn.sendGetData(invs); }; - var peerman = new PeerManager(); + var peerman = new PeerManager({ + network: 'testnet' + }); peerman.addPeer(new Peer('127.0.0.1', 18333)); diff --git a/examples/SendTx.js b/examples/SendTx.js index 5dddd8010..0eed1f1ae 100644 --- a/examples/SendTx.js +++ b/examples/SendTx.js @@ -3,13 +3,12 @@ var run = function() { // Replace '../bitcore' with 'bitcore' if you use this code elsewhere. var bitcore = require('../bitcore'); - var networks = bitcore.networks; var Peer = bitcore.Peer; var Transaction = bitcore.Transaction; var Address = bitcore.Address; var Script = bitcore.Script; var coinUtil = bitcore.util; - var PeerManager = require('../PeerManager'); + var PeerManager = bitcore.PeerManager; var createTx = function() { var TXIN = 'd05f35e0bbc495f6dcab03e599c8f5e32a07cdb4bc76964de201d06a2a7d8265'; From fc1dcbd9d099b1cc9d71b905cd0ddda603baec96 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Thu, 17 Apr 2014 12:38:03 -0300 Subject: [PATCH 19/22] fix and improve SendTx example --- README.md | 219 ++++++++++++++++++++++++--------------------- examples/SendTx.js | 127 ++++++++++++++++---------- 2 files changed, 199 insertions(+), 147 deletions(-) diff --git a/README.md b/README.md index df162db23..9ce5ebd21 100644 --- a/README.md +++ b/README.md @@ -62,42 +62,43 @@ addrs.forEach(function(addr) { For this example you need a running bitcoind instance with RPC enabled. ```js -var bitcore = require('bitcore'); -var networks = bitcore.networks; -var Peer = bitcore.Peer; -var PeerManager = require('soop').load('../PeerManager', { - network: networks.testnet -}); + var bitcore = require('../bitcore'); + var Peer = bitcore.Peer; + var PeerManager = bitcore.PeerManager; -var handleBlock = function(info) { - console.log('** Block Received **'); - console.log(info.message); -}; + var handleBlock = function(info) { + console.log('** Block Received **'); + console.log(info.message); + }; -var handleTx = function(info) { - var tx = info.message.tx.getStandardizedObject(); - console.log('** TX Received **'); - console.log(tx); -}; + var handleTx = function(info) { + var tx = info.message.tx.getStandardizedObject(); -var handleInv = function(info) { - console.log('** Inv **'); - console.log(info.message); - var invs = info.message.invs; - info.conn.sendGetData(invs); -}; + console.log('** TX Received **'); + console.log(tx); + }; -var peerman = new PeerManager(); + var handleInv = function(info) { + console.log('** Inv **'); + console.log(info.message); -peerman.addPeer(new Peer('127.0.0.1', 18333)); + var invs = info.message.invs; + info.conn.sendGetData(invs); + }; -peerman.on('connection', function(conn) { - conn.on('inv', handleInv); - conn.on('block', handleBlock); - conn.on('tx', handleTx); -}); + var peerman = new PeerManager({ + network: 'testnet' + }); -peerman.start(); + peerman.addPeer(new Peer('127.0.0.1', 18333)); + + peerman.on('connection', function(conn) { + conn.on('inv', handleInv); + conn.on('block', handleBlock); + conn.on('tx', handleTx); + }); + + peerman.start(); ``` PeerManager will emit the following events: 'version', 'verack', 'addr', 'getaddr', 'error' 'disconnect'; and will relay events like: 'tx', 'block', 'inv'. Please see [PeerManager.js](PeerManager.js), [Peer.js](Peer.js) and [Connection.js](Connection.js) @@ -140,83 +141,101 @@ by the transaction size. Documentation on the parameters of `TransactionBuilder` can be found on the source file. ```js -var bitcore = require('bitcore'); -var networks = bitcore.networks; -var Peer = bitcore.Peer; -var TransactionBuilder = bitcore.TransactionBuilder; -var PeerManager = require('soop').load('../PeerManager', { - network: networks.testnet -}); + var bitcore = require('bitcore'); + var Peer = bitcore.Peer; + var TransactionBuilder = bitcore.TransactionBuilder; + var PeerManager = bitcore.PeerManager; -// Unspent transactions can be found via the insight.bitcore.io or blockchain.info APIs -var unspent = [ - { - "address": "n4g2TFaQo8UgedwpkYdcQFF6xE2Ei9Czvy", - "txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1", - "scriptPubKey": "76a914fe021bac469a5c49915b2a8ffa7390a9ce5580f988ac", - "vout": 1, - "amount": 1.0101, - "confirmations":7 - }, - { - "address": "mhNCT9TwZAGF1tLPpZdqfkTmtBkY282YDW", - "txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2", - "scriptPubKey": "76a9141448534cb1a1ec44665b0eb2326e570814afe3f188ac", - "vout": 0, - "confirmations": 1, - "amount": 10 - } -]; + // Unspent transactions can be found via the insight.bitcore.io or blockchain.info APIs + var unspent = [{ + 'txid': '707108b5ba4f78dc951df4647a03365bf36432ea57fb641676045c5044daaea7', + 'vout': 0, + 'address': 'n3QDC7DzsMmN4mcyp3k7XGPX7zFXXHG387', + 'scriptPubKey': '76a914f00c4a92ee2314ab08ac0283dc8d07d9bf2be32388ac', + 'amount': 0.12345600, + 'confirmations': 43537 + }, { + 'txid': '87a158d32833cb555aea27b6a21af569ccaeb8f9b19691e05f1e6c2b3440bdb3', + 'vout': 1, + 'address': 'mxdrp9s4mVxS9X4RBYiLe99v59V81XA5C3', + 'scriptPubKey': '76a914bbc87986da6b17c7876db4efacf59a95e14f6cf588ac', + 'amount': 0.05749800, + 'confirmations': 43536 + } -// Private keys in WIF format (see TransactionBuilder.js for other options) -var keys = [ - "cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV", - "cPa87VgwZfowGZYaEenoQeJgRfKW6PhZ1R65EHTkN1K19cSvc92G", - "cPQ9DSbBRLva9av5nqeF5AGrh3dsdW8p2E5jS4P8bDWZAoQTeeKB" -]; + ]; -var peerman = new PeerManager(); -peerman.addPeer(new Peer('127.0.0.1', 18333)); + // Private keys in WIF format (see TransactionBuilder.js for other options) + var keys = [ + 'cQA75LXhV5JkMT8wkkqjR87SnHK4doh3c21p7PAd5tp8tc1tRBAY', + 'cRz85dz9AiDieRpEwoucfXXQa1jdHHghcv6YnnVVGZ3MQyR1X4u2', + 'cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV', + 'cPa87VgwZfowGZYaEenoQeJgRfKW6PhZ1R65EHTkN1K19cSvc92G', + 'cPQ9DSbBRLva9av5nqeF5AGrh3dsdW8p2E5jS4P8bDWZAoQTeeKB' + ]; -peerman.on('connect', function() { - var conn = peerman.getActiveConnection(); - if (conn) { - var outs = [{address: toAddress, amount: amt}]; - var opts = {remainderOut: {address: changeAddressString}}; - var TransactionBuilder = bitcore.TransactionBuilder; - - var tx = new TransactionBuilder(opts) - .setUnspent(unspent) - .setOutputs(outs) - .sign(keys) - .build(); - - /* Create and signing can be done in multiple steps: - * - * var builder = new bitcore.TransactionBuilder(opts) - * .setUnspent(utxos) - * .setOutputs(outs); - * - * // Sign with the first key - * builder.sign(key1); - * var tx = builder.build(); // Partially signed transaction - * - * // Sign with the second key - * builder.sign(key2); - * if (builder.isFullySigned()){ - * var tx = builder.build(); - * } - * - * var selectedUnspent = build.getSelectedUnspent(); // Retrieve selected unspent outputs from the transaction - */ - conn.sendTx(tx.serialize().toString('hex')); - } - conn.on('reject', function() { - console.log('Transaction Rejected'); + var peerman = new PeerManager({ + network: 'testnet' }); -}); + peerman.addPeer(new Peer('127.0.0.1', 18333)); -peerman.start(); + peerman.on('connect', function() { + var conn = peerman.getActiveConnection(); + if (conn) { + // define transaction output + var outs = [{ + address: 'mhNCT9TwZAGF1tLPpZdqfkTmtBkY282YDW', + amount: 0.1337 + }]; + // set change address + var opts = { + remainderOut: { + address: 'n4g2TFaQo8UgedwpkYdcQFF6xE2Ei9Czvy' + } + }; + var tx = new TransactionBuilder(opts) + .setUnspent(unspent) + .setOutputs(outs) + .sign(keys) + .build(); + + /* Create and signing can be done in multiple steps: + * + * var builder = new bitcore.TransactionBuilder(opts) + * .setUnspent(utxos) + * .setOutputs(outs); + * + * // Sign with the first key + * builder.sign(key1); + * var tx = builder.build(); // Partially signed transaction + * + * // Sign with the second key + * builder.sign(key2); + * if (builder.isFullySigned()){ + * var tx = builder.build(); + * } + * + * var selectedUnspent = build.getSelectedUnspent(); // Retrieve selected unspent outputs from the transaction + */ + + var txid = tx.getHash().toString('hex'); + console.log('Created transaction with txid '+txid); + var raw_tx = tx.serialize().toString('hex'); + console.log('Transaction raw hex dump:'); + console.log('-------------------------------------'); + console.log(raw_tx); + console.log('-------------------------------------'); + // finally, send transaction to the bitcoin network + conn.sendTx(tx); + + // for now, the network won't respond in any case + // (transaction accepted, transaction rejected) + // in the future, we may listen to 'reject' message + // see https://gist.github.com/gavinandresen/7079034 + } + }); + + peerman.start(); ``` ## Parsing a Script diff --git a/examples/SendTx.js b/examples/SendTx.js index 0eed1f1ae..b86eca6a2 100644 --- a/examples/SendTx.js +++ b/examples/SendTx.js @@ -4,67 +4,100 @@ var run = function() { // Replace '../bitcore' with 'bitcore' if you use this code elsewhere. var bitcore = require('../bitcore'); var Peer = bitcore.Peer; - var Transaction = bitcore.Transaction; - var Address = bitcore.Address; - var Script = bitcore.Script; - var coinUtil = bitcore.util; + var TransactionBuilder = bitcore.TransactionBuilder; var PeerManager = bitcore.PeerManager; - var createTx = function() { - var TXIN = 'd05f35e0bbc495f6dcab03e599c8f5e32a07cdb4bc76964de201d06a2a7d8265'; - var TXIN_N = 0; - var ADDR = 'muHct3YZ9Nd5Pq7uLYYhXRAxeW4EnpcaLz'; - var VAL = '0.001'; + // Unspent transactions can be found via the insight.bitcore.io or blockchain.info APIs + var unspent = [{ + 'txid': '707108b5ba4f78dc951df4647a03365bf36432ea57fb641676045c5044daaea7', + 'vout': 0, + 'address': 'n3QDC7DzsMmN4mcyp3k7XGPX7zFXXHG387', + 'scriptPubKey': '76a914f00c4a92ee2314ab08ac0283dc8d07d9bf2be32388ac', + 'amount': 0.12345600, + 'confirmations': 43537 + }, { + 'txid': '87a158d32833cb555aea27b6a21af569ccaeb8f9b19691e05f1e6c2b3440bdb3', + 'vout': 1, + 'address': 'mxdrp9s4mVxS9X4RBYiLe99v59V81XA5C3', + 'scriptPubKey': '76a914bbc87986da6b17c7876db4efacf59a95e14f6cf588ac', + 'amount': 0.05749800, + 'confirmations': 43536 + } - var txobj = { - version: 1, - lock_time: 0, - ins: [], - outs: [] - }; + ]; - var txin = { - s: coinUtil.EMPTY_BUFFER, // Add signature - q: 0xffffffff - }; + // Private keys in WIF format (see TransactionBuilder.js for other options) + var keys = [ + 'cQA75LXhV5JkMT8wkkqjR87SnHK4doh3c21p7PAd5tp8tc1tRBAY', + 'cRz85dz9AiDieRpEwoucfXXQa1jdHHghcv6YnnVVGZ3MQyR1X4u2', + 'cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV', + 'cPa87VgwZfowGZYaEenoQeJgRfKW6PhZ1R65EHTkN1K19cSvc92G', + 'cPQ9DSbBRLva9av5nqeF5AGrh3dsdW8p2E5jS4P8bDWZAoQTeeKB' + ]; - var hash = new Buffer(TXIN.split('').reverse(), 'hex'); - var vout = parseInt(TXIN_N); - var voutBuf = new Buffer(4); - - voutBuf.writeUInt32LE(vout, 0); - txin.o = Buffer.concat([hash, voutBuf]); - txobj.ins.push(txin); - - var addr = new Address(ADDR); - var script = Script.createPubKeyHashOut(addr.payload()); - var valueNum = coinUtil.parseValue(VAL); - var value = coinUtil.bigIntToValue(valueNum); - - var txout = { - v: value, - s: script.getBuffer(), - }; - txobj.outs.push(txout); - - return new Transaction(txobj); - - }; - - var peerman = new PeerManager(); + var peerman = new PeerManager({ + network: 'testnet' + }); peerman.addPeer(new Peer('127.0.0.1', 18333)); peerman.on('connect', function() { var conn = peerman.getActiveConnection(); if (conn) { - conn.sendTx(createTx()); + // define transaction output + var outs = [{ + address: 'mhNCT9TwZAGF1tLPpZdqfkTmtBkY282YDW', + amount: 0.1337 + }]; + // set change address + var opts = { + remainderOut: { + address: 'n4g2TFaQo8UgedwpkYdcQFF6xE2Ei9Czvy' + } + }; + var tx = new TransactionBuilder(opts) + .setUnspent(unspent) + .setOutputs(outs) + .sign(keys) + .build(); + + /* Create and signing can be done in multiple steps: + * + * var builder = new bitcore.TransactionBuilder(opts) + * .setUnspent(utxos) + * .setOutputs(outs); + * + * // Sign with the first key + * builder.sign(key1); + * var tx = builder.build(); // Partially signed transaction + * + * // Sign with the second key + * builder.sign(key2); + * if (builder.isFullySigned()){ + * var tx = builder.build(); + * } + * + * var selectedUnspent = build.getSelectedUnspent(); // Retrieve selected unspent outputs from the transaction + */ + + var txid = tx.getHash().toString('hex'); + console.log('Created transaction with txid '+txid); + var raw_tx = tx.serialize().toString('hex'); + console.log('Transaction raw hex dump:'); + console.log('-------------------------------------'); + console.log(raw_tx); + console.log('-------------------------------------'); + // finally, send transaction to the bitcoin network + conn.sendTx(tx); + + // for now, the network won't respond in any case + // (transaction accepted, transaction rejected) + // in the future, we may listen to 'reject' message + // see https://gist.github.com/gavinandresen/7079034 } - conn.on('reject', function() { - console.log('Transaction Rejected'); - }); }); peerman.start(); + }; module.exports.run = run; From a1ef9c52205f4da58121d6fc6ca1c4aff0991e52 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 17 Apr 2014 15:07:19 -0300 Subject: [PATCH 20/22] make Connection test only run in node ...since it doesn't make sense for the browser --- test/test.Connection.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/test.Connection.js b/test/test.Connection.js index cbf98ce46..5b6379e11 100644 --- a/test/test.Connection.js +++ b/test/test.Connection.js @@ -23,13 +23,16 @@ describe('Connection', function() { var c = new Connection(mSocket, mPeer); should.exist(c); }); - it('should create a proxied socket if instructed', function() { - var mPeer; - var c = new Connection(null, mPeer, { - proxy: { host: 'localhost', port: 9050 } + + if (typeof process !== 'undefined' && process.versions) { //node-only tests + it('should create a proxied socket if instructed', function() { + var mPeer; + var c = new Connection(null, mPeer, { + proxy: { host: 'localhost', port: 9050 } + }); + should.exist(c.socket); }); - should.exist(c.socket); - }); + }; }); From 026ebe38aa4618d21f0994b1551d95043de4813a Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 17 Apr 2014 15:07:41 -0300 Subject: [PATCH 21/22] update tor example to have better error messages --- examples/ConnectionTor.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/ConnectionTor.js b/examples/ConnectionTor.js index aa006f677..2d9d8a662 100644 --- a/examples/ConnectionTor.js +++ b/examples/ConnectionTor.js @@ -6,6 +6,9 @@ var dns = require('dns'); dns.resolve('dnsseed.bluematt.me', function(err, seeds) { // use the first peer var peer = new Peer(seeds[0], 8333); + + //Custom peer: + //var peer = new Peer('180.153.139.246', '8888'); // create a connection without an existing socket // but specify a socks5 proxy to create a socket @@ -21,7 +24,11 @@ dns.resolve('dnsseed.bluematt.me', function(err, seeds) { }); connection.on('error', function(err) { - console.log(err); + console.log('There was an error running this example.'); + console.log('Are you running Tor? Tor must running for this example to work.'); + console.log('If you still get an error, you may need to use a different proxy from here:'); + console.log('http://sockslist.net/'); + //console.log(err); }); }); From 9f278cf848c05360ea85f0d1965d5328afbde637 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Thu, 17 Apr 2014 15:17:33 -0300 Subject: [PATCH 22/22] fix "config" vs "options" vs "opts" bug --- Connection.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Connection.js b/Connection.js index eff0aa111..544c17c7e 100644 --- a/Connection.js +++ b/Connection.js @@ -31,9 +31,9 @@ function Connection(socket, peer, opts) { this.peer = peer; // check for socks5 proxy options and construct a proxied socket - if (this.options.proxy) { + if (this.config.proxy) { var Socks5Client = imports.Socks5Client || require('socks5-client'); - this.socket = new Socks5Client(config.proxy.host, config.proxy.port); + this.socket = new Socks5Client(this.config.proxy.host, this.config.proxy.port); } // A connection is considered "active" once we have received verack