diff --git a/.gitignore b/.gitignore index 1b3441e..5effac1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ build/ +browser/bundle.js +browser/vendor.js node_modules/ *.swp *~ diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..7b801cb --- /dev/null +++ b/.jshintrc @@ -0,0 +1,39 @@ +{ + "node": true, // Enable globals available when code is running inside of the NodeJS runtime environment. + "browser": true, // Standard browser globals e.g. `window`, `document`. + "esnext": true, // Allow ES.next specific features such as `const` and `let`. + "bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.). + "camelcase": false, // Permit only camelcase for `var` and `object indexes`. + "curly": false, // Require {} for every new block or scope. + "eqeqeq": true, // Require triple equals i.e. `===`. + "immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` + "latedef": true, // Prohibit variable use before definition. + "newcap": true, // Require capitalization of all constructor functions e.g. `new F()`. + "noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`. + "quotmark": "single", // Define quotes to string values. + "regexp": true, // Prohibit `.` and `[^...]` in regular expressions. + "undef": true, // Require all non-global variables be declared before they are used. + "unused": true, // Warn unused variables. + "strict": true, // Require `use strict` pragma in every file. + "trailing": true, // Prohibit trailing whitespaces. + "smarttabs": false, // Suppresses warnings about mixed tabs and spaces + "globals": { // Globals variables. + "angular": true + }, + "predef": [ // Extra globals. + "define", + "require", + "exports", + "module", + "describe", + "before", + "beforeEach", + "after", + "afterEach", + "requirejs", + "it" + ], + "indent": false, // Specify indentation spacing + "devel": true, // Allow development statements e.g. `console.log();`. + "noempty": true // Prohibit use of empty blocks. +} diff --git a/Address.js b/Address.js index b7c9866..e429a31 100644 --- a/Address.js +++ b/Address.js @@ -5,7 +5,7 @@ function ClassSpec(b) { function Address() { Address.super(this, arguments); - }; + } Address.superclass = superclass; superclass.applyEncodingsTo(Address); @@ -13,10 +13,10 @@ function ClassSpec(b) { Address.prototype.validate = function() { this.doAsBinary(function() { Address.super(this, 'validate', arguments); - if(this.data.length != 21) throw new Error('invalid data length'); + if(this.data.length !== 21) throw new Error('invalid data length'); }); }; return Address; -}; +} module.defineClass(ClassSpec); diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..32d0f9d --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,48 @@ +'use strict'; + +module.exports = function(grunt) { + + //Load NPM tasks + grunt.loadNpmTasks('grunt-browserify'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-mocha-test'); + + // Project Configuration + grunt.initConfig({ + browserify: { + client: { + src: ['bitcore.js'], + dest: 'browser/bundle.js', + options: { + debug: true, + alias: ['browserify-bignum/bignumber.js:bignum'], + standalone: 'bitcore', + } + }, + vendor: { + src: ['browser/vendor_load.js'], + dest: 'browser/vendor.js', + options: { + + } + } + }, + watch: { + scripts: { + files: ['**/*.js', '**/*.html', '!**/node_modules/**', '!**/bundle.js', '!**/vendor.js'], + tasks: ['browserify'/*, 'mochaTest'*/], + }, + }, + mochaTest: { + options: { + reporter: 'spec', + }, + src: ['test/*.js'], + }, + + }); + + grunt.registerTask('default', ['watch']); + +}; + diff --git a/PeerManager.js b/PeerManager.js index 6d9eee7..de38038 100644 --- a/PeerManager.js +++ b/PeerManager.js @@ -4,7 +4,8 @@ function spec(b) { var config = b.config || require('./config'); var log = b.log || require('./util/log'); var network = b.network || require('./networks')[config.network]; - var Connection = b.Connection || require('./Connection').createClass({config: config}); + var Connection = b.Connection || require('./Connection').createClass( + {config: config, network: network}); var Peer = b.Peer || require('./Peer').class(); var noop = function() {}; diff --git a/README.md b/README.md index 6a13695..ea7d4af 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,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, but the developer community needs reliable, open-source tools to implement bitcoin apps and services. +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.** @@ -17,7 +17,7 @@ Bitcore runs on [node](http://nodejs.org/), and can be installed via [npm](https npm install bitcore ``` -It's is a collection of objects useful to bitcoin applications; class-like idioms are enabled via [Classtool](https://github.com/gasteve/classtool). In most cases, a developer will require the object's class directly: +It is a collection of objects useful to bitcoin applications; class-like idioms are enabled via [Classtool](https://github.com/gasteve/classtool). In most cases, a developer will require the object's class directly: ``` var Address = require('bitcore/Address').class(); ``` @@ -46,7 +46,11 @@ Bitcore is still under heavy development and not quite ready for "drop-in" produ #Contributing Bitcore needs some developer love. Please send pull requests for bug fixes, code optimization, and ideas for improvement. +To build bitcore for the browser: +``` +npm install -g grunt-cli +grunt browserify +``` - -[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/dreyzehner/bitcore/trend.png)](https://bitdeli.com/free "Bitdeli Badge") +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/bitpay/bitcore/trend.png)](https://bitdeli.com/free "Bitdeli Badge") diff --git a/RpcClient.js b/RpcClient.js index b8948e1..80a1972 100644 --- a/RpcClient.js +++ b/RpcClient.js @@ -164,9 +164,14 @@ function ClassSpec(b) { }); res.on('end', function() { if(res.statusCode == 401) { - callback(new Error('bitcoin JSON-RPC connection rejected: unauthorized')); + callback(new Error('bitcoin JSON-RPC connection rejected: 401 unauthorized')); return; } + if(res.statusCode == 403) { + callback(new Error('bitcoin JSON-RPC connection rejected: 403 forbidden')); + return; + } + if(err) { callback(err); return; @@ -176,6 +181,7 @@ function ClassSpec(b) { } catch(e) { log.err(e.stack); log.err(buf); + log.err('HTTP Status code:' + res.statusCode); callback(e); return; } diff --git a/bitcore.js b/bitcore.js new file mode 100644 index 0000000..551a834 --- /dev/null +++ b/bitcore.js @@ -0,0 +1,16 @@ +/* + * Bitcore bindings for the browser + */ + + +module.exports.bignum = require('bignum'); +module.exports.base58 = require('base58-native'); +module.exports.EncodedData = require('./util/EncodedData'); +module.exports.VersionedData = require('./util/VersionedData'); +module.exports.Address= require('./Address'); + + +if (typeof process.versions === 'undefined') { + module.exports.bignum.config({EXPONENTIAL_AT: 9999999, DECIMAL_PLACES: 0, ROUNDING_MODE: 1}); +} + diff --git a/browser/sample.html b/browser/sample.html new file mode 100644 index 0000000..b02b85d --- /dev/null +++ b/browser/sample.html @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/browser/vendor_load.js b/browser/vendor_load.js new file mode 100644 index 0000000..9a4251f --- /dev/null +++ b/browser/vendor_load.js @@ -0,0 +1,5 @@ + +// load modules needed for testing in the browser + +var fs = require('fs'); + diff --git a/package.json b/package.json index 4a24d02..1d1dfe4 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,15 @@ "email": "stephen@bitpay.com" }, "contributors": [ - {"name": "Stefan Thomas", "email": "moon@justmoon.net"}, - {"name": "Jeff Garzik", "email": "jgarzik@bitpay.com"} - ], + { + "name": "Stefan Thomas", + "email": "moon@justmoon.net" + }, + { + "name": "Jeff Garzik", + "email": "jgarzik@bitpay.com" + } + ], "keywords": [ "bitcoin", "btc", @@ -25,16 +31,22 @@ "scripts": {}, "dependencies": { "classtool": "=1.0.0", - "base58-native": "=0.1.1", - "bindings": "=1.1.0", + "base58-native": "=0.1.3", + "bindings": "=1.1.1", "bufferput": "=0.1.1", "bignum": "=0.6.1", "binary": "=0.3.0", "step": "=0.0.4", "buffers": "=0.1.1", "buffertools": "=1.1.1", - "mocha": ">=1.15.1" + "mocha": ">=1.15.1", + "browserify-bignum": "git://github.com/maraoz/browserify-bignum.git" + }, + "devDependencies": { + "grunt-contrib-watch": "~0.5.3", + "grunt-mocha-test": "~0.8.2", + "grunt-browserify": "~1.3.0", + "chai": "~1.9.0" }, - "devDependencies": {}, "license": "MIT" } diff --git a/test/adapter.js b/test/adapter.js new file mode 100644 index 0000000..80a63ce --- /dev/null +++ b/test/adapter.js @@ -0,0 +1,23 @@ +'use strict'; + +if (typeof require === 'undefined') { + var that = this; + that.require = function(name) { + var split = name.split('/'); + if (split.length > 0) { + name = split.pop(); + } + var module = that[name]; + if (!module) { + throw new Error('Cannot find module "'+name+'"'); + } + return module; + }; + +} + + +if (typeof module === 'undefined') { + var that = this; + that.module = bitcore.module; +} diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..0d62a92 --- /dev/null +++ b/test/index.html @@ -0,0 +1,27 @@ + + + + Mocha + + + + + +
+ + + + + + + + + + + + + + + diff --git a/test/test.Address.js b/test/test.Address.js new file mode 100644 index 0000000..ec0787b --- /dev/null +++ b/test/test.Address.js @@ -0,0 +1,36 @@ +'use strict'; + +var chai = require('chai'); +var bitcore = require('../bitcore'); + +var should = chai.should(); + +var AddressModule = bitcore.Address; +var Address; + +describe('Address', function() { + it('should initialze the main object', function() { + should.exist(AddressModule); + }); + it('should be able to create class', function() { + Address = AddressModule.class(); + should.exist(Address); + }); + it('should be able to create Address object', function() { + var a = new Address('1KfyjCgBSMsLqiCbakfSdeoBUqMqLUiu3T'); + should.exist(a); + }); + it('should validate correctly', function() { + var a = new Address("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"); + var m = new Address("32QBdjycLwbDTuGafUwaU5p5GxzSLPYoF6"); + var b = new Address("11111111111111111111111111122222234"); + a.validate.bind(a).should.not.throw(Error); + m.validate.bind(m).should.not.throw(Error); + b.validate.bind(b).should.throw(Error); + }); +}); + + + + + diff --git a/test/test.EncodedData.js b/test/test.EncodedData.js new file mode 100644 index 0000000..26c657b --- /dev/null +++ b/test/test.EncodedData.js @@ -0,0 +1,28 @@ +'use strict'; + +var chai = require('chai'); +var bitcore = require('../bitcore'); + +var should = chai.should(); + +var EncodedDataModule = bitcore.EncodedData; +var EncodedData; + +describe('EncodedData', function() { + it('should initialze the main object', function() { + should.exist(EncodedDataModule); + }); + it('should be able to create class', function() { + EncodedData = EncodedDataModule.class(); + should.exist(EncodedData); + }); + it('should be able to create an instance', function() { + var ed = new EncodedData('1GMx4HdDmN78xzGvdQYkwrVqkmLDG1aMNT'); + should.exist(ed); + }); +}); + + + + + diff --git a/test/test.VersionedData.js b/test/test.VersionedData.js new file mode 100644 index 0000000..0f18360 --- /dev/null +++ b/test/test.VersionedData.js @@ -0,0 +1,34 @@ +'use strict'; + +var chai = require('chai'); +var bitcore = require('../bitcore'); + +var should = chai.should(); + +var VersionedDataModule = bitcore.VersionedData; +var VersionedData; + +describe('VersionedData', function() { + it('should initialze the main object', function() { + should.exist(VersionedDataModule); + }); + it('should be able to create class', function() { + VersionedData = VersionedDataModule.class(); + should.exist(VersionedData); + }); + it('should be able to create an instance', function() { + var vd = new VersionedData(); + should.exist(vd); + }); + it('should get correct version', function() { + var vda = new VersionedData('1GMx4HdDmN78xzGvdQYkwrVqkmLDG1aMNT'); + var vdb = new VersionedData('3746djr32k2Lp23UUbdkCTQ6zhMJ7d8MD7'); + vda.version().should.equal(0); + vdb.version().should.equal(5); + }); +}); + + + + + diff --git a/test/test.base58.js b/test/test.base58.js new file mode 100644 index 0000000..54d57eb --- /dev/null +++ b/test/test.base58.js @@ -0,0 +1,45 @@ +'use strict'; + +var chai = require('chai'); +var bitcore = require('../bitcore'); + +var expect = chai.expect; +var should = chai.should(); + +var bignum = bitcore.bignum; +var base58 = bitcore.base58; +var base58Check = base58.base58Check; + +describe('bignum module basics', function() { + it('should initialze the main object', function() { + should.exist(bitcore.bignum); + }); + it('should create a bignum from string', function() { + var n = bignum('9832087987979879879879879879879879879879879879'); + should.exist(n); + }); + it('should perform basic math operations', function() { + var b = bignum('782910138827292261791972728324982') + .sub('182373273283402171237474774728373') + .div(13); + b.toNumber().should.equal(46195143503376160811884457968969); + }); +}); + + +describe('base58 module', function() { + it('should initialze the main object', function() { + should.exist(bitcore.base58); + }); + it('should obtain the same string in base58 roundtrip', function() { + var m = 'mqqa8xSMVDyf9QxihGnPtap6Mh6qemUkcu'; + base58.encode(base58.decode(m)).should.equal(m); + }); + it('should obtain the same string in base58Check roundtrip', function() { + var m = '1QCJj1gPZKx2EwzGo9Ri8mMBs39STvDYcv'; + base58Check.encode(base58Check.decode(m)).should.equal(m); + }); +}); + + + diff --git a/test/basic.js b/test/test.basic.js similarity index 99% rename from test/basic.js rename to test/test.basic.js index 3939786..0b796c8 100644 --- a/test/basic.js +++ b/test/test.basic.js @@ -1,3 +1,4 @@ +'use strict'; var assert = require('assert'); var fs = require('fs'); diff --git a/test/test.main.js b/test/test.main.js new file mode 100644 index 0000000..aa6e8a2 --- /dev/null +++ b/test/test.main.js @@ -0,0 +1,13 @@ +'use strict'; + +var chai = require('chai'); +var bitcore = require('../bitcore'); + +var expect = chai.expect; +var should = chai.should(); + +describe('Initialization of bitcore', function() { + it('should initialze the main object', function() { + should.exist(bitcore); + }); +}); diff --git a/util/EncodedData.js b/util/EncodedData.js index 5c1e87e..05557fa 100644 --- a/util/EncodedData.js +++ b/util/EncodedData.js @@ -130,17 +130,20 @@ function ClassSpec(b) { }, }; + var no_conversion = function() {return this.data;}; for(var k in encodings) { - if(!encodings[k].converters[k]) - encodings[k].converters[k] = function() {return this.data;}; - encodings[k]._encoding = k; + if(encodings.hasOwnProperty(k)){ + if(!encodings[k].converters[k]) + encodings[k].converters[k] = no_conversion; + encodings[k]._encoding = k; + } } EncodedData.applyEncodingsTo = function(aClass) { var tmp = {}; for(var k in encodings) { var enc = encodings[k]; - var obj = {}; + var obj = {}; for(var j in enc) { obj[j] = enc[j]; } @@ -152,5 +155,5 @@ function ClassSpec(b) { EncodedData.applyEncodingsTo(EncodedData); return EncodedData; -}; +} module.defineClass(ClassSpec);