From d5153850ad44e344dd9047454967563a30fdd645 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Thu, 17 Apr 2014 11:19:54 -0300 Subject: [PATCH 1/3] 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 319a723..1545093 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 d822986..76daa5c 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 0d0bdba..2cccac9 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 3664660..5dddd80 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 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. */ From 84ec7bae4c845c20f7b17801775783d4872c0657 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Thu, 17 Apr 2014 11:27:48 -0300 Subject: [PATCH 2/3] 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 76daa5c..8e90317 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 2cccac9..06d2c26 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 5dddd80..0eed1f1 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 3/3] 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 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 -}); + 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 0eed1f1..b86eca6 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;