From 4d0c1ea551d198fc4c496506b4ed6fe0f37c885a Mon Sep 17 00:00:00 2001 From: buck Date: Wed, 4 Sep 2024 22:54:23 -0500 Subject: [PATCH 01/18] cleanup and fix generator templates --- turbo/generators/config.js | 4 ++-- turbo/generators/templates/.eslintrc.cjs.hbs | 10 +++------- turbo/generators/templates/jest.config.js.hbs | 4 ---- turbo/generators/templates/jest.config.ts.hbs | 13 +++++++++++++ turbo/generators/templates/package.json.hbs | 2 +- 5 files changed, 19 insertions(+), 14 deletions(-) delete mode 100644 turbo/generators/templates/jest.config.js.hbs create mode 100644 turbo/generators/templates/jest.config.ts.hbs diff --git a/turbo/generators/config.js b/turbo/generators/config.js index 464a756a..50284de6 100644 --- a/turbo/generators/config.js +++ b/turbo/generators/config.js @@ -34,7 +34,7 @@ module.exports = function(plop) { { type: "confirm", name: "jestConfig", - message: "Do you want a jest.config.js?", + message: "Do you want a jest.config.ts?", default: true, }, { @@ -128,7 +128,7 @@ module.exports = function(plop) { path: path.join(process.cwd(), `packages/${data.name}/.eslintrc.js`), templateFile: path.join( process.cwd(), - "turbo/generators/templates/.eslintrc.js.hbs" + "turbo/generators/templates/.eslintrc.cjs.hbs" ), }); } diff --git a/turbo/generators/templates/.eslintrc.cjs.hbs b/turbo/generators/templates/.eslintrc.cjs.hbs index 6c828954..afe36149 100644 --- a/turbo/generators/templates/.eslintrc.cjs.hbs +++ b/turbo/generators/templates/.eslintrc.cjs.hbs @@ -1,10 +1,6 @@ module.exports = { - root: true, - extends: [ - "@caravan/eslint-config/library.js" - ], - parser: "@typescript-eslint/parser", - parserOptions: { - project: true, + extends: ["@caravan/eslint-config/library.js"], + rules: { + "@typescript-eslint/no-duplicate-enum-values": "warn", }, }; diff --git a/turbo/generators/templates/jest.config.js.hbs b/turbo/generators/templates/jest.config.js.hbs deleted file mode 100644 index 3f878c46..00000000 --- a/turbo/generators/templates/jest.config.js.hbs +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - preset: "ts-jest", - testEnvironment: "node" -}; diff --git a/turbo/generators/templates/jest.config.ts.hbs b/turbo/generators/templates/jest.config.ts.hbs new file mode 100644 index 00000000..2b359dcc --- /dev/null +++ b/turbo/generators/templates/jest.config.ts.hbs @@ -0,0 +1,13 @@ +import type { JestConfigWithTsJest } from "ts-jest"; + +const config: JestConfigWithTsJest = { + extensionsToTreatAsEsm: [".ts"], + verbose: true, + preset: "ts-jest/presets/default-esm", + testEnvironment: "node", + testPathIgnorePatterns: ["./lib"], + transform: {}, + moduleDirectories: ["node_modules", ""], +}; + +export default config; diff --git a/turbo/generators/templates/package.json.hbs b/turbo/generators/templates/package.json.hbs index 94849fe4..30ae601d 100644 --- a/turbo/generators/templates/package.json.hbs +++ b/turbo/generators/templates/package.json.hbs @@ -30,6 +30,6 @@ "ci": "npm run lint && npm run test", "test": "jest src", "test:watch": "jest --watch src", - "test:debug": "node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:debug": "node --inspect-brk ../../node_modules/.bin/jest --runInBand" } } From e954343677d169d982dcdd0fd0e45e7ad3eaf70b Mon Sep 17 00:00:00 2001 From: buck Date: Wed, 4 Sep 2024 22:54:40 -0500 Subject: [PATCH 02/18] init bip32 package with paths utils --- package-lock.json | 744 +++++++++++++++++++++ packages/bip32/.eslintrc.js | 6 + packages/bip32/README.md | 1 + packages/bip32/jest.config.ts | 13 + packages/bip32/package.json | 54 ++ packages/bip32/src/__tests__/paths.test.ts | 117 ++++ packages/bip32/src/index.ts | 12 + packages/bip32/src/paths.ts | 212 ++++++ packages/bip32/src/types.ts | 5 + packages/bip32/tsconfig.json | 3 + packages/bip32/tsup.config.js | 6 + 11 files changed, 1173 insertions(+) create mode 100644 packages/bip32/.eslintrc.js create mode 100644 packages/bip32/README.md create mode 100644 packages/bip32/jest.config.ts create mode 100644 packages/bip32/package.json create mode 100644 packages/bip32/src/__tests__/paths.test.ts create mode 100644 packages/bip32/src/index.ts create mode 100644 packages/bip32/src/paths.ts create mode 100644 packages/bip32/src/types.ts create mode 100644 packages/bip32/tsconfig.json create mode 100644 packages/bip32/tsup.config.js diff --git a/package-lock.json b/package-lock.json index 36da805e..7a51c61d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2709,6 +2709,10 @@ "@noble/secp256k1": "^1.7.1" } }, + "node_modules/@caravan/bip32": { + "resolved": "packages/bip32", + "link": true + }, "node_modules/@caravan/bitcoin": { "resolved": "packages/caravan-bitcoin", "link": true @@ -4124,6 +4128,45 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "peer": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -4217,6 +4260,16 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@ethereumjs/common": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-4.2.0.tgz", @@ -4389,6 +4442,20 @@ "dev": true, "peer": true }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@hutson/parse-repository-url": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", @@ -22112,6 +22179,22 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "node_modules/synckit": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/table": { "version": "6.8.1", "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", @@ -24128,6 +24211,667 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "packages/bip32": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@caravan/bitcoin": "*", + "bip174": "^2.1.1" + }, + "devDependencies": { + "@caravan/eslint-config": "*", + "@caravan/typescript-config": "*", + "@inrupt/jest-jsdom-polyfills": "^3.2.1", + "@jest/globals": "^29.7.0", + "@types/jest": "^29.5.12", + "eslint-plugin-prettier": "^5.1.3", + "jest": "^29.4.1", + "jsdom": "24.0.0", + "jsdom-global": "3.0.2", + "ts-jest": "^29.1.2", + "ts-node": "^10.9.2", + "tsup": "^7.2.0", + "typescript": "^5.2.2" + }, + "engines": { + "node": ">=20" + } + }, + "packages/bip32/node_modules/@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "packages/bip32/node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "packages/bip32/node_modules/@eslint/js": { + "version": "9.9.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz", + "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==", + "dev": true, + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/bip32/node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "packages/bip32/node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "packages/bip32/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "packages/bip32/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "packages/bip32/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "peer": true + }, + "packages/bip32/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "packages/bip32/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "packages/bip32/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "packages/bip32/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "packages/bip32/node_modules/cssstyle": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz", + "integrity": "sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==", + "dev": true, + "dependencies": { + "rrweb-cssom": "^0.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "packages/bip32/node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "packages/bip32/node_modules/eslint": { + "version": "9.9.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz", + "integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==", + "dev": true, + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.9.1", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.0.2", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "packages/bip32/node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "packages/bip32/node_modules/eslint-scope": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", + "dev": true, + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "packages/bip32/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "packages/bip32/node_modules/espree": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", + "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "dev": true, + "peer": true, + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "packages/bip32/node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "peer": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/bip32/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "peer": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/bip32/node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "peer": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "packages/bip32/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "packages/bip32/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/bip32/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "packages/bip32/node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "packages/bip32/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "packages/bip32/node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "packages/bip32/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "packages/bip32/node_modules/jsdom": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.0.0.tgz", + "integrity": "sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==", + "dev": true, + "dependencies": { + "cssstyle": "^4.0.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.7", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.3", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.16.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "packages/bip32/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "peer": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/bip32/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "packages/bip32/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "peer": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/bip32/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/bip32/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "packages/bip32/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "packages/bip32/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dev": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "packages/bip32/node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "packages/bip32/node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "packages/bip32/node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "packages/bip32/node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dev": true, + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "packages/bip32/node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "packages/caravan-bitcoin": { "name": "@caravan/bitcoin", "version": "0.3.0", diff --git a/packages/bip32/.eslintrc.js b/packages/bip32/.eslintrc.js new file mode 100644 index 00000000..afe36149 --- /dev/null +++ b/packages/bip32/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + extends: ["@caravan/eslint-config/library.js"], + rules: { + "@typescript-eslint/no-duplicate-enum-values": "warn", + }, +}; diff --git a/packages/bip32/README.md b/packages/bip32/README.md new file mode 100644 index 00000000..bf283992 --- /dev/null +++ b/packages/bip32/README.md @@ -0,0 +1 @@ +# Bip32 \ No newline at end of file diff --git a/packages/bip32/jest.config.ts b/packages/bip32/jest.config.ts new file mode 100644 index 00000000..2b359dcc --- /dev/null +++ b/packages/bip32/jest.config.ts @@ -0,0 +1,13 @@ +import type { JestConfigWithTsJest } from "ts-jest"; + +const config: JestConfigWithTsJest = { + extensionsToTreatAsEsm: [".ts"], + verbose: true, + preset: "ts-jest/presets/default-esm", + testEnvironment: "node", + testPathIgnorePatterns: ["./lib"], + transform: {}, + moduleDirectories: ["node_modules", ""], +}; + +export default config; diff --git a/packages/bip32/package.json b/packages/bip32/package.json new file mode 100644 index 00000000..89e82bfa --- /dev/null +++ b/packages/bip32/package.json @@ -0,0 +1,54 @@ +{ + "name": "@caravan/bip32", + "version": "1.0.0", + "author": "unchained capital", + "description": "Package for working with bip32 and derivative objects like extended public keys", + "private": true, + "engines": { + "node": ">=20" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "module": "./dist/index.mjs", + "files": [ + "./dist/index.js", + "./dist/index.mjs", + "./dist/index.d.ts" + ], + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, + "license": "MIT", + "scripts": { + "build": "tsup src/index.ts --format cjs,esm --dts", + "dev": "npm run build -- --watch", + "lint": "eslint src/", + "ci": "npm run lint && npm run test", + "test": "jest src", + "test:watch": "jest --watch src", + "test:debug": "node --inspect-brk ../../node_modules/.bin/jest --runInBand" + }, + "dependencies": { + "@caravan/bitcoin": "*", + "bip174": "^2.1.1" + }, + "devDependencies": { + "@caravan/eslint-config": "*", + "@caravan/typescript-config": "*", + "@inrupt/jest-jsdom-polyfills": "^3.2.1", + "@jest/globals": "^29.7.0", + "@types/jest": "^29.5.12", + "eslint-plugin-prettier": "^5.1.3", + "jest": "^29.4.1", + "jsdom": "24.0.0", + "jsdom-global": "3.0.2", + "ts-jest": "^29.1.2", + "ts-node": "^10.9.2", + "tsup": "^7.2.0", + "typescript": "^5.2.2" + } +} diff --git a/packages/bip32/src/__tests__/paths.test.ts b/packages/bip32/src/__tests__/paths.test.ts new file mode 100644 index 00000000..7b3a1f1b --- /dev/null +++ b/packages/bip32/src/__tests__/paths.test.ts @@ -0,0 +1,117 @@ +import { Bip32Derivation } from "bip174/src/lib/interfaces"; + +import { + combineBip32Paths, + getMaskedKeyOrigin, + getUnmaskedPath, + isValidChildPubKey, + KeyOrigin, +} from ".."; +import { Network } from "@caravan/bitcoin"; + +const globalOrigin: KeyOrigin = { + xpub: "tpubDEtyXJKvCT2V3ccXBBrNPEGb8RNZjNMcGbx68CzE74zq7aKWwRz8up95PYCm7HrYRT7Sz42uFVpW1MgRzqRU7KTHsY6LgPcYMc53pqHC7uc", + bip32Path: "m/45'/0'/0'/42", + rootFingerprint: "9a6a2580", +}; + +const testChildDerivation = { + path: "m/45'/0'/0'/42/0/0", + pubkey: Buffer.from( + "03dee36bc0b80de80631a4dca518caf31672c6023bf5e8416fa2c9587a8a1a26bf", + "hex", + ), +} as unknown as Bip32Derivation; + +describe("getMaskedKeyOrigin", () => { + it("should return the masked origin", () => { + const masked = getMaskedKeyOrigin(globalOrigin.xpub); + expect(masked).toEqual({ + xpub: globalOrigin.xpub, + bip32Path: "m/0/0/0/0", + rootFingerprint: "86bd941f", + }); + }); +}); + +describe("isValidChildPubKey", () => { + it("should return true for a valid child pubkey", () => { + expect(isValidChildPubKey(testChildDerivation, globalOrigin, Network.REGTEST)).toBe( + true, + ); + }); + + it("should return true for valid masked key", () => { + const derivation = { + ...testChildDerivation, + path: "m/45/0/0/0/0/0", + }; + expect(isValidChildPubKey(derivation, globalOrigin, Network.REGTEST)).toBe(true); + }); + + it("should throw if child key is longer than parent", () => { + const derivation = { + ...testChildDerivation, + path: "m/45'/0'/0'/", + }; + expect(() => + isValidChildPubKey(derivation, globalOrigin, Network.REGTEST), + ).toThrow(); + }); + + it("should return false for an invalid child pubkey", () => { + const otherGlobal: KeyOrigin = { + xpub: "tpubDEtyXJKvDdEvUzbiBsHXXAqjnNvDJdQWLjyjCxSRzzHq77fKjbxFJ2uXuciR28CRk6dQzGwNw2Dby615BbykdWHDQZHCacY21JW3FCFKcme", + bip32Path: "m/45'/0'/0'/5222010", + rootFingerprint: "9a6a2580", + }; + expect(isValidChildPubKey(testChildDerivation, otherGlobal, Network.REGTEST)).toBe( + false, + ); + }); +}); + +describe("getUnmaskedPath", () => { + it("should return the unmasked path", () => { + const maskedChildDerivation = { + ...testChildDerivation, + path: "m/0/0/0/0/0/0", + }; + const unmaskedPath = getUnmaskedPath(maskedChildDerivation, globalOrigin); + expect(unmaskedPath).toBe(testChildDerivation.path); + }); +}); + +describe("combineBip32Paths", () => { + const firstPath = "m/45'/0'/0'/42"; + const secondPath = "m/0/0"; + it("should combine two valid bip32 paths", () => { + const combined = combineBip32Paths(firstPath, secondPath); + expect(combined).toBe("m/45'/0'/0'/42/0/0"); + }); + + it("should combine two valid bip32 paths with extra slashes", () => { + const combined = combineBip32Paths(`${firstPath}/`, `${secondPath}/`); + expect(combined).toBe("m/45'/0'/0'/42/0/0"); + }); + + it("should return the second path if the first path is 'm'", () => { + const firstPath = "m"; + const secondPath = "m/0/0/0/0"; + const combined = combineBip32Paths(firstPath, secondPath); + expect(combined).toBe(secondPath); + }); + + it("should return the first path if the second path is 'm'", () => { + const firstPath = "m/45'/0'/0'/42"; + const secondPath = "m"; + const combined = combineBip32Paths(firstPath, secondPath); + expect(combined).toBe(firstPath); + }); + + it("should throw an error for an invalid bip32 path", () => { + const firstPath = "m/45'/0'/0'/42"; + const secondPath = "invalid/path"; + expect(() => combineBip32Paths(firstPath, secondPath)).toThrow(); + }); +}); diff --git a/packages/bip32/src/index.ts b/packages/bip32/src/index.ts new file mode 100644 index 00000000..ef6a19ce --- /dev/null +++ b/packages/bip32/src/index.ts @@ -0,0 +1,12 @@ +export type { + KeyOrigin +} from "./types" + +export { + secureSecretPath, + combineBip32Paths, + getRandomChildXpub, + getMaskedKeyOrigin, + isValidChildPubKey, + getUnmaskedPath, +} from "./paths" diff --git a/packages/bip32/src/paths.ts b/packages/bip32/src/paths.ts new file mode 100644 index 00000000..176442a3 --- /dev/null +++ b/packages/bip32/src/paths.ts @@ -0,0 +1,212 @@ +import { randomInt } from "crypto"; +import { + bip32PathToSequence, + bip32SequenceToPath, + deriveChildExtendedPublicKey, + deriveChildPublicKey, + ExtendedPublicKey, + Network, + validateBIP32Path, +} from "@caravan/bitcoin"; +import { Bip32Derivation } from "bip174/src/lib/interfaces"; +import { KeyOrigin } from "./types"; +/** + * @description Returns a random BIP32 path of a given depth. The + * randomness is generated using the Node.js crypto module. + * Can be used for blinding an xpub. + * Based on buidl's equivalent function: + * https://github.com/buidl-bitcoin/buidl-python/blob/main/buidl/blinding.py + * + * Approx entropy by depth: + * 1: 31 + * 2: 62 + * 3: 93 + * 4: 124 + * 5: 155 + * 6: 186 + * 7: 217 + * 8: 248 + * 9: 279 + * @param depth + * @returns {string} - BIP32 path as string + */ +export const secureSecretPath = (depth: number = 4): string => { + if (!Number.isInteger(depth)) { + throw new Error(`depth must be an int: ${depth}`); + } + if (depth >= 32) { + throw new Error( + `BIP32 requires depth < 256, but this function will not allow you to go anywhere near this high: ${depth}`, + ); + } + if (depth < 1) { + throw new Error(`Depth must be > 0: ${depth}`); + } + const to_return: string[] = ["m"]; + for (let i = 0; i < depth; i++) { + const rand_int = randomInt(0, 2 ** 31 - 1); + to_return.push(rand_int.toString()); + } + return to_return.join("/"); +}; + + +/** + * Given two BIP32 paths, combine them into a single path. + * Useful for creating blinded xpubs when you have the source + * path and want to append the randomly generated one + * @param firstPath + * @param secondPath + * @returns {string} new combined bip32 path + */ +export const combineBip32Paths = ( + firstPath: string, + secondPath: string, +): string => { + let modifiedFirstPath = firstPath.toLowerCase().trim().replace("//", "/"); + if (modifiedFirstPath.endsWith("/")) { + modifiedFirstPath = modifiedFirstPath.slice(0, -1); + } + let modifiedSecondPath = secondPath.toLowerCase().trim().replace("//", "/"); + if (modifiedSecondPath.endsWith("/")) { + modifiedSecondPath = modifiedSecondPath.slice(0, -1); + } + + if (modifiedFirstPath === "m") { + return modifiedSecondPath; + } + + if (modifiedSecondPath === "m") { + return modifiedFirstPath; + } + + // Trim leading "m/" from the second path + modifiedSecondPath = modifiedSecondPath.slice(2); + const combined = `${modifiedFirstPath}/${modifiedSecondPath}`; + if (validateBIP32Path(combined)) { + throw new Error( + `Invalid bip32 path: ${combined}: ${firstPath} ${secondPath}`, + ); + } + return combined; +}; + +/** + * + * @param xpub {string} + * @param network [Network] + * @returns {string} - updated xpub with given network + */ +const setXpubNetwork = (xpub: string, network?: Network): string => { + if (!network) return xpub; + + const xpubObj = ExtendedPublicKey.fromBase58(xpub); + xpubObj.setNetwork(network); + return xpubObj.toBase58(); +}; + +/** + * Given a source xpub, derive a child xpub at a random path using secureSecretPath + * defaults to depth 4. Useful for creating blinded xpubs or generating random child + * xpubs (e.g. strands) + * @param sourceOrigin {KeyOrigin} + * @param network [Network] - if not provided will just default to the source xpub's network + * @returns {KeyOrigin} - Child xpub and path + */ +export const getRandomChildXpub = (sourceOrigin: KeyOrigin, depth = 4, network?: Network): KeyOrigin => { + const randomPath = secureSecretPath(depth); + + const childXpub = deriveChildExtendedPublicKey( + setXpubNetwork(sourceOrigin.xpub, network), + randomPath, + process.env.BITCOIN_NETWORK as Network, + ); + const childPath = combineBip32Paths(sourceOrigin.bip32Path, randomPath); + + return { + xpub: childXpub, + bip32Path: childPath, + rootFingerprint: sourceOrigin.rootFingerprint, + }; +}; + +/** + * @description Derive a masked key origin from an xpub. Useful for generating + * descriptors and wallet configurations for keys that don't need to have their + * key origin info revealed. + * Bip32 path will use all 0s for the depth of the given xpub and the + * root fingerprint will be set to the parent fingerprint of the xpub + * @param xpub {string} - xpub to mask + * @returns + */ +export const getMaskedKeyOrigin = (xpub: string): KeyOrigin => { + const { parentFingerprint, depth } = ExtendedPublicKey.fromBase58(xpub); + + // shouldn't happen but making typescript happy + if (!parentFingerprint || !depth) + throw new Error("Parent fingerprint or depth not found from xpub"); + + return { + xpub: xpub, + bip32Path: `m${"/0".repeat(depth)}`, + rootFingerprint: parentFingerprint.toString(16), // Convert parentFingerprint to hexadecimal string + }; +}; + +/** + * When you have a global xpub from a PSBT, it's useful to make + * sure that a child pubkey can be derived from that psbt. Sometimes + * the pubkey derivation comes from a masked and/or blinded xpub. + * So we need to combine the child derivation with the global + * and confirm that the pubkey can be derived from that source + * @param derivation {Bip32Derivation} - derivation to validate. + * This type is from the bitcoinjs-lib bip174 package + * @param globalXpub {KeyOrigin} - global xpub from the psbt + * @param network {Network} + * @returns {boolean} whether the child pubkey can be derived from the global xpub + */ +export const isValidChildPubKey = ( + derivation: Bip32Derivation, + globalXpub: KeyOrigin, + network: Network = Network.MAINNET, +): boolean => { + const globalSequence = bip32PathToSequence(globalXpub.bip32Path); + const derivationSequence = bip32PathToSequence(derivation.path); + + const difference = derivationSequence.length - globalSequence.length; + if (difference < 0) + throw new Error( + `Child key longer than parent: Parent: ${globalXpub.bip32Path}, Child: ${derivation.path}`, + ); + const lastElements = derivationSequence.slice(-difference); + const relativePath = bip32SequenceToPath(lastElements); + + const childPubkey = deriveChildPublicKey(globalXpub.xpub, relativePath, network); + return childPubkey === derivation.pubkey.toString("hex"); +}; + +/** + * Given a derivation and a global xpub, return the unmasked path + * that can be used to derive the child pubkey from the global xpub. + * This is useful when you have a child xpub (e.g. a blinded xpub) derived + * from a masked xpub and you need to generate the full, unmasked path. + * @param derivation {Bip32Derivation} + * @param globalXpub {KeyOrigin} + * @returns {string} - unmasked path + */ +export const getUnmaskedPath = ( + derivation: Bip32Derivation, + globalXpub: KeyOrigin, +): string => { + const globalSequence = bip32PathToSequence(globalXpub.bip32Path); + const derivationSequence = bip32PathToSequence(derivation.path); + + const difference = derivationSequence.length - globalSequence.length; + if (difference < 0) + throw new Error( + `Child key longer than parent: Parent: ${globalXpub.bip32Path}, Child: ${derivation.path}`, + ); + const lastElements = derivationSequence.slice(-difference); + + return bip32SequenceToPath(globalSequence.concat(lastElements)); +}; diff --git a/packages/bip32/src/types.ts b/packages/bip32/src/types.ts new file mode 100644 index 00000000..758a16ab --- /dev/null +++ b/packages/bip32/src/types.ts @@ -0,0 +1,5 @@ +export interface KeyOrigin { + xpub: string; + bip32Path: string; + rootFingerprint: string; +} diff --git a/packages/bip32/tsconfig.json b/packages/bip32/tsconfig.json new file mode 100644 index 00000000..47d7ebd3 --- /dev/null +++ b/packages/bip32/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@caravan/typescript-config/base.json" +} diff --git a/packages/bip32/tsup.config.js b/packages/bip32/tsup.config.js new file mode 100644 index 00000000..2aee42e6 --- /dev/null +++ b/packages/bip32/tsup.config.js @@ -0,0 +1,6 @@ +import { defineConfig } from 'tsup'; +import { polyfillNode } from "esbuild-plugin-polyfill-node"; + +export default defineConfig({ + esbuildPlugins: [polyfillNode()], +}); From a0609765a07899f8a60c7d32e0b360751dcb3f3b Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 5 Sep 2024 10:19:23 -0500 Subject: [PATCH 03/18] fix: eslint and add import ordering --- package-lock.json | 1032 +++++++++++++++++++++++++++------- packages/bip32/.eslintrc.cjs | 29 + packages/bip32/.eslintrc.js | 6 - packages/bip32/.prettierrc | 4 + packages/bip32/package.json | 2 + 5 files changed, 854 insertions(+), 219 deletions(-) create mode 100644 packages/bip32/.eslintrc.cjs delete mode 100644 packages/bip32/.eslintrc.js create mode 100644 packages/bip32/.prettierrc diff --git a/package-lock.json b/package-lock.json index 7a51c61d..265d8d11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4128,45 +4128,6 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@eslint/config-array": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", - "dev": true, - "peer": true, - "dependencies": { - "@eslint/object-schema": "^2.1.4", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -4260,16 +4221,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", - "dev": true, - "peer": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@ethereumjs/common": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-4.2.0.tgz", @@ -4442,20 +4393,6 @@ "dev": true, "peer": true }, - "node_modules/@humanwhocodes/retry": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", - "dev": true, - "peer": true, - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@hutson/parse-repository-url": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", @@ -6495,6 +6432,12 @@ "win32" ] }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, "node_modules/@scure/base": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", @@ -8553,9 +8496,12 @@ "hasInstallScript": true }, "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -9511,13 +9457,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10591,6 +10542,57 @@ "node": ">=12" } }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/dataloader": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz", @@ -10720,16 +10722,19 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { @@ -11179,6 +11184,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-iterator-helpers": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", @@ -11201,6 +11225,18 @@ "safe-array-concat": "^1.0.1" } }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", @@ -12714,15 +12750,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -13197,11 +13237,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -13230,11 +13270,11 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -13901,6 +13941,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", @@ -14185,11 +14240,11 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dependencies": { - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -19360,6 +19415,14 @@ "semver-compare": "^1.0.0" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.4.33", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", @@ -21165,15 +21228,16 @@ "dev": true }, "node_modules/set-function-length": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", - "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -23871,15 +23935,15 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -24212,6 +24276,7 @@ } }, "packages/bip32": { + "name": "@caravan/bip32", "version": "1.0.0", "license": "MIT", "dependencies": { @@ -24224,6 +24289,8 @@ "@inrupt/jest-jsdom-polyfills": "^3.2.1", "@jest/globals": "^29.7.0", "@types/jest": "^29.5.12", + "eslint": "^8.56.0", + "eslint-plugin-import": "^2.30.0", "eslint-plugin-prettier": "^5.1.3", "jest": "^29.4.1", "jsdom": "24.0.0", @@ -24242,22 +24309,20 @@ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "dev": true, - "peer": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "packages/bip32/node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", + "espree": "^9.6.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -24265,22 +24330,43 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "packages/bip32/node_modules/@eslint/js": { - "version": "9.9.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz", - "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, - "peer": true, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "packages/bip32/node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" } }, + "packages/bip32/node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, "packages/bip32/node_modules/@types/jest": { "version": "29.5.12", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", @@ -24296,7 +24382,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -24321,7 +24406,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -24336,15 +24420,91 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "packages/bip32/node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, - "peer": true + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "packages/bip32/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -24355,7 +24515,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -24372,7 +24531,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -24384,8 +24542,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "peer": true + "dev": true }, "packages/bip32/node_modules/cssstyle": { "version": "4.0.1", @@ -24412,39 +24569,116 @@ "node": ">=18" } }, - "packages/bip32/node_modules/eslint": { - "version": "9.9.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz", - "integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==", + "packages/bip32/node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, - "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.18.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.9.1", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.0", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.2", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.1.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "packages/bip32/node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", + "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", @@ -24458,20 +24692,91 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://eslint.org/donate" + "url": "https://opencollective.com/eslint" + } + }, + "packages/bip32/node_modules/eslint-module-utils": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.9.0.tgz", + "integrity": "sha512-McVbYmwA3NEKwRQY5g4aWMdcZE5xZxV8i8l7CqJSrameuGSQJtSWaL/LxTEzSKKaCcOhlpDR8XEfYXWPrdo/ZQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" }, - "peerDependencies": { - "jiti": "*" + "engines": { + "node": ">=4" }, "peerDependenciesMeta": { - "jiti": { + "eslint": { "optional": true } } }, + "packages/bip32/node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "packages/bip32/node_modules/eslint-plugin-import": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", + "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", + "dev": true, + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.9.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "packages/bip32/node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "packages/bip32/node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "packages/bip32/node_modules/eslint-plugin-prettier": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", @@ -24503,72 +24808,43 @@ } }, "packages/bip32/node_modules/eslint-scope": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", - "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "packages/bip32/node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", - "dev": true, - "peer": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "packages/bip32/node_modules/espree": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", - "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, - "peer": true, "dependencies": { - "acorn": "^8.12.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.0.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "packages/bip32/node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "peer": true, - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, "packages/bip32/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -24580,18 +24856,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/bip32/node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "packages/bip32/node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, - "peer": true, "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" }, "engines": { - "node": ">=16" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "packages/bip32/node_modules/glob-parent": { @@ -24599,7 +24878,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "peer": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -24608,13 +24886,15 @@ } }, "packages/bip32/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "peer": true, + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=18" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -24625,11 +24905,34 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "peer": true, "engines": { "node": ">=8" } }, + "packages/bip32/node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "packages/bip32/node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", @@ -24668,12 +24971,83 @@ "node": ">= 14" } }, + "packages/bip32/node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "packages/bip32/node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "packages/bip32/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -24726,7 +25100,6 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -24742,7 +25115,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -24750,12 +25122,60 @@ "node": "*" } }, + "packages/bip32/node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "packages/bip32/node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "packages/bip32/node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -24792,12 +25212,113 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "packages/bip32/node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "packages/bip32/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -24817,6 +25338,91 @@ "node": ">=18" } }, + "packages/bip32/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/bip32/node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "packages/bip32/node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "packages/bip32/node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "packages/bip32/node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", diff --git a/packages/bip32/.eslintrc.cjs b/packages/bip32/.eslintrc.cjs new file mode 100644 index 00000000..22ad2267 --- /dev/null +++ b/packages/bip32/.eslintrc.cjs @@ -0,0 +1,29 @@ +module.exports = { + extends: ["@caravan/eslint-config/library.js"], + plugins: ["import"], + rules: { + "@typescript-eslint/no-duplicate-enum-values": "warn", + "import/order": [ + "error", + { + groups: [ + "builtin", + "external", + "internal", + "parent", + "sibling", + "index", + ], + "newlines-between": "always", + alphabetize: { order: "asc", caseInsensitive: true }, + }, + ], + "sort-imports": [ + "error", + { + allowSeparatedGroups: true, + ignoreDeclarationSort: true, + }, + ], + }, +}; diff --git a/packages/bip32/.eslintrc.js b/packages/bip32/.eslintrc.js deleted file mode 100644 index afe36149..00000000 --- a/packages/bip32/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - extends: ["@caravan/eslint-config/library.js"], - rules: { - "@typescript-eslint/no-duplicate-enum-values": "warn", - }, -}; diff --git a/packages/bip32/.prettierrc b/packages/bip32/.prettierrc new file mode 100644 index 00000000..222861c3 --- /dev/null +++ b/packages/bip32/.prettierrc @@ -0,0 +1,4 @@ +{ + "tabWidth": 2, + "useTabs": false +} diff --git a/packages/bip32/package.json b/packages/bip32/package.json index 89e82bfa..2137ba4b 100644 --- a/packages/bip32/package.json +++ b/packages/bip32/package.json @@ -42,6 +42,8 @@ "@inrupt/jest-jsdom-polyfills": "^3.2.1", "@jest/globals": "^29.7.0", "@types/jest": "^29.5.12", + "eslint": "^8.56.0", + "eslint-plugin-import": "^2.30.0", "eslint-plugin-prettier": "^5.1.3", "jest": "^29.4.1", "jsdom": "24.0.0", From 88969861f48913181dde95c7fbcc3259adc66d8f Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 5 Sep 2024 12:07:46 -0500 Subject: [PATCH 04/18] fix: crypto polyfill --- packages/bip32/tsup.config.js | 4 ++-- packages/caravan-bitcoin/tsup.config.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/bip32/tsup.config.js b/packages/bip32/tsup.config.js index 2aee42e6..e09a7cff 100644 --- a/packages/bip32/tsup.config.js +++ b/packages/bip32/tsup.config.js @@ -1,6 +1,6 @@ -import { defineConfig } from 'tsup'; import { polyfillNode } from "esbuild-plugin-polyfill-node"; +import { defineConfig } from "tsup"; export default defineConfig({ - esbuildPlugins: [polyfillNode()], + esbuildPlugins: [polyfillNode({ polyfills: { crypto: true } })], }); diff --git a/packages/caravan-bitcoin/tsup.config.ts b/packages/caravan-bitcoin/tsup.config.ts index 5fc2938a..dedc3fef 100644 --- a/packages/caravan-bitcoin/tsup.config.ts +++ b/packages/caravan-bitcoin/tsup.config.ts @@ -1,6 +1,6 @@ -import { defineConfig } from 'tsup' +import { defineConfig } from "tsup"; import { polyfillNode } from "esbuild-plugin-polyfill-node"; export default defineConfig({ esbuildPlugins: [polyfillNode()], -}) +}); From 263cf65c610d5db748179e223d8edda66785750d Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 5 Sep 2024 12:08:18 -0500 Subject: [PATCH 05/18] split up key and path modules and add tests --- packages/bip32/src/__tests__/keys.test.ts | 134 +++++++++++++++++++++ packages/bip32/src/__tests__/paths.test.ts | 85 +++++-------- packages/bip32/src/index.ts | 14 +-- packages/bip32/src/keys.ts | 114 ++++++++++++++++++ packages/bip32/src/paths.ts | 107 +--------------- 5 files changed, 289 insertions(+), 165 deletions(-) create mode 100644 packages/bip32/src/__tests__/keys.test.ts create mode 100644 packages/bip32/src/keys.ts diff --git a/packages/bip32/src/__tests__/keys.test.ts b/packages/bip32/src/__tests__/keys.test.ts new file mode 100644 index 00000000..2f693bc3 --- /dev/null +++ b/packages/bip32/src/__tests__/keys.test.ts @@ -0,0 +1,134 @@ +import { Network, TEST_FIXTURES } from "@caravan/bitcoin"; +import { Bip32Derivation } from "bip174/src/lib/interfaces"; + +import { + getMaskedKeyOrigin, + getRandomChildXpub, + isValidChildPubKey, + setXpubNetwork, +} from "../keys"; +import * as mockPaths from "../paths"; +import { KeyOrigin } from "../types"; + +const globalOrigin: KeyOrigin = { + xpub: "tpubDEtyXJKvCT2V3ccXBBrNPEGb8RNZjNMcGbx68CzE74zq7aKWwRz8up95PYCm7HrYRT7Sz42uFVpW1MgRzqRU7KTHsY6LgPcYMc53pqHC7uc", + bip32Path: "m/45'/0'/0'/42", + rootFingerprint: "9a6a2580", +}; + +const testChildDerivation = { + path: "m/45'/0'/0'/42/0/0", + pubkey: Buffer.from( + "03dee36bc0b80de80631a4dca518caf31672c6023bf5e8416fa2c9587a8a1a26bf", + "hex", + ), +} as unknown as Bip32Derivation; + +describe("getMaskedKeyOrigin", () => { + it("should return the masked origin", () => { + const masked = getMaskedKeyOrigin(globalOrigin.xpub); + expect(masked).toEqual({ + xpub: globalOrigin.xpub, + bip32Path: "m/0/0/0/0", + rootFingerprint: "86bd941f", + }); + }); +}); + +describe("isValidChildPubKey", () => { + it("should return true for a valid child pubkey", () => { + expect( + isValidChildPubKey(testChildDerivation, globalOrigin, Network.REGTEST), + ).toBe(true); + }); + + it("should return true for valid masked key", () => { + const derivation = { + ...testChildDerivation, + path: "m/45/0/0/0/0/0", + }; + expect(isValidChildPubKey(derivation, globalOrigin, Network.REGTEST)).toBe( + true, + ); + }); + + it("should throw if child key is longer than parent", () => { + const derivation = { + ...testChildDerivation, + path: "m/45'/0'/0'/", + }; + expect(() => + isValidChildPubKey(derivation, globalOrigin, Network.REGTEST), + ).toThrow(); + }); + + it("should return false for an invalid child pubkey", () => { + const otherGlobal: KeyOrigin = { + xpub: "tpubDEtyXJKvDdEvUzbiBsHXXAqjnNvDJdQWLjyjCxSRzzHq77fKjbxFJ2uXuciR28CRk6dQzGwNw2Dby615BbykdWHDQZHCacY21JW3FCFKcme", + bip32Path: "m/45'/0'/0'/5222010", + rootFingerprint: "9a6a2580", + }; + expect( + isValidChildPubKey(testChildDerivation, otherGlobal, Network.REGTEST), + ).toBe(false); + }); +}); + +describe("setXpubNetwork", () => { + let xpub, tpub: string; + beforeEach(() => { + const node = TEST_FIXTURES.keys.open_source.nodes["m/45'/0'/0'/0"]; + xpub = node.xpub; + tpub = node.tpub; + }); + + it("should correctly convert", () => { + expect(setXpubNetwork(xpub, Network.TESTNET)).toBe(tpub); + expect(setXpubNetwork(tpub, Network.MAINNET)).toBe(xpub); + expect(setXpubNetwork(xpub, Network.REGTEST)).toBe(tpub); + }); + + it("should not change anything if no network specified", () => { + expect(setXpubNetwork(xpub)).toBe(xpub); + expect(setXpubNetwork(tpub)).toBe(tpub); + }); +}); + +jest.mock("../paths", () => { + return { + __esModule: true, + ...jest.requireActual("../paths"), + secureSecretPath: jest.fn(), + }; +}); + +describe("getRandomChildXpub", () => { + let parentPath, childPath, parent, child, depth; + beforeEach(() => { + depth = 2; + const nodes = TEST_FIXTURES.keys.open_source.nodes; + parentPath = "m/45'/0'/0'"; + parent = nodes[parentPath]; + // depth below the parent path + childPath = "m/45'/0'/0'/0/0"; + child = nodes["m/45'/0'/0'/0/0"]; + console.log(mockPaths); + (mockPaths.secureSecretPath as jest.Mock).mockReturnValue("m/0/0"); + }); + + afterAll(jest.restoreAllMocks); + it("should return a random child xpub", () => { + const keyOrigin = { + xpub: parent.xpub, + bip32Path: parentPath, + rootFingerprint: parent.fingerprint, + }; + const actual = getRandomChildXpub(keyOrigin, depth); + expect(actual).toEqual({ + xpub: child.xpub, + bip32Path: childPath, + rootFingerprint: child.fingerprint, + }); + expect(mockPaths.secureSecretPath).toHaveBeenCalledWith(depth); + }); +}); diff --git a/packages/bip32/src/__tests__/paths.test.ts b/packages/bip32/src/__tests__/paths.test.ts index 7b3a1f1b..452a5e27 100644 --- a/packages/bip32/src/__tests__/paths.test.ts +++ b/packages/bip32/src/__tests__/paths.test.ts @@ -1,13 +1,7 @@ import { Bip32Derivation } from "bip174/src/lib/interfaces"; -import { - combineBip32Paths, - getMaskedKeyOrigin, - getUnmaskedPath, - isValidChildPubKey, - KeyOrigin, -} from ".."; -import { Network } from "@caravan/bitcoin"; +import { combineBip32Paths, getUnmaskedPath, secureSecretPath } from "../paths"; +import { KeyOrigin } from "../types"; const globalOrigin: KeyOrigin = { xpub: "tpubDEtyXJKvCT2V3ccXBBrNPEGb8RNZjNMcGbx68CzE74zq7aKWwRz8up95PYCm7HrYRT7Sz42uFVpW1MgRzqRU7KTHsY6LgPcYMc53pqHC7uc", @@ -23,54 +17,6 @@ const testChildDerivation = { ), } as unknown as Bip32Derivation; -describe("getMaskedKeyOrigin", () => { - it("should return the masked origin", () => { - const masked = getMaskedKeyOrigin(globalOrigin.xpub); - expect(masked).toEqual({ - xpub: globalOrigin.xpub, - bip32Path: "m/0/0/0/0", - rootFingerprint: "86bd941f", - }); - }); -}); - -describe("isValidChildPubKey", () => { - it("should return true for a valid child pubkey", () => { - expect(isValidChildPubKey(testChildDerivation, globalOrigin, Network.REGTEST)).toBe( - true, - ); - }); - - it("should return true for valid masked key", () => { - const derivation = { - ...testChildDerivation, - path: "m/45/0/0/0/0/0", - }; - expect(isValidChildPubKey(derivation, globalOrigin, Network.REGTEST)).toBe(true); - }); - - it("should throw if child key is longer than parent", () => { - const derivation = { - ...testChildDerivation, - path: "m/45'/0'/0'/", - }; - expect(() => - isValidChildPubKey(derivation, globalOrigin, Network.REGTEST), - ).toThrow(); - }); - - it("should return false for an invalid child pubkey", () => { - const otherGlobal: KeyOrigin = { - xpub: "tpubDEtyXJKvDdEvUzbiBsHXXAqjnNvDJdQWLjyjCxSRzzHq77fKjbxFJ2uXuciR28CRk6dQzGwNw2Dby615BbykdWHDQZHCacY21JW3FCFKcme", - bip32Path: "m/45'/0'/0'/5222010", - rootFingerprint: "9a6a2580", - }; - expect(isValidChildPubKey(testChildDerivation, otherGlobal, Network.REGTEST)).toBe( - false, - ); - }); -}); - describe("getUnmaskedPath", () => { it("should return the unmasked path", () => { const maskedChildDerivation = { @@ -115,3 +61,30 @@ describe("combineBip32Paths", () => { expect(() => combineBip32Paths(firstPath, secondPath)).toThrow(); }); }); + +jest.mock("crypto", () => { + return { + ...jest.requireActual("crypto"), + randomInt: jest.fn(() => 42), + }; +}); + +describe("secureSecretPath", () => { + afterAll(jest.resetAllMocks); + it("should return a path of the desired depth", () => { + const depth = 4; + const securePath = secureSecretPath(depth); + expect(securePath.split("/").length).toBe(depth + 1); + }); + + it("should return a secure path", () => { + const expectedPath = "m/42/42/42/42"; + const securePath = secureSecretPath(); + expect(securePath).toBe(expectedPath); + }); + + it("should throw an error for invalid depth", () => { + expect(() => secureSecretPath(32)).toThrow(); + expect(() => secureSecretPath(0)).toThrow(); + }); +}); diff --git a/packages/bip32/src/index.ts b/packages/bip32/src/index.ts index ef6a19ce..41e5167a 100644 --- a/packages/bip32/src/index.ts +++ b/packages/bip32/src/index.ts @@ -1,12 +1,10 @@ -export type { - KeyOrigin -} from "./types" +export type { KeyOrigin } from "./types"; + +export { secureSecretPath, combineBip32Paths, getUnmaskedPath } from "./paths"; export { - secureSecretPath, - combineBip32Paths, + isValidChildPubKey, getRandomChildXpub, getMaskedKeyOrigin, - isValidChildPubKey, - getUnmaskedPath, -} from "./paths" + setXpubNetwork, +} from "./keys"; diff --git a/packages/bip32/src/keys.ts b/packages/bip32/src/keys.ts new file mode 100644 index 00000000..6740ef7a --- /dev/null +++ b/packages/bip32/src/keys.ts @@ -0,0 +1,114 @@ +import { + ExtendedPublicKey, + Network, + bip32PathToSequence, + bip32SequenceToPath, + deriveChildExtendedPublicKey, + deriveChildPublicKey, +} from "@caravan/bitcoin"; +import { Bip32Derivation } from "bip174/src/lib/interfaces"; + +import { combineBip32Paths, secureSecretPath } from "./paths"; +import { KeyOrigin } from "./types"; + +/** + * When you have a global xpub from a PSBT, it's useful to make + * sure that a child pubkey can be derived from that psbt. Sometimes + * the pubkey derivation comes from a masked and/or blinded xpub. + * So we need to combine the child derivation with the global + * and confirm that the pubkey can be derived from that source + * @param derivation {Bip32Derivation} - derivation to validate. + * This type is from the bitcoinjs-lib bip174 package + * @param globalXpub {KeyOrigin} - global xpub from the psbt + * @param network {Network} + * @returns {boolean} whether the child pubkey can be derived from the global xpub + */ +export const isValidChildPubKey = ( + derivation: Bip32Derivation, + globalXpub: KeyOrigin, + network: Network = Network.MAINNET, +): boolean => { + const globalSequence = bip32PathToSequence(globalXpub.bip32Path); + const derivationSequence = bip32PathToSequence(derivation.path); + + const difference = derivationSequence.length - globalSequence.length; + if (difference < 0) + throw new Error( + `Child key longer than parent: Parent: ${globalXpub.bip32Path}, Child: ${derivation.path}`, + ); + const lastElements = derivationSequence.slice(-difference); + const relativePath = bip32SequenceToPath(lastElements); + + const childPubkey = deriveChildPublicKey( + globalXpub.xpub, + relativePath, + network, + ); + return childPubkey === derivation.pubkey.toString("hex"); +}; + +/** + * + * @param xpub {string} + * @param network [Network] + * @returns {string} - updated xpub with given network + */ +export const setXpubNetwork = (xpub: string, network?: Network): string => { + if (!network) return xpub; + + const xpubObj = ExtendedPublicKey.fromBase58(xpub); + xpubObj.setNetwork(network); + return xpubObj.toBase58(); +}; + +/** + * @description Derive a masked key origin from an xpub. Useful for generating + * descriptors and wallet configurations for keys that don't need to have their + * key origin info revealed. + * Bip32 path will use all 0s for the depth of the given xpub and the + * root fingerprint will be set to the parent fingerprint of the xpub + * @param xpub {string} - xpub to mask + * @returns + */ +export const getMaskedKeyOrigin = (xpub: string): KeyOrigin => { + const { parentFingerprint, depth } = ExtendedPublicKey.fromBase58(xpub); + + // shouldn't happen but making typescript happy + if (!parentFingerprint || !depth) + throw new Error("Parent fingerprint or depth not found from xpub"); + + return { + xpub: xpub, + bip32Path: `m${"/0".repeat(depth)}`, + rootFingerprint: parentFingerprint.toString(16), // Convert parentFingerprint to hexadecimal string + }; +}; + +/** + * Given a source xpub, derive a child xpub at a random path using secureSecretPath + * defaults to depth 4. Useful for creating blinded xpubs or generating random child + * xpubs (e.g. strands) + * @param sourceOrigin {KeyOrigin} + * @param network [Network] - if not provided will just default to the source xpub's network + * @returns {KeyOrigin} - Child xpub and path + */ +export const getRandomChildXpub = ( + sourceOrigin: KeyOrigin, + depth: number = 4, + network: Network = Network.MAINNET, +): KeyOrigin => { + const randomPath = secureSecretPath(depth); + + const childXpub = deriveChildExtendedPublicKey( + setXpubNetwork(sourceOrigin.xpub, network), + randomPath, + network, + ); + const childPath = combineBip32Paths(sourceOrigin.bip32Path, randomPath); + + return { + xpub: childXpub, + bip32Path: childPath, + rootFingerprint: sourceOrigin.rootFingerprint, + }; +}; diff --git a/packages/bip32/src/paths.ts b/packages/bip32/src/paths.ts index 176442a3..7f2f64ca 100644 --- a/packages/bip32/src/paths.ts +++ b/packages/bip32/src/paths.ts @@ -1,15 +1,15 @@ -import { randomInt } from "crypto"; +// import { randomInt } from "crypto"; +import crypto from "crypto"; + import { bip32PathToSequence, bip32SequenceToPath, - deriveChildExtendedPublicKey, - deriveChildPublicKey, - ExtendedPublicKey, - Network, validateBIP32Path, } from "@caravan/bitcoin"; import { Bip32Derivation } from "bip174/src/lib/interfaces"; + import { KeyOrigin } from "./types"; + /** * @description Returns a random BIP32 path of a given depth. The * randomness is generated using the Node.js crypto module. @@ -44,13 +44,12 @@ export const secureSecretPath = (depth: number = 4): string => { } const to_return: string[] = ["m"]; for (let i = 0; i < depth; i++) { - const rand_int = randomInt(0, 2 ** 31 - 1); + const rand_int = crypto.randomInt(0, 2 ** 31 - 1); to_return.push(rand_int.toString()); } return to_return.join("/"); }; - /** * Given two BIP32 paths, combine them into a single path. * Useful for creating blinded xpubs when you have the source @@ -91,100 +90,6 @@ export const combineBip32Paths = ( return combined; }; -/** - * - * @param xpub {string} - * @param network [Network] - * @returns {string} - updated xpub with given network - */ -const setXpubNetwork = (xpub: string, network?: Network): string => { - if (!network) return xpub; - - const xpubObj = ExtendedPublicKey.fromBase58(xpub); - xpubObj.setNetwork(network); - return xpubObj.toBase58(); -}; - -/** - * Given a source xpub, derive a child xpub at a random path using secureSecretPath - * defaults to depth 4. Useful for creating blinded xpubs or generating random child - * xpubs (e.g. strands) - * @param sourceOrigin {KeyOrigin} - * @param network [Network] - if not provided will just default to the source xpub's network - * @returns {KeyOrigin} - Child xpub and path - */ -export const getRandomChildXpub = (sourceOrigin: KeyOrigin, depth = 4, network?: Network): KeyOrigin => { - const randomPath = secureSecretPath(depth); - - const childXpub = deriveChildExtendedPublicKey( - setXpubNetwork(sourceOrigin.xpub, network), - randomPath, - process.env.BITCOIN_NETWORK as Network, - ); - const childPath = combineBip32Paths(sourceOrigin.bip32Path, randomPath); - - return { - xpub: childXpub, - bip32Path: childPath, - rootFingerprint: sourceOrigin.rootFingerprint, - }; -}; - -/** - * @description Derive a masked key origin from an xpub. Useful for generating - * descriptors and wallet configurations for keys that don't need to have their - * key origin info revealed. - * Bip32 path will use all 0s for the depth of the given xpub and the - * root fingerprint will be set to the parent fingerprint of the xpub - * @param xpub {string} - xpub to mask - * @returns - */ -export const getMaskedKeyOrigin = (xpub: string): KeyOrigin => { - const { parentFingerprint, depth } = ExtendedPublicKey.fromBase58(xpub); - - // shouldn't happen but making typescript happy - if (!parentFingerprint || !depth) - throw new Error("Parent fingerprint or depth not found from xpub"); - - return { - xpub: xpub, - bip32Path: `m${"/0".repeat(depth)}`, - rootFingerprint: parentFingerprint.toString(16), // Convert parentFingerprint to hexadecimal string - }; -}; - -/** - * When you have a global xpub from a PSBT, it's useful to make - * sure that a child pubkey can be derived from that psbt. Sometimes - * the pubkey derivation comes from a masked and/or blinded xpub. - * So we need to combine the child derivation with the global - * and confirm that the pubkey can be derived from that source - * @param derivation {Bip32Derivation} - derivation to validate. - * This type is from the bitcoinjs-lib bip174 package - * @param globalXpub {KeyOrigin} - global xpub from the psbt - * @param network {Network} - * @returns {boolean} whether the child pubkey can be derived from the global xpub - */ -export const isValidChildPubKey = ( - derivation: Bip32Derivation, - globalXpub: KeyOrigin, - network: Network = Network.MAINNET, -): boolean => { - const globalSequence = bip32PathToSequence(globalXpub.bip32Path); - const derivationSequence = bip32PathToSequence(derivation.path); - - const difference = derivationSequence.length - globalSequence.length; - if (difference < 0) - throw new Error( - `Child key longer than parent: Parent: ${globalXpub.bip32Path}, Child: ${derivation.path}`, - ); - const lastElements = derivationSequence.slice(-difference); - const relativePath = bip32SequenceToPath(lastElements); - - const childPubkey = deriveChildPublicKey(globalXpub.xpub, relativePath, network); - return childPubkey === derivation.pubkey.toString("hex"); -}; - /** * Given a derivation and a global xpub, return the unmasked path * that can be used to derive the child pubkey from the global xpub. From a7c243b573f2571e85b6a5bd5356802586ec9bcc Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 5 Sep 2024 12:09:40 -0500 Subject: [PATCH 06/18] add readme for bip32 package --- packages/bip32/README.md | 48 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/bip32/README.md b/packages/bip32/README.md index bf283992..20bd7a55 100644 --- a/packages/bip32/README.md +++ b/packages/bip32/README.md @@ -1 +1,47 @@ -# Bip32 \ No newline at end of file +# Bip32 Utilities + +## Paths + +### `secureSecretPath` + +Generates a random BIP32 path of a given depth. The randomness is generated using the Node.js crypto module. This can be used for blinding an xpub. The function ensures that the depth is an integer and less than 32. + +### `combineBip32Paths` + +Given two BIP32 paths, combine them into a single path. +Useful for creating blinded xpubs when you have the source +path and want to append the randomly generated one + +### `getUnmaskedPath` + +Given a derivation and a global xpub, return the unmasked path +that can be used to derive the child pubkey from the global xpub. +This is useful when you have a child xpub (e.g. a blinded xpub) derived +from a masked xpub and you need to generate the full, unmasked path. + +## Keys + +### `getRandomChildXpub` + +Given a source xpub, derive a child xpub at a random path using secureSecretPath +defaults to depth 4. Useful for creating blinded xpubs or generating random child xpubs (e.g. strands) + +### `getMaskedKeyOrigin` + +Derive a masked key origin from an xpub. Useful for generating +descriptors and wallet configurations for keys that don't need to have their +key origin info revealed. +Bip32 path will use all 0s for the depth of the given xpub and the +root fingerprint will be set to the parent fingerprint of the xpub + +### `isValidChildPubKey` + +When you have a global xpub from a PSBT, it's useful to make +sure that a child pubkey can be derived from that psbt. Sometimes +the pubkey derivation comes from a masked and/or blinded xpub. +So we need to combine the child derivation with the global +and confirm that the pubkey can be derived from that source + +### `setXpubNetwork` + +Sets and updates serialization of xpub for a network accordingly. From 786b957b8951d00d963278542766989287ef9861 Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 5 Sep 2024 13:46:55 -0500 Subject: [PATCH 07/18] fix: package.json and module types --- package-lock.json | 2 +- packages/bip32/package.json | 9 +++++---- packages/caravan-psbt/package.json | 7 ++++--- turbo/generators/templates/package.json.hbs | 7 ++++--- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 265d8d11..e86065d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24277,7 +24277,7 @@ }, "packages/bip32": { "name": "@caravan/bip32", - "version": "1.0.0", + "version": "1.0.0-beta", "license": "MIT", "dependencies": { "@caravan/bitcoin": "*", diff --git a/packages/bip32/package.json b/packages/bip32/package.json index 2137ba4b..d42f3691 100644 --- a/packages/bip32/package.json +++ b/packages/bip32/package.json @@ -1,9 +1,10 @@ { "name": "@caravan/bip32", - "version": "1.0.0", + "version": "1.0.0-beta", "author": "unchained capital", "description": "Package for working with bip32 and derivative objects like extended public keys", "private": true, + "type": "module", "engines": { "node": ">=20" }, @@ -12,13 +13,13 @@ "module": "./dist/index.mjs", "files": [ "./dist/index.js", - "./dist/index.mjs", + "./dist/index.cjs", "./dist/index.d.ts" ], "exports": { ".": { - "import": "./dist/index.mjs", - "require": "./dist/index.js", + "import": "./dist/index.js", + "require": "./dist/index.cjs", "types": "./dist/index.d.ts" } }, diff --git a/packages/caravan-psbt/package.json b/packages/caravan-psbt/package.json index 24f7e54d..c8fc5551 100644 --- a/packages/caravan-psbt/package.json +++ b/packages/caravan-psbt/package.json @@ -2,18 +2,19 @@ "name": "@caravan/psbt", "version": "1.4.2", "description": "typescript library for working with PSBTs", + "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", "module": "./dist/index.mjs", "files": [ "./dist/index.js", - "./dist/index.mjs", + "./dist/index.cjs", "./dist/index.d.ts" ], "exports": { ".": { - "import": "./dist/index.mjs", - "require": "./dist/index.js", + "import": "./dist/index.js", + "require": "./dist/index.cjs", "types": "./dist/index.d.ts" } }, diff --git a/turbo/generators/templates/package.json.hbs b/turbo/generators/templates/package.json.hbs index 30ae601d..a32a6532 100644 --- a/turbo/generators/templates/package.json.hbs +++ b/turbo/generators/templates/package.json.hbs @@ -4,6 +4,7 @@ "author": "{{author}}", "description": "{{description}}", "private": true, + "type": "module", "engines": { "node": ">=20" }, @@ -12,13 +13,13 @@ "module": "./dist/index.mjs", "files": [ "./dist/index.js", - "./dist/index.mjs", + "./dist/index.cjs", "./dist/index.d.ts" ], "exports": { ".": { - "import": "./dist/index.mjs", - "require": "./dist/index.js", + "import": "./dist/index.js", + "require": "./dist/index.cjs", "types": "./dist/index.d.ts" } }, From 0ee09efb8c527a918b879f546e2ec65f827ed807 Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 5 Sep 2024 16:28:35 -0500 Subject: [PATCH 08/18] cleanup --- packages/caravan-bitcoin/tsconfig.json | 1 - turbo/generators/templates/package.json.hbs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/caravan-bitcoin/tsconfig.json b/packages/caravan-bitcoin/tsconfig.json index 66d43cf0..f49f0d71 100644 --- a/packages/caravan-bitcoin/tsconfig.json +++ b/packages/caravan-bitcoin/tsconfig.json @@ -16,7 +16,6 @@ "emitDeclarationOnly": true, // Ensure that Babel can safely transpile files in the TypeScript project "isolatedModules": true, - "outDir": "lib", }, "include": [ "src" diff --git a/turbo/generators/templates/package.json.hbs b/turbo/generators/templates/package.json.hbs index a32a6532..3e77dc9a 100644 --- a/turbo/generators/templates/package.json.hbs +++ b/turbo/generators/templates/package.json.hbs @@ -8,9 +8,9 @@ "engines": { "node": ">=20" }, + "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", - "module": "./dist/index.mjs", "files": [ "./dist/index.js", "./dist/index.cjs", From 11ef1db9962bb48b6124b88bcff3210c9da59f37 Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 5 Sep 2024 22:23:03 -0500 Subject: [PATCH 09/18] custom util for getting random ints in node or browser --- package-lock.json | 4 +++- packages/bip32/package.json | 6 ++--- packages/bip32/src/__tests__/utils.test.ts | 26 ++++++++++++++++++++ packages/bip32/src/utils.ts | 28 ++++++++++++++++++++++ packages/bip32/tsup.config.js | 2 +- 5 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 packages/bip32/src/__tests__/utils.test.ts create mode 100644 packages/bip32/src/utils.ts diff --git a/package-lock.json b/package-lock.json index e86065d9..ba2e6be8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "version": "1.2.0", "license": "MIT", "dependencies": { + "@caravan/bip32": "*", "@caravan/bitcoin": "*", "@caravan/clients": "*", "@caravan/descriptors": "^0.1.1", @@ -24281,7 +24282,8 @@ "license": "MIT", "dependencies": { "@caravan/bitcoin": "*", - "bip174": "^2.1.1" + "bip174": "^2.1.1", + "buffer": "^6.0.3" }, "devDependencies": { "@caravan/eslint-config": "*", diff --git a/packages/bip32/package.json b/packages/bip32/package.json index d42f3691..beac89e4 100644 --- a/packages/bip32/package.json +++ b/packages/bip32/package.json @@ -4,13 +4,12 @@ "author": "unchained capital", "description": "Package for working with bip32 and derivative objects like extended public keys", "private": true, - "type": "module", "engines": { "node": ">=20" }, + "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", - "module": "./dist/index.mjs", "files": [ "./dist/index.js", "./dist/index.cjs", @@ -35,7 +34,8 @@ }, "dependencies": { "@caravan/bitcoin": "*", - "bip174": "^2.1.1" + "bip174": "^2.1.1", + "buffer": "^6.0.3" }, "devDependencies": { "@caravan/eslint-config": "*", diff --git a/packages/bip32/src/__tests__/utils.test.ts b/packages/bip32/src/__tests__/utils.test.ts new file mode 100644 index 00000000..bf10efde --- /dev/null +++ b/packages/bip32/src/__tests__/utils.test.ts @@ -0,0 +1,26 @@ +import { secureRandomInt } from "../utils"; + +describe("secureRandomInt", () => { + it("returns a number within the specified range", () => { + const min = 10; + const max = 20; + for (let i = 0; i < 100; i++) { + const result = secureRandomInt(min, max); + expect(result).toBeGreaterThanOrEqual(min); + expect(result).toBeLessThanOrEqual(max); + } + }); + + it("returns a number within the default range when no arguments are provided", () => { + const DEFAULT_MAX = 2 ** 31 - 1; + for (let i = 0; i < 100; i++) { + const result = secureRandomInt(); + expect(result).toBeGreaterThanOrEqual(0); + expect(result).toBeLessThanOrEqual(DEFAULT_MAX); + } + }); + + it("throws a RangeError when min is greater than max", () => { + expect(() => secureRandomInt(20, 10)).toThrow(RangeError); + }); +}); diff --git a/packages/bip32/src/utils.ts b/packages/bip32/src/utils.ts new file mode 100644 index 00000000..96a515ce --- /dev/null +++ b/packages/bip32/src/utils.ts @@ -0,0 +1,28 @@ +import { Buffer } from "buffer"; +import crypto from "crypto"; +const DEFAULT_MAX = 2 ** 31 - 1; +export const secureRandomInt = (min = 0, max = DEFAULT_MAX): number => { + if (typeof min !== "number" || typeof max !== "number") { + throw new TypeError("Arguments must be numbers"); + } + + if (min > max) { + throw new RangeError("Min should not be greater than max"); + } + + let getCrypto; + if (typeof crypto !== "undefined" && crypto.getRandomValues) { + // Browser environment + getCrypto = crypto.getRandomValues; + } else if (typeof crypto.randomFillSync !== "undefined") { + // Node.js environment + getCrypto = crypto.randomFillSync; + } else { + throw new Error("Crypto not available in this environment"); + } + + const buffer = Buffer.alloc(4); + getCrypto(buffer); + const randomValue = buffer.readUInt32BE(0); + return min + (randomValue % (max - min + 1)); +}; diff --git a/packages/bip32/tsup.config.js b/packages/bip32/tsup.config.js index e09a7cff..00ff69bd 100644 --- a/packages/bip32/tsup.config.js +++ b/packages/bip32/tsup.config.js @@ -2,5 +2,5 @@ import { polyfillNode } from "esbuild-plugin-polyfill-node"; import { defineConfig } from "tsup"; export default defineConfig({ - esbuildPlugins: [polyfillNode({ polyfills: { crypto: true } })], + esbuildPlugins: [polyfillNode({ polyfills: { crypto: false } })], }); From d5d9e249f3f025f0a2954c7ff6a8acec9e9d6c51 Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 5 Sep 2024 22:24:20 -0500 Subject: [PATCH 10/18] use custom random int for blinded paths --- packages/bip32/src/__tests__/paths.test.ts | 27 ++++++++++++++-------- packages/bip32/src/paths.ts | 12 ++++------ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/packages/bip32/src/__tests__/paths.test.ts b/packages/bip32/src/__tests__/paths.test.ts index 452a5e27..40594b36 100644 --- a/packages/bip32/src/__tests__/paths.test.ts +++ b/packages/bip32/src/__tests__/paths.test.ts @@ -62,29 +62,38 @@ describe("combineBip32Paths", () => { }); }); -jest.mock("crypto", () => { +jest.mock("../utils", () => { return { - ...jest.requireActual("crypto"), - randomInt: jest.fn(() => 42), + __esModule: true, + secureRandomInt: jest.fn(() => 42), }; }); describe("secureSecretPath", () => { - afterAll(jest.resetAllMocks); - it("should return a path of the desired depth", () => { + it("should return a path of the desired depth", async () => { const depth = 4; const securePath = secureSecretPath(depth); expect(securePath.split("/").length).toBe(depth + 1); }); - it("should return a secure path", () => { + it("should return a secure path", async () => { const expectedPath = "m/42/42/42/42"; const securePath = secureSecretPath(); expect(securePath).toBe(expectedPath); }); - it("should throw an error for invalid depth", () => { - expect(() => secureSecretPath(32)).toThrow(); - expect(() => secureSecretPath(0)).toThrow(); + it("should throw an error for invalid depth", async () => { + let failures = 0; + try { + await secureSecretPath(32); + } catch (e: any) { + failures += 1; + } + try { + await secureSecretPath(0); + } catch (e: any) { + failures += 1; + } + expect(failures).toBe(2); }); }); diff --git a/packages/bip32/src/paths.ts b/packages/bip32/src/paths.ts index 7f2f64ca..c4bbe765 100644 --- a/packages/bip32/src/paths.ts +++ b/packages/bip32/src/paths.ts @@ -1,6 +1,3 @@ -// import { randomInt } from "crypto"; -import crypto from "crypto"; - import { bip32PathToSequence, bip32SequenceToPath, @@ -9,6 +6,7 @@ import { import { Bip32Derivation } from "bip174/src/lib/interfaces"; import { KeyOrigin } from "./types"; +import { secureRandomInt } from "./utils"; /** * @description Returns a random BIP32 path of a given depth. The @@ -42,12 +40,12 @@ export const secureSecretPath = (depth: number = 4): string => { if (depth < 1) { throw new Error(`Depth must be > 0: ${depth}`); } - const to_return: string[] = ["m"]; + const toReturn: string[] = ["m"]; for (let i = 0; i < depth; i++) { - const rand_int = crypto.randomInt(0, 2 ** 31 - 1); - to_return.push(rand_int.toString()); + const randInt = secureRandomInt(); + toReturn.push(randInt.toString()); } - return to_return.join("/"); + return toReturn.join("/"); }; /** From c9b2fa7b58297234773d32c966fc350c00472b6e Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 5 Sep 2024 22:24:40 -0500 Subject: [PATCH 11/18] util for getting blinded xpub from source --- packages/bip32/README.md | 5 +++++ packages/bip32/src/__tests__/keys.test.ts | 26 ++++++++++++++++++++--- packages/bip32/src/index.ts | 1 + packages/bip32/src/keys.ts | 23 +++++++++++++++++++- 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/packages/bip32/README.md b/packages/bip32/README.md index 20bd7a55..695507f1 100644 --- a/packages/bip32/README.md +++ b/packages/bip32/README.md @@ -45,3 +45,8 @@ and confirm that the pubkey can be derived from that source ### `setXpubNetwork` Sets and updates serialization of xpub for a network accordingly. + +### `getBlindedXpub` + +Given a source xpub, derive a blinded xpub at a random path. +Will target 128 bits of entropy for the path with a depth of 4. diff --git a/packages/bip32/src/__tests__/keys.test.ts b/packages/bip32/src/__tests__/keys.test.ts index 2f693bc3..307d65cd 100644 --- a/packages/bip32/src/__tests__/keys.test.ts +++ b/packages/bip32/src/__tests__/keys.test.ts @@ -2,6 +2,7 @@ import { Network, TEST_FIXTURES } from "@caravan/bitcoin"; import { Bip32Derivation } from "bip174/src/lib/interfaces"; import { + getBlindedXpub, getMaskedKeyOrigin, getRandomChildXpub, isValidChildPubKey, @@ -112,18 +113,17 @@ describe("getRandomChildXpub", () => { // depth below the parent path childPath = "m/45'/0'/0'/0/0"; child = nodes["m/45'/0'/0'/0/0"]; - console.log(mockPaths); (mockPaths.secureSecretPath as jest.Mock).mockReturnValue("m/0/0"); }); afterAll(jest.restoreAllMocks); - it("should return a random child xpub", () => { + it("should return a random child xpub", async () => { const keyOrigin = { xpub: parent.xpub, bip32Path: parentPath, rootFingerprint: parent.fingerprint, }; - const actual = getRandomChildXpub(keyOrigin, depth); + const actual = await getRandomChildXpub(keyOrigin, depth); expect(actual).toEqual({ xpub: child.xpub, bip32Path: childPath, @@ -132,3 +132,23 @@ describe("getRandomChildXpub", () => { expect(mockPaths.secureSecretPath).toHaveBeenCalledWith(depth); }); }); + +// Very similar to the above test, but only works on an xpub +describe("getBlindedXpub", () => { + let parentPath, parent, child; + beforeEach(() => { + const nodes = TEST_FIXTURES.keys.open_source.nodes; + parentPath = "m/45'/0'/0'"; + parent = nodes[parentPath]; + // depth below the parent path + child = nodes["m/45'/0'/0'/0/0"]; + (mockPaths.secureSecretPath as jest.Mock).mockReturnValue("m/0/0"); + }); + + afterAll(jest.restoreAllMocks); + it("should return a random child key origin", async () => { + const actual = await getBlindedXpub(parent.xpub); + expect(actual.xpub).toEqual(child.xpub); + expect(actual.bip32Path).toEqual("*/0/0"); + }); +}); diff --git a/packages/bip32/src/index.ts b/packages/bip32/src/index.ts index 41e5167a..9adf1743 100644 --- a/packages/bip32/src/index.ts +++ b/packages/bip32/src/index.ts @@ -7,4 +7,5 @@ export { getRandomChildXpub, getMaskedKeyOrigin, setXpubNetwork, + getBlindedXpub, } from "./keys"; diff --git a/packages/bip32/src/keys.ts b/packages/bip32/src/keys.ts index 6740ef7a..3a7dff83 100644 --- a/packages/bip32/src/keys.ts +++ b/packages/bip32/src/keys.ts @@ -5,6 +5,7 @@ import { bip32SequenceToPath, deriveChildExtendedPublicKey, deriveChildPublicKey, + getNetworkFromPrefix, } from "@caravan/bitcoin"; import { Bip32Derivation } from "bip174/src/lib/interfaces"; @@ -98,7 +99,6 @@ export const getRandomChildXpub = ( network: Network = Network.MAINNET, ): KeyOrigin => { const randomPath = secureSecretPath(depth); - const childXpub = deriveChildExtendedPublicKey( setXpubNetwork(sourceOrigin.xpub, network), randomPath, @@ -112,3 +112,24 @@ export const getRandomChildXpub = ( rootFingerprint: sourceOrigin.rootFingerprint, }; }; + +/** + * @description Given a source xpub, derive a blinded xpub at a random path. + * Will target 128 bits of entropy for the path with a depth of 4. + * @param rawXpub {string} - xpub to blind + * @returns + */ +export const getBlindedXpub = (rawXpub: string): KeyOrigin => { + const xpub = ExtendedPublicKey.fromBase58(rawXpub); + if (!xpub.depth) throw new Error("Depth not found from xpub"); + const secretPath = secureSecretPath(4); + + const network = getNetworkFromPrefix(rawXpub.slice(0, 4)); + const newKey = deriveChildExtendedPublicKey(rawXpub, secretPath, network); + + return { + xpub: newKey, + bip32Path: `*/${secretPath.split("/").slice(1).join("/")}`, + rootFingerprint: xpub?.parentFingerprint?.toString(16) || "", + }; +}; From cd3b5c26c9f35ddc883b8faaf61583841f152251 Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 5 Sep 2024 22:25:17 -0500 Subject: [PATCH 12/18] feat: blind xpub support for text input --- apps/coordinator/package.json | 1 + .../Coldcard/ColdcardFileReader.jsx | 2 +- .../Wallet/ExtendedPublicKeyImporter.jsx | 4 +- .../Wallet/TextExtendedPublicKeyImporter.jsx | 55 --------- .../Wallet/TextExtendedPublicKeyImporter.tsx | 108 ++++++++++++++++++ 5 files changed, 113 insertions(+), 57 deletions(-) delete mode 100644 apps/coordinator/src/components/Wallet/TextExtendedPublicKeyImporter.jsx create mode 100644 apps/coordinator/src/components/Wallet/TextExtendedPublicKeyImporter.tsx diff --git a/apps/coordinator/package.json b/apps/coordinator/package.json index 73eccd2d..ef8606b8 100644 --- a/apps/coordinator/package.json +++ b/apps/coordinator/package.json @@ -94,6 +94,7 @@ "not op_mini all" ], "dependencies": { + "@caravan/bip32": "*", "@caravan/bitcoin": "*", "@caravan/clients": "*", "@caravan/descriptors": "^0.1.1", diff --git a/apps/coordinator/src/components/Coldcard/ColdcardFileReader.jsx b/apps/coordinator/src/components/Coldcard/ColdcardFileReader.jsx index bb319821..16f2ad10 100644 --- a/apps/coordinator/src/components/Coldcard/ColdcardFileReader.jsx +++ b/apps/coordinator/src/components/Coldcard/ColdcardFileReader.jsx @@ -2,7 +2,7 @@ import React from "react"; import PropTypes from "prop-types"; import Dropzone from "react-dropzone"; -import { Buffer } from "buffer/"; +import { Buffer } from "buffer"; import { Box, Button, FormHelperText, Grid, TextField } from "@mui/material"; import { CloudUpload as UploadIcon } from "@mui/icons-material"; import { PSBT_MAGIC_HEX } from "@caravan/bitcoin"; diff --git a/apps/coordinator/src/components/Wallet/ExtendedPublicKeyImporter.jsx b/apps/coordinator/src/components/Wallet/ExtendedPublicKeyImporter.jsx index 7cbcb15b..04836c5b 100644 --- a/apps/coordinator/src/components/Wallet/ExtendedPublicKeyImporter.jsx +++ b/apps/coordinator/src/components/Wallet/ExtendedPublicKeyImporter.jsx @@ -89,7 +89,9 @@ class ExtendedPublicKeyImporter extends React.Component { Enter as text - {this.renderImportByMethod()} + + {this.renderImportByMethod()} + ); }; diff --git a/apps/coordinator/src/components/Wallet/TextExtendedPublicKeyImporter.jsx b/apps/coordinator/src/components/Wallet/TextExtendedPublicKeyImporter.jsx deleted file mode 100644 index 83ebd637..00000000 --- a/apps/coordinator/src/components/Wallet/TextExtendedPublicKeyImporter.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; - -// Components -import { TextField, Box } from "@mui/material"; - -class TextExtendedPublicKeyImporter extends React.Component { - constructor(props) { - super(props); - this.state = { - error: "", - }; - } - - render = () => { - const { extendedPublicKeyImporter } = this.props; - const { error } = this.state; - return ( - - - - ); - }; - - hasError = () => { - const { error } = this.state; - return error !== ""; - }; - - setError = (value) => { - this.setState({ error: value }); - }; - - handleChange = (event) => { - const { validateAndSetExtendedPublicKey } = this.props; - validateAndSetExtendedPublicKey(event.target.value, this.setError); - }; -} -TextExtendedPublicKeyImporter.propTypes = { - extendedPublicKeyImporter: PropTypes.shape({ - extendedPublicKey: PropTypes.string, - }).isRequired, - validateAndSetExtendedPublicKey: PropTypes.func.isRequired, -}; - -export default TextExtendedPublicKeyImporter; diff --git a/apps/coordinator/src/components/Wallet/TextExtendedPublicKeyImporter.tsx b/apps/coordinator/src/components/Wallet/TextExtendedPublicKeyImporter.tsx new file mode 100644 index 00000000..cafdf3d2 --- /dev/null +++ b/apps/coordinator/src/components/Wallet/TextExtendedPublicKeyImporter.tsx @@ -0,0 +1,108 @@ +import React, { useEffect, useState } from "react"; + +// Components +import { + TextField, + Box, + Button, + Grid, + Alert, + AlertTitle, + Typography, +} from "@mui/material"; +import { Network, validateExtendedPublicKey } from "@caravan/bitcoin"; +import { getBlindedXpub } from "@caravan/bip32"; +import Copyable from "../Copyable"; + +const TextExtendedPublicKeyImporter = ({ + validateAndSetExtendedPublicKey, + network, +}: { + validateAndSetExtendedPublicKey: ( + arg: string, + arg1: (e: string) => void, + ) => void; + network: Network; +}) => { + const [error, setError] = useState(""); + const [xpub, setXpub] = useState(""); + const [original, setOriginal] = useState(""); + const [blindedPath, setBlindedPath] = useState(""); + + const handleSubmit = () => { + validateAndSetExtendedPublicKey(xpub, setError); + }; + + const handleBlind = () => { + setOriginal(xpub); + const blinded = getBlindedXpub(xpub); + setXpub(blinded.xpub); + setBlindedPath(blinded.bip32Path); + }; + + useEffect(() => { + if (xpub) { + const error = validateExtendedPublicKey(xpub, network); + setError(error); + } + }, [xpub]); + + return ( + + setXpub(e.target.value)} + error={error !== ""} + helperText={error} + multiline + disabled={Boolean(original && blindedPath)} + /> + + + + + + + + + {blindedPath && original && ( + + + +

+ Blinded Info (IMPORTANT: Save this info) +

+
+ + + Without the full bip32 path, your funds will be irrecoverable + + . This notice will disappear once you hit "Enter". + + Blinded Path: + + Source Xpub: + + Blinded Xpub: + +
+
+ )} +
+ ); +}; + +export default TextExtendedPublicKeyImporter; From 74bd5a9ec043126baa9e62f5f819fe96909aed0d Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 5 Sep 2024 22:58:25 -0500 Subject: [PATCH 13/18] make new package publishable --- packages/bip32/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/bip32/package.json b/packages/bip32/package.json index beac89e4..224ac803 100644 --- a/packages/bip32/package.json +++ b/packages/bip32/package.json @@ -1,9 +1,8 @@ { "name": "@caravan/bip32", - "version": "1.0.0-beta", + "version": "1.0.0", "author": "unchained capital", "description": "Package for working with bip32 and derivative objects like extended public keys", - "private": true, "engines": { "node": ">=20" }, From 6723174ac248090e7194fa792612e1540a94fa39 Mon Sep 17 00:00:00 2001 From: buck Date: Thu, 5 Sep 2024 23:00:36 -0500 Subject: [PATCH 14/18] revert --- packages/caravan-psbt/package.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/caravan-psbt/package.json b/packages/caravan-psbt/package.json index c8fc5551..24f7e54d 100644 --- a/packages/caravan-psbt/package.json +++ b/packages/caravan-psbt/package.json @@ -2,19 +2,18 @@ "name": "@caravan/psbt", "version": "1.4.2", "description": "typescript library for working with PSBTs", - "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", "module": "./dist/index.mjs", "files": [ "./dist/index.js", - "./dist/index.cjs", + "./dist/index.mjs", "./dist/index.d.ts" ], "exports": { ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs", + "import": "./dist/index.mjs", + "require": "./dist/index.js", "types": "./dist/index.d.ts" } }, From 7faf1599e4b4c8e99b0fec48cfc59283e25fadee Mon Sep 17 00:00:00 2001 From: buck Date: Sun, 8 Sep 2024 13:53:41 -0500 Subject: [PATCH 15/18] feat: reused functionality extracted to a utility for getting relative child sequence --- packages/bip32/README.md | 5 +++ packages/bip32/src/__tests__/paths.test.ts | 22 +++++++++++- packages/bip32/src/keys.ts | 19 +++++------ packages/bip32/src/paths.ts | 39 +++++++++++++++++----- 4 files changed, 66 insertions(+), 19 deletions(-) diff --git a/packages/bip32/README.md b/packages/bip32/README.md index 695507f1..39d24da9 100644 --- a/packages/bip32/README.md +++ b/packages/bip32/README.md @@ -19,6 +19,11 @@ that can be used to derive the child pubkey from the global xpub. This is useful when you have a child xpub (e.g. a blinded xpub) derived from a masked xpub and you need to generate the full, unmasked path. +### `getRelativeBip32Sequence` + +A utility to use when you have a parent and child bip32 paths and want the "relative" +sequence (the path elements from the child that are added on to the parent). + ## Keys ### `getRandomChildXpub` diff --git a/packages/bip32/src/__tests__/paths.test.ts b/packages/bip32/src/__tests__/paths.test.ts index 40594b36..fd6d2dbe 100644 --- a/packages/bip32/src/__tests__/paths.test.ts +++ b/packages/bip32/src/__tests__/paths.test.ts @@ -1,6 +1,11 @@ import { Bip32Derivation } from "bip174/src/lib/interfaces"; -import { combineBip32Paths, getUnmaskedPath, secureSecretPath } from "../paths"; +import { + combineBip32Paths, + getRelativeBip32Sequence, + getUnmaskedPath, + secureSecretPath, +} from "../paths"; import { KeyOrigin } from "../types"; const globalOrigin: KeyOrigin = { @@ -97,3 +102,18 @@ describe("secureSecretPath", () => { expect(failures).toBe(2); }); }); + +describe("getRelativeBip32Sequence", () => { + it("should return the relative BIP32 sequence for a child key", () => { + const parentPath = "m/45'/0'/0'"; + const childPath = "m/45'/0'/0'/0/0"; + const relativeSequence = getRelativeBip32Sequence(parentPath, childPath); + expect(relativeSequence).toEqual([0, 0]); + }); + + it("should throw an error if the child key is longer than the parent key", () => { + const childPath = "m/45'/0'/0'"; + const parentPath = "m/45'/0'/0'/0/0/0"; + expect(() => getRelativeBip32Sequence(parentPath, childPath)).toThrow(); + }); +}); diff --git a/packages/bip32/src/keys.ts b/packages/bip32/src/keys.ts index 3a7dff83..c6096c11 100644 --- a/packages/bip32/src/keys.ts +++ b/packages/bip32/src/keys.ts @@ -9,7 +9,11 @@ import { } from "@caravan/bitcoin"; import { Bip32Derivation } from "bip174/src/lib/interfaces"; -import { combineBip32Paths, secureSecretPath } from "./paths"; +import { + combineBip32Paths, + getRelativeBip32Sequence, + secureSecretPath, +} from "./paths"; import { KeyOrigin } from "./types"; /** @@ -29,15 +33,10 @@ export const isValidChildPubKey = ( globalXpub: KeyOrigin, network: Network = Network.MAINNET, ): boolean => { - const globalSequence = bip32PathToSequence(globalXpub.bip32Path); - const derivationSequence = bip32PathToSequence(derivation.path); - - const difference = derivationSequence.length - globalSequence.length; - if (difference < 0) - throw new Error( - `Child key longer than parent: Parent: ${globalXpub.bip32Path}, Child: ${derivation.path}`, - ); - const lastElements = derivationSequence.slice(-difference); + const lastElements = getRelativeBip32Sequence( + globalXpub.bip32Path, + derivation.path, + ); const relativePath = bip32SequenceToPath(lastElements); const childPubkey = deriveChildPublicKey( diff --git a/packages/bip32/src/paths.ts b/packages/bip32/src/paths.ts index c4bbe765..33beb546 100644 --- a/packages/bip32/src/paths.ts +++ b/packages/bip32/src/paths.ts @@ -88,6 +88,33 @@ export const combineBip32Paths = ( return combined; }; +/** + * A utility function to get the relative BIP32 sequence of a child key + * Given two paths, we want the path difference from the child. + * + * @example + * getRelativeBip32Sequence("m/45'/0'/0'", "m/45'/0'/0'/0/0") + * // returns [0, 0] + * @param parentPath The path of the parent key from which the child should be derived + * @param childPath The full path of the child derived from the parent + * @returns + */ +export const getRelativeBip32Sequence = ( + parentPath: string, + childPath: string, +) => { + const parentSequence = bip32PathToSequence(parentPath); + const childSequence = bip32PathToSequence(childPath); + + const difference = childSequence.length - parentSequence.length; + if (difference < 0) { + throw new Error( + `Child key longer than parent: Parent: ${parentPath}, Child: ${childPath}`, + ); + } + return childSequence.slice(-difference); +}; + /** * Given a derivation and a global xpub, return the unmasked path * that can be used to derive the child pubkey from the global xpub. @@ -102,14 +129,10 @@ export const getUnmaskedPath = ( globalXpub: KeyOrigin, ): string => { const globalSequence = bip32PathToSequence(globalXpub.bip32Path); - const derivationSequence = bip32PathToSequence(derivation.path); - - const difference = derivationSequence.length - globalSequence.length; - if (difference < 0) - throw new Error( - `Child key longer than parent: Parent: ${globalXpub.bip32Path}, Child: ${derivation.path}`, - ); - const lastElements = derivationSequence.slice(-difference); + const lastElements = getRelativeBip32Sequence( + globalXpub.bip32Path, + derivation.path, + ); return bip32SequenceToPath(globalSequence.concat(lastElements)); }; From d37724000546710904838f30ecf841873da752e7 Mon Sep 17 00:00:00 2001 From: buck Date: Sun, 8 Sep 2024 13:53:47 -0500 Subject: [PATCH 16/18] nits --- .../src/components/Wallet/TextExtendedPublicKeyImporter.tsx | 4 ++-- package-lock.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/coordinator/src/components/Wallet/TextExtendedPublicKeyImporter.tsx b/apps/coordinator/src/components/Wallet/TextExtendedPublicKeyImporter.tsx index cafdf3d2..46208e35 100644 --- a/apps/coordinator/src/components/Wallet/TextExtendedPublicKeyImporter.tsx +++ b/apps/coordinator/src/components/Wallet/TextExtendedPublicKeyImporter.tsx @@ -19,8 +19,8 @@ const TextExtendedPublicKeyImporter = ({ network, }: { validateAndSetExtendedPublicKey: ( - arg: string, - arg1: (e: string) => void, + extendedPublicKey: string, + errCb: (e: string) => void, ) => void; network: Network; }) => { diff --git a/package-lock.json b/package-lock.json index ba2e6be8..987a5a53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24278,7 +24278,7 @@ }, "packages/bip32": { "name": "@caravan/bip32", - "version": "1.0.0-beta", + "version": "1.0.0", "license": "MIT", "dependencies": { "@caravan/bitcoin": "*", From 632df87fc16da2f348c85d9003e1910a86297964 Mon Sep 17 00:00:00 2001 From: buck Date: Sun, 8 Sep 2024 13:56:46 -0500 Subject: [PATCH 17/18] lint fixes --- packages/bip32/src/__tests__/paths.test.ts | 4 ++-- packages/bip32/src/keys.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/bip32/src/__tests__/paths.test.ts b/packages/bip32/src/__tests__/paths.test.ts index fd6d2dbe..0d898723 100644 --- a/packages/bip32/src/__tests__/paths.test.ts +++ b/packages/bip32/src/__tests__/paths.test.ts @@ -91,12 +91,12 @@ describe("secureSecretPath", () => { let failures = 0; try { await secureSecretPath(32); - } catch (e: any) { + } catch (e: unknown) { failures += 1; } try { await secureSecretPath(0); - } catch (e: any) { + } catch (e: unknown) { failures += 1; } expect(failures).toBe(2); diff --git a/packages/bip32/src/keys.ts b/packages/bip32/src/keys.ts index c6096c11..6b0e35da 100644 --- a/packages/bip32/src/keys.ts +++ b/packages/bip32/src/keys.ts @@ -1,7 +1,6 @@ import { ExtendedPublicKey, Network, - bip32PathToSequence, bip32SequenceToPath, deriveChildExtendedPublicKey, deriveChildPublicKey, From 6eff96c1676b90dde92763a4bb09c749f3225343 Mon Sep 17 00:00:00 2001 From: buck Date: Mon, 9 Sep 2024 11:18:27 -0500 Subject: [PATCH 18/18] d'oh --- packages/bip32/src/paths.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bip32/src/paths.ts b/packages/bip32/src/paths.ts index 33beb546..10f448c2 100644 --- a/packages/bip32/src/paths.ts +++ b/packages/bip32/src/paths.ts @@ -109,7 +109,7 @@ export const getRelativeBip32Sequence = ( const difference = childSequence.length - parentSequence.length; if (difference < 0) { throw new Error( - `Child key longer than parent: Parent: ${parentPath}, Child: ${childPath}`, + `Child key shorter than parent: Parent: ${parentPath}, Child: ${childPath}`, ); } return childSequence.slice(-difference);