Fix merge conflicts

This commit is contained in:
wltsmrz
2014-05-19 12:07:24 -07:00
13 changed files with 349 additions and 204 deletions

View File

@@ -3497,10 +3497,10 @@ sjcl.ecc.point.prototype.isOnCurve = function() {
var component_b = self.curve.b; var component_b = self.curve.b;
var field_modulus = self.curve.field.modulus; var field_modulus = self.curve.field.modulus;
var y_squared_mod_field_order = self.y.mul(self.y).mod(field_modulus); var left_hand_side = self.y.mul(self.y).mod(field_modulus);
var x_cubed_plus_ax_plus_b = self.x.mul(self.x).mul(self.x).add(component_a.mul(self.x)).add(component_b).mod(field_modulus); var right_hand_side = self.x.mul(self.x).mul(self.x).add(component_a.mul(self.x)).add(component_b).mod(field_modulus);
return y_squared_mod_field_order.equals(x_cubed_plus_ax_plus_b); return left_hand_side.equals(right_hand_side);
}; };

43
scripts/verify_ledger_json.js Normal file → Executable file
View File

@@ -1,14 +1,49 @@
var fs = require('fs'); var fs = require('fs');
var Ledger = require('../src/js/ripple/ledger').Ledger; var Ledger = require('../src/js/ripple/ledger').Ledger;
if (process.argc < 1) { function parse_options(from, flags) {
var argv = from.slice(),
opts = {argv:argv};
flags.forEach(function(f) {
// Do we have the flag?
var flag_index = argv.indexOf('--' + f);
// normalize the name of the flag
f = f.replace('-', '_');
// opts has Boolean value for normalized flag key
opts[f] = !!~flag_index;
if (opts[f]) {
// remove the flag from the argv
argv.splice(flag_index, 1);
}
});
return opts;
}
var opts = parse_options(process.argv.slice(2), // remove `node` and `this.js`
['sanity-test']);
if (opts.argv.length < 1) {
console.error("Usage: scripts/verify_ledger_json path/to/ledger.json"); console.error("Usage: scripts/verify_ledger_json path/to/ledger.json");
console.error(" optional: --sanity-test (json>binary>json>binary)");
process.exit(1); process.exit(1);
} }
var json = fs.readFileSync(process.argv[2], 'utf-8'); var json = fs.readFileSync(opts.argv[0], 'utf-8');
var ledger = Ledger.from_json(JSON.parse(json)); var ledger = Ledger.from_json(JSON.parse(json));
console.log("Calculated transaction hash: "+ledger.calc_tx_hash().to_hex()) // This will serialize each accountState object to binary and then back to json
console.log("Transaction hash in header: "+ledger.ledger_json.transaction_hash); // before finally serializing for hashing. This is mostly to expose any issues
// with ripple-libs binary <--> json codecs.
if (opts.sanity_test) {
console.log("All accountState nodes will be processed from " +
"json->binary->json->binary. This may take some time " +
"with large ledgers.");
}
console.log("Transaction hash in header: " + ledger.ledger_json.transaction_hash);
console.log("Calculated transaction hash: " + ledger.calc_tx_hash().to_hex());
console.log("Account state hash in header: " + ledger.ledger_json.account_hash);
console.log("Calculated account state hash: " + ledger.calc_account_hash(
{sanity_test:opts.sanity_test})
.to_hex());

View File

