diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..fce8114 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - "0.10" + +script: "mocha" diff --git a/Address.js b/Address.js index b9a4ed1..c3bf0ee 100644 --- a/Address.js +++ b/Address.js @@ -1,7 +1,9 @@ 'use strict'; var imports = require('soop').imports(); -var parent = imports.parent || require('./util/VersionedData'); -var networks= imports.networks || require('./networks'); +var coinUtil = imports.coinUtil || require('./util/util'); +var parent = imports.parent || require('./util/VersionedData'); +var networks = imports.networks || require('./networks'); +var Script = imports.Script || require('./Script'); function Address() { Address.super(this, arguments); @@ -10,6 +12,47 @@ function Address() { Address.parent = parent; parent.applyEncodingsTo(Address); +//create a pubKeyHash address +Address.fromPubKey = function(pubKey, network) { + if (!network) + network = 'livenet'; + + if (pubKey.length != 33 && pubKey.length != 65) + throw new Error('Invalid public key'); + + var version = networks[network].addressVersion; + var hash = coinUtil.sha256ripe160(pubKey); + + return new Address(version, hash); +}; + +//create a p2sh m-of-n multisig address +Address.fromPubKeys = function(mReq, pubKeys, network, opts) { + if (!network) + network = 'livenet'; + + for (var i in pubKeys) { + var pubKey = pubKeys[i]; + if (pubKey.length != 33 && pubKey.length != 65) + throw new Error('Invalid public key'); + } + + var script = Script.createMultisig(mReq, pubKeys, opts); + + return Address.fromScript(script, network); +}; + +//create a p2sh address from redeemScript +Address.fromScript = function(script, network) { + if (!network) + network = 'livenet'; + + var version = networks[network].P2SHVersion; + var buf = script.getBuffer(); + var hash = coinUtil.sha256ripe160(buf); + + return new Address(version, hash); +}; Address.prototype.validate = function() { this.doAsBinary(function() { diff --git a/Connection.js b/Connection.js index f273ec3..319a723 100644 --- a/Connection.js +++ b/Connection.js @@ -186,10 +186,15 @@ Connection.prototype.sendVersion = function () { }; Connection.prototype.sendGetBlocks = function (starts, stop, wantHeaders) { + // Default value for stop is 0 to get as many blocks as possible (500) + stop = stop || util.NULL_HASH; + var put = new Put(); - put.word32le(this.sendVer); + // https://en.bitcoin.it/wiki/Protocol_specification#getblocks + put.word32le(this.sendVer); put.varint(starts.length); + for (var i = 0; i < starts.length; i++) { if (starts[i].length != 32) { throw new Error('Invalid hash length'); @@ -443,8 +448,8 @@ Connection.prototype.parseMessage = function (command, payload) { data.headers = []; for (i = 0; i < data.count; i++) { var header = new Block(); -header.parse(parser); -data.headers.push(header); + header.parse(parser); + data.headers.push(header); } break; diff --git a/PeerManager.js b/PeerManager.js index c6e2029..d822986 100644 --- a/PeerManager.js +++ b/PeerManager.js @@ -111,6 +111,9 @@ PeerManager.prototype.addConnection = function(socketConn, peer) { }; PeerManager.prototype.handleVersion = function(e) { + e.peer.version = e.message.version; + e.peer.start_height = e.message.start_height; + if (!e.conn.inbound) { // TODO: Advertise our address (if listening) } @@ -132,7 +135,7 @@ PeerManager.prototype.handleReady = function (e) { }); if(this.isConnected == false) { - this.emit('netConnected'); + this.emit('netConnected', e); this.isConnected = true; } }; diff --git a/README.md b/README.md index b2a9396..1cd568f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ Bitcore ======= +[![Build Status](https://travis-ci.org/bitpay/bitcore.svg?branch=master)](https://travis-ci.org/bitpay/bitcore) + 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. diff --git a/browser/build.js b/browser/build.js index 43ae350..941d520 100644 --- a/browser/build.js +++ b/browser/build.js @@ -64,7 +64,10 @@ var createBitcore = function(opts) { opts.dir = opts.dir || ''; // concat browser vendor files - exec('cd ' + opts.dir + 'browser; sh concat.sh', puts); + var cwd = process.cwd(); + process.chdir(opts.dir + 'browser'); + exec('sh concat.sh', puts); + process.chdir(cwd); if (!opts.includeall && (!opts.submodules || opts.submodules.length === 0)) { if (!opts.stdout) console.log('Must use either -s or -a option. For more info use the --help option'); diff --git a/examples/CreateAndSignTx-PayToScriptHash.js b/examples/CreateAndSignTx-PayToScriptHash.js index 8b035ee..f80593c 100644 --- a/examples/CreateAndSignTx-PayToScriptHash.js +++ b/examples/CreateAndSignTx-PayToScriptHash.js @@ -41,10 +41,10 @@ var run = function() { }); // multisig p2sh - var opts = {nreq:3, pubkeys:pubkeys, amount:0.05}; + var opts = {nreq:3, pubkeys:pubkeys}; // p2scriphash p2sh - //var opts = [{address: an_address, amount:0.05}]; + //var opts = [{address: an_address}]; var info = Builder.infoForP2sh(opts, 'testnet'); var p2shScript = info.scriptBufHex; diff --git a/examples/PayToScriptHashAddress.js b/examples/PayToScriptHashAddress.js new file mode 100644 index 0000000..846d6d2 --- /dev/null +++ b/examples/PayToScriptHashAddress.js @@ -0,0 +1,17 @@ +var bitcore = require('../bitcore'); +var Address = bitcore.Address; +var bitcoreUtil = bitcore.util; +var Script = bitcore.Script; +var network = bitcore.networks.livenet; + + +var script = ''; // write down your script here +var s = Script.fromHumanReadable(script); +var hash = bitcoreUtil.sha256ripe160(s.getBuffer()); +var version = network.addressScript; + +var addr = new Address(version, hash); +var addrStr = addr.as('base58'); + +// This outputs the "address" of thescript +console.log(addrStr); diff --git a/examples/SimpleP2Pmonitor.js b/examples/SimpleP2Pmonitor.js new file mode 100644 index 0000000..1791b9b --- /dev/null +++ b/examples/SimpleP2Pmonitor.js @@ -0,0 +1,72 @@ +/** + * This is a simple script that will display network messages. + * It users the Peer / Connection classes * directly instead of + * relying on PeerManager. + */ + +// replace by require('bitcore') if you use somewhere else +var bitcore = require('../'); + +//bitcore.config.logger = 'debug'; + +var Peer = bitcore.Peer, + Connection = bitcore.Connection; + +var peer = new Peer('127.0.0.1', 8333); + +var socket = peer.createConnection(); + +var con = new Connection(socket, peer); + +con.on('error', function (msg) { + var peer = msg.peer, err = msg.err; + console.error('Error connecting to peer', peer.host + ':' + peer.port, '(' + err.message + ')'); +}); + +con.on('disconnect', function (msg) { + console.log('disconnect: ', msg); +}); + +con.on('connect', function (msg) { + console.log('Connected to %s', msg.peer.host + ':' + msg.peer.port); +}); + +/* Listen P2P messages */ + +// Make a log function available to all listeners +// The log function is just like console.log except it prefixes +// messages with [host:port] +function listen (event_name, fn) { + con.on(event_name, function (event) { + fn(event, function () { + var args = Array.prototype.slice.call(arguments); + var str = args.shift(); + str = '[%s:%s] ' + str; + args = [ str, event.peer.host, event.peer.port ].concat(args); + console.log.apply(console, args); + }); + }); +} + +listen('getaddr', function (event, log) { + log('Received message getaddr'); + log(event); +}); + +listen('verack', function (event, log) { + log('Received message verack'); +}); + +listen('version', function (event, log) { + log('Received message version (%s)', event.message.version); +}); + +listen('addr', function (event, log) { + log('Received message addr (%s addresses)', event.message.addrs.length); +}); + +listen('inv', function (event, log) { + log('Received message inv (%s invs)', event.message.count); + console.log(event.message.invs); +}); + diff --git a/examples/browser/README.md b/examples/browser/README.md new file mode 100644 index 0000000..6dcc8b6 --- /dev/null +++ b/examples/browser/README.md @@ -0,0 +1 @@ +Run `node browser/build.js -a` in the repository's root directory before using those examples. diff --git a/examples/example.html b/examples/browser/example.html similarity index 94% rename from examples/example.html rename to examples/browser/example.html index 74a1f64..b0914b3 100644 --- a/examples/example.html +++ b/examples/browser/example.html @@ -9,12 +9,12 @@
       
- + - + diff --git a/examples/simple.html b/examples/browser/simple.html similarity index 52% rename from examples/simple.html rename to examples/browser/simple.html index 740b3e6..18f35ef 100644 --- a/examples/simple.html +++ b/examples/browser/simple.html @@ -1,12 +1,13 @@ - + +

     
   
 
diff --git a/package.json b/package.json
index d702782..7613cd2 100644
--- a/package.json
+++ b/package.json
@@ -79,6 +79,20 @@
     "browser-pack": "~2.0.1",
     "istanbul": "~0.2.6"
   },
+  "testling": {
+    "harness": "mocha-tdd",
+    "html": "test/index-testling.html",
+    "browsers": [
+      "ie/6..latest",
+      "chrome/22..latest",
+      "firefox/16..latest",
+      "safari/latest",
+      "opera/11.0..latest",
+      "iphone/6",
+      "ipad/6",
+      "android-browser/latest"
+    ]
+  },
   "license": "MIT",
   "engines": {
     "node": ">=0.10"
diff --git a/test/index-testling.html b/test/index-testling.html
new file mode 100644
index 0000000..e6a9447
--- /dev/null
+++ b/test/index-testling.html
@@ -0,0 +1,52 @@
+
+
+  
+    Mocha
+    
+    
+    
+  
+  
+    
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/test.Address.js b/test/test.Address.js index 1982553..bffc08a 100644 --- a/test/test.Address.js +++ b/test/test.Address.js @@ -82,5 +82,60 @@ describe('Address', function() { // script testnet new Address('2NBSBcf2KfjPEEqVusmrWdmUeNHRiUTS3Li').isScript().should.equal(true); }); + + describe('#fromPubKey', function() { + it('should make this pubkeyhash address from uncompressed this public key', function() { + var pubkey = new Buffer('04fa05ce8b25010cb6e17a30e0b66668bf083c40687547748ec330ee77adf53a42abd3d26148cbacfcf79c907ddefeb2c37f8bebc0a695ba79d634449d871de218', 'hex'); + var hash = bitcore.util.sha256ripe160(pubkey); + var addr = new Address(0, hash); + addr.toString().should.equal(Address.fromPubKey(pubkey).toString()); + }); + }); + + describe('#fromPubKeys', function() { + it('should make this p2sh multisig address from these pubkeys', function() { + var pubkey1 = new Buffer('03e0973263b4e0d5f5f56d25d430e777ab3838ff644db972c0bf32c31da5686c27', 'hex'); + var pubkey2 = new Buffer('0371f94c57cc013507101e30794161f4e6b9efd58a9ea68838daf429b7feac8cb2', 'hex'); + var pubkey3 = new Buffer('032c0d2e394541e2efdc7ac3500e16e7e69df541f38670402e95aa477202fa06bb', 'hex'); + var sortedPubKeys = [pubkey3, pubkey2, pubkey1]; + var mReq = 2; + var script = bitcore.Script.createMultisig(mReq, sortedPubKeys, {noSorting: true}); + var hash = bitcore.util.sha256ripe160(script.getBuffer()); + var version = bitcore.networks['livenet'].P2SHVersion; + var addr = new Address(version, hash); + var addr2 = Address.fromPubKeys(mReq, sortedPubKeys); + addr.toString().should.equal(addr2.toString()); + }); + }); + + describe('#fromScript', function() { + it('should make this p2sh multisig address from these pubkeys', function() { + var pubkey1 = new Buffer('03e0973263b4e0d5f5f56d25d430e777ab3838ff644db972c0bf32c31da5686c27', 'hex'); + var pubkey2 = new Buffer('0371f94c57cc013507101e30794161f4e6b9efd58a9ea68838daf429b7feac8cb2', 'hex'); + var pubkey3 = new Buffer('032c0d2e394541e2efdc7ac3500e16e7e69df541f38670402e95aa477202fa06bb', 'hex'); + var pubKeys = [pubkey1, pubkey2, pubkey3]; + var mReq = 2; + var script = bitcore.Script.createMultisig(mReq, pubKeys); + var addr = Address.fromScript(script); + var addr2 = Address.fromPubKeys(mReq, pubKeys); + addr.toString().should.equal(addr2.toString()); + }); + + it('it should make this hand-crafted address', function() { + var pubkey1 = new Buffer('03e0973263b4e0d5f5f56d25d430e777ab3838ff644db972c0bf32c31da5686c27', 'hex'); + var pubkey2 = new Buffer('0371f94c57cc013507101e30794161f4e6b9efd58a9ea68838daf429b7feac8cb2', 'hex'); + var pubkey3 = new Buffer('032c0d2e394541e2efdc7ac3500e16e7e69df541f38670402e95aa477202fa06bb', 'hex'); + var pubKeys = [pubkey1, pubkey2, pubkey3]; + var mReq = 2; + var script = bitcore.Script.createMultisig(mReq, pubKeys); + var addr = Address.fromScript(script); + + var hash = bitcore.util.sha256ripe160(script.getBuffer()); + var version = bitcore.networks['livenet'].P2SHVersion; + var addr2 = new Address(version, hash); + + addr.toString().should.equal(addr2.toString()); + }); + }); }); diff --git a/test/test.PeerManager.js b/test/test.PeerManager.js index 5f3aa7f..ab6565d 100644 --- a/test/test.PeerManager.js +++ b/test/test.PeerManager.js @@ -5,7 +5,7 @@ var bitcore = bitcore || require('../bitcore'); var should = chai.should(); -var PeerManagerModule = bitcore.PeerManager || require('./PeerManager'); +var PeerManagerModule = bitcore.PeerManager; var PeerManager; diff --git a/test/test.TransactionBuilder.js b/test/test.TransactionBuilder.js index dc32198..e8804d0 100644 --- a/test/test.TransactionBuilder.js +++ b/test/test.TransactionBuilder.js @@ -563,7 +563,7 @@ describe('TransactionBuilder', function() { }; var data = getInfoForP2sh(); // multisig p2sh - var p2shOpts = {nreq:3, pubkeys:data.pubkeys, amount:0.05}; + var p2shOpts = {nreq:3, pubkeys:data.pubkeys}; var info = TransactionBuilder.infoForP2sh(p2shOpts, network); var outs = outs || [{ @@ -644,7 +644,7 @@ describe('TransactionBuilder', function() { remainderOut: {address: 'mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd'}, }; // p2hash/ p2sh - var p2shOpts = {address:'mgwqzy6pF5BSc72vxHBFSnnhNEBcV4TJzV', amount:0.05}; + var p2shOpts = {address:'mgwqzy6pF5BSc72vxHBFSnnhNEBcV4TJzV'}; var info = TransactionBuilder.infoForP2sh(p2shOpts, network); //addr: 2NAwCQ1jPYPrSsyBQvfP6AJ6d6SSxnHsZ4e diff --git a/test/test.util.js b/test/test.util.js index b8c422d..22180ff 100644 --- a/test/test.util.js +++ b/test/test.util.js @@ -187,4 +187,17 @@ describe('util', function() { }); }); }); + describe('#calcDifficulty', function() { + var bitsgenesis = 486604799; + it('should work for the bits from the genesis block; ' + bitsgenesis, function() { + var difficulty = coinUtil.calcDifficulty(bitsgenesis); + difficulty.should.equal(1); + }); + var randomotherbits = 419476394; + it('should work for the bits in a randomly chosen block, eg [00000000000000001fef2bbc6da9b65e16f9187b7d88f15a308490bf2c9b8e1d] ' + randomotherbits, function() { + var difficulty = coinUtil.calcDifficulty(randomotherbits); + difficulty.should.equal(6119726089); + }); + }); + }); diff --git a/util/util.js b/util/util.js index eec49b2..f89dce8 100644 --- a/util/util.js +++ b/util/util.js @@ -510,4 +510,4 @@ exports.INT64_MAX = INT64_MAX; // makes 1 BTC exports.COIN = 100000000; -exports.MAX_TARGET = new Buffer('00000000FFFF0000000000000000000000000000000000000000000000000000', 'hex'); +var MAX_TARGET = exports.MAX_TARGET = new Buffer('00000000FFFF0000000000000000000000000000000000000000000000000000', 'hex');