mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-27 15:45:48 +00:00
Fix merge conflicts
This commit is contained in:
@@ -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
43
scripts/verify_ledger_json.js
Normal file → Executable 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());
|
||||||
|
|||||||
@@ -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 ],
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
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
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
39
test/ledger-test.js
Normal 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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user