diff --git a/Connection.js b/Connection.js index 27a4fb6..eff0aa1 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'); @@ -25,15 +24,16 @@ var BIP0031_VERSION = 60000; function Connection(socket, peer, opts) { Connection.super(this, arguments); - this.options = opts || {}; - + this.config = opts || bitcoreDefaults; + + this.network = networks[this.config.network] || networks.livenet; this.socket = socket; this.peer = peer; // 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); + this.socket = new Socks5Client(config.proxy.host, config.proxy.port); } // A connection is considered "active" once we have received verack @@ -146,7 +146,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)); @@ -160,7 +160,7 @@ Connection.prototype.handleMessage = function(message) { break; case 'ping': - if ("object" === typeof message.nonce) { + if ('object' === typeof message.nonce) { this.sendPong(message.nonce); } break; @@ -300,7 +300,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'; @@ -323,13 +323,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())); } }; @@ -338,7 +338,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; @@ -351,7 +351,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] && @@ -386,13 +386,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 273d9f6..8e90317 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; @@ -28,7 +25,7 @@ function PeerManager() { this.interval = 5000; this.minConnections = 8; this.minKnownPeers = 10; -}; +} PeerManager.parent = imports.parent || require('events').EventEmitter; PeerManager.Connection = Connection; @@ -95,8 +92,8 @@ PeerManager.prototype.connectTo = function(peer) { } }; -PeerManager.prototype.addConnection = function(socketConn, peer, opts) { - var conn = new Connection(socketConn, peer, opts); +PeerManager.prototype.addConnection = function(socketConn, peer) { + var conn = new Connection(socketConn, peer, this.config); this.connections.push(conn); this.emit('connection', conn); diff --git a/README.md b/README.md index df162db..9ce5ebd 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 -}); - -// 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 - } -]; - -// 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 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 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 = [{ + '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 = [ + 'cQA75LXhV5JkMT8wkkqjR87SnHK4doh3c21p7PAd5tp8tc1tRBAY', + 'cRz85dz9AiDieRpEwoucfXXQa1jdHHghcv6YnnVVGZ3MQyR1X4u2', + 'cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV', + 'cPa87VgwZfowGZYaEenoQeJgRfKW6PhZ1R65EHTkN1K19cSvc92G', + 'cPQ9DSbBRLva9av5nqeF5AGrh3dsdW8p2E5jS4P8bDWZAoQTeeKB' + ]; + + 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) { + // 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(); + peerman.start(); ``` ## Parsing a Script diff --git a/examples/PeerManager.js b/examples/PeerManager.js index 0d0bdba..06d2c26 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 **'); @@ -29,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 3664660..b86eca6 100644 --- a/examples/SendTx.js +++ b/examples/SendTx.js @@ -3,71 +3,101 @@ 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('soop').load('../PeerManager', { - network: networks.testnet - }); - - var createTx = function() { - var TXIN = 'd05f35e0bbc495f6dcab03e599c8f5e32a07cdb4bc76964de201d06a2a7d8265'; - var TXIN_N = 0; - var ADDR = 'muHct3YZ9Nd5Pq7uLYYhXRAxeW4EnpcaLz'; - var VAL = '0.001'; - - var txobj = { - version: 1, - lock_time: 0, - ins: [], - outs: [] - }; - - var txin = { - s: coinUtil.EMPTY_BUFFER, // Add signature - q: 0xffffffff - }; - - 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 TransactionBuilder = bitcore.TransactionBuilder; + var PeerManager = bitcore.PeerManager; - var txout = { - v: value, - s: script.getBuffer(), - }; - txobj.outs.push(txout); + // 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 + } - return new Transaction(txobj); + ]; - }; + // Private keys in WIF format (see TransactionBuilder.js for other options) + var keys = [ + 'cQA75LXhV5JkMT8wkkqjR87SnHK4doh3c21p7PAd5tp8tc1tRBAY', + 'cRz85dz9AiDieRpEwoucfXXQa1jdHHghcv6YnnVVGZ3MQyR1X4u2', + 'cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV', + 'cPa87VgwZfowGZYaEenoQeJgRfKW6PhZ1R65EHTkN1K19cSvc92G', + 'cPQ9DSbBRLva9av5nqeF5AGrh3dsdW8p2E5jS4P8bDWZAoQTeeKB' + ]; - 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; diff --git a/examples/SimpleP2Pmonitor.js b/examples/SimpleP2Pmonitor.js index 1791b9b..838e578 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. */