This commit is contained in:
wltsmrz
2013-09-09 13:58:43 -07:00
parent 6ce83107c8
commit e944f0f5c4

View File

@@ -1,14 +1,14 @@
var binformat = require('./binformat'), var binformat = require('./binformat');
sjcl = require('../../../build/sjcl'), var sjcl = require('../../../build/sjcl');
extend = require('extend'), var extend = require('extend');
stypes = require('./serializedtypes'); var stypes = require('./serializedtypes');
var UInt256 = require('./uint256').UInt256; var UInt256 = require('./uint256').UInt256;
var SerializedObject = function (buf) { var SerializedObject = function (buf) {
if (Array.isArray(buf) || (Buffer && Buffer.isBuffer(buf)) ) { if (Array.isArray(buf) || (Buffer && Buffer.isBuffer(buf)) ) {
this.buffer = buf; this.buffer = buf;
} else if ("string" === typeof buf) { } else if (typeof buf === 'string') {
this.buffer = sjcl.codec.bytes.fromBits(sjcl.codec.hex.toBits(buf)); this.buffer = sjcl.codec.bytes.fromBits(sjcl.codec.hex.toBits(buf));
} else if (!buf) { } else if (!buf) {
this.buffer = []; this.buffer = [];
@@ -19,30 +19,32 @@ var SerializedObject = function (buf) {
}; };
SerializedObject.from_json = function (obj) { SerializedObject.from_json = function (obj) {
var typedef;
var so = new SerializedObject(); var so = new SerializedObject();
var typedef;
// Create a copy of the object so we don't modify it // Create a copy of the object so we don't modify it
obj = extend({}, obj); obj = extend({}, obj);
if ("number" === typeof obj.TransactionType) { switch (typeof obj.TransactionType) {
obj.TransactionType = SerializedObject.lookup_type_tx(obj.TransactionType); case 'number':
obj.TransactionType = SerializedObject.lookup_type_tx(obj.TransactionType);
if (!obj.TransactionType) { if (!obj.TransactionType) {
throw new Error("Transaction type ID is invalid."); throw new Error("Transaction type ID is invalid.");
}
break;
case 'string':
typedef = binformat.tx[obj.TransactionType].slice();
obj.TransactionType = typedef.shift();
break;
default:
if (typeof obj.LedgerEntryType !== 'undefined') {
// XXX: TODO
throw new Error("Ledger entry binary format not yet implemented.");
} else {
throw new Error("Object to be serialized must contain either " + "TransactionType or LedgerEntryType.");
} }
} }
if ("string" === typeof obj.TransactionType) {
typedef = binformat.tx[obj.TransactionType].slice();
obj.TransactionType = typedef.shift();
} else if ("undefined" !== typeof obj.LedgerEntryType) {
// XXX: TODO
throw new Error("Ledger entry binary format not yet implemented.");
} else throw new Error("Object to be serialized must contain either " +
"TransactionType or LedgerEntryType.");
so.serialize(typedef, obj); so.serialize(typedef, obj);
return so; return so;
@@ -57,26 +59,26 @@ SerializedObject.prototype.resetPointer = function () {
this.pointer = 0; this.pointer = 0;
}; };
function readOrPeek(advance) {
var readOrPeek = function (advance) {
return function(numberOfBytes) { return function(numberOfBytes) {
var start = this.pointer; var start = this.pointer;
var end = start+numberOfBytes; var end = start + numberOfBytes;
if (end > this.buffer.length) { if (end > this.buffer.length) {
throw new Error("There aren't that many bytes left."); throw new Error("There aren't that many bytes left.");
} else { } else {
var result = this.buffer.slice(start,end); var result = this.buffer.slice(start, end);
if (advance) {this.pointer = end;} if (advance) {
return result; this.pointer = end;
} }
} return result;
} }
}
};
SerializedObject.prototype.read = readOrPeek(true); SerializedObject.prototype.read = readOrPeek(true);
SerializedObject.prototype.peek = readOrPeek(false); SerializedObject.prototype.peek = readOrPeek(false);
SerializedObject.prototype.to_bits = function () {
SerializedObject.prototype.to_bits = function ()
{
return sjcl.codec.bytes.toBits(this.buffer); return sjcl.codec.bytes.toBits(this.buffer);
}; };
@@ -84,211 +86,196 @@ SerializedObject.prototype.to_hex = function () {
return sjcl.codec.hex.fromBits(this.to_bits()).toUpperCase(); return sjcl.codec.hex.fromBits(this.to_bits()).toUpperCase();
}; };
var TRANSACTION_TYPES = { var TRANSACTION_TYPES = {
0:"Payment", 0: "Payment",
3:"AccountSet", 3: "AccountSet",
5:"SetRegularKey", 5: "SetRegularKey",
7:"OfferCreate", 7: "OfferCreate",
8:"OfferCancel", 8: "OfferCancel",
9:"Contract", 9: "Contract",
10:"RemoveContract", 10: "RemoveContract",
20:"TrustSet", 20: "TrustSet",
100:"EnableFeature", 100: "EnableFeature",
101:"SetFee" 101: "SetFee"
}; };
var LEDGER_ENTRY_TYPES = { var LEDGER_ENTRY_TYPES = {
97:"AccountRoot", 97: "AccountRoot",
99:"Contract", 99: "Contract",
100:"DirectoryNode", 100: "DirectoryNode",
102:"Features", 102: "Features",
103:"GeneratorMap", 103: "GeneratorMap",
104:"LedgerHashes", 104: "LedgerHashes",
110:"Nickname", 110: "Nickname",
111:"Offer", 111: "Offer",
114:"RippleState", 114: "RippleState",
115:"FeeSettings" 115: "FeeSettings"
}; };
var TRANSACTION_RESULTS = { var TRANSACTION_RESULTS = {
0 :"tesSUCCESS", 0 : "tesSUCCESS",
100:"tecCLAIM", 100: "tecCLAIM",
101:"tecPATH_PARTIAL", 101: "tecPATH_PARTIAL",
102:"tecUNFUNDED_ADD", 102: "tecUNFUNDED_ADD",
103:"tecUNFUNDED_OFFER", 103: "tecUNFUNDED_OFFER",
104:"tecUNFUNDED_PAYMENT", 104: "tecUNFUNDED_PAYMENT",
105:"tecFAILED_PROCESSING", 105: "tecFAILED_PROCESSING",
121:"tecDIR_FULL", 121: "tecDIR_FULL",
122:"tecINSUF_RESERVE_LINE", 122: "tecINSUF_RESERVE_LINE",
123:"tecINSUF_RESERVE_OFFER", 123: "tecINSUF_RESERVE_OFFER",
124:"tecNO_DST", 124: "tecNO_DST",
125:"tecNO_DST_INSUF_XRP", 125: "tecNO_DST_INSUF_XRP",
126:"tecNO_LINE_INSUF_RESERVE", 126: "tecNO_LINE_INSUF_RESERVE",
127:"tecNO_LINE_REDUNDANT", 127: "tecNO_LINE_REDUNDANT",
128:"tecPATH_DRY", 128: "tecPATH_DRY",
129:"tecUNFUNDED", // Deprecated, old ambiguous unfunded. 129: "tecUNFUNDED", // Deprecated, old ambiguous unfunded.
130:"tecMASTER_DISABLED", 130: "tecMASTER_DISABLED",
131:"tecNO_REGULAR_KEY", 131: "tecNO_REGULAR_KEY",
132:"tecOWNERS" 132: "tecOWNERS"
}; };
SerializedObject.prototype.to_json = function() { SerializedObject.prototype.to_json = function() {
var old_pointer = this.pointer; var old_pointer = this.pointer;
this.resetPointer(); this.resetPointer();
var output = {}; var output = { };
while (true) {
var key_and_value = stypes.parse_whatever(this);
var key = key_and_value[0];
var value = key_and_value[1];
output[key] = jsonify_structure(value,key);
if (this.pointer == this.buffer.length) {
break;
} else if (this.pointer > this.buffer.length) {
console.log("WARNING: Buffer length exceeded during SerializedObject.to_json");
break;
}
}
this.pointer = old_pointer;
return output;
}
function jsonify_structure(thing,field_name) { while (this.pointer < this.buffer.length) {
var output; var key_and_value = stypes.parse_whatever(this);
var typeof_thing = typeof thing; var key = key_and_value[0];
if ("number" === typeof thing) { //Special codes var value = key_and_value[1];
if (field_name) { output[key] = jsonify_structure(value,key);
if (field_name === "LedgerEntryType") {
output = LEDGER_ENTRY_TYPES[thing] || thing;
} else if (field_name === "TransactionResult") {
output = TRANSACTION_RESULTS[thing] || thing;
} else if (field_name === "TransactionType") {
output = TRANSACTION_TYPES[thing] || thing;
} else {
output = thing;
}
} else {
output = thing;
}
} else if ("object" === typeof thing &&
"function" === typeof thing.to_json) {
output = thing.to_json();
} else if (Array.isArray(thing)) {
//console.log("here2");
//iterate over array []
output = [];
for (var i=0; i< thing.length; i++) {
output.push(jsonify_structure(thing[i]));
}
} else if ("object" === typeof thing) {
//console.log("here1", thing);
//iterate over object {}
output = {};
var keys = Object.keys(thing);
for (var i=0; i<keys.length; i++) {
var key = keys[i];
var value = thing[key];
output[key] = jsonify_structure(value,key);
}
} else {
output = thing;
} }
return output; this.pointer = old_pointer;
return output;
} }
SerializedObject.prototype.serialize = function (typedef, obj) function jsonify_structure(thing, field_name) {
{ var output;
switch (typeof thing) {
case 'number':
switch (field_name) {
case 'LedgerEntryType':
output = LEDGER_ENTRY_TYPES[thing] || thing;
break;
case 'TransactionResult':
output = TRANSACTION_RESULTS[thing] || thing;
break;
case 'TransactionType':
output = TRANSACTION_TYPES[thing] || thing;
break;
default:
output = thing;
}
break;
case 'object':
if (typeof thing.to_json === 'function') {
output = thing.to_json();
} else {
output = new thing.constructor;
var keys = Object.keys(thing);
for (var i=0; i<keys.length; i++) {
var key = keys[i];
output[key] = jsonify_structure(thing[key], key);
}
}
break;
default:
output = thing;
}
return output;
};
SerializedObject.prototype.serialize = function (typedef, obj) {
// Ensure canonical order // Ensure canonical order
typedef = SerializedObject._sort_typedef(typedef.slice()); typedef = SerializedObject._sort_typedef(typedef.slice());
// Serialize fields // Serialize fields
for (var i = 0, l = typedef.length; i < l; i++) { for (var i=0, l=typedef.length; i<l; i++) {
var spec = typedef[i]; var spec = typedef[i];
this.serialize_field(spec, obj); this.serialize_field(spec, obj);
} }
}; };
SerializedObject.prototype.signing_hash = function (prefix) SerializedObject.prototype.signing_hash = function (prefix) {
{
var sign_buffer = new SerializedObject(); var sign_buffer = new SerializedObject();
stypes.Int32.serialize(sign_buffer, prefix); stypes.Int32.serialize(sign_buffer, prefix);
sign_buffer.append(this.buffer); sign_buffer.append(this.buffer);
return sign_buffer.hash_sha512_half(); return sign_buffer.hash_sha512_half();
}; };
SerializedObject.prototype.hash_sha512_half = function () SerializedObject.prototype.hash_sha512_half = function () {
{
var bits = sjcl.codec.bytes.toBits(this.buffer), var bits = sjcl.codec.bytes.toBits(this.buffer),
hash = sjcl.bitArray.bitSlice(sjcl.hash.sha512.hash(bits), 0, 256); hash = sjcl.bitArray.bitSlice(sjcl.hash.sha512.hash(bits), 0, 256);
return UInt256.from_hex(sjcl.codec.hex.fromBits(hash)); return UInt256.from_hex(sjcl.codec.hex.fromBits(hash));
}; };
SerializedObject.prototype.serialize_field = function (spec, obj) SerializedObject.prototype.serialize_field = function (spec, obj) {
{ var spec = spec.slice();
spec = spec.slice(); var name = spec.shift();
var presence = spec.shift();
var field_id = spec.shift();
var Type = spec.shift();
var name = spec.shift(), if (typeof obj[name] !== 'undefined') {
presence = spec.shift(),
field_id = spec.shift(),
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)); this.append(SerializedObject.get_field_header(Type.id, field_id));
try { try {
Type.serialize(this, obj[name]); Type.serialize(this, obj[name]);
} catch (e) { } catch (e) {
// Add field name to message and rethrow // Add field name to message and rethrow
e.message = "Error serializing '"+name+"': "+e.message; e.message = "Error serializing '" + name + "': " + e.message;
throw e; throw e;
} }
} else if (presence === binformat.REQUIRED) { } else if (presence === binformat.REQUIRED) {
throw new Error('Missing required field '+name); throw new Error('Missing required field ' + name);
} }
}; };
SerializedObject.get_field_header = function (type_id, field_id) SerializedObject.get_field_header = function (type_id, field_id) {
{
var buffer = [0]; var buffer = [0];
if (type_id > 0xf) buffer.push(type_id & 0xff);
else buffer[0] += (type_id & 0xf) << 4;
if (field_id > 0xf) buffer.push(field_id & 0xff); if (type_id > 0xf) {
else buffer[0] += field_id & 0xf; buffer.push(type_id & 0xff);
} else {
buffer[0] += (type_id & 0xf) << 4;
}
if (field_id > 0xf) {
buffer.push(field_id & 0xff);
} else {
buffer[0] += field_id & 0xf;
}
return buffer; return buffer;
}; };
function sort_field_compare(a, b) { function sort_field_compare(a, b) {
// Sort by type id first, then by field id // Sort by type id first, then by field id
return a[3].id !== b[3].id ? return a[3].id !== b[3].id ? a[3].id - b[3].id : a[2] - b[2];
a[3].id - b[3].id :
a[2] - b[2];
}; };
SerializedObject._sort_typedef = function (typedef) { SerializedObject._sort_typedef = function (typedef) {
return typedef.sort(sort_field_compare); return typedef.sort(sort_field_compare);
}; };
SerializedObject.lookup_type_tx = function (id) { SerializedObject.lookup_type_tx = function (id) {
for (var i in binformat.tx) { var keys = Object.keys(binformat.tx);
if (!binformat.tx.hasOwnProperty(i)) continue; var result = null;
if (binformat.tx[i][0] === id) { for (var i=0; i<keys.length; i++) {
return i; var key = keys[i];
if (binformat.tx[key][0] === id) {
result = key;
break;
} }
} }
return null; return result;
}; };
exports.SerializedObject = SerializedObject; exports.SerializedObject = SerializedObject;