diff --git a/src/js/serializedobject.js b/src/js/serializedobject.js index e66a2001..f346f695 100644 --- a/src/js/serializedobject.js +++ b/src/js/serializedobject.js @@ -92,7 +92,7 @@ SerializedObject.prototype.serialize_field = function (spec, obj) Type = spec.shift(); if ("undefined" !== typeof obj[name]) { - console.log(name, Type.id, field_id); + //console.log(name, Type.id, field_id); this.append(SerializedObject.get_field_header(Type.id, field_id)); try { diff --git a/src/js/serializedtypes.js b/src/js/serializedtypes.js index 2c61b64b..93009a9f 100644 --- a/src/js/serializedtypes.js +++ b/src/js/serializedtypes.js @@ -12,7 +12,8 @@ var extend = require('extend'), var amount = require('./amount'), UInt160 = amount.UInt160, - Amount = amount.Amount; + Amount = amount.Amount, + Currency= amount.Currency; // Shortcuts var hex = sjcl.codec.hex, @@ -47,7 +48,7 @@ SerializedType.prototype.serialize_varint = function (so, val) { } else throw new Error("Variable integer overflow."); }; -exports.Int8 = new SerializedType({ +var STInt8 = exports.Int8 = new SerializedType({ serialize: function (so, val) { so.append([val & 0xff]); }, @@ -56,7 +57,7 @@ exports.Int8 = new SerializedType({ } }); -exports.Int16 = new SerializedType({ +var STInt16 = exports.Int16 = new SerializedType({ serialize: function (so, val) { so.append([ val >>> 8 & 0xff, @@ -65,10 +66,11 @@ exports.Int16 = new SerializedType({ }, parse: function (so) { // XXX + throw new Error("Parsing Int16 not implemented"); } }); -exports.Int32 = new SerializedType({ +var STInt32 = exports.Int32 = new SerializedType({ serialize: function (so, val) { so.append([ val >>> 24 & 0xff, @@ -79,46 +81,82 @@ exports.Int32 = new SerializedType({ }, parse: function (so) { // XXX + throw new Error("Parsing Int32 not implemented"); } }); -exports.Int64 = new SerializedType({ +var STInt64 = exports.Int64 = new SerializedType({ serialize: function (so, val) { // XXX + throw new Error("Serializing Int64 not implemented"); }, parse: function (so) { // XXX + throw new Error("Parsing Int64 not implemented"); } }); -exports.Hash128 = new SerializedType({ +var STHash128 = exports.Hash128 = new SerializedType({ serialize: function (so, val) { // XXX + throw new Error("Serializing Hash128 not implemented"); }, parse: function (so) { // XXX + throw new Error("Parsing Hash128 not implemented"); } }); -exports.Hash256 = new SerializedType({ +var STHash256 = exports.Hash256 = new SerializedType({ serialize: function (so, val) { // XXX + throw new Error("Serializing Hash256 not implemented"); }, parse: function (so) { // XXX + throw new Error("Parsing Hash256 not implemented"); } }); -exports.Hash160 = new SerializedType({ +var STHash160 = exports.Hash160 = new SerializedType({ serialize: function (so, val) { // XXX + throw new Error("Serializing Hash160 not implemented"); }, parse: function (so) { // XXX + throw new Error("Parsing Hash160 not implemented"); } }); -exports.Amount = new SerializedType({ +// Internal +var STCurrency = new SerializedType({ + serialize: function (so, val) { + var currency = val.to_json(); + if ("string" === typeof currency && currency.length === 3) { + var currencyCode = currency.toUpperCase(), + currencyData = utils.arraySet(20, 0); + + if (!/^[A-Z]{3}$/.test(currencyCode)) { + throw new Error('Invalid currency code'); + } + + currencyData[12] = currencyCode.charCodeAt(0) & 0xff; + currencyData[13] = currencyCode.charCodeAt(1) & 0xff; + currencyData[14] = currencyCode.charCodeAt(2) & 0xff; + + so.append(currencyData); + } else { + throw new Error('Tried to serialize invalid/unimplemented currency type.'); + } + }, + parse: function (so) { + // XXX + throw new Error("Parsing Currency not implemented"); + } +}); + +var STAmount = exports.Amount = new SerializedType({ serialize: function (so, val) { var amount = Amount.from_json(val); if (!amount.is_valid()) { @@ -126,6 +164,7 @@ exports.Amount = new SerializedType({ } // Amount (64-bit integer) + var valueBytes = utils.arraySet(8, 0); if (amount.is_native()) { var valueHex = amount._value.toString(16); @@ -137,103 +176,138 @@ exports.Amount = new SerializedType({ valueHex = "0" + valueHex; } - var valueBytes = bytes.fromBits(hex.toBits(valueHex)); + valueBytes = bytes.fromBits(hex.toBits(valueHex)); // Clear most significant two bits - these bits should already be 0 if // Amount enforces the range correctly, but we'll clear them anyway just // so this code can make certain guarantees about the encoded value. valueBytes[0] &= 0x3f; if (!amount.is_negative()) valueBytes[0] |= 0x40; - - so.append(valueBytes); } else { - // XXX - throw new Error("Non-native amounts not implemented!"); + var hi = 0, lo = 0; + + // First bit: non-native + hi |= 1 << 31; + + if (!amount.is_zero()) { + // Second bit: non-negative? + if (!amount.is_negative()) hi |= 1 << 30; + + // Next eight bits: offset/exponent + hi |= ((97 + amount._offset) & 0xff) << 22; + + // Remaining 52 bits: mantissa + hi |= amount._value.shiftRight(32).intValue() & 0x3fffff; + lo = amount._value.intValue() & 0xffffffff; + } + + valueBytes = sjcl.codec.bytes.fromBits([hi, lo]); } + so.append(valueBytes); + if (!amount.is_native()) { // Currency (160-bit hash) - var currency = amount.currency().to_json(); - if ("string" === typeof currency && currency.length === 3) { - var currencyCode = currency.toUpperCase(), - currencyData = utils.arraySet(20, 0); - - if (!/^[A-Z]{3}$/.test(currencyCode)) { - throw new Error('Invalid currency code'); - } - - currencyData[12] = currencyCode.charCodeAt(0) & 0xff; - currencyData[13] = currencyCode.charCodeAt(1) & 0xff; - currencyData[14] = currencyCode.charCodeAt(2) & 0xff; - - var currencyBits = bytes.toBits(currencyData), - currencyHash = sjcl.hash.ripemd160.hash(currencyBits); - - so.append(bytes.fromBits(currencyHash)); - } else { - throw new Error('Tried to serialize invalid/unimplemented currency type.'); - } + var currency = amount.currency(); + STCurrency.serialize(so, currency); // Issuer (160-bit hash) - // XXX + so.append(amount.issuer().to_bytes()); } }, parse: function (so) { // XXX + throw new Error("Parsing Amount not implemented"); } }); -exports.VariableLength = new SerializedType({ +var STVL = exports.VariableLength = new SerializedType({ serialize: function (so, val) { if ("string" === typeof val) this.serialize_hex(so, val); else throw new Error("Unknown datatype."); }, parse: function (so) { // XXX + throw new Error("Parsing VL not implemented"); } }); -exports.Account = new SerializedType({ +var STAccount = exports.Account = new SerializedType({ serialize: function (so, val) { var account = UInt160.from_json(val); this.serialize_hex(so, account.to_hex()); }, parse: function (so) { // XXX + throw new Error("Parsing Account not implemented"); } }); -exports.PathSet = new SerializedType({ +var STPathSet = exports.PathSet = new SerializedType({ serialize: function (so, val) { // XXX + for (var i = 0, l = val.length; i < l; i++) { + for (var j = 0, l2 = val[i].length; j < l2; j++) { + var entry = val[i][j]; + + var type = 0; + + if (entry.account) type |= 0x01; + if (entry.currency) type |= 0x10; + if (entry.issuer) type |= 0x20; + + STInt8.serialize(so, type); + + if (entry.account) { + so.append(UInt160.from_json(entry.account).to_bytes()); + } + if (entry.currency) { + var currency = Currency.from_json(entry.currency); + STCurrency.serialize(so, currency); + } + if (entry.issuer) { + so.append(UInt160.from_json(entry.issuer).to_bytes()); + } + } + + if (j < l2) STInt8.serialize(so, 0xff); + } + STInt8.serialize(so, 0x00); }, parse: function (so) { // XXX + throw new Error("Parsing PathSet not implemented"); } }); -exports.Vector256 = new SerializedType({ +var STVector256 = exports.Vector256 = new SerializedType({ serialize: function (so, val) { // XXX + throw new Error("Serializing Vector256 not implemented"); }, parse: function (so) { // XXX + throw new Error("Parsing Vector256 not implemented"); } }); -exports.Object = new SerializedType({ +var STObject = exports.Object = new SerializedType({ serialize: function (so, val) { // XXX + throw new Error("Serializing Object not implemented"); }, parse: function (so) { // XXX + throw new Error("Parsing Object not implemented"); } }); -exports.Array = new SerializedType({ +var STArray = exports.Array = new SerializedType({ serialize: function (so, val) { // XXX + throw new Error("Serializing Array not implemented"); }, parse: function (so) { // XXX + throw new Error("Parsing Array not implemented"); } });