Skip to content

Commit

Permalink
[fix] bigcompany#1 Safeguard amounts precision
Browse files Browse the repository at this point in the history
  • Loading branch information
sevastos committed Dec 24, 2013
1 parent 273a8e6 commit a53fa6a
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 27 deletions.
18 changes: 16 additions & 2 deletions lib/deposit.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
var bignum = require('bignumber.js');

module['exports'] = function (options, callback) {
var wallet = require('resource').wallet;
wallet.get(options.id, function(err, result){
Expand All @@ -6,10 +8,22 @@ module['exports'] = function (options, callback) {
}
if (typeof result.currencies[options.currency] === 'undefined') {
result.currencies[options.currency] = {
"amount": 0
'amount': '0'
};
}
result.currencies[options.currency].amount += options.amount;

// Checks for loss of precision
if (typeof options.amount === 'number' && String(options.amount).length > 15) {
console.log('warning', 'Possible precision loss on transaction amount:', options.amount);
}
var currencyAmount = result.currencies[options.currency].amount;
if (typeof currencyAmount === 'number' && String(currencyAmount).length > 15) {
console.log('warning', 'Possible precision loss on balance amount:', currencyAmount);
}
var amount = bignum(options.amount)
.plus(result.currencies[options.currency].amount);
result.currencies[options.currency].amount = amount.toFixed();

if (result.status === "new") {
result.status = "active";
}
Expand Down
17 changes: 14 additions & 3 deletions lib/withdraw.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
var bignum = require('bignumber.js');

module['exports'] = function (options, callback) {
var wallet = require('resource').wallet;
wallet.get(options.id, function(err, result){
Expand All @@ -7,12 +9,21 @@ module['exports'] = function (options, callback) {
if(typeof result.currencies[options.currency] === 'undefined') {
return callback(err, 'failure');
}
if(result.currencies[options.currency].amount < options.amount) {

// Checks for loss of precision
if (typeof options.amount === 'number' && String(options.amount).length > 15) {
console.log('warning', 'Possible precision loss on transaction amount:', options.amount);
}
options.amount = bignum(options.amount);

if(bignum(result.currencies[options.currency].amount).lt(options.amount)) {
return callback(new Error('Insufficient funds'), 'insufficient funds');
}
result.currencies[options.currency].amount = result.currencies[options.currency].amount - options.amount;
result.currencies[options.currency].amount = bignum(result.currencies[options.currency].amount)
.minus(options.amount)
.toFixed();
result.save(function(err){
callback(err, { 'amount': options.amount, 'currency': options.currency, 'owner': result.owner, id: result.id });
callback(err, { 'amount': options.amount.toFixed(), 'currency': options.currency, 'owner': result.owner, id: result.id });
});
});
};
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
},
"main": "./index.js",
"dependencies": {
"resource": "0.5.x"
"resource": "0.5.x",
"bignumber.js": "~1.3.0"
},
"devDependencies": {
"tap": "*"
},
"scripts": {
"test": "tap test/"
Expand Down
73 changes: 53 additions & 20 deletions test/basic-wallet-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ tap.test('can get created wallet', function (t) {
});

tap.test('can make deposit into fresh wallet', function (t) {
wallet.deposit({ id: id, currency: "BTC" , "amount": 1 }, function (err, result){
wallet.deposit({ id: id, currency: "BTC" , "amount": "1" }, function (err, result){
t.equal(err, null);
t.equal(result.currencies.BTC.amount, 1);
t.equal(result.currencies.BTC.amount, "1");
t.end();
})
});
Expand All @@ -40,15 +40,15 @@ tap.test('get same wallet, verify deposit', function (t) {
t.equal(err, null);
t.equal(result.owner, 'marak');
t.type(result.currencies, Object);
t.equal(result.currencies.BTC.amount, 1);
t.equal(result.currencies.BTC.amount, '1');
t.end();
})
});

tap.test('can make additional deposit into wallet', function (t) {
wallet.deposit({ id: id, currency: "BTC" , "amount": 30 }, function (err, result){
wallet.deposit({ id: id, currency: "BTC" , "amount": "30" }, function (err, result){
t.equal(err, null);
t.equal(result.currencies.BTC.amount, 31);
t.equal(result.currencies.BTC.amount, "31");
t.end();
})
});
Expand All @@ -59,15 +59,15 @@ tap.test('get same wallet, verify second deposit', function (t) {
t.equal(err, null);
t.equal(result.owner, 'marak');
t.type(result.currencies, Object);
t.equal(result.currencies.BTC.amount, 31);
t.equal(result.currencies.BTC.amount, '31');
t.end();
})
});

tap.test('can make deposit of another type of currency', function (t) {
wallet.deposit({ id: id, currency: "PPC" , "amount": 1 }, function (err, result){
wallet.deposit({ id: id, currency: "PPC" , "amount": "1" }, function (err, result){
t.equal(err, null);
t.equal(result.currencies.BTC.amount, 31);
t.equal(result.currencies.BTC.amount, "31");
t.end();
})
});
Expand All @@ -77,16 +77,16 @@ tap.test('get same wallet, verify deposit', function (t) {
t.equal(err, null);
t.equal(result.owner, 'marak');
t.type(result.currencies, Object);
t.equal(result.currencies.BTC.amount, 31);
t.equal(result.currencies.PPC.amount, 1);
t.equal(result.currencies.BTC.amount, '31');
t.equal(result.currencies.PPC.amount, '1');
t.end();
})
});

tap.test('can make withdrawl from first currency', function (t) {
wallet.withdraw({ id: id, currency: "BTC" , "amount": 7 }, function (err, result){
wallet.withdraw({ id: id, currency: "BTC" , "amount": "7" }, function (err, result){
t.equal(err, null);
t.equal(result.amount, 7);
t.equal(result.amount, "7");
t.end();
})
});
Expand All @@ -96,15 +96,15 @@ tap.test('verify withdrawl', function (t) {
t.equal(err, null);
t.equal(result.owner, 'marak');
t.type(result.currencies, Object);
t.equal(result.currencies.BTC.amount, 24);
t.equal(result.currencies.BTC.amount, '24');
t.end();
})
});

tap.test('can make deposit with fixed point decimal', function (t) {
wallet.deposit({ id: id, currency: "BTC" , "amount": 0.0000001 }, function (err, result){
wallet.deposit({ id: id, currency: "BTC" , "amount": "0.00000001" }, function (err, result){
t.equal(err, null);
t.equal(result.currencies.BTC.amount, 24.0000001);
t.equal(result.currencies.BTC.amount, "24.00000001");
t.end();
})
});
Expand All @@ -114,17 +114,17 @@ tap.test('get same wallet, verify deposit', function (t) {
t.equal(err, null);
t.equal(result.owner, 'marak');
t.type(result.currencies, Object);
t.equal(result.currencies.BTC.amount, 24.0000001);
t.equal(result.currencies.PPC.amount, 1);
t.equal(result.currencies.BTC.amount, '24.00000001');
t.equal(result.currencies.PPC.amount, '1');
t.end();
})
});

tap.test('can make withdrawl with fixed point decimal', function (t) {
wallet.withdraw({ id: id, currency: "BTC" , "amount": 0.0000001 }, function (err, result){
wallet.withdraw({ id: id, currency: "BTC" , "amount": "0.00000001" }, function (err, result){
t.equal(err, null);
console.log(err, result)
t.equal(result.amount, 0.0000001);
t.equal(result.amount, "0.00000001");
t.end();
})
});
Expand All @@ -134,10 +134,43 @@ tap.test('verify withdrawl', function (t) {
t.equal(err, null);
t.equal(result.owner, 'marak');
t.type(result.currencies, Object);
t.equal(result.currencies.BTC.amount, 24);
t.equal(result.currencies.BTC.amount, '24');
t.end();
})
});

tap.test('can make deposit with doubles', function (t) {
wallet.deposit({ id: id, currency: "BTC" , "amount": "20123456.00000001" }, function (err, result){
t.equal(err, null);
t.equal(result.currencies.BTC.amount, "20123480.00000001");
t.end();
});
});

tap.test('can make withdrawl with doubles', function (t) {
wallet.withdraw({ id: id, currency: "BTC" , "amount": "20123474.00000003" }, function (err, result){
t.equal(err, null);
t.equal(result.amount, "20123474.00000003");
t.end();
});
});

tap.test('prevent binary float-point rounding loss', function (t) {
wallet.get({ id: id }, function (err, result){
t.equal(err, null);
t.equal(result.owner, 'marak');
t.type(result.currencies, Object);
t.equal(result.currencies.BTC.amount, '5.99999998');
t.end();
})
});

tap.test('cannot withdraw more than the available balance', function (t) {
wallet.withdraw({ id: id, currency: "BTC" , "amount": "5.99999999" }, function (err, result){
t.type(err, Object);
t.equal(err.message, 'Insufficient funds');
t.equal(result, undefined);
t.end();
});
});

2 changes: 1 addition & 1 deletion wallet.mschema.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var currency = {
"required": true
},
"amount": {
"type": "number",
"type": "string",
"required": true
}
};
Expand Down

0 comments on commit a53fa6a

Please sign in to comment.