Skip to content

Commit 423ec7d

Browse files
committed
Merge pull request #294 from clark800/baseconverter
Refactor base conversion
2 parents f221c82 + 914cd6e commit 423ec7d

File tree

4 files changed

+186
-168
lines changed

4 files changed

+186
-168
lines changed

src/js/ripple/base.js

+59-128
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,74 @@
1-
var sjcl = require('./utils').sjcl;
2-
var utils = require('./utils');
3-
var extend = require('extend');
1+
'use strict';
2+
var _ = require('lodash');
3+
var sjcl = require('./utils').sjcl;
4+
var utils = require('./utils');
5+
var extend = require('extend');
6+
var convertBase = require('./baseconverter');
47

58
var Base = {};
69

710
var alphabets = Base.alphabets = {
8-
ripple: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz',
9-
tipple: 'RPShNAF39wBUDnEGHJKLM4pQrsT7VWXYZ2bcdeCg65jkm8ofqi1tuvaxyz',
10-
bitcoin: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
11+
ripple: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz',
12+
tipple: 'RPShNAF39wBUDnEGHJKLM4pQrsT7VWXYZ2bcdeCg65jkm8ofqi1tuvaxyz',
13+
bitcoin: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
1114
};
1215

1316
extend(Base, {
14-
VER_NONE : 1,
15-
VER_NODE_PUBLIC : 28,
16-
VER_NODE_PRIVATE : 32,
17-
VER_ACCOUNT_ID : 0,
18-
VER_ACCOUNT_PUBLIC : 35,
19-
VER_ACCOUNT_PRIVATE : 34,
20-
VER_FAMILY_GENERATOR : 41,
21-
VER_FAMILY_SEED : 33
17+
VER_NONE: 1,
18+
VER_NODE_PUBLIC: 28,
19+
VER_NODE_PRIVATE: 32,
20+
VER_ACCOUNT_ID: 0,
21+
VER_ACCOUNT_PUBLIC: 35,
22+
VER_ACCOUNT_PRIVATE: 34,
23+
VER_FAMILY_GENERATOR: 41,
24+
VER_FAMILY_SEED: 33
2225
});
2326

2427
function sha256(bytes) {
25-
return sjcl.codec.bytes.fromBits(sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes)));
28+
return sjcl.codec.bytes.fromBits(
29+
sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes)));
2630
}
2731

