Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for bignumbers #17

Merged

Conversation

pedrobranco
Copy link
Collaborator

This PR adds support for parsing correctly values with more than 15 significant digits (as string).

Fixes the issue #16.

@ruimarinho
Copy link
Owner

@pedrobranco can you add a test for this? Also, have you considered simply using a reviver function instead, as supported by JSON.parse?

@pedrobranco
Copy link
Collaborator Author

pedrobranco commented Aug 25, 2016

can you add a test for this

Yes.

Also, have you considered simply using a reviver function instead, as supported by JSON.parse?

Already tried the reviver function, but already returns the value without the correct precision.

> JSON.parse('{ "value": 150000000000000.00000000000000001 }', (k,v) => { console.log(k,v)})
value 150000000000000
 {}

@ruimarinho
Copy link
Owner

What's the difference between this and https://www.npmjs.com/package/json-bigint or https://github.com/josdejong/lossless-json?

@pedrobranco
Copy link
Collaborator Author

pedrobranco commented Aug 25, 2016

IMO json-bigint-string is less intrusive by parsing only the unsupported values as string.

const LosslessJSON = require('lossless-json');

let text = '{"normal":2.3,"long":123456789012345678901,"big":2.3e+500}';
let json = LosslessJSON.parse(text);
/*
{ normal: { [Number: 2.3] value: '2.3', type: 'LosslessNumber', isLosslessNumber: true },
  long:
   r {
     value: '123456789012345678901',
     type: 'LosslessNumber',
     isLosslessNumber: true },
  big:
   r {
     value: '2.3e+500',
     type: 'LosslessNumber',
     isLosslessNumber: true } }
*/

json.normal === 2.3 // false

let originalJson = JSON.parse(text); // { normal: 2.3, long: 123456789012345680000, big: Infinity }
originalJson.normal === 2.3 // true

WDYT?

@ruimarinho
Copy link
Owner

After digging a bit, I think we can use json-bigint with { storeAsString: true } options. Additionally, I would add strict checking as duplicate keys is valid on JSON but the native JSON.parse will only hold the last value. The end result could be something like:

const JSONBigInt = require('json-bigint')({ storeAsString: true, strict: true });

This qualifies for a breaking change so this would need to come in a form of a major version.

package.json Outdated
@@ -39,6 +39,7 @@
},
"dependencies": {
"bluebird": "^3.1.1",
"json-bigint-string": "^1.0.0",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switch to json-bigint instead.

Copy link
Owner

@ruimarinho ruimarinho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove dist/ files, as they are generated during the release phase.

@@ -118,7 +118,8 @@ class Client {

return this.request.postAsync({
auth: _.pickBy(this.auth, _.identity),
body,
body: JSON.stringify(body),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make it clear that if users of the client want to keep precision, this should send those values as strings.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -199,7 +199,7 @@ describe('Client', () => {
it('should return the proof-of-work difficulty', async () => {
const difficulty = await new Client(config.bitcoind).getDifficulty();

difficulty.should.be.a.Number();
difficulty.should.be.a.String();
Copy link
Owner

@ruimarinho ruimarinho Sep 19, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you rebase as this has been fixed already?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be unrelated to the changes on this PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are parsing big numbers as String this method will now return the result as a String.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is difficulty a big number?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

@ruimarinho
Copy link
Owner

@pedrobranco can you rebase please?

@ruimarinho
Copy link
Owner

I would like to release this on the next major @pedrobranco.

@@ -10,7 +10,7 @@ sut:
- bitcoind-username-only

bitcoind:
image: seegno/bitcoind:0.13-alpine
image: seegno/bitcoind:0.13.0-alpine
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebase?

src/parser.js Outdated
@@ -5,6 +5,7 @@

import RpcError from './errors/rpc-error';
import _ from 'lodash';
import jsonBigInt from 'json-bigint';
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSONBigInt?

Copy link
Collaborator Author

@pedrobranco pedrobranco Feb 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eslint rule triggers http://eslint.org/docs/rules/new-cap. Should we ignore it?

src/parser.js Outdated
@@ -52,6 +53,9 @@ export default class Parser {
throw new RpcError(response.statusCode);
}

// Parsing the body with custom parser to support BigNumbers.
body = jsonBigInt({ storeAsString: true }).parse(body);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you address the other comments? (e.g. strict?)

@pedrobranco pedrobranco force-pushed the feature/support-bignumbers branch 2 times, most recently from bba077c to 0382821 Compare February 8, 2017 12:26
@pedrobranco
Copy link
Collaborator Author

@ruimarinho Rebased.

README.md Outdated
@@ -91,6 +91,10 @@ client.getInfo().then(([body, headers]) => console.log(body, headers));
const [body, headers] = await client.getInfo();
```

### Floating point number precision in JavaScript

Due to [Javascript floating point precision](http://floating-point-gui.de/), to prevent precision loss when using `bitcoin-core` we should pass all big numbers (numbers with more than 15 significant digits) as string instead of using `Number` data type.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JavaScript's

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to JavaScript's limited floating point precision, all big numbers (numbers with more than 15 significant digits) are returned as strings to prevent precision loss.

@ruimarinho
Copy link
Owner

I'm thinking that returning as a big number (instead of a string) could be an interesting option of the client. /cc @fixe @nunofgs?

@fixe
Copy link

fixe commented Feb 9, 2017

+1 for using just string.

@pedrobranco pedrobranco force-pushed the feature/support-bignumbers branch 2 times, most recently from 824e5d8 to 3ee0621 Compare February 10, 2017 15:22
@ruimarinho ruimarinho merged commit dc42ebe into ruimarinho:master Feb 12, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants