Add BIP21 implementation

This commit is contained in:
Yemel Jardi 2014-08-11 16:41:19 -03:00
parent 3b60170e9f
commit ac525f226c
7 changed files with 585 additions and 41710 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);

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

104
lib/BIP21.js Normal file
View File

@ -0,0 +1,104 @@
'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 = uri.match('[^:]*:/?/?([^?]*)');
this.setAddress(group && group[1]);
for (var arg in info.query) {
var val = info.query[arg];
if (arg == 'amount') val = Number(val);
this.data[arg] = val;
}
}
BIP21.prototype.isValid = function() {
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);
return !!valid;
}
BIP21.prototype.setAddress = function(addr) {
if (addr) {
this.address = Address.validate(addr) ? new Address(addr) : addr;
}
return this;
}
BIP21.prototype.getURI = function() {
if (!this.isValid()) throw new Error('Invalid state');
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>

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

@ -0,0 +1,157 @@
'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 fail with wrong arguments', function() {
(function() {
new BIP21(12);
}).should.throw(Error);
(function() {
new BIP21();
}).should.not.throw(Error);
});
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');
});
});