index.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.getAccountPath = exports.isValidMnemonic = exports.entropyToMnemonic = exports.mnemonicToEntropy = exports.mnemonicToSeed = exports.HDNode = exports.defaultPath = void 0;
  4. var basex_1 = require("@ethersproject/basex");
  5. var bytes_1 = require("@ethersproject/bytes");
  6. var bignumber_1 = require("@ethersproject/bignumber");
  7. var strings_1 = require("@ethersproject/strings");
  8. var pbkdf2_1 = require("@ethersproject/pbkdf2");
  9. var properties_1 = require("@ethersproject/properties");
  10. var signing_key_1 = require("@ethersproject/signing-key");
  11. var sha2_1 = require("@ethersproject/sha2");
  12. var transactions_1 = require("@ethersproject/transactions");
  13. var wordlists_1 = require("@ethersproject/wordlists");
  14. var logger_1 = require("@ethersproject/logger");
  15. var _version_1 = require("./_version");
  16. var logger = new logger_1.Logger(_version_1.version);
  17. var N = bignumber_1.BigNumber.from("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
  18. // "Bitcoin seed"
  19. var MasterSecret = (0, strings_1.toUtf8Bytes)("Bitcoin seed");
  20. var HardenedBit = 0x80000000;
  21. // Returns a byte with the MSB bits set
  22. function getUpperMask(bits) {
  23. return ((1 << bits) - 1) << (8 - bits);
  24. }
  25. // Returns a byte with the LSB bits set
  26. function getLowerMask(bits) {
  27. return (1 << bits) - 1;
  28. }
  29. function bytes32(value) {
  30. return (0, bytes_1.hexZeroPad)((0, bytes_1.hexlify)(value), 32);
  31. }
  32. function base58check(data) {
  33. return basex_1.Base58.encode((0, bytes_1.concat)([data, (0, bytes_1.hexDataSlice)((0, sha2_1.sha256)((0, sha2_1.sha256)(data)), 0, 4)]));
  34. }
  35. function getWordlist(wordlist) {
  36. if (wordlist == null) {
  37. return wordlists_1.wordlists["en"];
  38. }
  39. if (typeof (wordlist) === "string") {
  40. var words = wordlists_1.wordlists[wordlist];
  41. if (words == null) {
  42. logger.throwArgumentError("unknown locale", "wordlist", wordlist);
  43. }
  44. return words;
  45. }
  46. return wordlist;
  47. }
  48. var _constructorGuard = {};
  49. exports.defaultPath = "m/44'/60'/0'/0/0";
  50. ;
  51. var HDNode = /** @class */ (function () {
  52. /**
  53. * This constructor should not be called directly.
  54. *
  55. * Please use:
  56. * - fromMnemonic
  57. * - fromSeed
  58. */
  59. function HDNode(constructorGuard, privateKey, publicKey, parentFingerprint, chainCode, index, depth, mnemonicOrPath) {
  60. /* istanbul ignore if */
  61. if (constructorGuard !== _constructorGuard) {
  62. throw new Error("HDNode constructor cannot be called directly");
  63. }
  64. if (privateKey) {
  65. var signingKey = new signing_key_1.SigningKey(privateKey);
  66. (0, properties_1.defineReadOnly)(this, "privateKey", signingKey.privateKey);
  67. (0, properties_1.defineReadOnly)(this, "publicKey", signingKey.compressedPublicKey);
  68. }
  69. else {
  70. (0, properties_1.defineReadOnly)(this, "privateKey", null);
  71. (0, properties_1.defineReadOnly)(this, "publicKey", (0, bytes_1.hexlify)(publicKey));
  72. }
  73. (0, properties_1.defineReadOnly)(this, "parentFingerprint", parentFingerprint);
  74. (0, properties_1.defineReadOnly)(this, "fingerprint", (0, bytes_1.hexDataSlice)((0, sha2_1.ripemd160)((0, sha2_1.sha256)(this.publicKey)), 0, 4));
  75. (0, properties_1.defineReadOnly)(this, "address", (0, transactions_1.computeAddress)(this.publicKey));
  76. (0, properties_1.defineReadOnly)(this, "chainCode", chainCode);
  77. (0, properties_1.defineReadOnly)(this, "index", index);
  78. (0, properties_1.defineReadOnly)(this, "depth", depth);
  79. if (mnemonicOrPath == null) {
  80. // From a source that does not preserve the path (e.g. extended keys)
  81. (0, properties_1.defineReadOnly)(this, "mnemonic", null);
  82. (0, properties_1.defineReadOnly)(this, "path", null);
  83. }
  84. else if (typeof (mnemonicOrPath) === "string") {
  85. // From a source that does not preserve the mnemonic (e.g. neutered)
  86. (0, properties_1.defineReadOnly)(this, "mnemonic", null);
  87. (0, properties_1.defineReadOnly)(this, "path", mnemonicOrPath);
  88. }
  89. else {
  90. // From a fully qualified source
  91. (0, properties_1.defineReadOnly)(this, "mnemonic", mnemonicOrPath);
  92. (0, properties_1.defineReadOnly)(this, "path", mnemonicOrPath.path);
  93. }
  94. }
  95. Object.defineProperty(HDNode.prototype, "extendedKey", {
  96. get: function () {
  97. // We only support the mainnet values for now, but if anyone needs
  98. // testnet values, let me know. I believe current sentiment is that
  99. // we should always use mainnet, and use BIP-44 to derive the network
  100. // - Mainnet: public=0x0488B21E, private=0x0488ADE4
  101. // - Testnet: public=0x043587CF, private=0x04358394
  102. if (this.depth >= 256) {
  103. throw new Error("Depth too large!");
  104. }
  105. return base58check((0, bytes_1.concat)([
  106. ((this.privateKey != null) ? "0x0488ADE4" : "0x0488B21E"),
  107. (0, bytes_1.hexlify)(this.depth),
  108. this.parentFingerprint,
  109. (0, bytes_1.hexZeroPad)((0, bytes_1.hexlify)(this.index), 4),
  110. this.chainCode,
  111. ((this.privateKey != null) ? (0, bytes_1.concat)(["0x00", this.privateKey]) : this.publicKey),
  112. ]));
  113. },
  114. enumerable: false,
  115. configurable: true
  116. });
  117. HDNode.prototype.neuter = function () {
  118. return new HDNode(_constructorGuard, null, this.publicKey, this.parentFingerprint, this.chainCode, this.index, this.depth, this.path);
  119. };
  120. HDNode.prototype._derive = function (index) {
  121. if (index > 0xffffffff) {
  122. throw new Error("invalid index - " + String(index));
  123. }
  124. // Base path
  125. var path = this.path;
  126. if (path) {
  127. path += "/" + (index & ~HardenedBit);
  128. }
  129. var data = new Uint8Array(37);
  130. if (index & HardenedBit) {
  131. if (!this.privateKey) {
  132. throw new Error("cannot derive child of neutered node");
  133. }
  134. // Data = 0x00 || ser_256(k_par)
  135. data.set((0, bytes_1.arrayify)(this.privateKey), 1);
  136. // Hardened path
  137. if (path) {
  138. path += "'";
  139. }
  140. }
  141. else {
  142. // Data = ser_p(point(k_par))
  143. data.set((0, bytes_1.arrayify)(this.publicKey));
  144. }
  145. // Data += ser_32(i)
  146. for (var i = 24; i >= 0; i -= 8) {
  147. data[33 + (i >> 3)] = ((index >> (24 - i)) & 0xff);
  148. }
  149. var I = (0, bytes_1.arrayify)((0, sha2_1.computeHmac)(sha2_1.SupportedAlgorithm.sha512, this.chainCode, data));
  150. var IL = I.slice(0, 32);
  151. var IR = I.slice(32);
  152. // The private key
  153. var ki = null;
  154. // The public key
  155. var Ki = null;
  156. if (this.privateKey) {
  157. ki = bytes32(bignumber_1.BigNumber.from(IL).add(this.privateKey).mod(N));
  158. }
  159. else {
  160. var ek = new signing_key_1.SigningKey((0, bytes_1.hexlify)(IL));
  161. Ki = ek._addPoint(this.publicKey);
  162. }
  163. var mnemonicOrPath = path;
  164. var srcMnemonic = this.mnemonic;
  165. if (srcMnemonic) {
  166. mnemonicOrPath = Object.freeze({
  167. phrase: srcMnemonic.phrase,
  168. path: path,
  169. locale: (srcMnemonic.locale || "en")
  170. });
  171. }
  172. return new HDNode(_constructorGuard, ki, Ki, this.fingerprint, bytes32(IR), index, this.depth + 1, mnemonicOrPath);
  173. };
  174. HDNode.prototype.derivePath = function (path) {
  175. var components = path.split("/");
  176. if (components.length === 0 || (components[0] === "m" && this.depth !== 0)) {
  177. throw new Error("invalid path - " + path);
  178. }
  179. if (components[0] === "m") {
  180. components.shift();
  181. }
  182. var result = this;
  183. for (var i = 0; i < components.length; i++) {
  184. var component = components[i];
  185. if (component.match(/^[0-9]+'$/)) {
  186. var index = parseInt(component.substring(0, component.length - 1));
  187. if (index >= HardenedBit) {
  188. throw new Error("invalid path index - " + component);
  189. }
  190. result = result._derive(HardenedBit + index);
  191. }
  192. else if (component.match(/^[0-9]+$/)) {
  193. var index = parseInt(component);
  194. if (index >= HardenedBit) {
  195. throw new Error("invalid path index - " + component);
  196. }
  197. result = result._derive(index);
  198. }
  199. else {
  200. throw new Error("invalid path component - " + component);
  201. }
  202. }
  203. return result;
  204. };
  205. HDNode._fromSeed = function (seed, mnemonic) {
  206. var seedArray = (0, bytes_1.arrayify)(seed);
  207. if (seedArray.length < 16 || seedArray.length > 64) {
  208. throw new Error("invalid seed");
  209. }
  210. var I = (0, bytes_1.arrayify)((0, sha2_1.computeHmac)(sha2_1.SupportedAlgorithm.sha512, MasterSecret, seedArray));
  211. return new HDNode(_constructorGuard, bytes32(I.slice(0, 32)), null, "0x00000000", bytes32(I.slice(32)), 0, 0, mnemonic);
  212. };
  213. HDNode.fromMnemonic = function (mnemonic, password, wordlist) {
  214. // If a locale name was passed in, find the associated wordlist
  215. wordlist = getWordlist(wordlist);
  216. // Normalize the case and spacing in the mnemonic (throws if the mnemonic is invalid)
  217. mnemonic = entropyToMnemonic(mnemonicToEntropy(mnemonic, wordlist), wordlist);
  218. return HDNode._fromSeed(mnemonicToSeed(mnemonic, password), {
  219. phrase: mnemonic,
  220. path: "m",
  221. locale: wordlist.locale
  222. });
  223. };
  224. HDNode.fromSeed = function (seed) {
  225. return HDNode._fromSeed(seed, null);
  226. };
  227. HDNode.fromExtendedKey = function (extendedKey) {
  228. var bytes = basex_1.Base58.decode(extendedKey);
  229. if (bytes.length !== 82 || base58check(bytes.slice(0, 78)) !== extendedKey) {
  230. logger.throwArgumentError("invalid extended key", "extendedKey", "[REDACTED]");
  231. }
  232. var depth = bytes[4];
  233. var parentFingerprint = (0, bytes_1.hexlify)(bytes.slice(5, 9));
  234. var index = parseInt((0, bytes_1.hexlify)(bytes.slice(9, 13)).substring(2), 16);
  235. var chainCode = (0, bytes_1.hexlify)(bytes.slice(13, 45));
  236. var key = bytes.slice(45, 78);
  237. switch ((0, bytes_1.hexlify)(bytes.slice(0, 4))) {
  238. // Public Key
  239. case "0x0488b21e":
  240. case "0x043587cf":
  241. return new HDNode(_constructorGuard, null, (0, bytes_1.hexlify)(key), parentFingerprint, chainCode, index, depth, null);
  242. // Private Key
  243. case "0x0488ade4":
  244. case "0x04358394 ":
  245. if (key[0] !== 0) {
  246. break;
  247. }
  248. return new HDNode(_constructorGuard, (0, bytes_1.hexlify)(key.slice(1)), null, parentFingerprint, chainCode, index, depth, null);
  249. }
  250. return logger.throwArgumentError("invalid extended key", "extendedKey", "[REDACTED]");
  251. };
  252. return HDNode;
  253. }());
  254. exports.HDNode = HDNode;
  255. function mnemonicToSeed(mnemonic, password) {
  256. if (!password) {
  257. password = "";
  258. }
  259. var salt = (0, strings_1.toUtf8Bytes)("mnemonic" + password, strings_1.UnicodeNormalizationForm.NFKD);
  260. return (0, pbkdf2_1.pbkdf2)((0, strings_1.toUtf8Bytes)(mnemonic, strings_1.UnicodeNormalizationForm.NFKD), salt, 2048, 64, "sha512");
  261. }
  262. exports.mnemonicToSeed = mnemonicToSeed;
  263. function mnemonicToEntropy(mnemonic, wordlist) {
  264. wordlist = getWordlist(wordlist);
  265. logger.checkNormalize();
  266. var words = wordlist.split(mnemonic);
  267. if ((words.length % 3) !== 0) {
  268. throw new Error("invalid mnemonic");
  269. }
  270. var entropy = (0, bytes_1.arrayify)(new Uint8Array(Math.ceil(11 * words.length / 8)));
  271. var offset = 0;
  272. for (var i = 0; i < words.length; i++) {
  273. var index = wordlist.getWordIndex(words[i].normalize("NFKD"));
  274. if (index === -1) {
  275. throw new Error("invalid mnemonic");
  276. }
  277. for (var bit = 0; bit < 11; bit++) {
  278. if (index & (1 << (10 - bit))) {
  279. entropy[offset >> 3] |= (1 << (7 - (offset % 8)));
  280. }
  281. offset++;
  282. }
  283. }
  284. var entropyBits = 32 * words.length / 3;
  285. var checksumBits = words.length / 3;
  286. var checksumMask = getUpperMask(checksumBits);
  287. var checksum = (0, bytes_1.arrayify)((0, sha2_1.sha256)(entropy.slice(0, entropyBits / 8)))[0] & checksumMask;
  288. if (checksum !== (entropy[entropy.length - 1] & checksumMask)) {
  289. throw new Error("invalid checksum");
  290. }
  291. return (0, bytes_1.hexlify)(entropy.slice(0, entropyBits / 8));
  292. }
  293. exports.mnemonicToEntropy = mnemonicToEntropy;
  294. function entropyToMnemonic(entropy, wordlist) {
  295. wordlist = getWordlist(wordlist);
  296. entropy = (0, bytes_1.arrayify)(entropy);
  297. if ((entropy.length % 4) !== 0 || entropy.length < 16 || entropy.length > 32) {
  298. throw new Error("invalid entropy");
  299. }
  300. var indices = [0];
  301. var remainingBits = 11;
  302. for (var i = 0; i < entropy.length; i++) {
  303. // Consume the whole byte (with still more to go)
  304. if (remainingBits > 8) {
  305. indices[indices.length - 1] <<= 8;
  306. indices[indices.length - 1] |= entropy[i];
  307. remainingBits -= 8;
  308. // This byte will complete an 11-bit index
  309. }
  310. else {
  311. indices[indices.length - 1] <<= remainingBits;
  312. indices[indices.length - 1] |= entropy[i] >> (8 - remainingBits);
  313. // Start the next word
  314. indices.push(entropy[i] & getLowerMask(8 - remainingBits));
  315. remainingBits += 3;
  316. }
  317. }
  318. // Compute the checksum bits
  319. var checksumBits = entropy.length / 4;
  320. var checksum = (0, bytes_1.arrayify)((0, sha2_1.sha256)(entropy))[0] & getUpperMask(checksumBits);
  321. // Shift the checksum into the word indices
  322. indices[indices.length - 1] <<= checksumBits;
  323. indices[indices.length - 1] |= (checksum >> (8 - checksumBits));
  324. return wordlist.join(indices.map(function (index) { return wordlist.getWord(index); }));
  325. }
  326. exports.entropyToMnemonic = entropyToMnemonic;
  327. function isValidMnemonic(mnemonic, wordlist) {
  328. try {
  329. mnemonicToEntropy(mnemonic, wordlist);
  330. return true;
  331. }
  332. catch (error) { }
  333. return false;
  334. }
  335. exports.isValidMnemonic = isValidMnemonic;
  336. function getAccountPath(index) {
  337. if (typeof (index) !== "number" || index < 0 || index >= HardenedBit || index % 1) {
  338. logger.throwArgumentError("invalid account index", "index", index);
  339. }
  340. return "m/44'/60'/" + index + "'/0/0";
  341. }
  342. exports.getAccountPath = getAccountPath;
  343. //# sourceMappingURL=index.js.map