@@ -208,6 +208,7 @@ var base = [
[ 'TransactionType' , REQUIRED ], [ 'TransactionType' , REQUIRED ],
[ 'Flags' , OPTIONAL ], [ 'Flags' , OPTIONAL ],
[ 'SourceTag' , OPTIONAL ], [ 'SourceTag' , OPTIONAL ],
[ 'LastLedgerSequence' , OPTIONAL ],
[ 'Account' , REQUIRED ], [ 'Account' , REQUIRED ],
[ 'Sequence' , REQUIRED ], [ 'Sequence' , REQUIRED ],
[ 'Fee' , REQUIRED ], [ 'Fee' , REQUIRED ],
@@ -274,133 +275,105 @@ exports.tx = {
]) ])
}; };
exports.ledger = { var sleBase = [
AccountRoot: [97], ['LedgerIndex', OPTIONAL],
Contract: [99], ['LedgerEntryType', REQUIRED],
DirectoryNode: [100], ['Flags', REQUIRED]
Features: [102], ];
GeneratorMap: [103],
LedgerHashes: [104],
Nickname: [110],
Offer: [111],
RippleState: [114],
FeeSettings: [115]
};
/*
TODO:
Need `base` factored out
AccountRoot needs AccountTxnID
{ exports.ledger = {
'AccountRoot': [97, AccountRoot: [97].concat(sleBase,[
['LedgerEntryType', REQUIRED], ['Sequence', REQUIRED],
['Flags', REQUIRED], ['PreviousTxnLgrSeq', REQUIRED],
['Sequence', REQUIRED], ['TransferRate', OPTIONAL],
['PreviousTxnLgrSeq', REQUIRED], ['WalletSize', OPTIONAL],
['TransferRate', OPTIONAL], ['OwnerCount', REQUIRED],
['WalletSize', OPTIONAL], ['EmailHash', OPTIONAL],
['OwnerCount', REQUIRED], ['PreviousTxnID', REQUIRED],
['EmailHash', OPTIONAL], ['AccountTxnID', OPTIONAL],
['PreviousTxnID', REQUIRED], ['WalletLocator', OPTIONAL],
['LedgerIndex', OPTIONAL], ['Balance', REQUIRED],
['WalletLocator', OPTIONAL], ['MessageKey', OPTIONAL],
['Balance', REQUIRED], ['Domain', OPTIONAL],
['MessageKey', OPTIONAL,], ['Account', REQUIRED],
['Domain', OPTIONAL,], ['RegularKey', OPTIONAL]]),
['Account', REQUIRED], Contract: [99].concat(sleBase,[
['RegularKey', OPTIONAL]], ['PreviousTxnLgrSeq', REQUIRED],
'Contract': [99, ['Expiration', REQUIRED],
['LedgerEntryType', REQUIRED], ['BondAmount', REQUIRED],
['Flags', REQUIRED], ['PreviousTxnID', REQUIRED],
['PreviousTxnLgrSeq', REQUIRED], ['Balance', REQUIRED],
['Expiration', REQUIRED], ['FundCode', OPTIONAL],
['BondAmount', REQUIRED], ['RemoveCode', OPTIONAL],
['PreviousTxnID', REQUIRED], ['ExpireCode', OPTIONAL],
['LedgerIndex', OPTIONAL], ['CreateCode', OPTIONAL],
['Balance', REQUIRED], ['Account', REQUIRED],
['FundCode', OPTIONAL], ['Owner', REQUIRED],
['RemoveCode', OPTIONAL], ['Issuer', REQUIRED]]),
['ExpireCode', OPTIONAL], DirectoryNode: [100].concat(sleBase,[
['CreateCode', OPTIONAL], ['IndexNext', OPTIONAL],
['Account', REQUIRED], ['IndexPrevious', OPTIONAL],
['Owner', REQUIRED], ['ExchangeRate', OPTIONAL],
['Issuer', REQUIRED]], ['RootIndex', REQUIRED],
'DirectoryNode': [100, ['Owner', OPTIONAL],
['LedgerEntryType', REQUIRED], ['TakerPaysCurrency', OPTIONAL],
['Flags', REQUIRED], ['TakerPaysIssuer', OPTIONAL],
['IndexNext', OPTIONAL], ['TakerGetsCurrency', OPTIONAL],
['IndexPrevious', OPTIONAL], ['TakerGetsIssuer', OPTIONAL],
['ExchangeRate', OPTIONAL], ['Indexes', REQUIRED]]),
['LedgerIndex', OPTIONAL], EnabledFeatures: [102].concat(sleBase,[
['RootIndex', REQUIRED], ['Features', REQUIRED]]),
['Owner', OPTIONAL], FeeSettings: [115].concat(sleBase,[
['TakerPaysCurrency', OPTIONAL], ['ReferenceFeeUnits', REQUIRED],
['TakerPaysIssuer', OPTIONAL], ['ReserveBase', REQUIRED],
['TakerGetsCurrency', OPTIONAL], ['ReserveIncrement', REQUIRED],
['TakerGetsIssuer', OPTIONAL], ['BaseFee', REQUIRED],
['Indexes', REQUIRED]], ['LedgerIndex', OPTIONAL]]),
'EnabledFeatures': [102, GeneratorMap: [103].concat(sleBase,[
['LedgerEntryType', REQUIRED], ['Generator', REQUIRED]]),
['Flags', REQUIRED], LedgerHashes: [104].concat(sleBase,[
['LedgerIndex', OPTIONAL], ['LedgerEntryType', REQUIRED],
['Features', REQUIRED]], ['Flags', REQUIRED],
'FeeSettings': [115, ['FirstLedgerSequence', OPTIONAL],
['LedgerEntryType', REQUIRED], ['LastLedgerSequence', OPTIONAL],
['Flags', REQUIRED], ['LedgerIndex', OPTIONAL],
['ReferenceFeeUnits', REQUIRED], ['Hashes', REQUIRED]]),
['ReserveBase', REQUIRED], Nickname: [110].concat(sleBase,[
['ReserveIncrement', REQUIRED], ['LedgerEntryType', REQUIRED],
['BaseFee', REQUIRED], ['Flags', REQUIRED],
['LedgerIndex', OPTIONAL]], ['LedgerIndex', OPTIONAL],
'GeneratorMap': [103, ['MinimumOffer', OPTIONAL],
['LedgerEntryType', REQUIRED], ['Account', REQUIRED]]),
['Flags', REQUIRED], Offer: [111].concat(sleBase,[
['LedgerIndex', OPTIONAL], ['LedgerEntryType', REQUIRED],
['Generator', REQUIRED,]], ['Flags', REQUIRED],
'LedgerHashes': [104, ['Sequence', REQUIRED],
['LedgerEntryType', REQUIRED], ['PreviousTxnLgrSeq', REQUIRED],
['Flags', REQUIRED], ['Expiration', OPTIONAL],
['FirstLedgerSequence', OPTIONAL], ['BookNode', REQUIRED],
['LastLedgerSequence', OPTIONAL], ['OwnerNode', REQUIRED],
['LedgerIndex', OPTIONAL], ['PreviousTxnID', REQUIRED],
['Hashes', REQUIRED]], ['LedgerIndex', OPTIONAL],
'Nickname': [110, ['BookDirectory', REQUIRED],
['LedgerEntryType', REQUIRED], ['TakerPays', REQUIRED],
['Flags', REQUIRED], ['TakerGets', REQUIRED],
['LedgerIndex', OPTIONAL], ['Account', REQUIRED]]),
['MinimumOffer', OPTIONAL], RippleState: [114].concat(sleBase,[
['Account', REQUIRED]], ['LedgerEntryType', REQUIRED],
'Offer': [111, ['Flags', REQUIRED],
['LedgerEntryType', REQUIRED], ['PreviousTxnLgrSeq', REQUIRED],
['Flags', REQUIRED], ['HighQualityIn', OPTIONAL],
['Sequence', REQUIRED], ['HighQualityOut', OPTIONAL],
['PreviousTxnLgrSeq', REQUIRED], ['LowQualityIn', OPTIONAL],
['Expiration', OPTIONAL], ['LowQualityOut', OPTIONAL],
['BookNode', REQUIRED], ['LowNode', OPTIONAL],
['OwnerNode', REQUIRED], ['HighNode', OPTIONAL],
['PreviousTxnID', REQUIRED], ['PreviousTxnID', REQUIRED],
['LedgerIndex', OPTIONAL], ['LedgerIndex', OPTIONAL],
['BookDirectory', REQUIRED], ['Balance', REQUIRED],
['TakerPays', REQUIRED], ['LowLimit', REQUIRED],
['TakerGets', REQUIRED], ['HighLimit', REQUIRED]])
['Account', REQUIRED]],
'RippleState': [114,
['LedgerEntryType', REQUIRED],
['Flags', REQUIRED],
['PreviousTxnLgrSeq', REQUIRED],
['HighQualityIn', OPTIONAL],
['HighQualityOut', OPTIONAL],
['LowQualityIn', OPTIONAL],
['LowQualityOut', OPTIONAL],
['LowNode', OPTIONAL],
['HighNode', OPTIONAL],
['PreviousTxnID', REQUIRED],
['LedgerIndex', OPTIONAL],
['Balance', REQUIRED],
['LowLimit', REQUIRED],
['HighLimit', REQUIRED]]
} }
*/
exports.metadata = [ exports.metadata = [
[ 'TransactionIndex' , REQUIRED ], [ 'TransactionIndex' , REQUIRED ],

View File

@@ -17,6 +17,8 @@ exports.HASH_TX_ID = 0x54584E00; // 'TXN'
exports.HASH_TX_NODE = 0x534E4400; // 'TND' exports.HASH_TX_NODE = 0x534E4400; // 'TND'
// inner node in tree // inner node in tree
exports.HASH_INNER_NODE = 0x4D494E00; // 'MIN' exports.HASH_INNER_NODE = 0x4D494E00; // 'MIN'
// leaf node in tree
exports.HASH_LEAF_NODE = 0x4D4C4E00; // 'MLN'
// inner transaction to sign // inner transaction to sign
exports.HASH_TX_SIGN = 0x53545800; // 'STX' exports.HASH_TX_SIGN = 0x53545800; // 'STX'
// inner transaction to sign (TESTNET) // inner transaction to sign (TESTNET)

View File

@@ -37,4 +37,41 @@ Ledger.prototype.calc_tx_hash = function () {
return tx_map.hash(); return tx_map.hash();
}; };
/**
* @param options.sanity_test {Boolean}
*
* If `true`, will serialize each accountState item to binary and then back to
* json before finally serializing for hashing. This is mostly to expose any
* issues with ripple-lib's binary <--> json codecs.
*
*/
Ledger.prototype.calc_account_hash = function (options) {
var account_map = new SHAMap();
var erred;
this.ledger_json.accountState.forEach(function (le) {
var data = SerializedObject.from_json(le);
if (options != null && options.sanity_test) {
try {
var json = data.to_json();
data = SerializedObject.from_json(json);
} catch (e) {
console.log("account state item: ", le);
console.log("to_json() ",json);
console.log("exception: ", e);
erred = true;
}
};
account_map.add_item(le.index, data, SHAMapTreeNode.TYPE_ACCOUNT_STATE);
});
if (erred) {
throw new Error("There were errors with sanity_test"); // all logged above
}
return account_map.hash();
};
exports.Ledger = Ledger; exports.Ledger = Ledger;

View File

@@ -52,6 +52,14 @@ SerializedObject.from_json = function(obj) {
} }
} }
if (typeof obj.LedgerEntryType === 'number') {
obj.LedgerEntryType = SerializedObject.lookup_type_le(obj.LedgerEntryType);
if (!obj.LedgerEntryType) {
throw new Error('LedgerEntryType ID is invalid.');
}
}
if (typeof obj.TransactionType === 'string') { if (typeof obj.TransactionType === 'string') {
typedef = binformat.tx[obj.TransactionType]; typedef = binformat.tx[obj.TransactionType];
if (!Array.isArray(typedef)) { if (!Array.isArray(typedef)) {
@@ -60,9 +68,16 @@ SerializedObject.from_json = function(obj) {
typedef = typedef.slice(); typedef = typedef.slice();
obj.TransactionType = typedef.shift(); obj.TransactionType = typedef.shift();
} else if (typeof obj.LedgerEntryType !== 'undefined') { } else if (typeof obj.LedgerEntryType === 'string') {
// XXX: TODO typedef = binformat.ledger[obj.LedgerEntryType];
throw new Error('Ledger entry binary format not yet implemented.');
if (!Array.isArray(typedef)) {
throw new Error('LedgerEntryType is invalid');
}
typedef = typedef.slice();
obj.LedgerEntryType = typedef.shift();
} else if (typeof obj.AffectedNodes === 'object') { } else if (typeof obj.AffectedNodes === 'object') {
typedef = binformat.metadata; typedef = binformat.metadata;
} else { } else {
@@ -80,7 +95,7 @@ SerializedObject.from_json = function(obj) {
SerializedObject.check_no_missing_fields = function(typedef, obj) { SerializedObject.check_no_missing_fields = function(typedef, obj) {
var missing_fields = []; var missing_fields = [];
for (var i=typedef.length - 1; i>=0; i--) { for (var i = typedef.length - 1; i >= 0; i--) {
var spec = typedef[i]; var spec = typedef[i];
var field = spec[0]; var field = spec[0];
var requirement = spec[1]; var requirement = spec[1];
@@ -95,13 +110,14 @@ SerializedObject.check_no_missing_fields = function(typedef, obj) {
if (obj.TransactionType !== void(0)) { if (obj.TransactionType !== void(0)) {
object_name = SerializedObject.lookup_type_tx(obj.TransactionType); object_name = SerializedObject.lookup_type_tx(obj.TransactionType);
} else if (obj.LedgerEntryType != null){
object_name = SerializedObject.lookup_type_le(obj.LedgerEntryType);
} else { } else {
object_name = 'TransactionMetaData'; object_name = "TransactionMetaData";
} /*else { }
TODO: LedgerEntryType ...
}*/
throw new Error(object_name + ' is missing fields: ' + JSON.stringify(missing_fields)); throw new Error(object_name + " is missing fields: " +
JSON.stringify(missing_fields));
}; };
}; };
@@ -302,4 +318,9 @@ SerializedObject.lookup_type_tx = function(id) {
return TRANSACTION_TYPES[id]; return TRANSACTION_TYPES[id];
}; };
SerializedObject.lookup_type_le = function (id) {
assert(typeof id === 'number');
return LEDGER_ENTRY_TYPES[id];
};
exports.SerializedObject = SerializedObject; exports.SerializedObject = SerializedObject;

View File

@@ -220,7 +220,10 @@ var STInt64 = exports.Int64 = new SerializedType({
serialize_hex(so, hex, true); //noLength = true serialize_hex(so, hex, true); //noLength = true
}, },
parse: function (so) { parse: function (so) {
var result = new BigInteger(so.read(8), 256); var bytes = so.read(8);
// We need to add a 0, so if the high bit is set it won't think it's a
// pessimistic numeric fraek. What doth lief?
var result = new BigInteger([0].concat(bytes), 256);
assert(result instanceof BigInteger); assert(result instanceof BigInteger);
return result; return result;
} }

View File

@@ -7,7 +7,7 @@ var UInt256 = require('./uint256').UInt256;
var SerializedObject = require('./serializedobject').SerializedObject; var SerializedObject = require('./serializedobject').SerializedObject;
function SHAMap() { function SHAMap() {
this.root = new SHAMapTreeNodeInner(); this.root = new SHAMapTreeNodeInner(0);
}; };
SHAMap.prototype.add_item = function(tag, node, type) { SHAMap.prototype.add_item = function(tag, node, type) {
@@ -31,7 +31,10 @@ SHAMapTreeNode.TYPE_TRANSACTION_NM = 2;
SHAMapTreeNode.TYPE_TRANSACTION_MD = 3; SHAMapTreeNode.TYPE_TRANSACTION_MD = 3;
SHAMapTreeNode.TYPE_ACCOUNT_STATE = 4; SHAMapTreeNode.TYPE_ACCOUNT_STATE = 4;
SHAMapTreeNode.prototype.add_item = function(tag_segment, node) { /**
* @param tag {String} 64 hexadecimal characters
*/
SHAMapTreeNode.prototype.add_item = function(tag, node) {
throw new Error('Called unimplemented virtual method SHAMapTreeNode#add_item.'); throw new Error('Called unimplemented virtual method SHAMapTreeNode#add_item.');
}; };
@@ -42,48 +45,48 @@ SHAMapTreeNode.prototype.hash = function() {
/** /**
* Inner (non-leaf) node in a SHAMap tree. * Inner (non-leaf) node in a SHAMap tree.
*/ */
function SHAMapTreeNodeInner() { function SHAMapTreeNodeInner(depth) {
SHAMapTreeNode.call(this); SHAMapTreeNode.call(this);
this.leaves = {}; this.leaves = {};
this.type = SHAMapTreeNode.INNER; this.type = SHAMapTreeNode.INNER;
this.depth = depth == null ? 0 : depth;
this.empty = true; this.empty = true;
} }
util.inherits(SHAMapTreeNodeInner, SHAMapTreeNode); util.inherits(SHAMapTreeNodeInner, SHAMapTreeNode);
SHAMapTreeNodeInner.prototype.add_item = function(tag_segment, node) { /**
var current_node = this.get_node(tag_segment[0]); * @param tag {String} (equates to a ledger entries `index`)
*/
SHAMapTreeNodeInner.prototype.add_item = function (tag, node) {
var depth = this.depth;
var existing_node = this.get_node(tag[depth]);
if (current_node) { if (existing_node) {
// A node already exists in this slot // A node already exists in this slot
if (existing_node instanceof SHAMapTreeNodeInner) {
if (current_node instanceof SHAMapTreeNodeInner) {
// There is an inner node, so we need to go deeper // There is an inner node, so we need to go deeper
current_node.add_item(tag_segment.slice(1), node); existing_node.add_item(tag, node);
} else if (current_node.get_segment() === tag_segment) { } else if (existing_node.tag === tag) {
// Collision // Collision
throw new Error('Tried to add a node to a SHAMap that was already in there.'); throw new Error('Tried to add a node to a SHAMap that was already in there.');
} else { } else {
// Turn it into an inner node // Turn it into an inner node
var new_inner_node = new SHAMapTreeNodeInner(); var new_inner_node = new SHAMapTreeNodeInner(depth + 1);
// Move the existing leaf node down one level // Parent new and existing node
current_node.set_segment(current_node.get_segment().slice(1)); new_inner_node.add_item(existing_node.tag, existing_node);
new_inner_node.set_node(current_node.get_segment()[0], current_node); new_inner_node.add_item(tag, node);
// Add the new node next to it
new_inner_node.add_item(tag_segment.slice(1), node);
// And place the newly created inner node in the slot // And place the newly created inner node in the slot
this.set_node(tag_segment[0], new_inner_node); this.set_node(tag[depth], new_inner_node);
} }
} else { } else {
// Neat, we have a nice open spot for the new node // Neat, we have a nice open spot for the new node
node.set_segment(tag_segment); this.set_node(tag[depth], node);
this.set_node(tag_segment[0], node);
} }
}; };
@@ -129,37 +132,30 @@ SHAMapTreeNodeInner.prototype.hash = function() {
function SHAMapTreeNodeLeaf(tag, node, type) { function SHAMapTreeNodeLeaf(tag, node, type) {
SHAMapTreeNode.call(this); SHAMapTreeNode.call(this);
if (typeof tag === 'string') { if (typeof tag !== 'string') {
tag = UInt256.from_hex(tag);
} else if (!(tag instanceof UInt256)){
throw new Error('Tag is unexpected type.'); throw new Error('Tag is unexpected type.');
} }
this.tag = tag; this.tag = tag;
this.tag_segment = null; this.tag_bytes = UInt256.from_hex(this.tag).to_bytes();
this.type = type; this.type = type;
this.node = node; this.node = node;
}; };
util.inherits(SHAMapTreeNodeLeaf, SHAMapTreeNode); util.inherits(SHAMapTreeNodeLeaf, SHAMapTreeNode);
SHAMapTreeNodeLeaf.prototype.get_segment = function(segment) { SHAMapTreeNodeLeaf.prototype.hash = function () {
return this.tag_segment;
};
SHAMapTreeNodeLeaf.prototype.set_segment = function(segment) {
this.tag_segment = segment;
};
SHAMapTreeNodeLeaf.prototype.hash = function() {
var buffer = new SerializedObject(); var buffer = new SerializedObject();
switch (this.type) { switch (this.type) {
case SHAMapTreeNode.TYPE_ACCOUNT_STATE:
buffer.append(this.node);
buffer.append(this.tag_bytes);
return buffer.hash(hashprefixes.HASH_LEAF_NODE);
case SHAMapTreeNode.TYPE_TRANSACTION_NM: case SHAMapTreeNode.TYPE_TRANSACTION_NM:
return this.tag; return this.tag_bytes;
case SHAMapTreeNode.TYPE_TRANSACTION_MD: case SHAMapTreeNode.TYPE_TRANSACTION_MD:
buffer.append(this.node); buffer.append(this.node);
buffer.append(this.tag.to_bytes()); buffer.append(this.tag_bytes);
return buffer.hash(hashprefixes.HASH_TX_NODE); return buffer.hash(hashprefixes.HASH_TX_NODE);
default: default:
throw new Error('Tried to hash a SHAMap node of unknown type.'); throw new Error('Tried to hash a SHAMap node of unknown type.');

1
test/fixtures/ledger-full-38129.json vendored Normal file

File diff suppressed because one or more lines are too long

1
test/fixtures/ledger-full-40000.json vendored Normal file

File diff suppressed because one or more lines are too long

39
test/ledger-test.js Normal file
View File

@@ -0,0 +1,39 @@
var assert = require('assert');
var fs = require('fs');
var utils = require('./testutils');
var Ledger = utils.load_module('ledger').Ledger;
var config = require('./testutils').get_config();
/**
* @param ledger_index {Number}
* Expects a corresponding ledger dump in $repo/test/fixtures/ folder
*/
create_ledger_test = function (ledger_index) {
describe(String(ledger_index), function() {
var path = __dirname + '/fixtures/ledger-full-'+ledger_index+'.json';
var ledger_raw = fs.readFileSync(path),
ledger_json = JSON.parse(ledger_raw),
ledger = Ledger.from_json(ledger_json);
it('has account_hash of '+ ledger_json.account_hash, function() {
assert.equal(ledger_json.account_hash,
ledger.calc_account_hash({sanity_test:true}).to_hex());
})
it('has transaction_hash of '+ ledger_json.transaction_hash, function() {
assert.equal(ledger_json.transaction_hash,
ledger.calc_tx_hash().to_hex());
})
})
}
describe('Ledger', function() {
// This is the first recorded ledger with a non empty transaction set
create_ledger_test(38129);
// Because, why not.
create_ledger_test(40000);
});
// vim:sw=2:sts=2:ts=8:et

View File

@@ -3,8 +3,8 @@ var assert = require('assert');
var SerializedObject = utils.load_module('serializedobject').SerializedObject; var SerializedObject = utils.load_module('serializedobject').SerializedObject;
describe('Serialized object', function() { describe('Serialized object', function() {
describe('Serialized object', function() { describe('#from_json(v).to_json() == v', function(){
it('From json and back', function() { it('outputs same as passed to from_json', function() {
var input_json = { var input_json = {
Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Amount: '274579388', Amount: '274579388',
@@ -35,27 +35,60 @@ describe('Serialized object', function() {
assert.deepEqual(input_json, output_json); assert.deepEqual(input_json, output_json);
}); });
}); });
describe('Format validation', function() { describe('#from_json', function() {
// Peercover actually had a problem submitting transactions without a `Fee` it('understands TransactionType as a Number', function() {
// and rippled was only informing of "transaction is invalid"
it('should throw an Error when there is a missing field', function() {
var input_json = { var input_json = {
// no non required fields
Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Amount: '274579388', Amount: '274579388',
Destination: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS', Destination: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Fee: '15',
Sequence: 351, Sequence: 351,
SigningPubKey: '02854B06CE8F3E65323F89260E9E19B33DA3E01B30EA4CA172612DE77973FAC58A', SigningPubKey: '02',// VL field ;)
TransactionType: 'Payment', TransactionType: 0 //
TxnSignature: '30450221009DA3A42DD25E3B22EC45AD8BA8FC7A954264264A816D300B2DF69F814D7D4DD2022072C9627F97EEC6DA13DE841E06E2CD985EF06A0FBB15DDBF0800D0730C8986BF'
}; };
assert.throws ( var output_json = SerializedObject.from_json(input_json).to_json();
function() {
var output_json = SerializedObject.from_json(input_json); assert.equal(0, input_json.TransactionType);
}, assert.equal("Payment", output_json.TransactionType);
/Payment is missing fields: \["Fee"\]/
);
}); });
}); it('understands LedgerEntryType as a Number', function() {
var input_json = {
// no, non required fields
"LedgerEntryType": 100,
"Flags": 0,
"Indexes": [],
"RootIndex": "000360186E008422E06B72D5B275E29EE3BE9D87A370F424E0E7BF613C465909"
}
var output_json = SerializedObject.from_json(input_json).to_json();
assert.equal(100, input_json.LedgerEntryType);
assert.equal("DirectoryNode", output_json.LedgerEntryType);
});
describe('Format validation', function() {
// Peercover actually had a problem submitting transactions without a `Fee`
// and rippled was only informing of "transaction is invalid"
it('should throw an Error when there is a missing field', function() {
var input_json = {
Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Amount: '274579388',
Destination: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
Sequence: 351,
SigningPubKey: '02854B06CE8F3E65323F89260E9E19B33DA3E01B30EA4CA172612DE77973FAC58A',
TransactionType: 'Payment',
TxnSignature: '30450221009DA3A42DD25E3B22EC45AD8BA8FC7A954264264A816D300B2DF69F814D7D4DD2022072C9627F97EEC6DA13DE841E06E2CD985EF06A0FBB15DDBF0800D0730C8986BF'
};
assert.throws (
function() {
var output_json = SerializedObject.from_json(input_json);
},
/Payment is missing fields: \["Fee"\]/
);
});
});
})
}); });
// vim:sw=2:sts=2:ts=8:et // vim:sw=2:sts=2:ts=8:et

View File

@@ -282,6 +282,15 @@ describe('Serialized types', function() {
types.Int64.serialize(so, 4294967295.5); types.Int64.serialize(so, 4294967295.5);
assert.strictEqual(so.to_hex(), '00000000FFFFFFFF'); assert.strictEqual(so.to_hex(), '00000000FFFFFFFF');
}); });
it('Does not get confused when the high bit is set', function () {
var so = new SerializedObject();
types.Int64.serialize(so, "8B2386F26F8E232B");
assert.strictEqual(so.to_hex(), '8B2386F26F8E232B');
var so = new SerializedObject("8B2386F26F8E232B");
var num = types.Int64.parse(so);
// We get a positive number
assert.strictEqual(num.toString(16), '8b2386f26f8e232b');
});
it('Serialize "0123456789ABCDEF"', function () { it('Serialize "0123456789ABCDEF"', function () {
var so = new SerializedObject(); var so = new SerializedObject();
types.Int64.serialize(so, '0123456789ABCDEF'); types.Int64.serialize(so, '0123456789ABCDEF');
@@ -798,21 +807,16 @@ describe('Serialized types', function() {
var so = new SerializedObject(hex); var so = new SerializedObject(hex);
var as_json = so.to_json(); var as_json = so.to_json();
var expected_json = { var expected_json = {
"LedgerEntryType": "DirectoryNode", "LedgerEntryType": "DirectoryNode",
"Owner": "rh6kN9s7spSb3vdv6H8ZGYzsddSLeEUGmc", "Owner": "rh6kN9s7spSb3vdv6H8ZGYzsddSLeEUGmc",
"Flags": 0, "Flags": 0,
"Indexes": [ "Indexes": [
"081342A0AB45459A54D8E4FA1842339A102680216CF9A152BCE4F4CE467D8246" "081342A0AB45459A54D8E4FA1842339A102680216CF9A152BCE4F4CE467D8246"
], ],
"RootIndex": "000360186E008422E06B72D5B275E29EE3BE9D87A370F424E0E7BF613C465909" "RootIndex": "000360186E008422E06B72D5B275E29EE3BE9D87A370F424E0E7BF613C465909"
} }
assert.deepEqual(as_json, expected_json); assert.deepEqual(as_json, expected_json);
assert.throws(function () { assert.strictEqual(SerializedObject.from_json(expected_json).to_hex(), hex)
// This is an encoded reminder/TODO:
// Serializing ledger entries isn't currently supported, but when it
// is, this should no longer throw, and the test will fail
/*assert.strictEqual(*/SerializedObject.from_json(expected_json).to_hex()/*, hex)*/;
})
}); });
it('Serialize empty object {}', function () { it('Serialize empty object {}', function () {
var so = new SerializedObject(); var so = new SerializedObject();