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.

index.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. const bignumber_js_1 = __importDefault(require("bignumber.js"));
  7. const bip32_1 = require("bip32");
  8. const bip39_1 = require("bip39");
  9. const isEqual_1 = __importDefault(require("lodash/isEqual"));
  10. const ripple_address_codec_1 = require("ripple-address-codec");
  11. const ripple_binary_codec_1 = require("ripple-binary-codec");
  12. const ripple_keypairs_1 = require("ripple-keypairs");
  13. const ECDSA_1 = __importDefault(require("../ECDSA"));
  14. const errors_1 = require("../errors");
  15. const common_1 = require("../models/transactions/common");
  16. const utils_1 = require("../models/utils");
  17. const utils_2 = require("../sugar/utils");
  18. const hashLedger_1 = require("../utils/hashes/hashLedger");
  19. const rfc1751_1 = require("./rfc1751");
  20. const DEFAULT_ALGORITHM = ECDSA_1.default.ed25519;
  21. const DEFAULT_DERIVATION_PATH = "m/44'/144'/0'/0/0";
  22. function hexFromBuffer(buffer) {
  23. return buffer.toString('hex').toUpperCase();
  24. }
  25. class Wallet {
  26. constructor(publicKey, privateKey, opts = {}) {
  27. this.publicKey = publicKey;
  28. this.privateKey = privateKey;
  29. this.classicAddress = opts.masterAddress
  30. ? (0, utils_2.ensureClassicAddress)(opts.masterAddress)
  31. : (0, ripple_keypairs_1.deriveAddress)(publicKey);
  32. this.seed = opts.seed;
  33. }
  34. get address() {
  35. return this.classicAddress;
  36. }
  37. static generate(algorithm = DEFAULT_ALGORITHM) {
  38. const seed = (0, ripple_keypairs_1.generateSeed)({ algorithm });
  39. return Wallet.fromSeed(seed);
  40. }
  41. static fromSeed(seed, opts = {}) {
  42. return Wallet.deriveWallet(seed, {
  43. algorithm: opts.algorithm,
  44. masterAddress: opts.masterAddress,
  45. });
  46. }
  47. static fromEntropy(entropy, opts = {}) {
  48. var _a;
  49. const algorithm = (_a = opts.algorithm) !== null && _a !== void 0 ? _a : DEFAULT_ALGORITHM;
  50. const options = {
  51. entropy: Uint8Array.from(entropy),
  52. algorithm,
  53. };
  54. const seed = (0, ripple_keypairs_1.generateSeed)(options);
  55. return Wallet.deriveWallet(seed, {
  56. algorithm,
  57. masterAddress: opts.masterAddress,
  58. });
  59. }
  60. static fromMnemonic(mnemonic, opts = {}) {
  61. var _a;
  62. if (opts.mnemonicEncoding === 'rfc1751') {
  63. return Wallet.fromRFC1751Mnemonic(mnemonic, {
  64. masterAddress: opts.masterAddress,
  65. algorithm: opts.algorithm,
  66. });
  67. }
  68. if (!(0, bip39_1.validateMnemonic)(mnemonic)) {
  69. throw new errors_1.ValidationError('Unable to parse the given mnemonic using bip39 encoding');
  70. }
  71. const seed = (0, bip39_1.mnemonicToSeedSync)(mnemonic);
  72. const masterNode = (0, bip32_1.fromSeed)(seed);
  73. const node = masterNode.derivePath((_a = opts.derivationPath) !== null && _a !== void 0 ? _a : DEFAULT_DERIVATION_PATH);
  74. if (node.privateKey === undefined) {
  75. throw new errors_1.ValidationError('Unable to derive privateKey from mnemonic input');
  76. }
  77. const publicKey = hexFromBuffer(node.publicKey);
  78. const privateKey = hexFromBuffer(node.privateKey);
  79. return new Wallet(publicKey, `00${privateKey}`, {
  80. masterAddress: opts.masterAddress,
  81. });
  82. }
  83. static fromRFC1751Mnemonic(mnemonic, opts) {
  84. const seed = (0, rfc1751_1.rfc1751MnemonicToKey)(mnemonic);
  85. let encodeAlgorithm;
  86. if (opts.algorithm === ECDSA_1.default.ed25519) {
  87. encodeAlgorithm = 'ed25519';
  88. }
  89. else {
  90. encodeAlgorithm = 'secp256k1';
  91. }
  92. const encodedSeed = (0, ripple_address_codec_1.encodeSeed)(seed, encodeAlgorithm);
  93. return Wallet.fromSeed(encodedSeed, {
  94. masterAddress: opts.masterAddress,
  95. algorithm: opts.algorithm,
  96. });
  97. }
  98. static deriveWallet(seed, opts = {}) {
  99. var _a;
  100. const { publicKey, privateKey } = (0, ripple_keypairs_1.deriveKeypair)(seed, {
  101. algorithm: (_a = opts.algorithm) !== null && _a !== void 0 ? _a : DEFAULT_ALGORITHM,
  102. });
  103. return new Wallet(publicKey, privateKey, {
  104. seed,
  105. masterAddress: opts.masterAddress,
  106. });
  107. }
  108. sign(transaction, multisign) {
  109. let multisignAddress = false;
  110. if (typeof multisign === 'string' && multisign.startsWith('X')) {
  111. multisignAddress = multisign;
  112. }
  113. else if (multisign) {
  114. multisignAddress = this.classicAddress;
  115. }
  116. const tx = Object.assign({}, transaction);
  117. if (tx.TxnSignature || tx.Signers) {
  118. throw new errors_1.ValidationError('txJSON must not contain "TxnSignature" or "Signers" properties');
  119. }
  120. removeTrailingZeros(tx);
  121. const txToSignAndEncode = Object.assign({}, tx);
  122. txToSignAndEncode.SigningPubKey = multisignAddress ? '' : this.publicKey;
  123. if (multisignAddress) {
  124. const signer = {
  125. Account: multisignAddress,
  126. SigningPubKey: this.publicKey,
  127. TxnSignature: computeSignature(txToSignAndEncode, this.privateKey, multisignAddress),
  128. };
  129. txToSignAndEncode.Signers = [{ Signer: signer }];
  130. }
  131. else {
  132. txToSignAndEncode.TxnSignature = computeSignature(txToSignAndEncode, this.privateKey);
  133. }
  134. const serialized = (0, ripple_binary_codec_1.encode)(txToSignAndEncode);
  135. this.checkTxSerialization(serialized, tx);
  136. return {
  137. tx_blob: serialized,
  138. hash: (0, hashLedger_1.hashSignedTx)(serialized),
  139. };
  140. }
  141. verifyTransaction(signedTransaction) {
  142. const tx = typeof signedTransaction === 'string'
  143. ? (0, ripple_binary_codec_1.decode)(signedTransaction)
  144. : signedTransaction;
  145. const messageHex = (0, ripple_binary_codec_1.encodeForSigning)(tx);
  146. const signature = tx.TxnSignature;
  147. return (0, ripple_keypairs_1.verify)(messageHex, signature, this.publicKey);
  148. }
  149. getXAddress(tag = false, isTestnet = false) {
  150. return (0, ripple_address_codec_1.classicAddressToXAddress)(this.classicAddress, tag, isTestnet);
  151. }
  152. checkTxSerialization(serialized, tx) {
  153. var _a;
  154. const decoded = (0, ripple_binary_codec_1.decode)(serialized);
  155. const txCopy = Object.assign({}, tx);
  156. if (!decoded.TxnSignature && !decoded.Signers) {
  157. throw new errors_1.ValidationError('Serialized transaction must have a TxnSignature or Signers property');
  158. }
  159. delete decoded.TxnSignature;
  160. delete decoded.Signers;
  161. if (!tx.SigningPubKey) {
  162. delete decoded.SigningPubKey;
  163. }
  164. (_a = txCopy.Memos) === null || _a === void 0 ? void 0 : _a.map((memo) => {
  165. const memoCopy = Object.assign({}, memo);
  166. if (memo.Memo.MemoData) {
  167. if (!(0, utils_1.isHex)(memo.Memo.MemoData)) {
  168. throw new errors_1.ValidationError('MemoData field must be a hex value');
  169. }
  170. memoCopy.Memo.MemoData = memo.Memo.MemoData.toUpperCase();
  171. }
  172. if (memo.Memo.MemoType) {
  173. if (!(0, utils_1.isHex)(memo.Memo.MemoType)) {
  174. throw new errors_1.ValidationError('MemoType field must be a hex value');
  175. }
  176. memoCopy.Memo.MemoType = memo.Memo.MemoType.toUpperCase();
  177. }
  178. if (memo.Memo.MemoFormat) {
  179. if (!(0, utils_1.isHex)(memo.Memo.MemoFormat)) {
  180. throw new errors_1.ValidationError('MemoFormat field must be a hex value');
  181. }
  182. memoCopy.Memo.MemoFormat = memo.Memo.MemoFormat.toUpperCase();
  183. }
  184. return memo;
  185. });
  186. if (txCopy.TransactionType === 'NFTokenMint' && txCopy.URI) {
  187. if (!(0, utils_1.isHex)(txCopy.URI)) {
  188. throw new errors_1.ValidationError('URI must be a hex value');
  189. }
  190. txCopy.URI = txCopy.URI.toUpperCase();
  191. }
  192. Object.keys(txCopy).forEach((key) => {
  193. const standard_currency_code_len = 3;
  194. if (txCopy[key] && (0, common_1.isIssuedCurrency)(txCopy[key])) {
  195. const decodedAmount = decoded[key];
  196. const decodedCurrency = decodedAmount.currency;
  197. const txCurrency = txCopy[key].currency;
  198. if (txCurrency.length === standard_currency_code_len &&
  199. txCurrency.toUpperCase() === 'XRP') {
  200. throw new errors_1.XrplError(`Trying to sign an issued currency with a similar standard code to XRP (received '${txCurrency}'). XRP is not an issued currency.`);
  201. }
  202. const amount = txCopy[key];
  203. if (amount.currency.length !== decodedCurrency.length) {
  204. if (decodedCurrency.length === standard_currency_code_len) {
  205. decodedAmount.currency = isoToHex(decodedCurrency);
  206. }
  207. else {
  208. txCopy[key].currency = isoToHex(txCopy[key].currency);
  209. }
  210. }
  211. }
  212. });
  213. if (!(0, isEqual_1.default)(decoded, txCopy)) {
  214. const data = {
  215. decoded,
  216. tx,
  217. };
  218. const error = new errors_1.ValidationError('Serialized transaction does not match original txJSON. See error.data', data);
  219. throw error;
  220. }
  221. }
  222. }
  223. Wallet.fromSecret = Wallet.fromSeed;
  224. function computeSignature(tx, privateKey, signAs) {
  225. if (signAs) {
  226. const classicAddress = (0, ripple_address_codec_1.isValidXAddress)(signAs)
  227. ? (0, ripple_address_codec_1.xAddressToClassicAddress)(signAs).classicAddress
  228. : signAs;
  229. return (0, ripple_keypairs_1.sign)((0, ripple_binary_codec_1.encodeForMultisigning)(tx, classicAddress), privateKey);
  230. }
  231. return (0, ripple_keypairs_1.sign)((0, ripple_binary_codec_1.encodeForSigning)(tx), privateKey);
  232. }
  233. function removeTrailingZeros(tx) {
  234. if (tx.TransactionType === 'Payment' &&
  235. typeof tx.Amount !== 'string' &&
  236. tx.Amount.value.includes('.') &&
  237. tx.Amount.value.endsWith('0')) {
  238. tx.Amount = Object.assign({}, tx.Amount);
  239. tx.Amount.value = new bignumber_js_1.default(tx.Amount.value).toString();
  240. }
  241. }
  242. function isoToHex(iso) {
  243. const bytes = Buffer.alloc(20);
  244. if (iso !== 'XRP') {
  245. const isoBytes = iso.split('').map((chr) => chr.charCodeAt(0));
  246. bytes.set(isoBytes, 12);
  247. }
  248. return bytes.toString('hex').toUpperCase();
  249. }
  250. exports.default = Wallet;
  251. //# sourceMappingURL=index.js.map