Add hidden computeTreeHashes option

This commit is contained in:
wilsonianb
2018-08-29 17:49:30 -05:00
committed by Elliot Lee
parent dbe20d6574
commit b03795df09
2 changed files with 67 additions and 24 deletions

View File

@@ -28,27 +28,25 @@ function hashLedgerHeader(ledgerHeader) {
function computeTransactionHash(ledger, version,
options: ComputeLedgerHashOptions) {
let transactions: any[]
if (options.headerOnly === undefined) {
options.headerOnly = true // by default, allow rawTransactions to be omitted
}
if (ledger.rawTransactions === undefined) {
if (options.headerOnly === true) {
return ledger.transactionHash
} else {
try {
transactions = ledger.transactions.map(tx =>
JSON.parse(tx.rawTransaction))
} catch (e) {
if (e.toString() === 'SyntaxError: Unexpected' +
' token u in JSON at position 0') {
// one or more of the `tx.rawTransaction`s is undefined
throw new common.errors.ValidationError('ledger'
+ ' is missing raw transactions')
}
if (ledger.rawTransactions) {
transactions = JSON.parse(ledger.rawTransactions)
} else if (ledger.transactions) {
try {
transactions = ledger.transactions.map(tx =>
JSON.parse(tx.rawTransaction))
} catch (e) {
if (e.toString() === 'SyntaxError: Unexpected' +
' token u in JSON at position 0') {
// one or more of the `tx.rawTransaction`s is undefined
throw new common.errors.ValidationError('ledger'
+ ' is missing raw transactions')
}
}
} else {
transactions = JSON.parse(ledger.rawTransactions)
if (options.computeTreeHashes)
throw new common.errors.ValidationError('transactions'
+ ' property is missing from the ledger')
return ledger.transactionHash
}
const txs = _.map(transactions, tx => {
const mergeTx = _.assign({}, _.omit(tx, 'tx'), tx.tx || {})
@@ -69,8 +67,12 @@ function computeTransactionHash(ledger, version,
return transactionHash
}
function computeStateHash(ledger, version) {
function computeStateHash(ledger, version,
options: ComputeLedgerHashOptions) {
if (ledger.rawState === undefined) {
if (options.computeTreeHashes)
throw new common.errors.ValidationError('rawState'
+ ' property is missing from the ledger')
return ledger.stateHash
}
const state = JSON.parse(ledger.rawState)
@@ -85,7 +87,7 @@ function computeStateHash(ledger, version) {
const sLCF_SHAMapV2 = 0x02
export type ComputeLedgerHashOptions = {
headerOnly?: boolean
computeTreeHashes?: boolean
}
function computeLedgerHash(ledger: any,
@@ -93,7 +95,7 @@ function computeLedgerHash(ledger: any,
const version = ((ledger.closeFlags & sLCF_SHAMapV2) === 0) ? 1 : 2
const subhashes = {
transactionHash: computeTransactionHash(ledger, version, options),
stateHash: computeStateHash(ledger, version)
stateHash: computeStateHash(ledger, version, options)
}
return hashLedgerHeader(_.assign({}, ledger, subhashes))
}

View File

@@ -2550,7 +2550,7 @@ describe('RippleAPI', function () {
.then(response => {
const ledger = _.assign({}, response,
{ parentCloseTime: response.closeTime });
const hash = this.api.computeLedgerHash(ledger, {headerOnly: false});
const hash = this.api.computeLedgerHash(ledger, {computeTreeHashes: true});
assert.strictEqual(hash,
'E6DB7365949BF9814D76BCC730B01818EB9136A89DB224F3F9F5AAE4569D758E');
});
@@ -2573,7 +2573,7 @@ describe('RippleAPI', function () {
let hash;
try {
hash = this.api.computeLedgerHash(ledger, {headerOnly: false});
hash = this.api.computeLedgerHash(ledger, {computeTreeHashes: true});
} catch (error) {
assert(error instanceof this.api.errors.ValidationError);
assert.strictEqual(error.message, 'transactionHash in header does not match computed hash of transactions');
@@ -2604,7 +2604,7 @@ describe('RippleAPI', function () {
let hash;
try {
hash = this.api.computeLedgerHash(ledger, {headerOnly: false});
hash = this.api.computeLedgerHash(ledger, {computeTreeHashes: true});
} catch (error) {
assert(error instanceof this.api.errors.ValidationError);
assert.strictEqual(error.message, 'ledger'
@@ -2615,6 +2615,47 @@ describe('RippleAPI', function () {
});
});
it('computeLedgerHash - given ledger without state or transactions - only compute ledger hash', function () {
const request = {
includeTransactions: true,
includeState: true,
includeAllData: true,
ledgerVersion: 38129
};
return this.api.getLedger(request).then(ledger => {
assert.strictEqual(ledger.transactions[0].rawTransaction, "{\"Account\":\"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV\",\"Amount\":\"10000000000\",\"Destination\":\"rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj\",\"Fee\":\"10\",\"Flags\":0,\"Sequence\":62,\"SigningPubKey\":\"034AADB09CFF4A4804073701EC53C3510CDC95917C2BB0150FB742D0C66E6CEE9E\",\"TransactionType\":\"Payment\",\"TxnSignature\":\"3045022022EB32AECEF7C644C891C19F87966DF9C62B1F34BABA6BE774325E4BB8E2DD62022100A51437898C28C2B297112DF8131F2BB39EA5FE613487DDD611525F1796264639\",\"hash\":\"3B1A4E1C9BB6A7208EB146BCDB86ECEA6068ED01466D933528CA2B4C64F753EF\",\"meta\":{\"AffectedNodes\":[{\"CreatedNode\":{\"LedgerEntryType\":\"AccountRoot\",\"LedgerIndex\":\"4C6ACBD635B0F07101F7FA25871B0925F8836155462152172755845CE691C49E\",\"NewFields\":{\"Account\":\"rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj\",\"Balance\":\"10000000000\",\"Sequence\":1}}},{\"ModifiedNode\":{\"FinalFields\":{\"Account\":\"r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV\",\"Balance\":\"981481999380\",\"Flags\":0,\"OwnerCount\":0,\"Sequence\":63},\"LedgerEntryType\":\"AccountRoot\",\"LedgerIndex\":\"B33FDD5CF3445E1A7F2BE9B06336BEBD73A5E3EE885D3EF93F7E3E2992E46F1A\",\"PreviousFields\":{\"Balance\":\"991481999390\",\"Sequence\":62},\"PreviousTxnID\":\"2485FDC606352F1B0785DA5DE96FB9DBAF43EB60ECBB01B7F6FA970F512CDA5F\",\"PreviousTxnLgrSeq\":31317}}],\"TransactionIndex\":0,\"TransactionResult\":\"tesSUCCESS\"},\"ledger_index\":38129}");
ledger.parentCloseTime = ledger.closeTime;
const computeLedgerHash = this.api.computeLedgerHash;
const ValidationError = this.api.errors.ValidationError
function testCompute(ledger, expectedError) {
let hash = computeLedgerHash(ledger);
assert.strictEqual(hash,
'E6DB7365949BF9814D76BCC730B01818EB9136A89DB224F3F9F5AAE4569D758E');
// fail if required to compute tree hashes
try {
hash = computeLedgerHash(ledger, {computeTreeHashes: true});
} catch (error) {
assert(error instanceof ValidationError);
assert.strictEqual(error.message, expectedError);
return;
}
assert(false, 'Should throw ValidationError instead of producing hash: ' + hash);
}
const transactions = ledger.transactions;
delete ledger.transactions;
testCompute(ledger, 'transactions property is missing from the ledger');
delete ledger.rawState;
testCompute(ledger, 'transactions property is missing from the ledger');
ledger.transactions = transactions;
testCompute(ledger, 'rawState property is missing from the ledger');
});
});
it('computeLedgerHash - wrong hash', function () {
const request = {
includeTransactions: true,