Updates to allow verify_ledger_json.js to calculate account_state hash

This commit is contained in:
Nicholas Dudfield
2014-05-06 13:34:01 +07:00
parent 473d8a8d8c
commit a222f2be98
7 changed files with 156 additions and 139 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);
}; };

View File

@@ -9,6 +9,8 @@ if (process.argc < 1) {
var json = fs.readFileSync(process.argv[2], 'utf-8'); var json = fs.readFileSync(process.argv[2], '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())
console.log("Transaction hash in header: "+ledger.ledger_json.transaction_hash); 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().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,16 @@ Ledger.prototype.calc_tx_hash = function () {
return tx_map.hash(); return tx_map.hash();
}; };
Ledger.prototype.calc_account_hash = function () {
var account_map = new SHAMap();
this.ledger_json.accountState.forEach(function (le) {
var data = SerializedObject.from_json(le);
account_map.add_item(le.index, data, SHAMapTreeNode.TYPE_ACCOUNT_STATE);
});
return account_map.hash();
};
exports.Ledger = Ledger; exports.Ledger = Ledger;

View File

@@ -53,6 +53,14 @@ SerializedObject.from_json = function (obj) {
} }
} }
if ("number" === typeof obj.TransactionType) {
obj.LedgerEntryType = SerializedObject.lookup_type_le(obj.LedgerEntryType);
if (!obj.LedgerEntryType) {
throw new Error('LedgerEntryType ID is invalid.');
}
}
if ("string" === typeof obj.TransactionType) { if ("string" === typeof obj.TransactionType) {
typedef = binformat.tx[obj.TransactionType]; typedef = binformat.tx[obj.TransactionType];
@@ -62,9 +70,16 @@ SerializedObject.from_json = function (obj) {
typedef = typedef.slice(); typedef = typedef.slice();
obj.TransactionType = typedef.shift(); obj.TransactionType = typedef.shift();
} else if ("undefined" !== typeof obj.LedgerEntryType) { } else if ("string" === typeof obj.LedgerEntryType) {
// 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 ("object" === typeof obj.AffectedNodes) { } else if ("object" === typeof obj.AffectedNodes) {
typedef = binformat.metadata; typedef = binformat.metadata;
} else { } else {
@@ -81,7 +96,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]
@@ -91,17 +106,18 @@ SerializedObject.check_no_missing_fields = function (typedef, obj) {
missing_fields.push(field); missing_fields.push(field);
}; };
}; };
if (missing_fields.length > 0) { if (missing_fields.length > 0) {
var object_name; var object_name;
if (obj.TransactionType != null) { if (obj.TransactionType != null) {
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: " +
}*/
throw new Error(object_name + " is missing fields: " +
JSON.stringify(missing_fields)); JSON.stringify(missing_fields));
}; };
} }
@@ -296,4 +312,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

@@ -44,13 +44,16 @@ 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;
// TODO
this.depth == depth == null ? 0 : depth;
this.empty = true; this.empty = true;
} }
@@ -156,6 +159,10 @@ SHAMapTreeNodeLeaf.prototype.set_segment = function (segment) {
SHAMapTreeNodeLeaf.prototype.hash = function () { 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.to_bytes());
return buffer.hash(hashprefixes.HASH_LEAF_NODE);
case SHAMapTreeNode.TYPE_TRANSACTION_NM: case SHAMapTreeNode.TYPE_TRANSACTION_NM:
return this.tag; return this.tag;
case SHAMapTreeNode.TYPE_TRANSACTION_MD: case SHAMapTreeNode.TYPE_TRANSACTION_MD: