You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

hashLedger.js 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. "use strict";
  2. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  3. if (k2 === undefined) k2 = k;
  4. var desc = Object.getOwnPropertyDescriptor(m, k);
  5. if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
  6. desc = { enumerable: true, get: function() { return m[k]; } };
  7. }
  8. Object.defineProperty(o, k2, desc);
  9. }) : (function(o, m, k, k2) {
  10. if (k2 === undefined) k2 = k;
  11. o[k2] = m[k];
  12. }));
  13. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  14. Object.defineProperty(o, "default", { enumerable: true, value: v });
  15. }) : function(o, v) {
  16. o["default"] = v;
  17. });
  18. var __importStar = (this && this.__importStar) || function (mod) {
  19. if (mod && mod.__esModule) return mod;
  20. var result = {};
  21. if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
  22. __setModuleDefault(result, mod);
  23. return result;
  24. };
  25. var __importDefault = (this && this.__importDefault) || function (mod) {
  26. return (mod && mod.__esModule) ? mod : { "default": mod };
  27. };
  28. Object.defineProperty(exports, "__esModule", { value: true });
  29. exports.hashStateTree = exports.hashTxTree = exports.hashLedgerHeader = exports.hashSignedTx = void 0;
  30. const bignumber_js_1 = __importDefault(require("bignumber.js"));
  31. const ripple_binary_codec_1 = require("ripple-binary-codec");
  32. const errors_1 = require("../../errors");
  33. const HashPrefix_1 = __importDefault(require("./HashPrefix"));
  34. const sha512Half_1 = __importDefault(require("./sha512Half"));
  35. const SHAMap_1 = __importStar(require("./SHAMap"));
  36. const HEX = 16;
  37. function intToHex(integer, byteLength) {
  38. const foo = Number(integer)
  39. .toString(HEX)
  40. .padStart(byteLength * 2, '0');
  41. return foo;
  42. }
  43. function bytesToHex(bytes) {
  44. return Buffer.from(bytes).toString('hex');
  45. }
  46. function bigintToHex(integerString, byteLength) {
  47. const hex = new bignumber_js_1.default(integerString).toString(HEX);
  48. return hex.padStart(byteLength * 2, '0');
  49. }
  50. function addLengthPrefix(hex) {
  51. const length = hex.length / 2;
  52. if (length <= 192) {
  53. return bytesToHex([length]) + hex;
  54. }
  55. if (length <= 12480) {
  56. const prefix = length - 193;
  57. return bytesToHex([193 + (prefix >>> 8), prefix & 0xff]) + hex;
  58. }
  59. if (length <= 918744) {
  60. const prefix = length - 12481;
  61. return (bytesToHex([
  62. 241 + (prefix >>> 16),
  63. (prefix >>> 8) & 0xff,
  64. prefix & 0xff,
  65. ]) + hex);
  66. }
  67. throw new errors_1.XrplError('Variable integer overflow.');
  68. }
  69. function hashSignedTx(tx) {
  70. let txBlob;
  71. let txObject;
  72. if (typeof tx === 'string') {
  73. txBlob = tx;
  74. txObject = (0, ripple_binary_codec_1.decode)(tx);
  75. }
  76. else {
  77. txBlob = (0, ripple_binary_codec_1.encode)(tx);
  78. txObject = tx;
  79. }
  80. if (txObject.TxnSignature === undefined && txObject.Signers === undefined) {
  81. throw new errors_1.ValidationError('The transaction must be signed to hash it.');
  82. }
  83. const prefix = HashPrefix_1.default.TRANSACTION_ID.toString(16).toUpperCase();
  84. return (0, sha512Half_1.default)(prefix.concat(txBlob));
  85. }
  86. exports.hashSignedTx = hashSignedTx;
  87. function hashLedgerHeader(ledgerHeader) {
  88. const prefix = HashPrefix_1.default.LEDGER.toString(HEX).toUpperCase();
  89. const ledger = prefix +
  90. intToHex(Number(ledgerHeader.ledger_index), 4) +
  91. bigintToHex(ledgerHeader.total_coins, 8) +
  92. ledgerHeader.parent_hash +
  93. ledgerHeader.transaction_hash +
  94. ledgerHeader.account_hash +
  95. intToHex(ledgerHeader.parent_close_time, 4) +
  96. intToHex(ledgerHeader.close_time, 4) +
  97. intToHex(ledgerHeader.close_time_resolution, 1) +
  98. intToHex(ledgerHeader.close_flags, 1);
  99. return (0, sha512Half_1.default)(ledger);
  100. }
  101. exports.hashLedgerHeader = hashLedgerHeader;
  102. function hashTxTree(transactions) {
  103. var _a;
  104. const shamap = new SHAMap_1.default();
  105. for (const txJSON of transactions) {
  106. const txBlobHex = (0, ripple_binary_codec_1.encode)(txJSON);
  107. const metaHex = (0, ripple_binary_codec_1.encode)((_a = txJSON.metaData) !== null && _a !== void 0 ? _a : {});
  108. const txHash = hashSignedTx(txBlobHex);
  109. const data = addLengthPrefix(txBlobHex) + addLengthPrefix(metaHex);
  110. shamap.addItem(txHash, data, SHAMap_1.NodeType.TRANSACTION_METADATA);
  111. }
  112. return shamap.hash;
  113. }
  114. exports.hashTxTree = hashTxTree;
  115. function hashStateTree(entries) {
  116. const shamap = new SHAMap_1.default();
  117. entries.forEach((ledgerEntry) => {
  118. const data = (0, ripple_binary_codec_1.encode)(ledgerEntry);
  119. shamap.addItem(ledgerEntry.index, data, SHAMap_1.NodeType.ACCOUNT_STATE);
  120. });
  121. return shamap.hash;
  122. }
  123. exports.hashStateTree = hashStateTree;
  124. function computeTransactionHash(ledger, options) {
  125. const { transaction_hash } = ledger;
  126. if (!options.computeTreeHashes) {
  127. return transaction_hash;
  128. }
  129. if (ledger.transactions == null) {
  130. throw new errors_1.ValidationError('transactions is missing from the ledger');
  131. }
  132. const transactionHash = hashTxTree(ledger.transactions);
  133. if (transaction_hash !== transactionHash) {
  134. throw new errors_1.ValidationError('transactionHash in header' +
  135. ' does not match computed hash of transactions', {
  136. transactionHashInHeader: transaction_hash,
  137. computedHashOfTransactions: transactionHash,
  138. });
  139. }
  140. return transactionHash;
  141. }
  142. function computeStateHash(ledger, options) {
  143. const { account_hash } = ledger;
  144. if (!options.computeTreeHashes) {
  145. return account_hash;
  146. }
  147. if (ledger.accountState == null) {
  148. throw new errors_1.ValidationError('accountState is missing from the ledger');
  149. }
  150. const stateHash = hashStateTree(ledger.accountState);
  151. if (account_hash !== stateHash) {
  152. throw new errors_1.ValidationError('stateHash in header does not match computed hash of state');
  153. }
  154. return stateHash;
  155. }
  156. function hashLedger(ledger, options = {}) {
  157. const subhashes = {
  158. transaction_hash: computeTransactionHash(ledger, options),
  159. account_hash: computeStateHash(ledger, options),
  160. };
  161. return hashLedgerHeader(Object.assign(Object.assign({}, ledger), subhashes));
  162. }
  163. exports.default = hashLedger;
  164. //# sourceMappingURL=hashLedger.js.map