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();