Merge pull request #35 from isocolsky/units

Parse amount in different units
This commit is contained in:
Matias Alejo Garcia 2015-02-18 16:04:57 -03:00
commit 7539156001
4 changed files with 111 additions and 21 deletions

View File

@ -17,17 +17,6 @@ var BASE_URL = 'http://localhost:3001/copay/api';
var WALLET_CRITICAL_DATA = ['xPrivKey', 'm', 'publicKeyRing']; var WALLET_CRITICAL_DATA = ['xPrivKey', 'm', 'publicKeyRing'];
function _createProposalOpts(opts, signingKey) {
var args = {
toAddress: opts.toAddress,
amount: opts.amount,
message: _encryptProposalMessage(opts.message, signingKey),
};
var hash = WalletUtils.getProposalHash(args.toAddress, args.amount, args.message);
args.proposalSignature = WalletUtils.signMessage(hash, signingKey);
return args;
};
function _encryptProposalMessage(message, encryptingKey) { function _encryptProposalMessage(message, encryptingKey) {
if (!message) return null; if (!message) return null;
return WalletUtils.encryptMessage(message, encryptingKey); return WalletUtils.encryptMessage(message, encryptingKey);
@ -298,18 +287,31 @@ API.prototype.getStatus = function(cb) {
/** /**
* send * send
* *
* @param inArgs * @param opts
* @param inArgs.toAddress * @param opts.toAddress
* @param inArgs.amount * @param opts.amount
* @param inArgs.message * @param opts.message
*/ */
API.prototype.sendTxProposal = function(inArgs, cb) { API.prototype.sendTxProposal = function(opts, cb) {
var self = this; var self = this;
this._loadAndCheck(function(err, data) { this._loadAndCheck(function(err, data) {
if (err) return cb(err); if (err) return cb(err);
var args = _createProposalOpts(inArgs, data.signingPrivKey); var amount;
try {
amount = WalletUtils.parseAmount(opts.amount);
} catch (ex) {
return cb(ex);
}
var args = {
toAddress: opts.toAddress,
amount: amount,
message: _encryptProposalMessage(opts.message, data.signingPrivKey),
};
var hash = WalletUtils.getProposalHash(args.toAddress, args.amount, args.message);
args.proposalSignature = WalletUtils.signMessage(hash, data.signingPrivKey);
var url = '/v1/txproposals/'; var url = '/v1/txproposals/';
self._doPostRequest(url, args, data, cb); self._doPostRequest(url, args, data, cb);

View File

@ -1,11 +1,13 @@
var _ = require('lodash'); var _ = require('lodash');
var sjcl = require('sjcl');
var Bitcore = require('bitcore'); var Bitcore = require('bitcore');
var Address = Bitcore.Address; var Address = Bitcore.Address;
var PrivateKey = Bitcore.PrivateKey; var PrivateKey = Bitcore.PrivateKey;
var PublicKey = Bitcore.PublicKey; var PublicKey = Bitcore.PublicKey;
var crypto = Bitcore.crypto; var crypto = Bitcore.crypto;
var HDPath = require('./hdpath'); var HDPath = require('./hdpath');
var sjcl = require('sjcl'); var Utils = require('./utils');
function WalletUtils() {}; function WalletUtils() {};
@ -98,4 +100,31 @@ WalletUtils.decryptMessage = function(cyphertextJson, privKey) {
return sjcl.decrypt(key, cyphertextJson); return sjcl.decrypt(key, cyphertextJson);
}; };
WalletUtils.UNITS = {
'btc': 100000000,
'bit': 100,
'sat': 1,
};
WalletUtils.parseAmount = function(text) {
var regex = '^(\\d*(\\.\\d{0,8})?)\\s*(' + _.keys(WalletUtils.UNITS).join('|') + ')?$';
var match = new RegExp(regex, 'i').exec(text.trim());
if (!match || match.length === 0) throw new Error('Invalid amount');
var amount = parseFloat(match[1]);
if (!_.isNumber(amount) || _.isNaN(amount)) throw new Error('Invalid amount');
var unit = (match[3] || 'sat').toLowerCase();
var rate = WalletUtils.UNITS[unit];
if (!rate) throw new Error('Invalid unit')
var amountSat = Utils.strip(amount * rate);
if (amountSat != Math.round(amountSat)) throw new Error('Invalid amount');
return amountSat;
};
module.exports = WalletUtils; module.exports = WalletUtils;

View File

@ -183,13 +183,13 @@ describe('client API ', function() {
var args = { var args = {
toAddress: '2N3fA6wDtnebzywPkGuNK9KkFaEzgbPRRTq', toAddress: '2N3fA6wDtnebzywPkGuNK9KkFaEzgbPRRTq',
amount: 100000, amount: '200bit',
message: 'some message', message: 'some message',
}; };
client.sendTxProposal(args, function(err) { client.sendTxProposal(args, function(err) {
var callArgs = request.getCall(0).args[0].body; var callArgs = request.getCall(0).args[0].body;
callArgs.toAddress.should.equal(args.toAddress); callArgs.toAddress.should.equal(args.toAddress);
callArgs.amount.should.equal(args.amount); callArgs.amount.should.equal(20000);
callArgs.message.should.not.equal(args.message); callArgs.message.should.not.equal(args.message);
var decryptedMsg = WalletUtils.decryptMessage(callArgs.message, '42798f82c4ed9ace4d66335165071edf180e70bc0fc08dacb3e35185a2141d5b'); var decryptedMsg = WalletUtils.decryptMessage(callArgs.message, '42798f82c4ed9ace4d66335165071edf180e70bc0fc08dacb3e35185a2141d5b');
decryptedMsg.should.equal(args.message); decryptedMsg.should.equal(args.message);

View File

@ -6,7 +6,6 @@ var sinon = require('sinon');
var should = chai.should(); var should = chai.should();
var WalletUtils = require('../lib/walletutils'); var WalletUtils = require('../lib/walletutils');
var aText = 'hola'; var aText = 'hola';
var aPubKey = '03bec86ad4a8a91fe7c11ec06af27246ec55094db3d86098b7d8b2f12afe47627f'; var aPubKey = '03bec86ad4a8a91fe7c11ec06af27246ec55094db3d86098b7d8b2f12afe47627f';
var aPrivKey = '09458c090a69a38368975fb68115df2f4b0ab7d1bc463fc60c67aa1730641d6c'; var aPrivKey = '09458c090a69a38368975fb68115df2f4b0ab7d1bc463fc60c67aa1730641d6c';
@ -58,6 +57,7 @@ describe('WalletUtils', function() {
res.should.equal(true); res.should.equal(true);
}); });
}); });
describe('#signMessage #verifyMessage round trip', function() { describe('#signMessage #verifyMessage round trip', function() {
it('Should sign and verify', function() { it('Should sign and verify', function() {
var aLongerText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; var aLongerText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
@ -74,4 +74,63 @@ describe('WalletUtils', function() {
msg.should.equal('hello world'); msg.should.equal('hello world');
}); });
}); });
describe('#parseAmount', function() {
it('should successfully parse amounts', function() {
var texts = {
'1': 1,
'0': 0,
'1.': 1,
'000000.0000': 0,
'123': 123,
'123sat': 123,
'123 sat': 123,
'00123 sat': 123,
'1.23bit': 123,
'1.23 bit': 123,
'0 bit': 0,
'.45bit': 45,
'1btc': 100000000,
' 1btc': 100000000,
'9999btc': 999900000000,
'0.00000001btc': 1,
'00000.00000001BTC': 1,
'0.00000001 BTC': 1,
'0.123btc': 12300000,
'0.123 bTc': 12300000,
};
_.each(texts, function(satoshi, text) {
var amount = WalletUtils.parseAmount(text);
amount.should.equal(satoshi);
});
});
it('should fail to parse incorrect amounts', function() {
var texts = [
'',
' ',
'btc',
'1satoshi',
'no-number',
'-3',
'1 b t c',
'btc1',
'btc 1',
'1,234',
'0.000000001btc',
'0.1sat',
'0.123bit',
'2.000000009btc',
];
_.each(texts, function(text) {
var valid = true;
try {
var amount = WalletUtils.parseAmount(text);
} catch (e) {
valid = false;
}
valid.should.be.false;
});
});
});
}); });