Files
xahau.js/src/core/ledger.js
2015-10-08 15:48:23 -07:00

181 lines
5.3 KiB
JavaScript

'use strict';
const BigNumber = require('bignumber.js');
const Transaction = require('./transaction').Transaction;
const SHAMap = require('./shamap').SHAMap;
const SHAMapTreeNode = require('./shamap').SHAMapTreeNode;
const SerializedObject = require('./serializedobject').SerializedObject;
const stypes = require('./serializedtypes');
const UInt160 = require('./uint160').UInt160;
const Currency = require('./currency').Currency;
function Ledger() {
this.ledger_json = {};
}
Ledger.from_json = function(v) {
const ledger = new Ledger();
ledger.parse_json(v);
return ledger;
};
Ledger.space = require('./ledgerspaces');
/**
* Generate the key for an AccountRoot entry.
*
* @param {String|UInt160} accountArg - Ripple Account
* @return {UInt256}
*/
Ledger.calcAccountRootEntryHash =
Ledger.prototype.calcAccountRootEntryHash = function(accountArg) {
const account = UInt160.from_json(accountArg);
const index = new SerializedObject();
index.append([0, Ledger.space.account.charCodeAt(0)]);
index.append(account.to_bytes());
return index.hash();
};
/**
* Generate the key for an Offer entry.
*
* @param {String|UInt160} accountArg - Ripple Account
* @param {Number} sequence - Sequence number of the OfferCreate transaction
* that instantiated this offer.
* @return {UInt256}
*/
Ledger.calcOfferEntryHash =
Ledger.prototype.calcOfferEntryHash = function(accountArg, sequence) {
const account = UInt160.from_json(accountArg);
const index = new SerializedObject();
index.append([0, Ledger.space.offer.charCodeAt(0)]);
index.append(account.to_bytes());
stypes.Int32.serialize(index, sequence);
return index.hash();
};
/**
* Generate the key for a RippleState entry.
*
* The ordering of the two account parameters does not matter.
*
* @param {String|UInt160} _account1 - First Ripple Account
* @param {String|UInt160} _account2 - Second Ripple Account
* @param {String|Currency} _currency - The currency code
* @return {UInt256}
*/
Ledger.calcRippleStateEntryHash =
Ledger.prototype.calcRippleStateEntryHash = function(
_account1, _account2, _currency) {
const currency = Currency.from_json(_currency);
const account1 = UInt160.from_json(_account1);
const account2 = UInt160.from_json(_account2);
if (!account1.is_valid()) {
throw new Error('Invalid first account');
}
if (!account2.is_valid()) {
throw new Error('Invalid second account');
}
if (!currency.is_valid()) {
throw new Error('Invalid currency');
}
const swap = account1.greater_than(account2);
const lowAccount = swap ? account2 : account1;
const highAccount = swap ? account1 : account2;
const index = new SerializedObject();
index.append([0, Ledger.space.rippleState.charCodeAt(0)]);
index.append(lowAccount.to_bytes());
index.append(highAccount.to_bytes());
index.append(currency.to_bytes());
return index.hash();
};
Ledger.prototype.parse_json = function(v) {
this.ledger_json = v;
};
Ledger.prototype.calc_tx_hash = function() {
const tx_map = new SHAMap();
this.ledger_json.transactions.forEach(function(tx_json) {
const tx = Transaction.from_json(tx_json);
const meta = SerializedObject.from_json(tx_json.metaData);
const data = new SerializedObject();
stypes.VariableLength.serialize(data, tx.serialize());
stypes.VariableLength.serialize(data, meta.to_hex());
tx_map.add_item(tx.hash(), data, SHAMapTreeNode.TYPE_TRANSACTION_MD);
});
return tx_map.hash();
};
/**
* @param {Object} options - object
*
* @param {Boolean} [options.sanity_test=false] - 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.
*
* @return {UInt256} - hash of shamap
*/
Ledger.prototype.calc_account_hash = function(options) {
const account_map = new SHAMap();
let erred;
this.ledger_json.accountState.forEach(function(le) {
let data = SerializedObject.from_json(le);
let json;
if (options && options.sanity_test) {
try {
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();
};
// see rippled Ledger::updateHash()
Ledger.calculateLedgerHash =
Ledger.prototype.calculateLedgerHash = function(ledgerHeader) {
const so = new SerializedObject();
const prefix = 0x4C575200;
const totalCoins = (new BigNumber(ledgerHeader.total_coins)).toString(16);
stypes.Int32.serialize(so, Number(ledgerHeader.ledger_index));
stypes.Int64.serialize(so, totalCoins);
stypes.Hash256.serialize(so, ledgerHeader.parent_hash);
stypes.Hash256.serialize(so, ledgerHeader.transaction_hash);
stypes.Hash256.serialize(so, ledgerHeader.account_hash);
stypes.Int32.serialize(so, ledgerHeader.parent_close_time);
stypes.Int32.serialize(so, ledgerHeader.close_time);
stypes.Int8.serialize(so, ledgerHeader.close_time_resolution);
stypes.Int8.serialize(so, ledgerHeader.close_flags);
return so.hash(prefix).to_hex();
};
exports.Ledger = Ledger;