Merge pull request #483 from yemel/feature/BIP21

Feature/bip21
This commit is contained in:
Manuel Aráoz 2014-08-12 12:17:40 -04:00
commit 8e1329d240
8 changed files with 595 additions and 41713 deletions

View File

@ -46,6 +46,7 @@ requireWhenAccessed('BinaryParser', './util/BinaryParser');
requireWhenAccessed('Address', './lib/Address');
requireWhenAccessed('AuthMessage', './lib/AuthMessage');
requireWhenAccessed('HierarchicalKey', './lib/HierarchicalKey');
requireWhenAccessed('BIP21', './lib/BIP21');
Object.defineProperty(module.exports, 'BIP32', {
get: function() {
console.log('BIP32 is deprecated. Use bitcore.HierarchicalKey instead.');

View File

@ -16,6 +16,7 @@ var modules = [
'lib/AuthMessage',
'lib/Base58',
'lib/HierarchicalKey',
'lib/BIP21',
'lib/BIP39',
'lib/BIP39WordlistEn',
'lib/Block',
@ -73,6 +74,7 @@ var createBitcore = function(opts) {
//modules included in "all" but not included in "main" bundle
if (opts.includemain) {
submodules = JSON.parse(JSON.stringify(modules));
submodules.splice(submodules.indexOf('lib/BIP21'), 1);
submodules.splice(submodules.indexOf('lib/BIP39'), 1);
submodules.splice(submodules.indexOf('lib/BIP39WordlistEn'), 1);
submodules.splice(submodules.indexOf('lib/PayPro'), 1);
@ -81,7 +83,7 @@ var createBitcore = function(opts) {
submodules.splice(submodules.indexOf('lib/PeerManager'), 1);
submodules.splice(submodules.indexOf('lib/NetworkMonitor'), 1);
var assert = require('assert');
assert(submodules.length == modules.length - 7);
assert(submodules.length == modules.length - 8);
}
if (opts.submodules) {

File diff suppressed because one or more lines are too long

View File

@ -1121,6 +1121,7 @@ var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
? Uint8Array
: Array
var ZERO = '0'.charCodeAt(0)
var PLUS = '+'.charCodeAt(0)
var SLASH = '/'.charCodeAt(0)
var NUMBER = '0'.charCodeAt(0)
@ -1229,9 +1230,9 @@ var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
return output
}
exports.toByteArray = b64ToByteArray
exports.fromByteArray = uint8ToBase64
}(typeof exports === 'undefined' ? (this.base64js = {}) : exports))
module.exports.toByteArray = b64ToByteArray
module.exports.fromByteArray = uint8ToBase64
}())
},{}],4:[function(require,module,exports){
exports.read = function(buffer, offset, isLE, mLen, nBytes) {
@ -2002,8 +2003,8 @@ function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
}).call(this,require("/home/maraoz/git/bitcore/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./support/isBuffer":7,"/home/maraoz/git/bitcore/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":6,"inherits":5}],"3kNi7S":[function(require,module,exports){
}).call(this,require("/Users/yemeljardi/code/bitcore/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"),typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./support/isBuffer":7,"/Users/yemeljardi/code/bitcore/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":6,"inherits":5}],"3kNi7S":[function(require,module,exports){
/*jslint eqeqeq: false, onevar: false, forin: true, nomen: false, regexp: false, plusplus: false*/
/*global module, require, __dirname, document*/
/**
@ -2930,8 +2931,8 @@ module.exports=require('3kNi7S');
}
}(typeof sinon == "object" && sinon || null));
}).call(this,require("/home/maraoz/git/bitcore/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"))
},{"../sinon":"3kNi7S","/home/maraoz/git/bitcore/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":6}],13:[function(require,module,exports){
}).call(this,require("/Users/yemeljardi/code/bitcore/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js"))
},{"../sinon":"3kNi7S","/Users/yemeljardi/code/bitcore/node_modules/browserify/node_modules/insert-module-globals/node_modules/process/browser.js":6}],13:[function(require,module,exports){
/**
* @depend ../sinon.js
* @depend match.js

111
lib/BIP21.js Normal file
View File

@ -0,0 +1,111 @@
'use strict';
// BIP21
// =======
// Helper for parsing and building bitcoin: URIs
//
// Examples:
// =======
// var uriString = 'bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?message=Hey%20there&amount=1.212';
//
// var uri = new BIP21(uriString);
// uri.isValid() // true
// uri.address // bitcore.Address object
// uri.data.message // 'Hey there'
// uri.data.amount // 1.212
//
// uriString = new BIP21({
// address: '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj',
// message: 'Hey there',
// amount: 1.212
// }).getURI();
var URL = require('url');
var Address = require('./Address');
var BIP21 = function(arg) {
this.data = {};
this.address = undefined;
if (typeof(arg) == 'string') {
this.parse(arg);
} else if (typeof(arg) == 'object') {
this.fromObj(arg);
} else if (typeof(arg) != 'undefined') {
throw new Error('Invalid argument');
}
}
BIP21.prototype.fromObj = function(obj) {
for (var key in obj) {
this.data[key] = obj[key];
}
if (obj.address) {
delete this.data.address;
this.setAddress(obj.address);
}
}
BIP21.prototype.parse = function(uri) {
var info = URL.parse(uri, true);
if (info.protocol != 'bitcoin:') {
throw new Error('Invalid protocol');
}
// workaround to host insensitiveness
var group = /[^:]*:\/?\/?([^?]*)/.exec(uri);
this.setAddress(group && group[1]);
for (var arg in info.query) {
var val = info.query[arg];
if (arg === 'amount') val = Number(val);
if (arg === 'r') this.data.merchant = val;
this.data[arg] = val;
}
}
BIP21.prototype.isValid = function(known) {
var knownArguments = known || [];
var valid = true;
if (typeof(this.data.amount) != 'undefined') {
valid &= !isNaN(this.data.amount);
}
if (this.address) {
valid &= typeof(this.address) == 'object' && this.address.isValid();
}
// Require address or PayPro info
valid &= !!(this.address || this.data.r);
// Check required arguments
for (var key in this.data) {
if (key.indexOf('req-') == 0) {
valid &= knownArguments.indexOf(key) != -1;
}
}
return !!valid;
}
BIP21.prototype.setAddress = function(addr) {
if (addr) {
this.address = Address.validate(addr) ? new Address(addr) : addr;
}
return this;
}
BIP21.prototype.getURI = function() {
return URL.format({
protocol: 'bitcoin:',
host: this.address,
query: this.data
});
}
module.exports = BIP21;

View File

@ -21,6 +21,7 @@
<script src="test.Base58.js"></script>
<script src="test.basic.js"></script>
<script src="test.Bignum.js"></script>
<script src="test.BIP21.js"></script>
<script src="test.BIP39.js"></script>
<script src="test.Block.js"></script>
<script src="test.Bloom.js"></script>

156
test/test.BIP21.js Normal file
View File

@ -0,0 +1,156 @@
'use strict';
var chai = chai || require('chai');
var should = chai.should();
var bitcore = bitcore || require('../bitcore');
var BIP21 = bitcore.BIP21;
describe('BIP21', function() {
it('should support livent address', function() {
var uri;
uri = new BIP21('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj');
uri.isValid().should.be.true;
uri.address.network().name = 'livenet';
uri.address.toString().should.equal('1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj');
uri = new BIP21('bitcoin:3FDFgmroEu5EFxxhHUkZL8vNJ3xrjUF3mB');
uri.isValid().should.be.true;
uri.address.network().name = 'livenet';
uri.address.toString().should.equal('3FDFgmroEu5EFxxhHUkZL8vNJ3xrjUF3mB');
});
it('should support testnet address', function() {
var uri;
uri = new BIP21('bitcoin:myNUd9RyL6VcLNdiTkYPDz9pQK6fo2JqYy');
uri.isValid().should.be.true;
uri.address.network().name = 'testnet';
uri.address.toString().should.equal('myNUd9RyL6VcLNdiTkYPDz9pQK6fo2JqYy');
uri = new BIP21('bitcoin:2N3EPfMSXoaEcNfuvLETKVfF4iddDsnwoYN');
uri.isValid().should.be.true;
uri.address.network().name = 'testnet';
uri.address.toString().should.equal('2N3EPfMSXoaEcNfuvLETKVfF4iddDsnwoYN');
});
it('should support double slash scheme', function() {
var uri = new BIP21('bitcoin://1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj');
uri.isValid().should.be.true;
uri.address.toString().should.equal('1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj');
});
it('should support numeric amounts', function() {
var uri = new BIP21('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?amount=12.10001');
uri.isValid().should.be.true;
should.exist(uri.data.amount);
uri.data.amount.should.equal(12.10001);
});
it('should support extra arguments', function() {
var uri = new BIP21('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?message=Donation%20for%20project%20xyz&label=myLabel&other=xD');
uri.isValid().should.be.true;
should.exist(uri.data.message);
uri.data.message.should.equal('Donation for project xyz');
should.exist(uri.data.label);
uri.data.label.should.equal('myLabel');
should.exist(uri.data.other);
uri.data.other.should.equal('xD');
});
it('should validate address', function() {
var uri = new BIP21('bitcoin:invalidAddress');
uri.isValid().should.be.false;
uri.address.should.equal('invalidAddress');
});
it('should validate amount', function() {
var uri = new BIP21('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?amount=bad');
uri.isValid().should.be.false;
isNaN(uri.data.amount).should.be.true;
});
it('should support payment protocol', function() {
var uri = new BIP21('bitcoin:?message=Hi');
uri.isValid().should.be.false;
uri = new BIP21('bitcoin:?message=Hi&r=some-data');
uri.isValid().should.be.true;
});
it('should build from an object', function() {
var uri = new BIP21({
address: '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj',
amount: 1.10001,
message: 'Hello World'
});
uri.isValid().should.be.true;
uri.address.toString().should.equal('1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj');
uri.data.amount.should.equal(1.10001);
uri.data.message.should.equal('Hello World');
});
it('should build from an object', function() {
var uri = new BIP21({
address: new bitcore.Address('1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj'),
amount: 1.10001,
message: 'Hello World'
});
uri.isValid().should.be.true;
uri.address.toString().should.equal('1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj');
uri.data.amount.should.equal(1.10001);
uri.data.message.should.equal('Hello World');
});
it('should generate a valid URI', function() {
new BIP21({
address: '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj',
}).getURI().should.equal(
'bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj'
);
new BIP21({
address: '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj',
amount: 1.10001,
message: 'Hello World'
}).getURI().should.equal(
'bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?amount=1.10001&message=Hello%20World'
);
new BIP21({
r: 'payment-info'
}).getURI().should.equal(
'bitcoin:?r=payment-info'
);
});
it('should be case insensitive to protocol', function() {
var uri1 = new BIP21('bItcOin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj');
var uri2 = new BIP21('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj');
uri1.address.toString().should.equal(uri2.address.toString());
});
it('should setAddress correctly', function() {
var uri = new BIP21();
uri.isValid().should.be.false;
uri.setAddress('1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj');
uri.address.network().name.should.equal('livenet');
});
it('should check required arguments', function() {
var uri = new BIP21('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?req-somethingyoudontunderstand=50&req-somethingelseyoudontget=999');
uri.isValid().should.be.false;
uri.isValid([
'req-somethingyoudontunderstand',
'req-somethingelseyoudontget'
]).should.be.true;
});
});

View File

@ -6,6 +6,8 @@ var bitcore = bitcore || require('../bitcore');
var BIP39 = bitcore.BIP39;
var BIP39WordlistEn = bitcore.BIP39WordlistEn;
var ON_TRAVIS = typeof(process) != 'undefined' && (process.env.TRAVIS || process.env.CI);
describe('BIP39', function() {
// From python reference code, formatting unchanged
var bip39_vectors = {
@ -147,8 +149,7 @@ describe('BIP39', function() {
//do not run these slow tests on TRAVIS which often fails
var vectors = bip39_vectors['english'];
var process = process || null;
if (!process || (!process.env.TRAVIS && !process.env.CI)) {
if (!ON_TRAVIS) {
for (var v = 0 ; v < vectors.length ; v++) {
(function(v){
it('should pass test vector ' + v, function() {