Skip to content

Commit

Permalink
Add computeLedgerHash method
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Clark committed Aug 14, 2015
1 parent 8c431b4 commit 92fbc61
Show file tree
Hide file tree
Showing 7 changed files with 594 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/api/index.js
Expand Up @@ -30,6 +30,7 @@ const errors = require('./common').errors;
const convertExceptions = require('./common').convertExceptions;
const generateWallet = convertExceptions(common.core.Wallet.generate);
const getLedgerHeader = require('./ledger/ledger-header');
const computeLedgerHash = require('./offline/ledgerhash');

function RippleAPI(options: {}) {
const _options = _.assign({}, options, {automatic_resubmission: false});
Expand Down Expand Up @@ -63,6 +64,7 @@ RippleAPI.prototype = {
sign,
submit,

computeLedgerHash,
generateWallet,
errors
};
Expand Down
52 changes: 52 additions & 0 deletions src/api/offline/ledgerhash.js
@@ -0,0 +1,52 @@
/* @flow */
'use strict';
const _ = require('lodash');
const common = require('../common');

function convertLedgerHeader(header) {
return {
accepted: header.accepted,
closed: header.closed,
account_hash: header.accountHash,
close_time: header.closeTime,
close_time_resolution: header.closeTimeResolution,
close_flags: header.closeFlags,
hash: header.ledgerHash,
ledger_hash: header.ledgerHash,
ledger_index: header.ledgerVersion.toString(),
seqNum: header.ledgerVersion.toString(),
parent_hash: header.parentLedgerHash,
parent_close_time: header.parentCloseTime,
total_coins: header.totalDrops,
totalCoins: header.totalDrops,
transaction_hash: header.transactionHash
};
}

function hashLedgerHeader(ledgerHeader) {
const header = convertLedgerHeader(ledgerHeader);
return common.core.Ledger.calculateLedgerHash(header);
}

function computeLedgerHash(ledgerHeader: Object, transactions: Array<Object>
): string {
if (transactions) {
const txs = _.map(transactions, tx => {
const mergeTx = _.assign({}, _.omit(tx, 'tx'), tx.tx || {});
const renameMeta = _.assign({}, _.omit(mergeTx, 'meta'),
tx.meta ? {metaData: tx.meta} : {});
return renameMeta;
});
const ledger = common.core.Ledger.from_json({transactions: txs});
const transactionHash = ledger.calc_tx_hash().to_hex();
if (ledgerHeader.transaction_hash !== undefined
&& ledgerHeader.transaction_hash !== transactionHash) {
throw new common.errors.ValidationError('transaction_hash in header'
+ ' does not match computed hash of transactions');
}
return hashLedgerHeader(_.assign({}, ledgerHeader, {transactionHash}));
}
return hashLedgerHeader(ledgerHeader);
}

module.exports = computeLedgerHash;
21 changes: 21 additions & 0 deletions src/core/ledger.js
@@ -1,4 +1,5 @@
'use strict';
const BigNumber = require('bignumber.js');
const Transaction = require('./transaction').Transaction;
const SHAMap = require('./shamap').SHAMap;
const SHAMapTreeNode = require('./shamap').SHAMapTreeNode;
Expand Down Expand Up @@ -156,4 +157,24 @@ Ledger.prototype.calc_account_hash = function(options) {
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;
27 changes: 27 additions & 0 deletions test/api-test.js
Expand Up @@ -754,4 +754,31 @@ describe('RippleAPI - offline', function() {
});
});
});

it('computeLedgerHash', function() {
const api = new RippleAPI();
const header = requests.computeLedgerHash.header;
const ledgerHash = api.computeLedgerHash(header);
assert.strictEqual(ledgerHash,
'F4D865D83EB88C1A1911B9E90641919A1314F36E1B099F8E95FE3B7C77BE3349');
});

it('computeLedgerHash - with transactions', function() {
const api = new RippleAPI();
const header = _.omit(requests.computeLedgerHash.header,
'transaction_hash');
const transactions = requests.computeLedgerHash.transactions;
const ledgerHash = api.computeLedgerHash(header, transactions);
assert.strictEqual(ledgerHash,
'F4D865D83EB88C1A1911B9E90641919A1314F36E1B099F8E95FE3B7C77BE3349');
});

it('computeLedgerHash - incorrent transaction_hash', function() {
const api = new RippleAPI();
const header = _.assign({}, requests.computeLedgerHash.header,
{transaction_hash:
'325EACC5271322539EEEC2D6A5292471EF1B3E72AE7180533EFC3B8F0AD435C9'});
const transactions = requests.computeLedgerHash.transactions;
assert.throws(() => api.computeLedgerHash(header, transactions));
});
});

0 comments on commit 92fbc61

Please sign in to comment.