From b2580ec17879daa324af9ac2f85b4309730a7169 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Mon, 11 Nov 2013 11:49:52 -0800 Subject: [PATCH 1/4] Disambiguate PathSets with XRP vs XRP IOUs. XRP IOUs are no longer valid, however they do exist in the network's historical transactions, so the serialization and deserialization need to be able to handle them. --- src/js/ripple/serializedtypes.js | 7 ++- test/serializedtypes-test.js | 96 +++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 11 deletions(-) diff --git a/src/js/ripple/serializedtypes.js b/src/js/ripple/serializedtypes.js index e8f4a1a6..de9150e6 100644 --- a/src/js/ripple/serializedtypes.js +++ b/src/js/ripple/serializedtypes.js @@ -482,7 +482,7 @@ var STPathSet = exports.PathSet = new SerializedType({ if (entry.currency) { var currency = Currency.from_json(entry.currency); - STCurrency.serialize(so, currency); + STCurrency.serialize(so, currency, entry.non_native); } if (entry.issuer) { @@ -532,7 +532,10 @@ var STPathSet = exports.PathSet = new SerializedType({ } if (tag_byte & this.typeCurrency) { //console.log('entry.currency'); - entry.currency = STCurrency.parse(so) + entry.currency = STCurrency.parse(so); + if (entry.currency === "XRP") { + entry.non_native = !entry.currency.is_native(); + } } if (tag_byte & this.typeIssuer) { //console.log('entry.issuer'); diff --git a/test/serializedtypes-test.js b/test/serializedtypes-test.js index 48af1ea3..6300af0d 100644 --- a/test/serializedtypes-test.js +++ b/test/serializedtypes-test.js @@ -564,6 +564,84 @@ describe('Serialized types', function() { }]]); assert.strictEqual(so.to_hex(), '31000000000000000000000000000000000000007B00000000000000000000000055534400000000000000000000000000000000000000000000000315FF31000000000000000000000000000000000000007B000000000000000000000000425443000000000000000000000000000000000000000000000003153100000000000000000000000000000000000003DB0000000000000000000000004555520000000000000000000000000000000000000000000000014100'); //TODO: Check this independently }); + it('Serialize path through XRP', function () { + // Appears in the history + // TX #0CBB429C456ED999CC691DFCC8E62E8C8C7E9522C2BEA967FED0D7E2A9B28D13 + // Note that XRP IOUs are no longer allowed, so this functionality is + // for historic transactions only. + var so = new SerializedObject(); + types.PathSet.serialize(so, [[ { + account: 123, + currency: 'USD', + issuer: 789 + }], + [{ + currency: "XRP" + }, + { + account: 987, + currency: 'EUR', + issuer: 321 + }]]); + assert.strictEqual(so.to_hex(), '31000000000000000000000000000000000000007B00000000000000000000000055534400000000000000000000000000000000000000000000000315FF1000000000000000000000000000000000000000003100000000000000000000000000000000000003DB0000000000000000000000004555520000000000000000000000000000000000000000000000014100'); + }); + it('Serialize path through XRP IOUs', function () { + // Appears in the history + // TX #0CBB429C456ED999CC691DFCC8E62E8C8C7E9522C2BEA967FED0D7E2A9B28D13 + // Note that XRP IOUs are no longer allowed, so this functionality is + // for historic transactions only. + var so = new SerializedObject(); + types.PathSet.serialize(so, [ + [{ + "account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K", + "currency": "BTC", + "issuer": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K" + }, { + "account": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo", + "currency": "BTC", + "issuer": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo" + }, { + "account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", + "currency": "BTC", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + }, { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + }], + [{ + "account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K", + "currency": "BTC", + "issuer": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K" + }, { + "account": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo", + "currency": "BTC", + "issuer": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo" + }, { + "account": "rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi", + "currency": "BTC", + "issuer": "rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi" + }, { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + }], + [{ + "account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K", + "currency": "BTC", + "issuer": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K" + }, { + "account": "r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn", + "currency": "BTC", + "issuer": "r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn" + }, { + "currency": "XRP", + "non_native": true + }, { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + }] + ]); + assert.strictEqual(so.to_hex(), '31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C131E4FE687C90257D3D2D694C8531CDEECBE84F33670000000000000000000000004254430000000000E4FE687C90257D3D2D694C8531CDEECBE84F3367310A20B3C85F482532A9578DBB3950B85CA06594D100000000000000000000000042544300000000000A20B3C85F482532A9578DBB3950B85CA06594D13000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1FF31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C131E4FE687C90257D3D2D694C8531CDEECBE84F33670000000000000000000000004254430000000000E4FE687C90257D3D2D694C8531CDEECBE84F33673115036E2D3F5437A83E5AC3CAEE34FF2C21DEB618000000000000000000000000425443000000000015036E2D3F5437A83E5AC3CAEE34FF2C21DEB6183000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1FF31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C13157180C769B66D942EE69E6DCC940CA48D82337AD000000000000000000000000425443000000000057180C769B66D942EE69E6DCC940CA48D82337AD1000000000000000000000000058525000000000003000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D100'); + }); it('Parse single empty path [[]]', function () { var so = new SerializedObject('00'); var parsed_path = types.PathSet.parse(so) @@ -573,15 +651,15 @@ describe('Serialized types', function() { var so = new SerializedObject('31000000000000000000000000000000000000007B00000000000000000000000055534400000000000000000000000000000000000000000000000315FF31000000000000000000000000000000000000007B000000000000000000000000425443000000000000000000000000000000000000000000000003153100000000000000000000000000000000000003DB0000000000000000000000004555520000000000000000000000000000000000000000000000014100'); var parsed_path = types.PathSet.parse(so); - var comp =[ [ { account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza', - currency: 'USD', - issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf' } ], - [ { account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza', - currency: 'BTC', - issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf' }, - { account: 'rrrrrrrrrrrrrrrrrrrpvQsW3V3', - currency: 'EUR', - issuer: 'rrrrrrrrrrrrrrrrrrrdHRtqg2' } ] ]; + var comp = [ [ { account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza', + currency: 'USD', + issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf' } ], + [ { account: 'rrrrrrrrrrrrrrrrrrrrNxV3Xza', + currency: 'BTC', + issuer: 'rrrrrrrrrrrrrrrrrrrpYnYCNYf' }, + { account: 'rrrrrrrrrrrrrrrrrrrpvQsW3V3', + currency: 'EUR', + issuer: 'rrrrrrrrrrrrrrrrrrrdHRtqg2' } ] ]; assert.deepEqual(SerializedObject.jsonify_structure(parsed_path, ""), comp); }); }); From 96ed9949329e9989212203996b8074f0d6242036 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Mon, 11 Nov 2013 12:17:09 -0800 Subject: [PATCH 2/4] Fixes on XRP path serialization/deserialization. --- src/js/ripple/serializedtypes.js | 9 ++--- test/serializedobject-test.js | 4 +-- test/serializedtypes-test.js | 58 +++++++++++++++++++------------- 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/js/ripple/serializedtypes.js b/src/js/ripple/serializedtypes.js index de9150e6..ad309af9 100644 --- a/src/js/ripple/serializedtypes.js +++ b/src/js/ripple/serializedtypes.js @@ -533,8 +533,9 @@ var STPathSet = exports.PathSet = new SerializedType({ if (tag_byte & this.typeCurrency) { //console.log('entry.currency'); entry.currency = STCurrency.parse(so); - if (entry.currency === "XRP") { - entry.non_native = !entry.currency.is_native(); + if (entry.currency.to_json() === "XRP" && + !entry.currency.is_native()) { + entry.non_native = true; } } if (tag_byte & this.typeIssuer) { @@ -628,14 +629,14 @@ function parse(so) { var type = TYPES_MAP[type_bits]; - assert(type, 'Unknown type: ' + type_bits); + assert(type, 'Unknown type - header byte is 0x' + tag_byte.toString(16)); var field_bits = tag_byte & 0x0f; var field_name = (field_bits === 0) ? field_name = FIELDS_MAP[type_bits][so.read(1)[0]] : field_name = FIELDS_MAP[type_bits][field_bits]; - assert(field_name, 'Unknown field: ' + tag_byte); + assert(field_name, 'Unknown field - header byte is 0x' + tag_byte.toString(16)); return [ field_name, type.parse(so) ]; //key, value }; diff --git a/test/serializedobject-test.js b/test/serializedobject-test.js index 09cf3a26..21c8a2ed 100644 --- a/test/serializedobject-test.js +++ b/test/serializedobject-test.js @@ -2,7 +2,7 @@ var utils = require('./testutils'); var assert = require('assert'); var SerializedObject = utils.load_module('serializedobject').SerializedObject; -describe('Serialied object', function() { +describe('Serialized object', function() { describe('Serialized object', function() { it('From json and back', function() { var input_json = { @@ -18,7 +18,7 @@ describe('Serialied object', function() { issuer: 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV' }, { - currency:'XRP' + currency: 'XRP' } ]], SendMax: { diff --git a/test/serializedtypes-test.js b/test/serializedtypes-test.js index 6300af0d..435786e2 100644 --- a/test/serializedtypes-test.js +++ b/test/serializedtypes-test.js @@ -565,33 +565,38 @@ describe('Serialized types', function() { assert.strictEqual(so.to_hex(), '31000000000000000000000000000000000000007B00000000000000000000000055534400000000000000000000000000000000000000000000000315FF31000000000000000000000000000000000000007B000000000000000000000000425443000000000000000000000000000000000000000000000003153100000000000000000000000000000000000003DB0000000000000000000000004555520000000000000000000000000000000000000000000000014100'); //TODO: Check this independently }); it('Serialize path through XRP', function () { - // Appears in the history - // TX #0CBB429C456ED999CC691DFCC8E62E8C8C7E9522C2BEA967FED0D7E2A9B28D13 - // Note that XRP IOUs are no longer allowed, so this functionality is - // for historic transactions only. + var hex = '31000000000000000000000000000000000000007B00000000000000000000000055534400000000000000000000000000000000000000000000000315FF1000000000000000000000000000000000000000003100000000000000000000000000000000000003DB0000000000000000000000004555520000000000000000000000000000000000000000000000014100'; + var json = [ + [ { + account: "rrrrrrrrrrrrrrrrrrrrNxV3Xza", + currency: 'USD', + issuer: "rrrrrrrrrrrrrrrrrrrpYnYCNYf" + }], + [{ + currency: "XRP" + }, { + account: "rrrrrrrrrrrrrrrrrrrpvQsW3V3", + currency: 'EUR', + issuer: "rrrrrrrrrrrrrrrrrrrdHRtqg2" + }] + ]; + var so = new SerializedObject(); - types.PathSet.serialize(so, [[ { - account: 123, - currency: 'USD', - issuer: 789 - }], - [{ - currency: "XRP" - }, - { - account: 987, - currency: 'EUR', - issuer: 321 - }]]); - assert.strictEqual(so.to_hex(), '31000000000000000000000000000000000000007B00000000000000000000000055534400000000000000000000000000000000000000000000000315FF1000000000000000000000000000000000000000003100000000000000000000000000000000000003DB0000000000000000000000004555520000000000000000000000000000000000000000000000014100'); + types.PathSet.serialize(so, json); + assert.strictEqual(so.to_hex(), hex); + + so = new SerializedObject(hex); + var parsed_path = SerializedObject.jsonify_structure(types.PathSet.parse(so)); + assert.deepEqual(parsed_path, json); }); it('Serialize path through XRP IOUs', function () { // Appears in the history // TX #0CBB429C456ED999CC691DFCC8E62E8C8C7E9522C2BEA967FED0D7E2A9B28D13 // Note that XRP IOUs are no longer allowed, so this functionality is // for historic transactions only. - var so = new SerializedObject(); - types.PathSet.serialize(so, [ + + var hex = '31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C131E4FE687C90257D3D2D694C8531CDEECBE84F33670000000000000000000000004254430000000000E4FE687C90257D3D2D694C8531CDEECBE84F3367310A20B3C85F482532A9578DBB3950B85CA06594D100000000000000000000000042544300000000000A20B3C85F482532A9578DBB3950B85CA06594D13000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1FF31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C131E4FE687C90257D3D2D694C8531CDEECBE84F33670000000000000000000000004254430000000000E4FE687C90257D3D2D694C8531CDEECBE84F33673115036E2D3F5437A83E5AC3CAEE34FF2C21DEB618000000000000000000000000425443000000000015036E2D3F5437A83E5AC3CAEE34FF2C21DEB6183000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1FF31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C13157180C769B66D942EE69E6DCC940CA48D82337AD000000000000000000000000425443000000000057180C769B66D942EE69E6DCC940CA48D82337AD1000000000000000000000000058525000000000003000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D100'; + var json = [ [{ "account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K", "currency": "BTC", @@ -639,12 +644,19 @@ describe('Serialized types', function() { "currency": "USD", "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" }] - ]); - assert.strictEqual(so.to_hex(), '31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C131E4FE687C90257D3D2D694C8531CDEECBE84F33670000000000000000000000004254430000000000E4FE687C90257D3D2D694C8531CDEECBE84F3367310A20B3C85F482532A9578DBB3950B85CA06594D100000000000000000000000042544300000000000A20B3C85F482532A9578DBB3950B85CA06594D13000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1FF31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C131E4FE687C90257D3D2D694C8531CDEECBE84F33670000000000000000000000004254430000000000E4FE687C90257D3D2D694C8531CDEECBE84F33673115036E2D3F5437A83E5AC3CAEE34FF2C21DEB618000000000000000000000000425443000000000015036E2D3F5437A83E5AC3CAEE34FF2C21DEB6183000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D1FF31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C13157180C769B66D942EE69E6DCC940CA48D82337AD000000000000000000000000425443000000000057180C769B66D942EE69E6DCC940CA48D82337AD1000000000000000000000000058525000000000003000000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D100'); + ]; + + var so = new SerializedObject(); + types.PathSet.serialize(so, json); + assert.strictEqual(so.to_hex(), hex); + + so = new SerializedObject(hex); + var parsed_path = SerializedObject.jsonify_structure(types.PathSet.parse(so)); + assert.deepEqual(parsed_path, json); }); it('Parse single empty path [[]]', function () { var so = new SerializedObject('00'); - var parsed_path = types.PathSet.parse(so) + var parsed_path = SerializedObject.jsonify_structure(types.PathSet.parse(so)); assert.deepEqual(parsed_path, [[]]); }); it('Parse [[e],[e,e]]', function () { From ae68e3a1a6a3f89e02ea94e80a9f409315f1f3bf Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Mon, 11 Nov 2013 13:29:02 -0800 Subject: [PATCH 3/4] Change "success" event parameters to more closely match "proposed" and "error". --- src/js/ripple/transactionmanager.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/ripple/transactionmanager.js b/src/js/ripple/transactionmanager.js index e860dfe5..9f2fee9a 100644 --- a/src/js/ripple/transactionmanager.js +++ b/src/js/ripple/transactionmanager.js @@ -28,8 +28,8 @@ function TransactionManager(account) { // transaction sequence number this._load_sequence(); - function cache_transaction(transaction) { - var transaction = TransactionManager.normalize_transaction(transaction); + function cache_transaction(res) { + var transaction = TransactionManager.normalize_transaction(res); var sequence = transaction.tx_json.Sequence; var hash = transaction.hash; @@ -38,7 +38,7 @@ function TransactionManager(account) { var pending = self._pending.get('_hash', hash); if (pending) { - pending.emit('success', transaction); + pending.emit('success', res); } else { self._cache[hash] = transaction; } From 05f3a970426cf6e3486934199394c79a4909ef33 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Tue, 12 Nov 2013 15:51:50 -0800 Subject: [PATCH 4/4] UInt160 can be an account or a plain hash. The UInt160 class used to be hardcoded to be an Account. This commit changes it so it can be used as an account or a plain hash. It will try to automatically self-classify based on how it is initialized. In the future we may want to have some dedicated classes rather than a single configurable UInt160. --- src/js/ripple/serializedtypes.js | 5 ++ src/js/ripple/uint.js | 3 -- src/js/ripple/uint160.js | 53 ++++++++++++++---- test/amount-test.js | 2 +- test/serializedtypes-test.js | 92 +++++++++++++++++++++++++++++--- 5 files changed, 135 insertions(+), 20 deletions(-) diff --git a/src/js/ripple/serializedtypes.js b/src/js/ripple/serializedtypes.js index ad309af9..88da4027 100644 --- a/src/js/ripple/serializedtypes.js +++ b/src/js/ripple/serializedtypes.js @@ -15,6 +15,7 @@ var sjcl = utils.sjcl; var UInt128 = require('./uint128').UInt128; var UInt160 = require('./uint160').UInt160; var UInt256 = require('./uint256').UInt256; +var Base = require('./base').Base; var amount = require('./amount'); var Amount = amount.Amount; @@ -381,6 +382,7 @@ var STAmount = exports.Amount = new SerializedType({ var currency = STCurrency.parse(so); var issuer_bytes = so.read(20); var issuer = UInt160.from_bytes(issuer_bytes); + issuer.set_version(Base.VER_ACCOUNT_ID); var offset = ((value_bytes[0] & 0x3f) << 2) + (value_bytes[1] >>> 6) - 97; var mantissa_bytes = value_bytes.slice(1); mantissa_bytes[0] &= 0x3f; @@ -441,6 +443,7 @@ var STAccount = exports.Account = new SerializedType({ } var result = UInt160.from_bytes(so.read(len)); + result.set_version(Base.VER_ACCOUNT_ID); //console.log('PARSED 160:', result.to_json()); if (false && !result.is_valid()) { @@ -529,6 +532,7 @@ var STPathSet = exports.PathSet = new SerializedType({ /*var bta = so.read(20); console.log('BTA:', bta);*/ entry.account = STHash160.parse(so); + entry.account.set_version(Base.VER_ACCOUNT_ID); } if (tag_byte & this.typeCurrency) { //console.log('entry.currency'); @@ -541,6 +545,7 @@ var STPathSet = exports.PathSet = new SerializedType({ if (tag_byte & this.typeIssuer) { //console.log('entry.issuer'); entry.issuer = STHash160.parse(so); //should know to use Base58? + entry.issuer.set_version(Base.VER_ACCOUNT_ID); //console.log('DONE WITH ISSUER!'); } diff --git a/src/js/ripple/uint.js b/src/js/ripple/uint.js index c9ccb6c2..486b0fc9 100644 --- a/src/js/ripple/uint.js +++ b/src/js/ripple/uint.js @@ -129,9 +129,6 @@ UInt.prototype.parse_generic = function (j) { if ('string' !== typeof j) { this._value = NaN; } - else if (j[0] === "r") { - this._value = Base.decode_check(Base.VER_ACCOUNT_ID, j); - } else if (this.constructor.width === j.length) { this._value = new BigInteger(utils.stringToArray(j), 256); } diff --git a/src/js/ripple/uint160.js b/src/js/ripple/uint160.js index 22fe0c4a..8b405959 100644 --- a/src/js/ripple/uint160.js +++ b/src/js/ripple/uint160.js @@ -15,6 +15,7 @@ var Base = require('./base').Base; var UInt160 = extend(function () { // Internal form: NaN or BigInteger this._value = NaN; + this._version_byte = void(0); }, UInt); UInt160.width = 20; @@ -28,6 +29,16 @@ var HEX_ONE = UInt160.HEX_ONE = '0000000000000000000000000000000000000 var STR_ZERO = UInt160.STR_ZERO = utils.hexToString(HEX_ZERO); var STR_ONE = UInt160.STR_ONE = utils.hexToString(HEX_ONE); +UInt160.prototype.set_version = function (j) { + this._version_byte = j; + + return this; +}; + +UInt160.prototype.get_version = function () { + return this._version_byte; +}; + // value = NaN on error. UInt160.prototype.parse_json = function (j) { // Canonicalize and validate @@ -36,13 +47,30 @@ UInt160.prototype.parse_json = function (j) { } if (typeof j === 'number' && !isNaN(j)) { - this._value = new BigInteger(String(j)); + // Allow raw numbers - DEPRECATED + // This is used mostly by the test suite and is supported + // as a legacy feature only. DO NOT RELY ON THIS BEHAVIOR. + this._value = new BigInteger(String(j)); + this._version_byte = Base.VER_ACCOUNT_ID; } else if (typeof j !== 'string') { this._value = NaN; } else if (j[0] === 'r') { - this._value = Base.decode_check(Base.VER_ACCOUNT_ID, j); + this._value = Base.decode_check(Base.VER_ACCOUNT_ID, j); + this._version_byte = Base.VER_ACCOUNT_ID; } else { - this._value = NaN; + this.parse_hex(j); + } + + return this; +}; + +UInt160.prototype.parse_generic = function (j) { + UInt.prototype.parse_generic.call(this, j); + + if (isNaN(this._value)) { + if ("string" === typeof j && j[0] === 'r') { + this._value = Base.decode_check(Base.VER_ACCOUNT_ID, j); + } } return this; @@ -50,17 +78,22 @@ UInt160.prototype.parse_json = function (j) { // XXX Json form should allow 0 and 1, C++ doesn't currently allow it. UInt160.prototype.to_json = function (opts) { - var opts = opts || {}; - var output = NaN; + opts = opts || {}; if (this._value instanceof BigInteger) { - output = Base.encode_check(Base.VER_ACCOUNT_ID, this.to_bytes()); - if (opts.gateways && output in opts.gateways) { - output = opts.gateways[output]; + // If this value has a type, return a Base58 encoded string. + if ("number" === typeof this._version_byte) { + var output = Base.encode_check(this._version_byte, this.to_bytes()); + if (opts.gateways && output in opts.gateways) { + output = opts.gateways[output]; + } + + return output; + } else { + return this.to_hex(); } } - - return output; + return NaN; }; exports.UInt160 = UInt160; diff --git a/test/amount-test.js b/test/amount-test.js index 58df7ff6..51c40282 100644 --- a/test/amount-test.js +++ b/test/amount-test.js @@ -36,7 +36,7 @@ describe('Amount', function() { assert.deepEqual(new BigInteger(), UInt160.from_generic('0')._value); }); it('Parse 0 export', function () { - assert.strictEqual(UInt160.ACCOUNT_ZERO, UInt160.from_generic('0').to_json()); + assert.strictEqual(UInt160.ACCOUNT_ZERO, UInt160.from_generic('0').set_version(0).to_json()); }); it('Parse 1', function () { assert.deepEqual(new BigInteger([1]), UInt160.from_generic('1')._value); diff --git a/test/serializedtypes-test.js b/test/serializedtypes-test.js index 435786e2..4f0551b9 100644 --- a/test/serializedtypes-test.js +++ b/test/serializedtypes-test.js @@ -378,19 +378,37 @@ describe('Serialized types', function() { describe('Hash160', function() { it('Serialize 0', function () { + var hex = '0000000000000000000000000000000000000000'; + var base58 = 'rrrrrrrrrrrrrrrrrrrrrhoLvTp'; var so = new SerializedObject(); - types.Hash160.serialize(so, 'rrrrrrrrrrrrrrrrrrrrrhoLvTp'); - assert.strictEqual(so.to_hex(), '0000000000000000000000000000000000000000'); + types.Hash160.serialize(so, base58); + assert.strictEqual(so.to_hex(), hex); + + so = new SerializedObject(); + types.Hash160.serialize(so, hex); + assert.strictEqual(so.to_hex(), hex); }); it('Serialize 1', function () { + var hex = '0000000000000000000000000000000000000001'; + var base58 = 'rrrrrrrrrrrrrrrrrrrrBZbvji'; var so = new SerializedObject(); - types.Hash160.serialize(so, 'rrrrrrrrrrrrrrrrrrrrBZbvji'); - assert.strictEqual(so.to_hex(), '0000000000000000000000000000000000000001'); + types.Hash160.serialize(so, base58); + assert.strictEqual(so.to_hex(), hex); + + so = new SerializedObject(); + types.Hash160.serialize(so, hex); + assert.strictEqual(so.to_hex(), hex); }); it('Serialize FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', function () { + var hex = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'; + var base58 = 'rQLbzfJH5BT1FS9apRLKV3G8dWEA5njaQi'; var so = new SerializedObject(); - types.Hash160.serialize(so, 'rQLbzfJH5BT1FS9apRLKV3G8dWEA5njaQi'); - assert.strictEqual(so.to_hex(), 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'); + types.Hash160.serialize(so, base58); + assert.strictEqual(so.to_hex(), hex); + + so = new SerializedObject(); + types.Hash160.serialize(so, hex); + assert.strictEqual(so.to_hex(), hex); }); it('Parse 0', function () { var val = '0000000000000000000000000000000000000000'; @@ -410,6 +428,14 @@ describe('Serialized types', function() { var num = types.Hash160.parse(so); assert.strictEqual(num.to_hex(), val); }); + it('Parse 0 as JSON', function () { + // Hash160 should be returned as hex in JSON, unlike + // addresses. + var val = '0000000000000000000000000000000000000000'; + var so = new SerializedObject(val); + var num = types.Hash160.parse(so); + assert.strictEqual(num.to_json(), val); + }); }); describe('Hash256', function() { @@ -539,6 +565,60 @@ describe('Serialized types', function() { }); }); + describe('Account', function() { + it('Serialize 0', function () { + var hex = '0000000000000000000000000000000000000000'; + var base58 = 'rrrrrrrrrrrrrrrrrrrrrhoLvTp'; + var so = new SerializedObject(); + types.Account.serialize(so, base58); + assert.strictEqual(so.to_hex(), "14"+hex); + + so = new SerializedObject(); + types.Account.serialize(so, hex); + assert.strictEqual(so.to_hex(), "14"+hex); + }); + it('Serialize 1', function () { + var hex = '0000000000000000000000000000000000000001'; + var base58 = 'rrrrrrrrrrrrrrrrrrrrBZbvji'; + var so = new SerializedObject(); + types.Account.serialize(so, base58); + assert.strictEqual(so.to_hex(), "14"+hex); + + so = new SerializedObject(); + types.Account.serialize(so, hex); + assert.strictEqual(so.to_hex(), "14"+hex); + }); + it('Serialize FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', function () { + var hex = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'; + var base58 = 'rQLbzfJH5BT1FS9apRLKV3G8dWEA5njaQi'; + var so = new SerializedObject(); + types.Account.serialize(so, base58); + assert.strictEqual(so.to_hex(), "14"+hex); + + so = new SerializedObject(); + types.Account.serialize(so, hex); + assert.strictEqual(so.to_hex(), "14"+hex); + }); + it('Parse 0', function () { + var val = '140000000000000000000000000000000000000000'; + var so = new SerializedObject(val); + var num = types.Account.parse(so); + assert.strictEqual(num.to_json(), 'rrrrrrrrrrrrrrrrrrrrrhoLvTp'); + }); + it('Parse 1', function () { + var val = '140000000000000000000000000000000000000001'; + var so = new SerializedObject(val); + var num = types.Account.parse(so); + assert.strictEqual(num.to_json(), 'rrrrrrrrrrrrrrrrrrrrBZbvji'); + }); + it('Parse HASH160_MAX', function () { + var val = '14FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'; + var so = new SerializedObject(val); + var num = types.Account.parse(so); + assert.strictEqual(num.to_json(), 'rQLbzfJH5BT1FS9apRLKV3G8dWEA5njaQi'); + }); + }); + describe('PathSet', function() { it('Serialize single empty path [[]]', function () { var so = new SerializedObject();