28-
function sha256hash(bytes) {
29-
return sha256(sha256(bytes));
30-
}
31-
32-
function divmod58(number, startAt) {
33-
var remainder = 0;
34-
for (var i = startAt; i < number.length; i++) {
35-
var digit256 = number[i] & 0xFF;
36-
var temp = remainder * 256 + digit256;
37-
number[i] = (temp / 58);
38-
remainder = temp % 58;
39-
}
40-
return remainder;
41-
}
42-
43-
function divmod256(number58, startAt) {
44-
var remainder = 0;
45-
for (var i = startAt; i < number58.length; i++) {
46-
var digit58 = number58[i] & 0xFF;
47-
var temp = remainder * 58 + digit58;
48-
number58[i] = (temp / 256);
49-
remainder = temp % 256;
50-
}
51-
return remainder;
52-
}
53-
54-
function encodeString (alphabet, input) {
55-
if (input.length == 0) {
32+
function encodeString(alphabet, input) {
33+
if (input.length === 0) {
5634
return [];
5735
}
5836

59-
// we need to copy the buffer for calc
60-
scratch = input.slice();
61-
62-
// Count leading zeroes.
63-
var zeroCount = 0;
64-
while (zeroCount < scratch.length &&
65-
scratch[zeroCount] == 0)
66-
++zeroCount;
67-
68-
// The actual encoding.
69-
var out = new Array(scratch.length * 2);
70-
var j = out.length;
71-
var startAt = zeroCount;
72-
73-
while (startAt < scratch.length) {
74-
var mod = divmod58(scratch, startAt);
75-
if (scratch[startAt] == 0) {
76-
++startAt;
37+
var leadingZeros = _.takeWhile(input, function(d) {
38+
return d === 0;
39+
});
40+
var out = convertBase(input, 256, 58).map(function(digit) {
41+
if (digit < 0 || digit >= alphabet.length) {
42+
throw new Error('Value ' + digit + ' is out of bounds for encoding');
7743
}
78-
out[--j] = alphabet[mod];
79-
}
80-
81-
// Strip extra 'r' if there are some after decoding.
82-
while (j < out.length && out[j] == alphabet[0]) ++j;
83-
// Add as many leading 'r' as there were leading zeros.
84-
while (--zeroCount >= 0) out[--j] = alphabet[0];
85-
while(j--) out.shift();
86-
87-
return out.join('');
44+
return alphabet[digit];
45+
});
46+
var prefix = leadingZeros.map(function() {
47+
return alphabet[0];
48+
});
49+
return prefix.concat(out).join('');
8850
}
8951

90-
function decodeString(indexes, input) {
91-
var isString = typeof input === 'string';
92-
93-
if (input.length == 0) {
52+
function decodeString(indexes, input) {
53+
if (input.length === 0) {
9454
return [];
9555
}
9656

97-
input58 = new Array(input.length);
98-
99-
// Transform the String to a base58 byte sequence
100-
for (var i = 0; i < input.length; ++i) {
101-
if (isString) {
102-
var c = input.charCodeAt(i);
103-
}
104-
105-
var digit58 = -1;
106-
if (c >= 0 && c < 128) {
107-
digit58 = indexes[c];
108-
}
109-
if (digit58 < 0) {
110-
throw new Error("Illegal character " + c + " at " + i);
111-
}
112-
113-
input58[i] = digit58;
114-
}
115-
// Count leading zeroes
116-
var zeroCount = 0;
117-
while (zeroCount < input58.length && input58[zeroCount] == 0) {
118-
++zeroCount;
119-
}
120-
// The encoding
121-
out = utils.arraySet(input.length, 0);
122-
var j = out.length;
123-
124-
var startAt = zeroCount;
125-
while (startAt < input58.length) {
126-
var mod = divmod256(input58, startAt);
127-
if (input58[startAt] == 0) {
128-
++startAt;
57+
var input58 = input.split('').map(function(c) {
58+
var charCode = c.charCodeAt(0);
59+
if (charCode >= indexes.length) {
60+
throw new Error('Character ' + c + ' is not valid for encoding');
12961
}
130-
out[--j] = mod;
131-
}
132-
133-
// Do no add extra leading zeroes, move j to first non null byte.
134-
while (j < out.length && (out[j] == 0)) ++j;
135-
136-
j -= zeroCount;
137-
while(j--) out.shift();
138-
139-
return out;
62+
return indexes[charCode];
63+
});
64+
var leadingZeros = _.takeWhile(input58, function(d) {
65+
return d === 0;
66+
});
67+
var out = convertBase(input58, 58, 256);
68+
var prefix = leadingZeros.map(function() {
69+
return 0;
70+
});
71+
return prefix.concat(out);
14072
}
14173

14274
function Base58(alphabet) {
@@ -151,8 +83,8 @@ function Base58(alphabet) {
15183
}
15284

15385
Base.encoders = {};
154-
Object.keys(alphabets).forEach(function(alphabet){
155-
Base.encoders[alphabet] = Base58(alphabets[alphabet]);
86+
Object.keys(alphabets).forEach(function(alphabet) {
87+
Base.encoders[alphabet] = new Base58(alphabets[alphabet]);
15688
});
15789

15890
// --> input: big-endian array of bytes.
@@ -165,22 +97,21 @@ Base.encode = function(input, alpha) {
16597
// <-- array of bytes or undefined.
16698
Base.decode = function(input, alpha) {
16799
if (typeof input !== 'string') {
168-
return void(0);
100+
return undefined;
169101
}
170102
try {
171103
return this.encoders[alpha || 'ripple'].decode(input);
172-
}
173-
catch(e) {
174-
return (void 0);
104+
} catch (e) {
105+
return undefined;
175106
}
176107
};
177108

178109
Base.verify_checksum = function(bytes) {
179-
var computed = sha256hash(bytes.slice(0, -4)).slice(0, 4);
110+
var computed = sha256(sha256(bytes.slice(0, -4))).slice(0, 4);
180111
var checksum = bytes.slice(-4);
181112
var result = true;
182113

183-
for (var i=0; i<4; i++) {
114+
for (var i = 0; i < 4; i++) {
184115
if (computed[i] !== checksum[i]) {
185116
result = false;
186117
break;
@@ -194,7 +125,7 @@ Base.verify_checksum = function(bytes) {
194125
// <-- String
195126
Base.encode_check = function(version, input, alphabet) {
196127
var buffer = [].concat(version, input);
197-
var check = sha256(sha256(buffer)).slice(0, 4);
128+
var check = sha256(sha256(buffer)).slice(0, 4);
198129

199130
return Base.encode([].concat(buffer, check), alphabet);
200131
};
@@ -217,7 +148,7 @@ Base.decode_check = function(version, input, alphabet) {
217148
if (Array.isArray(version)) {
218149
var match = false;
219150

220-
for (var i=0, l=version.length; i<l; i++) {
151+
for (var i = 0, l = version.length; i < l; i++) {
221152
match |= version[i] === buffer[0];
222153
}
223154

@@ -234,7 +165,7 @@ Base.decode_check = function(version, input, alphabet) {
234165
// intrepret the value as a negative number
235166
buffer[0] = 0;
236167

237-
return sjcl.bn.fromBits (
168+
return sjcl.bn.fromBits(
238169
sjcl.codec.bytes.toBits(buffer.slice(0, -4)));
239170
};
240171

src/js/ripple/baseconverter.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict';
2+
3+
function normalize(digitArray) {
4+
while (digitArray[0] === 0) {
5+
digitArray.shift();
6+
}
7+
return digitArray;
8+
}
9+
10+
function divmod(digitArray, base, divisor) {
11+
var remainder = 0;
12+
var quotient = [];
13+
for (var j = 0; j < digitArray.length; j++) {
14+
var temp = remainder * base + parseInt(digitArray[j], 10);
15+
quotient.push(Math.floor(temp / divisor));
16+
remainder = temp % divisor;
17+
}
18+
return {quotient: normalize(quotient), remainder: remainder};
19+
}
20+
21+
function convertBase(digitArray, fromBase, toBase) {
22+
var result = [];
23+
var dividend = digitArray;
24+
while (dividend.length > 0) {
25+
var qr = divmod(dividend, fromBase, toBase);
26+
result.unshift(qr.remainder);
27+
dividend = qr.quotient;
28+
}
29+
return normalize(result);
30+
}
31+
32+
module.exports = convertBase;

src/js/ripple/index.js

+42-40
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
1-
exports.Remote = require('./remote').Remote;
2-
exports.Request = require('./request').Request;
3-
exports.Amount = require('./amount').Amount;
4-
exports.Account = require('./account').Account;
5-
exports.Transaction = require('./transaction').Transaction;
6-
exports.Currency = require('./currency').Currency;
7-
exports.Base = require('./base').Base;
8-
exports.UInt128 = require('./uint128').UInt128;
9-
exports.UInt160 = require('./uint160').UInt160;
10-
exports.UInt256 = require('./uint256').UInt256;
11-
exports.Seed = require('./seed').Seed;
12-
exports.Meta = require('./meta').Meta;
1+
'use strict';
2+
exports.Remote = require('./remote').Remote;
3+
exports.Request = require('./request').Request;
4+
exports.Amount = require('./amount').Amount;
5+
exports.Account = require('./account').Account;
6+
exports.Transaction = require('./transaction').Transaction;
7+
exports.Currency = require('./currency').Currency;
8+
exports.Base = require('./base').Base;
9+
exports.UInt128 = require('./uint128').UInt128;
10+
exports.UInt160 = require('./uint160').UInt160;
11+
exports.UInt256 = require('./uint256').UInt256;
12+
exports.Seed = require('./seed').Seed;
13+
exports.Meta = require('./meta').Meta;
1314
exports.SerializedObject = require('./serializedobject').SerializedObject;
14-
exports.RippleError = require('./rippleerror').RippleError;
15-
exports.Message = require('./message').Message;
16-
exports.binformat = require('./binformat');
17-
exports.utils = require('./utils');
18-
exports.Server = require('./server').Server;
19-
exports.Wallet = require('./wallet');
20-
exports.Ledger = require('./ledger').Ledger;
15+
exports.RippleError = require('./rippleerror').RippleError;
16+
exports.Message = require('./message').Message;
17+
exports.binformat = require('./binformat');
18+
exports.utils = require('./utils');
19+
exports.Server = require('./server').Server;
20+
exports.Wallet = require('./wallet');
21+
exports.Ledger = require('./ledger').Ledger;
2122
exports.TransactionQueue = require('./transactionqueue').TransactionQueue;
22-
exports.RangeSet = require('./rangeset').RangeSet;
23+
exports.RangeSet = require('./rangeset').RangeSet;
24+
exports.convertBase = require('./baseconverter');
2325

2426
// Important: We do not guarantee any specific version of SJCL or for any
2527
// specific features to be included. The version and configuration may change at
@@ -28,35 +30,35 @@ exports.RangeSet = require('./rangeset').RangeSet;
2830
// However, for programs that are tied to a specific version of ripple.js like
2931
// the official client, it makes sense to expose the SJCL instance so we don't
3032
// have to include it twice.
31-
exports.sjcl = require('./utils').sjcl;
32-
exports.types = require('./serializedtypes');
33+
exports.sjcl = require('./utils').sjcl;
34+
exports.types = require('./serializedtypes');
3335

3436
exports.config = require('./config');
3537

3638
// camelCase to under_scored API conversion
37-
function attachUnderscored(c) {
38-
var o = exports[c];
39+
function attachUnderscored(name) {
40+
var o = exports[name];
3941

40-
Object.keys(o.prototype).forEach(function(key) {
41-
var UPPERCASE = /([A-Z]{1})[a-z]+/g;
42+
Object.keys(o.prototype).forEach(function(key) {
43+
var UPPERCASE = /([A-Z]{1})[a-z]+/g;
4244

43-
if (!UPPERCASE.test(key)) {
44-
return;
45-
}
45+
if (!UPPERCASE.test(key)) {
46+
return;
47+
}
4648

47-
var underscored = key.replace(UPPERCASE, function(c) {
48-
return '_' + c.toLowerCase();
49-
});
49+
var underscored = key.replace(UPPERCASE, function(c) {
50+
return '_' + c.toLowerCase();
51+
});
5052

51-
o.prototype[underscored] = o.prototype[key];
52-
});
53-
};
53+
o.prototype[underscored] = o.prototype[key];
54+
});
55+
}
5456

55-
[ 'Remote',
56-
'Request',
57-
'Transaction',
58-
'Account',
59-
'Server'
57+
['Remote',
58+
'Request',
59+
'Transaction',
60+
'Account',
61+
'Server'
6062
].forEach(attachUnderscored);
6163

6264
// vim:sw=2:sts=2:ts=8:et

0 commit comments

Comments
 (0)