chore: upgrade applesauce packages to v4.0.0

- Update all applesauce packages from 3.1.0 to 4.0.0
- Add applesauce-factory dependency
- Version 4.0.0 includes app-data helpers needed for NIP-78
This commit is contained in:
Gigi
2025-10-05 02:35:28 +01:00
parent 1b381a0f8c
commit 1b8c276529
229 changed files with 2533 additions and 7592 deletions

407
node_modules/.package-lock.json generated vendored
View File

@@ -1,6 +1,6 @@
{
"name": "markr",
"version": "0.1.1",
"name": "boris",
"version": "0.1.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
@@ -320,32 +320,6 @@
"node": ">=22.4.0"
}
},
"node_modules/@cashu/crypto": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@cashu/crypto/-/crypto-0.2.7.tgz",
"integrity": "sha512-1aaDfUjiHNXoJqg8nW+341TLWV9W28DsVNXJUKcHL0yAmwLs5+56SSnb8LLDJzPamLVoYL0U0bda91klAzptig==",
"license": "MIT",
"dependencies": {
"@noble/curves": "^1.3.0",
"@noble/hashes": "^1.3.3",
"@scure/bip32": "^1.3.3",
"@scure/bip39": "^1.2.2",
"buffer": "^6.0.3"
}
},
"node_modules/@cashu/crypto/node_modules/@scure/bip39": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz",
"integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "~1.8.0",
"@scure/base": "~1.2.5"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@@ -1914,16 +1888,16 @@
}
},
"node_modules/applesauce-accounts": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-accounts/-/applesauce-accounts-3.1.0.tgz",
"integrity": "sha512-F0xFi4CU5Ak917VcUap+6YIxyvbjP2BJejWBjVurFMY6tCK3kWWPQnoPc+GjU6LkMsL8BfQcOQ6wKIX4MvTB7Q==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-accounts/-/applesauce-accounts-4.0.0.tgz",
"integrity": "sha512-JHv63cRSSWXqbId6anNfOlPCE+sr3zJeHT6Jt9p+3X+hZkHrlNy5sLBK+fBHvKc5w/eQFsmnIWYfuLxPdqZcrA==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "^1.7.1",
"applesauce-core": "^3.1.0",
"applesauce-signers": "^3.1.0",
"applesauce-core": "^4.0.0",
"applesauce-signers": "^4.0.0",
"nanoid": "^5.1.5",
"nostr-tools": "~2.15",
"nostr-tools": "~2.17",
"rxjs": "^7.8.1"
},
"funding": {
@@ -1932,14 +1906,14 @@
}
},
"node_modules/applesauce-actions": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-actions/-/applesauce-actions-3.1.0.tgz",
"integrity": "sha512-wNZQr1qAmlQCjOQQXK0sHyNtcEbYbcUmA97bYg4KctSwlEQAI14fUTltYHdLZ3Zw4xJ9F0Xq4O2YCUJA4UOYlA==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-actions/-/applesauce-actions-4.0.0.tgz",
"integrity": "sha512-oYAjrazKGDINeVwypNDnV9eNSv7ZDTjNeV3azo5jeUU1haEQ0t+zwVWzGxk9/VutT1yWQHFsCZBInYZIegfLhQ==",
"license": "MIT",
"dependencies": {
"applesauce-core": "^3.1.0",
"applesauce-factory": "^3.1.0",
"nostr-tools": "~2.15",
"applesauce-core": "^4.0.0",
"applesauce-factory": "^4.0.0",
"nostr-tools": "~2.17",
"rxjs": "^7.8.1"
},
"funding": {
@@ -1970,90 +1944,7 @@
"url": "lightning:nostrudel@geyser.fund"
}
},
"node_modules/applesauce-content/node_modules/@noble/curves": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.3.2"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/applesauce-content/node_modules/@noble/curves/node_modules/@noble/hashes": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/applesauce-content/node_modules/@scure/bip32": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
"license": "MIT",
"dependencies": {
"@noble/curves": "~1.1.0",
"@noble/hashes": "~1.3.1",
"@scure/base": "~1.1.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/applesauce-content/node_modules/@scure/bip32/node_modules/@noble/curves": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.3.1"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/applesauce-content/node_modules/@scure/bip32/node_modules/@noble/curves/node_modules/@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/applesauce-content/node_modules/@scure/bip32/node_modules/@noble/hashes": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
"integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/applesauce-content/node_modules/@scure/bip32/node_modules/@scure/base": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz",
"integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==",
"license": "MIT",
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/applesauce-content/node_modules/applesauce-core": {
"node_modules/applesauce-core": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-core/-/applesauce-core-4.0.0.tgz",
"integrity": "sha512-cXg9lDU0PKpAeVw7FGN3jzKB/k9kX5YOI7uwzrZhibYB5PfpHAYRiVexxBuFdT2RNaDgQogXl75gV2hag5uLuw==",
@@ -2074,82 +1965,14 @@
"url": "lightning:nostrudel@geyser.fund"
}
},
"node_modules/applesauce-content/node_modules/nostr-tools": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.17.0.tgz",
"integrity": "sha512-lrvHM7cSaGhz7F0YuBvgHMoU2s8/KuThihDoOYk8w5gpVHTy0DeUCAgCN8uLGeuSl5MAWekJr9Dkfo5HClqO9w==",
"license": "Unlicense",
"dependencies": {
"@noble/ciphers": "^0.5.1",
"@noble/curves": "1.2.0",
"@noble/hashes": "1.3.1",
"@scure/base": "1.1.1",
"@scure/bip32": "1.3.1",
"@scure/bip39": "1.2.1",
"nostr-wasm": "0.1.0"
},
"peerDependencies": {
"typescript": ">=5.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/applesauce-content/node_modules/nostr-tools/node_modules/@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/applesauce-content/node_modules/nostr-tools/node_modules/@scure/base": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"license": "MIT"
},
"node_modules/applesauce-core": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-core/-/applesauce-core-3.1.0.tgz",
"integrity": "sha512-rIvtAYm8jJiLkv251yT12olmlmlkeT5x9kptWlAz0wMiAhymGG/RoWtMN80mbOAebjwcLCRLRfrAO6YYal1XpQ==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "^1.7.1",
"@scure/base": "^1.2.4",
"debug": "^4.4.0",
"fast-deep-equal": "^3.1.3",
"hash-sum": "^2.0.0",
"light-bolt11-decoder": "^3.2.0",
"nanoid": "^5.0.9",
"nostr-tools": "~2.15",
"rxjs": "^7.8.1"
},
"funding": {
"type": "lightning",
"url": "lightning:nostrudel@geyser.fund"
}
},
"node_modules/applesauce-factory": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-factory/-/applesauce-factory-3.1.0.tgz",
"integrity": "sha512-K9onWy8yvWnQp2c+a227IFHv65ToDH4B4yoLEOuLs2xOp7BwtZCoIdVZbWVk93X1KvA7pBTtXCjDbwTcH7LCjQ==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-factory/-/applesauce-factory-4.0.0.tgz",
"integrity": "sha512-Sqsg+bC7CkRXMxXLkO6YGoKxy/Aqtia9YenasS5qjPOQFmyFMwKRxaHCu6vX6KdpNSABusw0b9Tnn4gTh6CxLw==",
"license": "MIT",
"dependencies": {
"applesauce-content": "^3.1.0",
"applesauce-core": "^3.1.0",
"applesauce-content": "^4.0.0",
"applesauce-core": "^4.0.0",
"nanoid": "^5.0.9",
"nostr-tools": "^2.13"
},
@@ -2158,51 +1981,15 @@
"url": "lightning:nostrudel@geyser.fund"
}
},
"node_modules/applesauce-factory/node_modules/@cashu/cashu-ts": {
"version": "2.0.0-rc1",
"resolved": "https://registry.npmjs.org/@cashu/cashu-ts/-/cashu-ts-2.0.0-rc1.tgz",
"integrity": "sha512-39459l7x/fUMEgOsCdGLLl6rMekO4nbv+wEuavmyElh8hgN8t66wcb29AJvdFTb6K3lPACKF2rs/jAlPYrN7Ng==",
"license": "MIT",
"dependencies": {
"@cashu/crypto": "^0.2.7",
"@noble/curves": "^1.3.0",
"@noble/hashes": "^1.3.3",
"@scure/bip32": "^1.3.3",
"buffer": "^6.0.3"
}
},
"node_modules/applesauce-factory/node_modules/applesauce-content": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-content/-/applesauce-content-3.1.0.tgz",
"integrity": "sha512-dxXmEzMz5KQIdaKOVJg2ufphVPoECWa6l7NIQo5mXQGrjv3VrT5QY5x0MVWJWWcC4fRBwE8xIhJyfhIeosymMQ==",
"license": "MIT",
"dependencies": {
"@cashu/cashu-ts": "2.0.0-rc1",
"@types/hast": "^3.0.4",
"@types/mdast": "^4.0.4",
"@types/unist": "^3.0.3",
"applesauce-core": "^3.1.0",
"mdast-util-find-and-replace": "^3.0.2",
"nostr-tools": "~2.15",
"remark": "^15.0.1",
"remark-parse": "^11.0.0",
"unified": "^11.0.5",
"unist-util-visit-parents": "^6.0.1"
},
"funding": {
"type": "lightning",
"url": "lightning:nostrudel@geyser.fund"
}
},
"node_modules/applesauce-loaders": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-loaders/-/applesauce-loaders-3.1.0.tgz",
"integrity": "sha512-9IY5RYqoXcIgAAdJNuMjMBr+CI85z1yj708C92UiP9YMQ4mrIIEvZbMmWLApBzRn5XsmmEa00a/iNlXpkRg9Sw==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-loaders/-/applesauce-loaders-4.0.0.tgz",
"integrity": "sha512-yNlpzCeUlkpq1jehyHPre0C3ey9anMDTpPysqdV9Rca+U2QnAWduzQ+Eo8FVS1X9jZRD6s/gkLSnCnW1+kX7uQ==",
"license": "MIT",
"dependencies": {
"applesauce-core": "^3.1.0",
"applesauce-core": "^4.0.0",
"nanoid": "^5.0.9",
"nostr-tools": "~2.15",
"nostr-tools": "~2.17",
"rxjs": "^7.8.1"
},
"funding": {
@@ -2211,18 +1998,18 @@
}
},
"node_modules/applesauce-react": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-react/-/applesauce-react-3.1.0.tgz",
"integrity": "sha512-9zKbHOkXTGBLRe2uZvwAyPLTVPfelQQ7BvVTYHb2hg1JbhphMQqDSas3kaHBltt9ZCVh0xdv8JupmKWTUeCzag==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-react/-/applesauce-react-4.0.0.tgz",
"integrity": "sha512-eVDUf3GL1j4bsL1Y8GsC/2sywajLu1oJioCNajUsm68hf5+zIR0rLHWaA4y0o5Rcctf/O4UbYkFztj1XHcuHgg==",
"license": "MIT",
"dependencies": {
"applesauce-accounts": "^3.1.0",
"applesauce-actions": "^3.1.0",
"applesauce-content": "^3.1.0",
"applesauce-core": "^3.1.0",
"applesauce-factory": "^3.1.0",
"applesauce-accounts": "^4.0.0",
"applesauce-actions": "^4.0.0",
"applesauce-content": "^4.0.0",
"applesauce-core": "^4.0.0",
"applesauce-factory": "^4.0.0",
"hash-sum": "^2.0.0",
"nostr-tools": "~2.15",
"nostr-tools": "~2.17",
"observable-hooks": "^4.2.4",
"react": "^18.3.1",
"rxjs": "^7.8.1"
@@ -2232,52 +2019,16 @@
"url": "lightning:nostrudel@geyser.fund"
}
},
"node_modules/applesauce-react/node_modules/@cashu/cashu-ts": {
"version": "2.0.0-rc1",
"resolved": "https://registry.npmjs.org/@cashu/cashu-ts/-/cashu-ts-2.0.0-rc1.tgz",
"integrity": "sha512-39459l7x/fUMEgOsCdGLLl6rMekO4nbv+wEuavmyElh8hgN8t66wcb29AJvdFTb6K3lPACKF2rs/jAlPYrN7Ng==",
"license": "MIT",
"dependencies": {
"@cashu/crypto": "^0.2.7",
"@noble/curves": "^1.3.0",
"@noble/hashes": "^1.3.3",
"@scure/bip32": "^1.3.3",
"buffer": "^6.0.3"
}
},
"node_modules/applesauce-react/node_modules/applesauce-content": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-content/-/applesauce-content-3.1.0.tgz",
"integrity": "sha512-dxXmEzMz5KQIdaKOVJg2ufphVPoECWa6l7NIQo5mXQGrjv3VrT5QY5x0MVWJWWcC4fRBwE8xIhJyfhIeosymMQ==",
"license": "MIT",
"dependencies": {
"@cashu/cashu-ts": "2.0.0-rc1",
"@types/hast": "^3.0.4",
"@types/mdast": "^4.0.4",
"@types/unist": "^3.0.3",
"applesauce-core": "^3.1.0",
"mdast-util-find-and-replace": "^3.0.2",
"nostr-tools": "~2.15",
"remark": "^15.0.1",
"remark-parse": "^11.0.0",
"unified": "^11.0.5",
"unist-util-visit-parents": "^6.0.1"
},
"funding": {
"type": "lightning",
"url": "lightning:nostrudel@geyser.fund"
}
},
"node_modules/applesauce-relay": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-relay/-/applesauce-relay-3.1.0.tgz",
"integrity": "sha512-YseV51O3pc9IIX9MoP4XrVmkUq6a0u8h7n/B4zg0Y3bCQEs415LbM3UwIwZzF1DAEnphOu2xXgkH/9QCg5HvFg==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-relay/-/applesauce-relay-4.0.0.tgz",
"integrity": "sha512-qoWjh9dABdL7AuSe4cmKiyZhNvrVZBRXA1GQgWiKSynm+rNYP+6Rc4SDT3vndNMqx9jRKbL4jwvKeU5vkVpFRA==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "^1.7.1",
"applesauce-core": "^3.1.0",
"applesauce-core": "^4.0.0",
"nanoid": "^5.0.9",
"nostr-tools": "~2.15",
"nostr-tools": "~2.17",
"rxjs": "^7.8.1"
},
"funding": {
@@ -2286,18 +2037,18 @@
}
},
"node_modules/applesauce-signers": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/applesauce-signers/-/applesauce-signers-3.1.0.tgz",
"integrity": "sha512-7loxZ3hKSAhDRDs9rtVP/MPBZRmSbutE7g8/ALnXe5ihytbKSdF+0L5NBd0fxpSLMvmd8wEFOha6LsT03I5RCQ==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-signers/-/applesauce-signers-4.0.0.tgz",
"integrity": "sha512-AHrPtH1Oy0l1OS7jwd/DfseUpOhTE8JhZIUIElcQgAlqE7Cgg5FO+LF7dWRPUNb3zn8P+mzEQN2wJhby84SNcA==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "^1.7.1",
"@noble/secp256k1": "^1.7.1",
"@scure/base": "^1.2.4",
"applesauce-core": "^3.1.0",
"applesauce-core": "^4.0.0",
"debug": "^4.4.0",
"nanoid": "^5.0.9",
"nostr-tools": "~2.15",
"nostr-tools": "~2.17",
"rxjs": "^7.8.2"
},
"funding": {
@@ -2339,26 +2090,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
"version": "2.8.10",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz",
@@ -2426,30 +2157,6 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -3317,26 +3024,6 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "BSD-3-Clause"
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -4605,9 +4292,9 @@
"license": "MIT"
},
"node_modules/nostr-tools": {
"version": "2.15.2",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.15.2.tgz",
"integrity": "sha512-utmqVVS4HMDiwhIgI6Cr+KqA4aUhF3Sb755iO/qCiqxc5H9JW/9Z3N1RO/jKWpjP6q/Vx0lru7IYuiPvk+2/ng==",
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.17.0.tgz",
"integrity": "sha512-lrvHM7cSaGhz7F0YuBvgHMoU2s8/KuThihDoOYk8w5gpVHTy0DeUCAgCN8uLGeuSl5MAWekJr9Dkfo5HClqO9w==",
"license": "Unlicense",
"dependencies": {
"@noble/ciphers": "^0.5.1",

21
node_modules/@cashu/crypto/LICENSE generated vendored
View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 bitcoinjs contributors, gandlaf21
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

12
node_modules/@cashu/crypto/README.md generated vendored
View File

@@ -1,12 +0,0 @@
# Cashu Crypto
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/gandlafbtc/cashu-crypto-ts/node.js.yml)
![GitHub issues](https://img.shields.io/github/issues/gandlafbtc/cashu-crypto-ts)
![GitHub package.json version](https://img.shields.io/github/package-json/v/gandlafbtc/cashu-crypto-ts)
![npm](https://img.shields.io/npm/v/@gandlaf21/cashu-crypto)
![npm type definitions](https://img.shields.io/npm/types/@gandlaf21/cashu-crypto)
![npm bundle size](https://img.shields.io/bundlephobia/min/@gandlaf21/cashu-crypto)
[code coverage](https://gandlafbtc.github.io/cashu-crypto-ts/coverage)
Basic crypto operations for cashu wallets and mints written in TypeScript

View File

@@ -1,5 +0,0 @@
export declare const deriveSecret: (seed: Uint8Array, keysetId: string, counter: number) => Uint8Array;
export declare const deriveBlindingFactor: (seed: Uint8Array, keysetId: string, counter: number) => Uint8Array;
export declare const generateNewMnemonic: () => string;
export declare const deriveSeedFromMnemonic: (mnemonic: string) => Uint8Array;
//# sourceMappingURL=NUT09.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"NUT09.d.ts","sourceRoot":"","sources":["../../src/client/NUT09.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,YAAY,SAAU,UAAU,YAAY,MAAM,WAAW,MAAM,KAAG,UAElF,CAAC;AAEF,eAAO,MAAM,oBAAoB,SAC1B,UAAU,YACN,MAAM,WACP,MAAM,KACb,UAEF,CAAC;AAkBF,eAAO,MAAM,mBAAmB,QAAO,MAGtC,CAAC;AAEF,eAAO,MAAM,sBAAsB,aAAc,MAAM,KAAG,UAGzD,CAAC"}

View File

@@ -1,42 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deriveSeedFromMnemonic = exports.generateNewMnemonic = exports.deriveBlindingFactor = exports.deriveSecret = void 0;
const bip32_1 = require("@scure/bip32");
const index_js_1 = require("../common/index.js");
const bip39_1 = require("@scure/bip39");
const english_1 = require("@scure/bip39/wordlists/english");
const STANDARD_DERIVATION_PATH = `m/129372'/0'`;
var DerivationType;
(function (DerivationType) {
DerivationType[DerivationType["SECRET"] = 0] = "SECRET";
DerivationType[DerivationType["BLINDING_FACTOR"] = 1] = "BLINDING_FACTOR";
})(DerivationType || (DerivationType = {}));
const deriveSecret = (seed, keysetId, counter) => {
return derive(seed, keysetId, counter, DerivationType.SECRET);
};
exports.deriveSecret = deriveSecret;
const deriveBlindingFactor = (seed, keysetId, counter) => {
return derive(seed, keysetId, counter, DerivationType.BLINDING_FACTOR);
};
exports.deriveBlindingFactor = deriveBlindingFactor;
const derive = (seed, keysetId, counter, secretOrBlinding) => {
const hdkey = bip32_1.HDKey.fromMasterSeed(seed);
const keysetIdInt = (0, index_js_1.getKeysetIdInt)(keysetId);
const derivationPath = `${STANDARD_DERIVATION_PATH}/${keysetIdInt}'/${counter}'/${secretOrBlinding}`;
const derived = hdkey.derive(derivationPath);
if (derived.privateKey === null) {
throw new Error('Could not derive private key');
}
return derived.privateKey;
};
const generateNewMnemonic = () => {
const mnemonic = (0, bip39_1.generateMnemonic)(english_1.wordlist, 128);
return mnemonic;
};
exports.generateNewMnemonic = generateNewMnemonic;
const deriveSeedFromMnemonic = (mnemonic) => {
const seed = (0, bip39_1.mnemonicToSeedSync)(mnemonic);
return seed;
};
exports.deriveSeedFromMnemonic = deriveSeedFromMnemonic;
//# sourceMappingURL=NUT09.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"NUT09.js","sourceRoot":"","sources":["../../src/client/NUT09.ts"],"names":[],"mappings":";;;AAAA,wCAAqC;AACrC,iDAAoD;AACpD,wCAAoE;AACpE,4DAA0D;AAE1D,MAAM,wBAAwB,GAAG,cAAc,CAAC;AAEhD,IAAK,cAGJ;AAHD,WAAK,cAAc;IAClB,uDAAU,CAAA;IACV,yEAAmB,CAAA;AACpB,CAAC,EAHI,cAAc,KAAd,cAAc,QAGlB;AAEM,MAAM,YAAY,GAAG,CAAC,IAAgB,EAAE,QAAgB,EAAE,OAAe,EAAc,EAAE;IAC/F,OAAO,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;AAC/D,CAAC,CAAC;AAFW,QAAA,YAAY,gBAEvB;AAEK,MAAM,oBAAoB,GAAG,CACnC,IAAgB,EAChB,QAAgB,EAChB,OAAe,EACF,EAAE;IACf,OAAO,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,CAAC,eAAe,CAAC,CAAC;AACxE,CAAC,CAAC;AANW,QAAA,oBAAoB,wBAM/B;AAEF,MAAM,MAAM,GAAG,CACd,IAAgB,EAChB,QAAgB,EAChB,OAAe,EACf,gBAAgC,EACnB,EAAE;IACf,MAAM,KAAK,GAAG,aAAK,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,IAAA,yBAAc,EAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,cAAc,GAAG,GAAG,wBAAwB,IAAI,WAAW,KAAK,OAAO,KAAK,gBAAgB,EAAE,CAAC;IACrG,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAC7C,IAAI,OAAO,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,OAAO,CAAC,UAAU,CAAC;AAC3B,CAAC,CAAC;AAEK,MAAM,mBAAmB,GAAG,GAAW,EAAE;IAC/C,MAAM,QAAQ,GAAG,IAAA,wBAAgB,EAAC,kBAAQ,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,QAAQ,CAAC;AACjB,CAAC,CAAC;AAHW,QAAA,mBAAmB,uBAG9B;AAEK,MAAM,sBAAsB,GAAG,CAAC,QAAgB,EAAc,EAAE;IACtE,MAAM,IAAI,GAAG,IAAA,0BAAkB,EAAC,QAAQ,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC;AACb,CAAC,CAAC;AAHW,QAAA,sBAAsB,0BAGjC"}

View File

@@ -1,7 +0,0 @@
import { PrivKey } from '@noble/curves/abstract/utils';
import { Proof } from '../common/index.js';
export declare const createP2PKsecret: (pubkey: string) => Uint8Array;
export declare const signP2PKsecret: (secret: Uint8Array, privateKey: PrivKey) => Uint8Array;
export declare const getSignedProofs: (proofs: Array<Proof>, privateKey: string) => Array<Proof>;
export declare const getSignedProof: (proof: Proof, privateKey: PrivKey) => Proof;
//# sourceMappingURL=NUT11.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"NUT11.d.ts","sourceRoot":"","sources":["../../src/client/NUT11.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAA0B,MAAM,8BAA8B,CAAC;AAK/E,OAAO,EAAE,KAAK,EAAU,MAAM,oBAAoB,CAAC;AAEnD,eAAO,MAAM,gBAAgB,WAAY,MAAM,KAAG,UAUjD,CAAC;AAEF,eAAO,MAAM,cAAc,WAAY,UAAU,cAAc,OAAO,eAIrE,CAAC;AAEF,eAAO,MAAM,eAAe,WAAY,MAAM,KAAK,CAAC,cAAc,MAAM,KAAG,MAAM,KAAK,CAYrF,CAAC;AAEF,eAAO,MAAM,cAAc,UAAW,KAAK,cAAc,OAAO,KAAG,KAOlE,CAAC"}

View File

@@ -1,51 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSignedProof = exports.getSignedProofs = exports.signP2PKsecret = exports.createP2PKsecret = void 0;
const utils_1 = require("@noble/curves/abstract/utils");
const sha256_1 = require("@noble/hashes/sha256");
const secp256k1_1 = require("@noble/curves/secp256k1");
const utils_2 = require("@noble/hashes/utils");
const NUT11_js_1 = require("../common/NUT11.js");
const createP2PKsecret = (pubkey) => {
const newSecret = [
'P2PK',
{
nonce: (0, utils_1.bytesToHex)((0, utils_2.randomBytes)(32)),
data: pubkey
}
];
const parsed = JSON.stringify(newSecret);
return new TextEncoder().encode(parsed);
};
exports.createP2PKsecret = createP2PKsecret;
const signP2PKsecret = (secret, privateKey) => {
const msghash = (0, sha256_1.sha256)(new TextDecoder().decode(secret));
const sig = secp256k1_1.schnorr.sign(msghash, privateKey);
return sig;
};
exports.signP2PKsecret = signP2PKsecret;
const getSignedProofs = (proofs, privateKey) => {
return proofs.map((p) => {
try {
const parsed = (0, NUT11_js_1.parseSecret)(p.secret);
if (parsed[0] !== 'P2PK') {
throw new Error('unknown secret type');
}
return (0, exports.getSignedProof)(p, (0, utils_1.hexToBytes)(privateKey));
}
catch (error) {
return p;
}
});
};
exports.getSignedProofs = getSignedProofs;
const getSignedProof = (proof, privateKey) => {
if (!proof.witness) {
proof.witness = {
signatures: [(0, utils_1.bytesToHex)((0, exports.signP2PKsecret)(proof.secret, privateKey))]
};
}
return proof;
};
exports.getSignedProof = getSignedProof;
//# sourceMappingURL=NUT11.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"NUT11.js","sourceRoot":"","sources":["../../src/client/NUT11.ts"],"names":[],"mappings":";;;AAAA,wDAA+E;AAC/E,iDAA8C;AAC9C,uDAAkD;AAClD,+CAAkD;AAClD,iDAAiD;AAG1C,MAAM,gBAAgB,GAAG,CAAC,MAAc,EAAc,EAAE;IAC9D,MAAM,SAAS,GAAW;QACzB,MAAM;QACN;YACC,KAAK,EAAE,IAAA,kBAAU,EAAC,IAAA,mBAAW,EAAC,EAAE,CAAC,CAAC;YAClC,IAAI,EAAE,MAAM;SACZ;KACD,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACzC,CAAC,CAAC;AAVW,QAAA,gBAAgB,oBAU3B;AAEK,MAAM,cAAc,GAAG,CAAC,MAAkB,EAAE,UAAmB,EAAE,EAAE;IACzE,MAAM,OAAO,GAAG,IAAA,eAAM,EAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,mBAAO,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC9C,OAAO,GAAG,CAAC;AACZ,CAAC,CAAC;AAJW,QAAA,cAAc,kBAIzB;AAEK,MAAM,eAAe,GAAG,CAAC,MAAoB,EAAE,UAAkB,EAAgB,EAAE;IACzF,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACvB,IAAI,CAAC;YACJ,MAAM,MAAM,GAAW,IAAA,sBAAW,EAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACxC,CAAC;YACD,OAAO,IAAA,sBAAc,EAAC,CAAC,EAAE,IAAA,kBAAU,EAAC,UAAU,CAAC,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,CAAC;QACV,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC;AAZW,QAAA,eAAe,mBAY1B;AAEK,MAAM,cAAc,GAAG,CAAC,KAAY,EAAE,UAAmB,EAAS,EAAE;IAC1E,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACpB,KAAK,CAAC,OAAO,GAAG;YACf,UAAU,EAAE,CAAC,IAAA,kBAAU,EAAC,IAAA,sBAAc,EAAC,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;SAClE,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC,CAAC;AAPW,QAAA,cAAc,kBAOzB"}

View File

@@ -1,15 +0,0 @@
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
import type { BlindSignature, Proof, SerializedBlindedMessage, SerializedProof } from '../common/index.js';
export type BlindedMessage = {
B_: ProjPointType<bigint>;
r: bigint;
secret: Uint8Array;
};
export declare function createRandomBlindedMessage(): BlindedMessage;
export declare function blindMessage(secret: Uint8Array, r?: bigint): BlindedMessage;
export declare function unblindSignature(C_: ProjPointType<bigint>, r: bigint, A: ProjPointType<bigint>): ProjPointType<bigint>;
export declare function constructProofFromPromise(promise: BlindSignature, r: bigint, secret: Uint8Array, key: ProjPointType<bigint>): Proof;
export declare const serializeProof: (proof: Proof) => SerializedProof;
export declare const deserializeProof: (proof: SerializedProof) => Proof;
export declare const serializeBlindedMessage: (bm: BlindedMessage, amount: number) => SerializedBlindedMessage;
//# sourceMappingURL=index.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAInE,OAAO,KAAK,EACX,cAAc,EACd,KAAK,EACL,wBAAwB,EACxB,eAAe,EACf,MAAM,oBAAoB,CAAC;AAI5B,MAAM,MAAM,cAAc,GAAG;IAC5B,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,UAAU,CAAC;CACnB,CAAC;AAEF,wBAAgB,0BAA0B,IAAI,cAAc,CAE3D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,cAAc,CAQ3E;AAED,wBAAgB,gBAAgB,CAC/B,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,EACzB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GACtB,aAAa,CAAC,MAAM,CAAC,CAGvB;AAED,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,cAAc,EACvB,CAAC,EAAE,MAAM,EACT,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,GACxB,KAAK,CAUP;AAED,eAAO,MAAM,cAAc,UAAW,KAAK,KAAG,eAQ7C,CAAC;AAEF,eAAO,MAAM,gBAAgB,UAAW,eAAe,KAAG,KAQzD,CAAC;AACF,eAAO,MAAM,uBAAuB,OAC/B,cAAc,UACV,MAAM,KACZ,wBAKF,CAAC"}

View File

@@ -1,66 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serializeBlindedMessage = exports.deserializeProof = exports.serializeProof = exports.constructProofFromPromise = exports.unblindSignature = exports.blindMessage = exports.createRandomBlindedMessage = void 0;
const secp256k1_1 = require("@noble/curves/secp256k1");
const utils_1 = require("@noble/hashes/utils");
const utils_js_1 = require("../util/utils.js");
const index_js_1 = require("../common/index.js");
function createRandomBlindedMessage() {
return blindMessage((0, utils_1.randomBytes)(32));
}
exports.createRandomBlindedMessage = createRandomBlindedMessage;
function blindMessage(secret, r) {
const Y = (0, index_js_1.hashToCurve)(secret);
if (!r) {
r = (0, utils_js_1.bytesToNumber)(secp256k1_1.secp256k1.utils.randomPrivateKey());
}
const rG = secp256k1_1.secp256k1.ProjectivePoint.BASE.multiply(r);
const B_ = Y.add(rG);
return { B_, r, secret };
}
exports.blindMessage = blindMessage;
function unblindSignature(C_, r, A) {
const C = C_.subtract(A.multiply(r));
return C;
}
exports.unblindSignature = unblindSignature;
function constructProofFromPromise(promise, r, secret, key) {
const A = key;
const C = unblindSignature(promise.C_, r, A);
const proof = {
id: promise.id,
amount: promise.amount,
secret,
C
};
return proof;
}
exports.constructProofFromPromise = constructProofFromPromise;
const serializeProof = (proof) => {
return {
amount: proof.amount,
C: proof.C.toHex(true),
id: proof.id,
secret: new TextDecoder().decode(proof.secret),
witness: JSON.stringify(proof.witness)
};
};
exports.serializeProof = serializeProof;
const deserializeProof = (proof) => {
return {
amount: proof.amount,
C: (0, index_js_1.pointFromHex)(proof.C),
id: proof.id,
secret: new TextEncoder().encode(proof.secret),
witness: proof.witness ? JSON.parse(proof.witness) : undefined
};
};
exports.deserializeProof = deserializeProof;
const serializeBlindedMessage = (bm, amount) => {
return {
B_: bm.B_.toHex(true),
amount: amount
};
};
exports.serializeBlindedMessage = serializeBlindedMessage;
//# sourceMappingURL=index.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":";;;AACA,uDAAoD;AACpD,+CAA0E;AAC1E,+CAAiD;AAOjD,iDAA+D;AAS/D,SAAgB,0BAA0B;IACzC,OAAO,YAAY,CAAC,IAAA,mBAAW,EAAC,EAAE,CAAC,CAAC,CAAC;AACtC,CAAC;AAFD,gEAEC;AAED,SAAgB,YAAY,CAAC,MAAkB,EAAE,CAAU;IAC1D,MAAM,CAAC,GAAG,IAAA,sBAAW,EAAC,MAAM,CAAC,CAAC;IAC9B,IAAI,CAAC,CAAC,EAAE,CAAC;QACR,CAAC,GAAG,IAAA,wBAAa,EAAC,qBAAS,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,EAAE,GAAG,qBAAS,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtD,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrB,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;AAC1B,CAAC;AARD,oCAQC;AAED,SAAgB,gBAAgB,CAC/B,EAAyB,EACzB,CAAS,EACT,CAAwB;IAExB,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,OAAO,CAAC,CAAC;AACV,CAAC;AAPD,4CAOC;AAED,SAAgB,yBAAyB,CACxC,OAAuB,EACvB,CAAS,EACT,MAAkB,EAClB,GAA0B;IAE1B,MAAM,CAAC,GAAG,GAAG,CAAC;IACd,MAAM,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG;QACb,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM;QACN,CAAC;KACD,CAAC;IACF,OAAO,KAAK,CAAC;AACd,CAAC;AAfD,8DAeC;AAEM,MAAM,cAAc,GAAG,CAAC,KAAY,EAAmB,EAAE;IAC/D,OAAO;QACN,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;QACtB,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC9C,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC;KACtC,CAAC;AACH,CAAC,CAAC;AARW,QAAA,cAAc,kBAQzB;AAEK,MAAM,gBAAgB,GAAG,CAAC,KAAsB,EAAS,EAAE;IACjE,OAAO;QACN,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,CAAC,EAAE,IAAA,uBAAY,EAAC,KAAK,CAAC,CAAC,CAAC;QACxB,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC9C,OAAO,EAAE,KAAK,CAAC,OAAO,CAAA,CAAC,CAAA,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA,CAAC,CAAA,SAAS;KAC1D,CAAC;AACH,CAAC,CAAC;AARW,QAAA,gBAAgB,oBAQ3B;AACK,MAAM,uBAAuB,GAAG,CACtC,EAAkB,EAClB,MAAc,EACa,EAAE;IAC7B,OAAO;QACN,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;QACrB,MAAM,EAAE,MAAM;KACd,CAAC;AACH,CAAC,CAAC;AARW,QAAA,uBAAuB,2BAQlC"}

View File

@@ -1,3 +0,0 @@
import { Secret } from "./index.js";
export declare const parseSecret: (secret: string | Uint8Array) => Secret;
//# sourceMappingURL=NUT11.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"NUT11.d.ts","sourceRoot":"","sources":["../../src/common/NUT11.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,eAAO,MAAM,WAAW,WAAY,MAAM,GAAG,UAAU,WAStD,CAAC"}

View File

@@ -1,16 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseSecret = void 0;
const parseSecret = (secret) => {
try {
if (secret instanceof Uint8Array) {
secret = new TextDecoder().decode(secret);
}
return JSON.parse(secret);
}
catch (e) {
throw new Error("can't parse secret");
}
};
exports.parseSecret = parseSecret;
//# sourceMappingURL=NUT11.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"NUT11.js","sourceRoot":"","sources":["../../src/common/NUT11.ts"],"names":[],"mappings":";;;AAEO,MAAM,WAAW,GAAG,CAAC,MAA2B,EAAU,EAAE;IAClE,IAAI,CAAC;QACJ,IAAI,MAAM,YAAY,UAAU,EAAE,CAAC;YAClC,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACvC,CAAC;AACF,CAAC,CAAC;AATW,QAAA,WAAW,eAStB"}

View File

@@ -1,65 +0,0 @@
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
export type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N ? Acc[number] : Enumerate<N, [...Acc, Acc['length']]>;
export type IntRange<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>;
export type MintKeys = {
[k: string]: Uint8Array;
};
export type SerializedMintKeys = {
[k: string]: string;
};
export type Keyset = {
id: string;
unit: string;
active: boolean;
};
export type BlindSignature = {
C_: ProjPointType<bigint>;
amount: number;
id: string;
};
export type SerializedBlindSignature = {
C_: string;
amount: number;
id: string;
};
export type Proof = {
C: ProjPointType<bigint>;
secret: Uint8Array;
amount: number;
id: string;
witness?: Witness;
};
export type SerializedProof = {
C: string;
secret: string;
amount: number;
id: string;
witness?: string;
};
export type SerializedBlindedMessage = {
B_: string;
amount: number;
witness?: string;
};
export type Secret = [WellKnownSecret, SecretData];
export type WellKnownSecret = 'P2PK';
export type SecretData = {
nonce: string;
data: string;
tags?: Array<Array<string>>;
};
export type Witness = {
signatures: Array<string>;
};
export type Tags = {
[k: string]: string;
};
export type SigFlag = 'SIG_INPUTS' | 'SIG_ALL';
export declare function hashToCurve(secret: Uint8Array): ProjPointType<bigint>;
export declare function pointFromHex(hex: string): ProjPointType<bigint>;
export declare const getKeysetIdInt: (keysetId: string) => bigint;
export declare function createRandomPrivateKey(): Uint8Array;
export declare function serializeMintKeys(mintKeys: MintKeys): SerializedMintKeys;
export declare function deserializeMintKeys(serializedMintKeys: SerializedMintKeys): MintKeys;
export declare function deriveKeysetId(keys: MintKeys): string;
//# sourceMappingURL=index.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"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"}

View File

@@ -1,85 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deriveKeysetId = exports.deserializeMintKeys = exports.serializeMintKeys = exports.createRandomPrivateKey = exports.getKeysetIdInt = exports.pointFromHex = exports.hashToCurve = void 0;
const secp256k1_1 = require("@noble/curves/secp256k1");
const sha256_1 = require("@noble/hashes/sha256");
const utils_1 = require("@noble/curves/abstract/utils");
const utils_js_1 = require("../util/utils.js");
const buffer_1 = require("buffer/");
const DOMAIN_SEPARATOR = (0, utils_1.hexToBytes)('536563703235366b315f48617368546f43757276655f43617368755f');
function hashToCurve(secret) {
const msgToHash = (0, sha256_1.sha256)(buffer_1.Buffer.concat([DOMAIN_SEPARATOR, secret]));
const counter = new Uint32Array(1);
const maxIterations = 2 ** 16;
for (let i = 0; i < maxIterations; i++) {
const counterBytes = new Uint8Array(counter.buffer);
const hash = (0, sha256_1.sha256)(buffer_1.Buffer.concat([msgToHash, counterBytes]));
try {
return pointFromHex((0, utils_1.bytesToHex)(buffer_1.Buffer.concat([new Uint8Array([0x02]), hash])));
}
catch (error) {
counter[0]++;
}
}
throw new Error('No valid point found');
}
exports.hashToCurve = hashToCurve;
function pointFromHex(hex) {
return secp256k1_1.secp256k1.ProjectivePoint.fromHex(hex);
}
exports.pointFromHex = pointFromHex;
const getKeysetIdInt = (keysetId) => {
let keysetIdInt;
if (/^[a-fA-F0-9]+$/.test(keysetId)) {
keysetIdInt = (0, utils_js_1.hexToNumber)(keysetId) % BigInt(2 ** 31 - 1);
}
else {
//legacy keyset compatibility
keysetIdInt = (0, utils_js_1.bytesToNumber)((0, utils_js_1.encodeBase64toUint8)(keysetId)) % BigInt(2 ** 31 - 1);
}
return keysetIdInt;
};
exports.getKeysetIdInt = getKeysetIdInt;
function createRandomPrivateKey() {
return secp256k1_1.secp256k1.utils.randomPrivateKey();
}
exports.createRandomPrivateKey = createRandomPrivateKey;
function serializeMintKeys(mintKeys) {
const serializedMintKeys = {};
Object.keys(mintKeys).forEach((p) => {
serializedMintKeys[p] = (0, utils_1.bytesToHex)(mintKeys[p]);
});
return serializedMintKeys;
}
exports.serializeMintKeys = serializeMintKeys;
function deserializeMintKeys(serializedMintKeys) {
const mintKeys = {};
Object.keys(serializedMintKeys).forEach((p) => {
mintKeys[p] = (0, utils_1.hexToBytes)(serializedMintKeys[p]);
});
return mintKeys;
}
exports.deserializeMintKeys = deserializeMintKeys;
function deriveKeysetId(keys) {
const KEYSET_VERSION = '00';
const mapBigInt = (k) => {
return [BigInt(k[0]), k[1]];
};
const pubkeysConcat = Object.entries(serializeMintKeys(keys))
.map(mapBigInt)
.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0))
.map(([, pubKey]) => (0, utils_1.hexToBytes)(pubKey))
.reduce((prev, curr) => mergeUInt8Arrays(prev, curr), new Uint8Array());
const hash = (0, sha256_1.sha256)(pubkeysConcat);
const hashHex = buffer_1.Buffer.from(hash).toString('hex').slice(0, 14);
return '00' + hashHex;
}
exports.deriveKeysetId = deriveKeysetId;
function mergeUInt8Arrays(a1, a2) {
// sum of individual array lengths
const mergedArray = new Uint8Array(a1.length + a2.length);
mergedArray.set(a1);
mergedArray.set(a2, a1.length);
return mergedArray;
}
//# sourceMappingURL=index.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"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"}

View File

@@ -1,4 +0,0 @@
export declare const deriveSecret: (seed: Uint8Array, keysetId: string, counter: number) => Uint8Array;
export declare const deriveBlindingFactor: (seed: Uint8Array, keysetId: string, counter: number) => Uint8Array;
export declare const generateNewMnemonic: () => string;
export declare const deriveSeedFromMnemonic: (mnemonic: string) => Uint8Array;

View File

@@ -1,42 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deriveSeedFromMnemonic = exports.generateNewMnemonic = exports.deriveBlindingFactor = exports.deriveSecret = void 0;
const bip32_1 = require("@scure/bip32");
const index_js_1 = require("../common/index.js");
const bip39_1 = require("@scure/bip39");
const english_1 = require("@scure/bip39/wordlists/english");
const STANDARD_DERIVATION_PATH = `m/129372'/0'`;
var DerivationType;
(function (DerivationType) {
DerivationType[DerivationType["SECRET"] = 0] = "SECRET";
DerivationType[DerivationType["BLINDING_FACTOR"] = 1] = "BLINDING_FACTOR";
})(DerivationType || (DerivationType = {}));
const deriveSecret = (seed, keysetId, counter) => {
return derive(seed, keysetId, counter, DerivationType.SECRET);
};
exports.deriveSecret = deriveSecret;
const deriveBlindingFactor = (seed, keysetId, counter) => {
return derive(seed, keysetId, counter, DerivationType.BLINDING_FACTOR);
};
exports.deriveBlindingFactor = deriveBlindingFactor;
const derive = (seed, keysetId, counter, secretOrBlinding) => {
const hdkey = bip32_1.HDKey.fromMasterSeed(seed);
const keysetIdInt = (0, index_js_1.getKeysetIdInt)(keysetId);
const derivationPath = `${STANDARD_DERIVATION_PATH}/${keysetIdInt}'/${counter}'/${secretOrBlinding}`;
const derived = hdkey.derive(derivationPath);
if (derived.privateKey === null) {
throw new Error('Could not derive private key');
}
return derived.privateKey;
};
const generateNewMnemonic = () => {
const mnemonic = (0, bip39_1.generateMnemonic)(english_1.wordlist, 128);
return mnemonic;
};
exports.generateNewMnemonic = generateNewMnemonic;
const deriveSeedFromMnemonic = (mnemonic) => {
const seed = (0, bip39_1.mnemonicToSeedSync)(mnemonic);
return seed;
};
exports.deriveSeedFromMnemonic = deriveSeedFromMnemonic;
//# sourceMappingURL=NUT09.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"NUT09.js","sourceRoot":"","sources":["../../../src/client/NUT09.ts"],"names":[],"mappings":";;;AAAA,wCAAqC;AACrC,iDAAoD;AACpD,wCAAoE;AACpE,4DAA0D;AAE1D,MAAM,wBAAwB,GAAG,cAAc,CAAC;AAEhD,IAAK,cAGJ;AAHD,WAAK,cAAc;IAClB,uDAAU,CAAA;IACV,yEAAmB,CAAA;AACpB,CAAC,EAHI,cAAc,KAAd,cAAc,QAGlB;AAEM,MAAM,YAAY,GAAG,CAAC,IAAgB,EAAE,QAAgB,EAAE,OAAe,EAAc,EAAE;IAC/F,OAAO,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;AAC/D,CAAC,CAAC;AAFW,QAAA,YAAY,gBAEvB;AAEK,MAAM,oBAAoB,GAAG,CACnC,IAAgB,EAChB,QAAgB,EAChB,OAAe,EACF,EAAE;IACf,OAAO,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,CAAC,eAAe,CAAC,CAAC;AACxE,CAAC,CAAC;AANW,QAAA,oBAAoB,wBAM/B;AAEF,MAAM,MAAM,GAAG,CACd,IAAgB,EAChB,QAAgB,EAChB,OAAe,EACf,gBAAgC,EACnB,EAAE;IACf,MAAM,KAAK,GAAG,aAAK,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,IAAA,yBAAc,EAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,cAAc,GAAG,GAAG,wBAAwB,IAAI,WAAW,KAAK,OAAO,KAAK,gBAAgB,EAAE,CAAC;IACrG,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAC7C,IAAI,OAAO,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,OAAO,CAAC,UAAU,CAAC;AAC3B,CAAC,CAAC;AAEK,MAAM,mBAAmB,GAAG,GAAW,EAAE;IAC/C,MAAM,QAAQ,GAAG,IAAA,wBAAgB,EAAC,kBAAQ,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,QAAQ,CAAC;AACjB,CAAC,CAAC;AAHW,QAAA,mBAAmB,uBAG9B;AAEK,MAAM,sBAAsB,GAAG,CAAC,QAAgB,EAAc,EAAE;IACtE,MAAM,IAAI,GAAG,IAAA,0BAAkB,EAAC,QAAQ,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC;AACb,CAAC,CAAC;AAHW,QAAA,sBAAsB,0BAGjC"}

View File

@@ -1,6 +0,0 @@
import { PrivKey } from '@noble/curves/abstract/utils';
import { Proof } from '../common/index.js';
export declare const createP2PKsecret: (pubkey: string) => Uint8Array;
export declare const signP2PKsecret: (secret: Uint8Array, privateKey: PrivKey) => Uint8Array;
export declare const getSignedProofs: (proofs: Array<Proof>, privateKey: string) => Array<Proof>;
export declare const getSignedProof: (proof: Proof, privateKey: PrivKey) => Proof;

View File

@@ -1,51 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSignedProof = exports.getSignedProofs = exports.signP2PKsecret = exports.createP2PKsecret = void 0;
const utils_1 = require("@noble/curves/abstract/utils");
const sha256_1 = require("@noble/hashes/sha256");
const secp256k1_1 = require("@noble/curves/secp256k1");
const utils_2 = require("@noble/hashes/utils");
const NUT11_js_1 = require("../common/NUT11.js");
const createP2PKsecret = (pubkey) => {
const newSecret = [
'P2PK',
{
nonce: (0, utils_1.bytesToHex)((0, utils_2.randomBytes)(32)),
data: pubkey
}
];
const parsed = JSON.stringify(newSecret);
return new TextEncoder().encode(parsed);
};
exports.createP2PKsecret = createP2PKsecret;
const signP2PKsecret = (secret, privateKey) => {
const msghash = (0, sha256_1.sha256)(new TextDecoder().decode(secret));
const sig = secp256k1_1.schnorr.sign(msghash, privateKey);
return sig;
};
exports.signP2PKsecret = signP2PKsecret;
const getSignedProofs = (proofs, privateKey) => {
return proofs.map((p) => {
try {
const parsed = (0, NUT11_js_1.parseSecret)(p.secret);
if (parsed[0] !== 'P2PK') {
throw new Error('unknown secret type');
}
return (0, exports.getSignedProof)(p, (0, utils_1.hexToBytes)(privateKey));
}
catch (error) {
return p;
}
});
};
exports.getSignedProofs = getSignedProofs;
const getSignedProof = (proof, privateKey) => {
if (!proof.witness) {
proof.witness = {
signatures: [(0, utils_1.bytesToHex)((0, exports.signP2PKsecret)(proof.secret, privateKey))]
};
}
return proof;
};
exports.getSignedProof = getSignedProof;
//# sourceMappingURL=NUT11.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"NUT11.js","sourceRoot":"","sources":["../../../src/client/NUT11.ts"],"names":[],"mappings":";;;AAAA,wDAA+E;AAC/E,iDAA8C;AAC9C,uDAAkD;AAClD,+CAAkD;AAClD,iDAAiD;AAG1C,MAAM,gBAAgB,GAAG,CAAC,MAAc,EAAc,EAAE;IAC9D,MAAM,SAAS,GAAW;QACzB,MAAM;QACN;YACC,KAAK,EAAE,IAAA,kBAAU,EAAC,IAAA,mBAAW,EAAC,EAAE,CAAC,CAAC;YAClC,IAAI,EAAE,MAAM;SACZ;KACD,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACzC,CAAC,CAAC;AAVW,QAAA,gBAAgB,oBAU3B;AAEK,MAAM,cAAc,GAAG,CAAC,MAAkB,EAAE,UAAmB,EAAE,EAAE;IACzE,MAAM,OAAO,GAAG,IAAA,eAAM,EAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,mBAAO,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC9C,OAAO,GAAG,CAAC;AACZ,CAAC,CAAC;AAJW,QAAA,cAAc,kBAIzB;AAEK,MAAM,eAAe,GAAG,CAAC,MAAoB,EAAE,UAAkB,EAAgB,EAAE;IACzF,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACvB,IAAI,CAAC;YACJ,MAAM,MAAM,GAAW,IAAA,sBAAW,EAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACxC,CAAC;YACD,OAAO,IAAA,sBAAc,EAAC,CAAC,EAAE,IAAA,kBAAU,EAAC,UAAU,CAAC,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,CAAC;QACV,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC;AAZW,QAAA,eAAe,mBAY1B;AAEK,MAAM,cAAc,GAAG,CAAC,KAAY,EAAE,UAAmB,EAAS,EAAE;IAC1E,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACpB,KAAK,CAAC,OAAO,GAAG;YACf,UAAU,EAAE,CAAC,IAAA,kBAAU,EAAC,IAAA,sBAAc,EAAC,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;SAClE,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC,CAAC;AAPW,QAAA,cAAc,kBAOzB"}

View File

@@ -1,14 +0,0 @@
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
import type { BlindSignature, Proof, SerializedBlindedMessage, SerializedProof } from '../common/index.js';
export type BlindedMessage = {
B_: ProjPointType<bigint>;
r: bigint;
secret: Uint8Array;
};
export declare function createRandomBlindedMessage(): BlindedMessage;
export declare function blindMessage(secret: Uint8Array, r?: bigint): BlindedMessage;
export declare function unblindSignature(C_: ProjPointType<bigint>, r: bigint, A: ProjPointType<bigint>): ProjPointType<bigint>;
export declare function constructProofFromPromise(promise: BlindSignature, r: bigint, secret: Uint8Array, key: ProjPointType<bigint>): Proof;
export declare const serializeProof: (proof: Proof) => SerializedProof;
export declare const deserializeProof: (proof: SerializedProof) => Proof;
export declare const serializeBlindedMessage: (bm: BlindedMessage, amount: number) => SerializedBlindedMessage;

View File

@@ -1,66 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serializeBlindedMessage = exports.deserializeProof = exports.serializeProof = exports.constructProofFromPromise = exports.unblindSignature = exports.blindMessage = exports.createRandomBlindedMessage = void 0;
const secp256k1_1 = require("@noble/curves/secp256k1");
const utils_1 = require("@noble/hashes/utils");
const utils_js_1 = require("../util/utils.js");
const index_js_1 = require("../common/index.js");
function createRandomBlindedMessage() {
return blindMessage((0, utils_1.randomBytes)(32));
}
exports.createRandomBlindedMessage = createRandomBlindedMessage;
function blindMessage(secret, r) {
const Y = (0, index_js_1.hashToCurve)(secret);
if (!r) {
r = (0, utils_js_1.bytesToNumber)(secp256k1_1.secp256k1.utils.randomPrivateKey());
}
const rG = secp256k1_1.secp256k1.ProjectivePoint.BASE.multiply(r);
const B_ = Y.add(rG);
return { B_, r, secret };
}
exports.blindMessage = blindMessage;
function unblindSignature(C_, r, A) {
const C = C_.subtract(A.multiply(r));
return C;
}
exports.unblindSignature = unblindSignature;
function constructProofFromPromise(promise, r, secret, key) {
const A = key;
const C = unblindSignature(promise.C_, r, A);
const proof = {
id: promise.id,
amount: promise.amount,
secret,
C
};
return proof;
}
exports.constructProofFromPromise = constructProofFromPromise;
const serializeProof = (proof) => {
return {
amount: proof.amount,
C: proof.C.toHex(true),
id: proof.id,
secret: new TextDecoder().decode(proof.secret),
witness: JSON.stringify(proof.witness)
};
};
exports.serializeProof = serializeProof;
const deserializeProof = (proof) => {
return {
amount: proof.amount,
C: (0, index_js_1.pointFromHex)(proof.C),
id: proof.id,
secret: new TextEncoder().encode(proof.secret),
witness: proof.witness ? JSON.parse(proof.witness) : undefined
};
};
exports.deserializeProof = deserializeProof;
const serializeBlindedMessage = (bm, amount) => {
return {
B_: bm.B_.toHex(true),
amount: amount
};
};
exports.serializeBlindedMessage = serializeBlindedMessage;
//# sourceMappingURL=index.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":";;;AACA,uDAAoD;AACpD,+CAA0E;AAC1E,+CAAiD;AAOjD,iDAA+D;AAS/D,SAAgB,0BAA0B;IACzC,OAAO,YAAY,CAAC,IAAA,mBAAW,EAAC,EAAE,CAAC,CAAC,CAAC;AACtC,CAAC;AAFD,gEAEC;AAED,SAAgB,YAAY,CAAC,MAAkB,EAAE,CAAU;IAC1D,MAAM,CAAC,GAAG,IAAA,sBAAW,EAAC,MAAM,CAAC,CAAC;IAC9B,IAAI,CAAC,CAAC,EAAE,CAAC;QACR,CAAC,GAAG,IAAA,wBAAa,EAAC,qBAAS,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,EAAE,GAAG,qBAAS,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtD,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrB,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;AAC1B,CAAC;AARD,oCAQC;AAED,SAAgB,gBAAgB,CAC/B,EAAyB,EACzB,CAAS,EACT,CAAwB;IAExB,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,OAAO,CAAC,CAAC;AACV,CAAC;AAPD,4CAOC;AAED,SAAgB,yBAAyB,CACxC,OAAuB,EACvB,CAAS,EACT,MAAkB,EAClB,GAA0B;IAE1B,MAAM,CAAC,GAAG,GAAG,CAAC;IACd,MAAM,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG;QACb,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM;QACN,CAAC;KACD,CAAC;IACF,OAAO,KAAK,CAAC;AACd,CAAC;AAfD,8DAeC;AAEM,MAAM,cAAc,GAAG,CAAC,KAAY,EAAmB,EAAE;IAC/D,OAAO;QACN,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;QACtB,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC9C,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC;KACtC,CAAC;AACH,CAAC,CAAC;AARW,QAAA,cAAc,kBAQzB;AAEK,MAAM,gBAAgB,GAAG,CAAC,KAAsB,EAAS,EAAE;IACjE,OAAO;QACN,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,CAAC,EAAE,IAAA,uBAAY,EAAC,KAAK,CAAC,CAAC,CAAC;QACxB,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;QAC9C,OAAO,EAAE,KAAK,CAAC,OAAO,CAAA,CAAC,CAAA,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA,CAAC,CAAA,SAAS;KAC1D,CAAC;AACH,CAAC,CAAC;AARW,QAAA,gBAAgB,oBAQ3B;AACK,MAAM,uBAAuB,GAAG,CACtC,EAAkB,EAClB,MAAc,EACa,EAAE;IAC7B,OAAO;QACN,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;QACrB,MAAM,EAAE,MAAM;KACd,CAAC;AACH,CAAC,CAAC;AARW,QAAA,uBAAuB,2BAQlC"}

View File

@@ -1,2 +0,0 @@
import { Secret } from "./index.js";
export declare const parseSecret: (secret: string | Uint8Array) => Secret;

View File

@@ -1,16 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseSecret = void 0;
const parseSecret = (secret) => {
try {
if (secret instanceof Uint8Array) {
secret = new TextDecoder().decode(secret);
}
return JSON.parse(secret);
}
catch (e) {
throw new Error("can't parse secret");
}
};
exports.parseSecret = parseSecret;
//# sourceMappingURL=NUT11.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"NUT11.js","sourceRoot":"","sources":["../../../src/common/NUT11.ts"],"names":[],"mappings":";;;AAEO,MAAM,WAAW,GAAG,CAAC,MAA2B,EAAU,EAAE;IAClE,IAAI,CAAC;QACJ,IAAI,MAAM,YAAY,UAAU,EAAE,CAAC;YAClC,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACvC,CAAC;AACF,CAAC,CAAC;AATW,QAAA,WAAW,eAStB"}

View File

@@ -1,64 +0,0 @@
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
export type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N ? Acc[number] : Enumerate<N, [...Acc, Acc['length']]>;
export type IntRange<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>;
export type MintKeys = {
[k: string]: Uint8Array;
};
export type SerializedMintKeys = {
[k: string]: string;
};
export type Keyset = {
id: string;
unit: string;
active: boolean;
};
export type BlindSignature = {
C_: ProjPointType<bigint>;
amount: number;
id: string;
};
export type SerializedBlindSignature = {
C_: string;
amount: number;
id: string;
};
export type Proof = {
C: ProjPointType<bigint>;
secret: Uint8Array;
amount: number;
id: string;
witness?: Witness;
};
export type SerializedProof = {
C: string;
secret: string;
amount: number;
id: string;
witness?: string;
};
export type SerializedBlindedMessage = {
B_: string;
amount: number;
witness?: string;
};
export type Secret = [WellKnownSecret, SecretData];
export type WellKnownSecret = 'P2PK';
export type SecretData = {
nonce: string;
data: string;
tags?: Array<Array<string>>;
};
export type Witness = {
signatures: Array<string>;
};
export type Tags = {
[k: string]: string;
};
export type SigFlag = 'SIG_INPUTS' | 'SIG_ALL';
export declare function hashToCurve(secret: Uint8Array): ProjPointType<bigint>;
export declare function pointFromHex(hex: string): ProjPointType<bigint>;
export declare const getKeysetIdInt: (keysetId: string) => bigint;
export declare function createRandomPrivateKey(): Uint8Array;
export declare function serializeMintKeys(mintKeys: MintKeys): SerializedMintKeys;
export declare function deserializeMintKeys(serializedMintKeys: SerializedMintKeys): MintKeys;
export declare function deriveKeysetId(keys: MintKeys): string;

View File

@@ -1,85 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deriveKeysetId = exports.deserializeMintKeys = exports.serializeMintKeys = exports.createRandomPrivateKey = exports.getKeysetIdInt = exports.pointFromHex = exports.hashToCurve = void 0;
const secp256k1_1 = require("@noble/curves/secp256k1");
const sha256_1 = require("@noble/hashes/sha256");
const utils_1 = require("@noble/curves/abstract/utils");
const utils_js_1 = require("../util/utils.js");
const buffer_1 = require("buffer/");
const DOMAIN_SEPARATOR = (0, utils_1.hexToBytes)('536563703235366b315f48617368546f43757276655f43617368755f');
function hashToCurve(secret) {
const msgToHash = (0, sha256_1.sha256)(buffer_1.Buffer.concat([DOMAIN_SEPARATOR, secret]));
const counter = new Uint32Array(1);
const maxIterations = 2 ** 16;
for (let i = 0; i < maxIterations; i++) {
const counterBytes = new Uint8Array(counter.buffer);
const hash = (0, sha256_1.sha256)(buffer_1.Buffer.concat([msgToHash, counterBytes]));
try {
return pointFromHex((0, utils_1.bytesToHex)(buffer_1.Buffer.concat([new Uint8Array([0x02]), hash])));
}
catch (error) {
counter[0]++;
}
}
throw new Error('No valid point found');
}
exports.hashToCurve = hashToCurve;
function pointFromHex(hex) {
return secp256k1_1.secp256k1.ProjectivePoint.fromHex(hex);
}
exports.pointFromHex = pointFromHex;
const getKeysetIdInt = (keysetId) => {
let keysetIdInt;
if (/^[a-fA-F0-9]+$/.test(keysetId)) {
keysetIdInt = (0, utils_js_1.hexToNumber)(keysetId) % BigInt(2 ** 31 - 1);
}
else {
//legacy keyset compatibility
keysetIdInt = (0, utils_js_1.bytesToNumber)((0, utils_js_1.encodeBase64toUint8)(keysetId)) % BigInt(2 ** 31 - 1);
}
return keysetIdInt;
};
exports.getKeysetIdInt = getKeysetIdInt;
function createRandomPrivateKey() {
return secp256k1_1.secp256k1.utils.randomPrivateKey();
}
exports.createRandomPrivateKey = createRandomPrivateKey;
function serializeMintKeys(mintKeys) {
const serializedMintKeys = {};
Object.keys(mintKeys).forEach((p) => {
serializedMintKeys[p] = (0, utils_1.bytesToHex)(mintKeys[p]);
});
return serializedMintKeys;
}
exports.serializeMintKeys = serializeMintKeys;
function deserializeMintKeys(serializedMintKeys) {
const mintKeys = {};
Object.keys(serializedMintKeys).forEach((p) => {
mintKeys[p] = (0, utils_1.hexToBytes)(serializedMintKeys[p]);
});
return mintKeys;
}
exports.deserializeMintKeys = deserializeMintKeys;
function deriveKeysetId(keys) {
const KEYSET_VERSION = '00';
const mapBigInt = (k) => {
return [BigInt(k[0]), k[1]];
};
const pubkeysConcat = Object.entries(serializeMintKeys(keys))
.map(mapBigInt)
.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0))
.map(([, pubKey]) => (0, utils_1.hexToBytes)(pubKey))
.reduce((prev, curr) => mergeUInt8Arrays(prev, curr), new Uint8Array());
const hash = (0, sha256_1.sha256)(pubkeysConcat);
const hashHex = buffer_1.Buffer.from(hash).toString('hex').slice(0, 14);
return '00' + hashHex;
}
exports.deriveKeysetId = deriveKeysetId;
function mergeUInt8Arrays(a1, a2) {
// sum of individual array lengths
const mergedArray = new Uint8Array(a1.length + a2.length);
mergedArray.set(a1);
mergedArray.set(a2, a1.length);
return mergedArray;
}
//# sourceMappingURL=index.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/common/index.ts"],"names":[],"mappings":"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"}

View File

@@ -1 +0,0 @@
export {};

View File

@@ -1,4 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
throw new Error('Incorrect usage. Import submodules instead');
//# sourceMappingURL=index.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;AAAA,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC"}

View File

@@ -1,2 +0,0 @@
import { Proof } from '../common/index.js';
export declare const verifyP2PKSig: (proof: Proof) => boolean;

View File

@@ -1,37 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyP2PKSig = void 0;
const secp256k1_1 = require("@noble/curves/secp256k1");
const sha256_1 = require("@noble/hashes/sha256");
const NUT11_js_1 = require("../common/NUT11.js");
const verifyP2PKSig = (proof) => {
if (!proof.witness) {
throw new Error('could not verify signature, no witness provided');
}
const parsedSecret = (0, NUT11_js_1.parseSecret)(proof.secret);
// const tags = {} as Tags
// parsedSecret[1].tags.forEach((e: string[]) => {tags[e[0]]=e.shift()})
// if (tags.locktime) {
// const locktime = parseInt(tags.locktime[1])
// let isUnlocked = false
// if (Math.floor(Date.now() / 1000)>=locktime) {
// isUnlocked = true
// }
// }
// if (tags.sigflag as SigFlag) {
// if (tags.sigflag[0]==='SIG_INPUT') {
// }
// else if(tags.sigflag[0]==='SIG_ALL') {
// }
// else {
// throw new Error("Unknown sigflag");
// }
// }
// if (tags.n_sigs) {
// if (tags.pubkeys) {
// }
// }
return secp256k1_1.schnorr.verify(proof.witness.signatures[0], (0, sha256_1.sha256)(new TextDecoder().decode(proof.secret)), parsedSecret[1].data);
};
exports.verifyP2PKSig = verifyP2PKSig;
//# sourceMappingURL=NUT11.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"NUT11.js","sourceRoot":"","sources":["../../../src/mint/NUT11.ts"],"names":[],"mappings":";;;AAAA,uDAAkD;AAClD,iDAA8C;AAC9C,iDAAiD;AAG1C,MAAM,aAAa,GAAG,CAAC,KAAY,EAAE,EAAE;IAC7C,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,YAAY,GAAG,IAAA,sBAAW,EAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAE/C,0BAA0B;IAC1B,wEAAwE;IACxE,uBAAuB;IACvB,kDAAkD;IAElD,6BAA6B;IAC7B,qDAAqD;IACrD,4BAA4B;IAC5B,QAAQ;IACR,IAAI;IACJ,iCAAiC;IACjC,2CAA2C;IAE3C,QAAQ;IACR,6CAA6C;IAE7C,QAAQ;IACR,aAAa;IACb,8CAA8C;IAC9C,QAAQ;IACR,IAAI;IACJ,qBAAqB;IACrB,0BAA0B;IAE1B,QAAQ;IACR,IAAI;IAEJ,OAAO,mBAAO,CAAC,MAAM,CACpB,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAC3B,IAAA,eAAM,EAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAC9C,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CACpB,CAAC;AACH,CAAC,CAAC;AAtCW,QAAA,aAAa,iBAsCxB"}

View File

@@ -1,14 +0,0 @@
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
import { BlindSignature, IntRange, Keyset, MintKeys, Proof } from '../common/index.js';
export type KeysetPair = {
keysetId: string;
pubKeys: MintKeys;
privKeys: MintKeys;
};
export type KeysetWithKeys = Keyset & {
pubKeys: MintKeys;
};
export declare function createBlindSignature(B_: ProjPointType<bigint>, privateKey: Uint8Array, amount: number, id: string): BlindSignature;
export declare function getPubKeyFromPrivKey(privKey: Uint8Array): Uint8Array;
export declare function createNewMintKeys(pow2height: IntRange<0, 65>, seed?: Uint8Array): KeysetPair;
export declare function verifyProof(proof: Proof, privKey: Uint8Array): boolean;

View File

@@ -1,53 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyProof = exports.createNewMintKeys = exports.getPubKeyFromPrivKey = exports.createBlindSignature = void 0;
const secp256k1_1 = require("@noble/curves/secp256k1");
const utils_js_1 = require("../util/utils.js");
const index_js_1 = require("../common/index.js");
const bip32_1 = require("@scure/bip32");
const DERIVATION_PATH = "m/0'/0'/0'";
function createBlindSignature(B_, privateKey, amount, id) {
const C_ = B_.multiply((0, utils_js_1.bytesToNumber)(privateKey));
return { C_, amount, id };
}
exports.createBlindSignature = createBlindSignature;
function getPubKeyFromPrivKey(privKey) {
return secp256k1_1.secp256k1.getPublicKey(privKey, true);
}
exports.getPubKeyFromPrivKey = getPubKeyFromPrivKey;
function createNewMintKeys(pow2height, seed) {
let counter = 0n;
const pubKeys = {};
const privKeys = {};
let masterKey;
if (seed) {
masterKey = bip32_1.HDKey.fromMasterSeed(seed);
}
while (counter < pow2height) {
const index = (2n ** counter).toString();
if (masterKey) {
const k = masterKey.derive(`${DERIVATION_PATH}/${counter}`).privateKey;
if (k) {
privKeys[index] = k;
}
else {
throw new Error(`Could not derive Private key from: ${DERIVATION_PATH}/${counter}`);
}
}
else {
privKeys[index] = (0, index_js_1.createRandomPrivateKey)();
}
pubKeys[index] = getPubKeyFromPrivKey(privKeys[index]);
counter++;
}
const keysetId = (0, index_js_1.deriveKeysetId)(pubKeys);
return { pubKeys, privKeys, keysetId };
}
exports.createNewMintKeys = createNewMintKeys;
function verifyProof(proof, privKey) {
const Y = (0, index_js_1.hashToCurve)(proof.secret);
const aY = Y.multiply((0, utils_js_1.bytesToNumber)(privKey));
return aY.equals(proof.C);
}
exports.verifyProof = verifyProof;
//# sourceMappingURL=index.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mint/index.ts"],"names":[],"mappings":";;;AACA,uDAAoD;AACpD,+CAAiD;AAEjD,iDAAyF;AACzF,wCAAqC;AAErC,MAAM,eAAe,GAAG,YAAY,CAAC;AAYrC,SAAgB,oBAAoB,CACnC,EAAyB,EACzB,UAAsB,EACtB,MAAc,EACd,EAAU;IAEV,MAAM,EAAE,GAA0B,EAAE,CAAC,QAAQ,CAAC,IAAA,wBAAa,EAAC,UAAU,CAAC,CAAC,CAAC;IACzE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAC3B,CAAC;AARD,oDAQC;AAED,SAAgB,oBAAoB,CAAC,OAAmB;IACvD,OAAO,qBAAS,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAC9C,CAAC;AAFD,oDAEC;AAED,SAAgB,iBAAiB,CAAC,UAA2B,EAAE,IAAiB;IAC/E,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,SAAS,CAAC;IACd,IAAI,IAAI,EAAE,CAAC;QACV,SAAS,GAAG,aAAK,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAW,CAAC,EAAE,IAAI,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;QACjD,IAAI,SAAS,EAAE,CAAC;YACf,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,eAAe,IAAI,OAAO,EAAE,CAAC,CAAC,UAAU,CAAC;YACvE,IAAI,CAAC,EAAE,CAAC;gBACP,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;iBAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CAAC,sCAAsC,eAAe,IAAI,OAAO,EAAE,CAAC,CAAC;YACrF,CAAC;QACF,CAAC;aAAM,CAAC;YACP,QAAQ,CAAC,KAAK,CAAC,GAAG,IAAA,iCAAsB,GAAE,CAAC;QAC5C,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,GAAG,oBAAoB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACvD,OAAO,EAAE,CAAC;IACX,CAAC;IACD,MAAM,QAAQ,GAAG,IAAA,yBAAc,EAAC,OAAO,CAAC,CAAC;IACzC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AACxC,CAAC;AA1BD,8CA0BC;AAED,SAAgB,WAAW,CAAC,KAAY,EAAE,OAAmB;IAC5D,MAAM,CAAC,GAA0B,IAAA,sBAAW,EAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3D,MAAM,EAAE,GAA0B,CAAC,CAAC,QAAQ,CAAC,IAAA,wBAAa,EAAC,OAAO,CAAC,CAAC,CAAC;IACrE,OAAO,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC;AAJD,kCAIC"}

View File

@@ -1,3 +0,0 @@
export declare function bytesToNumber(bytes: Uint8Array): bigint;
export declare function hexToNumber(hex: string): bigint;
export declare function encodeBase64toUint8(base64String: string): Uint8Array;

View File

@@ -1,18 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.encodeBase64toUint8 = exports.hexToNumber = exports.bytesToNumber = void 0;
const utils_1 = require("@noble/curves/abstract/utils");
const buffer_1 = require("buffer/");
function bytesToNumber(bytes) {
return hexToNumber((0, utils_1.bytesToHex)(bytes));
}
exports.bytesToNumber = bytesToNumber;
function hexToNumber(hex) {
return BigInt(`0x${hex}`);
}
exports.hexToNumber = hexToNumber;
function encodeBase64toUint8(base64String) {
return buffer_1.Buffer.from(base64String, 'base64');
}
exports.encodeBase64toUint8 = encodeBase64toUint8;
//# sourceMappingURL=utils.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/util/utils.ts"],"names":[],"mappings":";;;AAAA,wDAA0D;AAC1D,oCAAiC;AAEjC,SAAgB,aAAa,CAAC,KAAiB;IAC9C,OAAO,WAAW,CAAC,IAAA,kBAAU,EAAC,KAAK,CAAC,CAAC,CAAC;AACvC,CAAC;AAFD,sCAEC;AAED,SAAgB,WAAW,CAAC,GAAW;IACtC,OAAO,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;AAC3B,CAAC;AAFD,kCAEC;AAED,SAAgB,mBAAmB,CAAC,YAAoB;IACvD,OAAO,eAAM,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAFD,kDAEC"}

View File

@@ -1 +0,0 @@
//# sourceMappingURL=index.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}

View File

@@ -1,3 +0,0 @@
"use strict";
throw new Error('Incorrect usage. Import submodules instead');
//# sourceMappingURL=index.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC"}

View File

@@ -1,3 +0,0 @@
import { Proof } from '../common/index.js';
export declare const verifyP2PKSig: (proof: Proof) => boolean;
//# sourceMappingURL=NUT11.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"NUT11.d.ts","sourceRoot":"","sources":["../../src/mint/NUT11.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,eAAO,MAAM,aAAa,UAAW,KAAK,YAsCzC,CAAC"}

View File

@@ -1,37 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyP2PKSig = void 0;
const secp256k1_1 = require("@noble/curves/secp256k1");
const sha256_1 = require("@noble/hashes/sha256");
const NUT11_js_1 = require("../common/NUT11.js");
const verifyP2PKSig = (proof) => {
if (!proof.witness) {
throw new Error('could not verify signature, no witness provided');
}
const parsedSecret = (0, NUT11_js_1.parseSecret)(proof.secret);
// const tags = {} as Tags
// parsedSecret[1].tags.forEach((e: string[]) => {tags[e[0]]=e.shift()})
// if (tags.locktime) {
// const locktime = parseInt(tags.locktime[1])
// let isUnlocked = false
// if (Math.floor(Date.now() / 1000)>=locktime) {
// isUnlocked = true
// }
// }
// if (tags.sigflag as SigFlag) {
// if (tags.sigflag[0]==='SIG_INPUT') {
// }
// else if(tags.sigflag[0]==='SIG_ALL') {
// }
// else {
// throw new Error("Unknown sigflag");
// }
// }
// if (tags.n_sigs) {
// if (tags.pubkeys) {
// }
// }
return secp256k1_1.schnorr.verify(proof.witness.signatures[0], (0, sha256_1.sha256)(new TextDecoder().decode(proof.secret)), parsedSecret[1].data);
};
exports.verifyP2PKSig = verifyP2PKSig;
//# sourceMappingURL=NUT11.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"NUT11.js","sourceRoot":"","sources":["../../src/mint/NUT11.ts"],"names":[],"mappings":";;;AAAA,uDAAkD;AAClD,iDAA8C;AAC9C,iDAAiD;AAG1C,MAAM,aAAa,GAAG,CAAC,KAAY,EAAE,EAAE;IAC7C,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,YAAY,GAAG,IAAA,sBAAW,EAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAE/C,0BAA0B;IAC1B,wEAAwE;IACxE,uBAAuB;IACvB,kDAAkD;IAElD,6BAA6B;IAC7B,qDAAqD;IACrD,4BAA4B;IAC5B,QAAQ;IACR,IAAI;IACJ,iCAAiC;IACjC,2CAA2C;IAE3C,QAAQ;IACR,6CAA6C;IAE7C,QAAQ;IACR,aAAa;IACb,8CAA8C;IAC9C,QAAQ;IACR,IAAI;IACJ,qBAAqB;IACrB,0BAA0B;IAE1B,QAAQ;IACR,IAAI;IAEJ,OAAO,mBAAO,CAAC,MAAM,CACpB,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAC3B,IAAA,eAAM,EAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAC9C,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CACpB,CAAC;AACH,CAAC,CAAC;AAtCW,QAAA,aAAa,iBAsCxB"}

View File

@@ -1,15 +0,0 @@
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
import { BlindSignature, IntRange, Keyset, MintKeys, Proof } from '../common/index.js';
export type KeysetPair = {
keysetId: string;
pubKeys: MintKeys;
privKeys: MintKeys;
};
export type KeysetWithKeys = Keyset & {
pubKeys: MintKeys;
};
export declare function createBlindSignature(B_: ProjPointType<bigint>, privateKey: Uint8Array, amount: number, id: string): BlindSignature;
export declare function getPubKeyFromPrivKey(privKey: Uint8Array): Uint8Array;
export declare function createNewMintKeys(pow2height: IntRange<0, 65>, seed?: Uint8Array): KeysetPair;
export declare function verifyProof(proof: Proof, privKey: Uint8Array): boolean;
//# sourceMappingURL=index.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mint/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAGnE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAMvF,MAAM,MAAM,UAAU,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,QAAQ,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IACrC,OAAO,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,wBAAgB,oBAAoB,CACnC,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,EACzB,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,GACR,cAAc,CAGhB;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,UAAU,cAEvD;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CA0B5F;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAItE"}

View File

@@ -1,53 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyProof = exports.createNewMintKeys = exports.getPubKeyFromPrivKey = exports.createBlindSignature = void 0;
const secp256k1_1 = require("@noble/curves/secp256k1");
const utils_js_1 = require("../util/utils.js");
const index_js_1 = require("../common/index.js");
const bip32_1 = require("@scure/bip32");
const DERIVATION_PATH = "m/0'/0'/0'";
function createBlindSignature(B_, privateKey, amount, id) {
const C_ = B_.multiply((0, utils_js_1.bytesToNumber)(privateKey));
return { C_, amount, id };
}
exports.createBlindSignature = createBlindSignature;
function getPubKeyFromPrivKey(privKey) {
return secp256k1_1.secp256k1.getPublicKey(privKey, true);
}
exports.getPubKeyFromPrivKey = getPubKeyFromPrivKey;
function createNewMintKeys(pow2height, seed) {
let counter = 0n;
const pubKeys = {};
const privKeys = {};
let masterKey;
if (seed) {
masterKey = bip32_1.HDKey.fromMasterSeed(seed);
}
while (counter < pow2height) {
const index = (2n ** counter).toString();
if (masterKey) {
const k = masterKey.derive(`${DERIVATION_PATH}/${counter}`).privateKey;
if (k) {
privKeys[index] = k;
}
else {
throw new Error(`Could not derive Private key from: ${DERIVATION_PATH}/${counter}`);
}
}
else {
privKeys[index] = (0, index_js_1.createRandomPrivateKey)();
}
pubKeys[index] = getPubKeyFromPrivKey(privKeys[index]);
counter++;
}
const keysetId = (0, index_js_1.deriveKeysetId)(pubKeys);
return { pubKeys, privKeys, keysetId };
}
exports.createNewMintKeys = createNewMintKeys;
function verifyProof(proof, privKey) {
const Y = (0, index_js_1.hashToCurve)(proof.secret);
const aY = Y.multiply((0, utils_js_1.bytesToNumber)(privKey));
return aY.equals(proof.C);
}
exports.verifyProof = verifyProof;
//# sourceMappingURL=index.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mint/index.ts"],"names":[],"mappings":";;;AACA,uDAAoD;AACpD,+CAAiD;AAEjD,iDAAyF;AACzF,wCAAqC;AAErC,MAAM,eAAe,GAAG,YAAY,CAAC;AAYrC,SAAgB,oBAAoB,CACnC,EAAyB,EACzB,UAAsB,EACtB,MAAc,EACd,EAAU;IAEV,MAAM,EAAE,GAA0B,EAAE,CAAC,QAAQ,CAAC,IAAA,wBAAa,EAAC,UAAU,CAAC,CAAC,CAAC;IACzE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAC3B,CAAC;AARD,oDAQC;AAED,SAAgB,oBAAoB,CAAC,OAAmB;IACvD,OAAO,qBAAS,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAC9C,CAAC;AAFD,oDAEC;AAED,SAAgB,iBAAiB,CAAC,UAA2B,EAAE,IAAiB;IAC/E,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,SAAS,CAAC;IACd,IAAI,IAAI,EAAE,CAAC;QACV,SAAS,GAAG,aAAK,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAW,CAAC,EAAE,IAAI,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;QACjD,IAAI,SAAS,EAAE,CAAC;YACf,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,eAAe,IAAI,OAAO,EAAE,CAAC,CAAC,UAAU,CAAC;YACvE,IAAI,CAAC,EAAE,CAAC;gBACP,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;iBAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CAAC,sCAAsC,eAAe,IAAI,OAAO,EAAE,CAAC,CAAC;YACrF,CAAC;QACF,CAAC;aAAM,CAAC;YACP,QAAQ,CAAC,KAAK,CAAC,GAAG,IAAA,iCAAsB,GAAE,CAAC;QAC5C,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,GAAG,oBAAoB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACvD,OAAO,EAAE,CAAC;IACX,CAAC;IACD,MAAM,QAAQ,GAAG,IAAA,yBAAc,EAAC,OAAO,CAAC,CAAC;IACzC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AACxC,CAAC;AA1BD,8CA0BC;AAED,SAAgB,WAAW,CAAC,KAAY,EAAE,OAAmB;IAC5D,MAAM,CAAC,GAA0B,IAAA,sBAAW,EAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3D,MAAM,EAAE,GAA0B,CAAC,CAAC,QAAQ,CAAC,IAAA,wBAAa,EAAC,OAAO,CAAC,CAAC,CAAC;IACrE,OAAO,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC;AAJD,kCAIC"}

View File

@@ -1,4 +0,0 @@
export declare function bytesToNumber(bytes: Uint8Array): bigint;
export declare function hexToNumber(hex: string): bigint;
export declare function encodeBase64toUint8(base64String: string): Uint8Array;
//# sourceMappingURL=utils.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/util/utils.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEvD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,UAAU,CAEpE"}

View File

@@ -1,18 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.encodeBase64toUint8 = exports.hexToNumber = exports.bytesToNumber = void 0;
const utils_1 = require("@noble/curves/abstract/utils");
const buffer_1 = require("buffer/");
function bytesToNumber(bytes) {
return hexToNumber((0, utils_1.bytesToHex)(bytes));
}
exports.bytesToNumber = bytesToNumber;
function hexToNumber(hex) {
return BigInt(`0x${hex}`);
}
exports.hexToNumber = hexToNumber;
function encodeBase64toUint8(base64String) {
return buffer_1.Buffer.from(base64String, 'base64');
}
exports.encodeBase64toUint8 = encodeBase64toUint8;
//# sourceMappingURL=utils.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/util/utils.ts"],"names":[],"mappings":";;;AAAA,wDAA0D;AAC1D,oCAAiC;AAEjC,SAAgB,aAAa,CAAC,KAAiB;IAC9C,OAAO,WAAW,CAAC,IAAA,kBAAU,EAAC,KAAK,CAAC,CAAC,CAAC;AACvC,CAAC;AAFD,sCAEC;AAED,SAAgB,WAAW,CAAC,GAAW;IACtC,OAAO,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;AAC3B,CAAC;AAFD,kCAEC;AAED,SAAgB,mBAAmB,CAAC,YAAoB;IACvD,OAAO,eAAM,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAFD,kDAEC"}

View File

@@ -1,100 +0,0 @@
{
"name": "@cashu/crypto",
"version": "0.2.7",
"description": "Basic cashu crypto functions",
"main": "./modules/index.js",
"files": [
"src",
"modules"
],
"scripts": {
"compile": "rm -rf modules && tsc && tsc -p tsconfig.esm.json",
"test": "jest --coverage --maxWorkers=1",
"dev": "tsc --watch",
"lint": "eslint --ext .js,.ts .",
"format": "prettier --write .",
"typedoc": "typedoc --entryPointStrategy expand ./src"
},
"repository": {
"type": "git",
"url": "git+https://github.com/cashubtc/cashu-crypto-ts.git"
},
"keywords": [
"blindsignature",
"ecash",
"chaumium",
"mint",
"cashu"
],
"author": "gandlaf21",
"license": "MIT",
"dependencies": {
"@noble/curves": "^1.3.0",
"@noble/hashes": "^1.3.3",
"@scure/bip32": "^1.3.3",
"@scure/bip39": "^1.2.2",
"buffer": "^6.0.3"
},
"devDependencies": {
"@types/jest": "^29.5.12",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"eslint": "^8.57.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-promise": "^6.1.1",
"jest": "^29.7.0",
"prettier": "^3.2.5",
"ts-jest": "^29.1.2",
"ts-jest-resolver": "^2.0.1",
"ts-node": "^10.9.2",
"typedoc": "^0.25.8",
"typescript": "^5.3.3"
},
"exports": {
"./modules": {
"types": "./modules/index.d.ts",
"import": "./modules/esm/index.js",
"default": "./modules/index.js"
},
"./modules/util": {
"types": "./modules/util/index.d.ts",
"import": "./modules/esm/util/index.js",
"default": "./modules/util/index.js"
},
"./modules/common": {
"types": "./modules/common/index.d.ts",
"import": "./modules/esm/common/index.js",
"default": "./modules/common/index.js"
},
"./modules/mint": {
"types": "./modules/mint/index.d.ts",
"import": "./modules/esm/mint/index.js",
"default": "./modules/mint/index.js"
},
"./modules/client": {
"types": "./modules/client/index.d.ts",
"import": "./modules/esm/client/index.js",
"default": "./modules/client/index.js"
},
"./modules/client/NUT09": {
"types": "./modules/client/NUT09.d.ts",
"import": "./modules/esm/client/NUT09.js",
"default": "./modules/client/NUT09.js"
},
"./modules/common/NUT11": {
"types": "./modules/common/NUT11.d.ts",
"import": "./modules/esm/common/NUT11.js",
"default": "./modules/common/NUT11.js"
},
"./modules/client/NUT11": {
"types": "./modules/client/NUT11.d.ts",
"import": "./modules/esm/client/NUT11.js",
"default": "./modules/client/NUT11.js"
},
"./modules/mint/NUT11": {
"types": "./modules/mint/NUT11.d.ts",
"import": "./modules/esm/mint/NUT11.js",
"default": "./modules/mint/NUT11.js"
}
}
}

View File

@@ -1,49 +0,0 @@
import { HDKey } from '@scure/bip32';
import { getKeysetIdInt } from '../common/index.js';
import { generateMnemonic, mnemonicToSeedSync } from '@scure/bip39';
import { wordlist } from '@scure/bip39/wordlists/english';
const STANDARD_DERIVATION_PATH = `m/129372'/0'`;
enum DerivationType {
SECRET = 0,
BLINDING_FACTOR = 1
}
export const deriveSecret = (seed: Uint8Array, keysetId: string, counter: number): Uint8Array => {
return derive(seed, keysetId, counter, DerivationType.SECRET);
};
export const deriveBlindingFactor = (
seed: Uint8Array,
keysetId: string,
counter: number
): Uint8Array => {
return derive(seed, keysetId, counter, DerivationType.BLINDING_FACTOR);
};
const derive = (
seed: Uint8Array,
keysetId: string,
counter: number,
secretOrBlinding: DerivationType
): Uint8Array => {
const hdkey = HDKey.fromMasterSeed(seed);
const keysetIdInt = getKeysetIdInt(keysetId);
const derivationPath = `${STANDARD_DERIVATION_PATH}/${keysetIdInt}'/${counter}'/${secretOrBlinding}`;
const derived = hdkey.derive(derivationPath);
if (derived.privateKey === null) {
throw new Error('Could not derive private key');
}
return derived.privateKey;
};
export const generateNewMnemonic = (): string => {
const mnemonic = generateMnemonic(wordlist, 128);
return mnemonic;
};
export const deriveSeedFromMnemonic = (mnemonic: string): Uint8Array => {
const seed = mnemonicToSeedSync(mnemonic);
return seed;
};

View File

@@ -1,47 +0,0 @@
import { PrivKey, bytesToHex, hexToBytes } from '@noble/curves/abstract/utils';
import { sha256 } from '@noble/hashes/sha256';
import { schnorr } from '@noble/curves/secp256k1';
import { randomBytes } from '@noble/hashes/utils';
import { parseSecret } from '../common/NUT11.js';
import { Proof, Secret } from '../common/index.js';
export const createP2PKsecret = (pubkey: string): Uint8Array => {
const newSecret: Secret = [
'P2PK',
{
nonce: bytesToHex(randomBytes(32)),
data: pubkey
}
];
const parsed = JSON.stringify(newSecret);
return new TextEncoder().encode(parsed);
};
export const signP2PKsecret = (secret: Uint8Array, privateKey: PrivKey) => {
const msghash = sha256(new TextDecoder().decode(secret));
const sig = schnorr.sign(msghash, privateKey);
return sig;
};
export const getSignedProofs = (proofs: Array<Proof>, privateKey: string): Array<Proof> => {
return proofs.map((p) => {
try {
const parsed: Secret = parseSecret(p.secret);
if (parsed[0] !== 'P2PK') {
throw new Error('unknown secret type');
}
return getSignedProof(p, hexToBytes(privateKey));
} catch (error) {
return p;
}
});
};
export const getSignedProof = (proof: Proof, privateKey: PrivKey): Proof => {
if (!proof.witness) {
proof.witness = {
signatures: [bytesToHex(signP2PKsecret(proof.secret, privateKey))]
};
}
return proof;
};

View File

@@ -1,87 +0,0 @@
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
import { secp256k1 } from '@noble/curves/secp256k1';
import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils';
import { bytesToNumber } from '../util/utils.js';
import type {
BlindSignature,
Proof,
SerializedBlindedMessage,
SerializedProof
} from '../common/index.js';
import { hashToCurve, pointFromHex } from '../common/index.js';
import { Witness } from '../common/index';
export type BlindedMessage = {
B_: ProjPointType<bigint>;
r: bigint;
secret: Uint8Array;
};
export function createRandomBlindedMessage(): BlindedMessage {
return blindMessage(randomBytes(32));
}
export function blindMessage(secret: Uint8Array, r?: bigint): BlindedMessage {
const Y = hashToCurve(secret);
if (!r) {
r = bytesToNumber(secp256k1.utils.randomPrivateKey());
}
const rG = secp256k1.ProjectivePoint.BASE.multiply(r);
const B_ = Y.add(rG);
return { B_, r, secret };
}
export function unblindSignature(
C_: ProjPointType<bigint>,
r: bigint,
A: ProjPointType<bigint>
): ProjPointType<bigint> {
const C = C_.subtract(A.multiply(r));
return C;
}
export function constructProofFromPromise(
promise: BlindSignature,
r: bigint,
secret: Uint8Array,
key: ProjPointType<bigint>
): Proof {
const A = key;
const C = unblindSignature(promise.C_, r, A);
const proof = {
id: promise.id,
amount: promise.amount,
secret,
C
};
return proof;
}
export const serializeProof = (proof: Proof): SerializedProof => {
return {
amount: proof.amount,
C: proof.C.toHex(true),
id: proof.id,
secret: new TextDecoder().decode(proof.secret),
witness: JSON.stringify(proof.witness)
};
};
export const deserializeProof = (proof: SerializedProof): Proof => {
return {
amount: proof.amount,
C: pointFromHex(proof.C),
id: proof.id,
secret: new TextEncoder().encode(proof.secret),
witness: proof.witness?JSON.parse(proof.witness):undefined
};
};
export const serializeBlindedMessage = (
bm: BlindedMessage,
amount: number
): SerializedBlindedMessage => {
return {
B_: bm.B_.toHex(true),
amount: amount
};
};

View File

@@ -1,12 +0,0 @@
import { Secret } from "./index.js";
export const parseSecret = (secret: string | Uint8Array): Secret => {
try {
if (secret instanceof Uint8Array) {
secret = new TextDecoder().decode(secret);
}
return JSON.parse(secret);
} catch (e) {
throw new Error("can't parse secret");
}
};

View File

@@ -1,154 +0,0 @@
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
import { secp256k1 } from '@noble/curves/secp256k1';
import { sha256 } from '@noble/hashes/sha256';
import { bytesToHex, hexToBytes } from '@noble/curves/abstract/utils';
import { bytesToNumber, encodeBase64toUint8, hexToNumber } from '../util/utils.js';
import { Buffer } from 'buffer/';
export type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N
? Acc[number]
: Enumerate<N, [...Acc, Acc['length']]>;
export type IntRange<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>;
export type MintKeys = { [k: string]: Uint8Array };
export type SerializedMintKeys = {
[k: string]: string;
};
export type Keyset = {
id: string;
unit: string;
active: boolean;
};
export type BlindSignature = {
C_: ProjPointType<bigint>;
amount: number;
id: string;
};
export type SerializedBlindSignature = {
C_: string;
amount: number;
id: string;
};
export type Proof = {
C: ProjPointType<bigint>;
secret: Uint8Array;
amount: number;
id: string;
witness?: Witness;
};
export type SerializedProof = {
C: string;
secret: string;
amount: number;
id: string;
witness?: string;
};
export type SerializedBlindedMessage = {
B_: string;
amount: number;
witness?: string;
};
export type Secret = [WellKnownSecret, SecretData];
export type WellKnownSecret = 'P2PK';
export type SecretData = {
nonce: string;
data: string;
tags?: Array<Array<string>>;
};
export type Witness = {
signatures: Array<string>;
};
export type Tags = {
[k: string]: string;
};
export type SigFlag = 'SIG_INPUTS' | 'SIG_ALL';
const DOMAIN_SEPARATOR = hexToBytes('536563703235366b315f48617368546f43757276655f43617368755f');
export function hashToCurve(secret: Uint8Array): ProjPointType<bigint> {
const msgToHash = sha256(Buffer.concat([DOMAIN_SEPARATOR, secret]));
const counter = new Uint32Array(1);
const maxIterations = 2 ** 16;
for (let i = 0; i < maxIterations; i++) {
const counterBytes = new Uint8Array(counter.buffer);
const hash = sha256(Buffer.concat([msgToHash, counterBytes]));
try {
return pointFromHex(bytesToHex(Buffer.concat([new Uint8Array([0x02]), hash])));
} catch (error) {
counter[0]++;
}
}
throw new Error('No valid point found');
}
export function pointFromHex(hex: string) {
return secp256k1.ProjectivePoint.fromHex(hex);
}
export const getKeysetIdInt = (keysetId: string): bigint => {
let keysetIdInt: bigint;
if (/^[a-fA-F0-9]+$/.test(keysetId)) {
keysetIdInt = hexToNumber(keysetId) % BigInt(2 ** 31 - 1);
} else {
//legacy keyset compatibility
keysetIdInt = bytesToNumber(encodeBase64toUint8(keysetId)) % BigInt(2 ** 31 - 1);
}
return keysetIdInt;
};
export function createRandomPrivateKey() {
return secp256k1.utils.randomPrivateKey();
}
export function serializeMintKeys(mintKeys: MintKeys): SerializedMintKeys {
const serializedMintKeys: SerializedMintKeys = {};
Object.keys(mintKeys).forEach((p) => {
serializedMintKeys[p] = bytesToHex(mintKeys[p]);
});
return serializedMintKeys;
}
export function deserializeMintKeys(serializedMintKeys: SerializedMintKeys): MintKeys {
const mintKeys: MintKeys = {};
Object.keys(serializedMintKeys).forEach((p) => {
mintKeys[p] = hexToBytes(serializedMintKeys[p]);
});
return mintKeys;
}
export function deriveKeysetId(keys: MintKeys): string {
const KEYSET_VERSION = '00';
const mapBigInt = (k: [string, string]): [bigint, string] => {
return [BigInt(k[0]), k[1]];
};
const pubkeysConcat = Object.entries(serializeMintKeys(keys))
.map(mapBigInt)
.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0))
.map(([, pubKey]) => hexToBytes(pubKey))
.reduce((prev, curr) => mergeUInt8Arrays(prev, curr), new Uint8Array());
const hash = sha256(pubkeysConcat);
const hashHex = Buffer.from(hash).toString('hex').slice(0, 14);
return '00' + hashHex;
}
function mergeUInt8Arrays(a1: Uint8Array, a2: Uint8Array): Uint8Array {
// sum of individual array lengths
const mergedArray = new Uint8Array(a1.length + a2.length);
mergedArray.set(a1);
mergedArray.set(a2, a1.length);
return mergedArray;
}

View File

@@ -1 +0,0 @@
throw new Error('Incorrect usage. Import submodules instead');

View File

@@ -1,44 +0,0 @@
import { schnorr } from '@noble/curves/secp256k1';
import { sha256 } from '@noble/hashes/sha256';
import { parseSecret } from '../common/NUT11.js';
import { Proof } from '../common/index.js';
export const verifyP2PKSig = (proof: Proof) => {
if (!proof.witness) {
throw new Error('could not verify signature, no witness provided');
}
const parsedSecret = parseSecret(proof.secret);
// const tags = {} as Tags
// parsedSecret[1].tags.forEach((e: string[]) => {tags[e[0]]=e.shift()})
// if (tags.locktime) {
// const locktime = parseInt(tags.locktime[1])
// let isUnlocked = false
// if (Math.floor(Date.now() / 1000)>=locktime) {
// isUnlocked = true
// }
// }
// if (tags.sigflag as SigFlag) {
// if (tags.sigflag[0]==='SIG_INPUT') {
// }
// else if(tags.sigflag[0]==='SIG_ALL') {
// }
// else {
// throw new Error("Unknown sigflag");
// }
// }
// if (tags.n_sigs) {
// if (tags.pubkeys) {
// }
// }
return schnorr.verify(
proof.witness.signatures[0],
sha256(new TextDecoder().decode(proof.secret)),
parsedSecret[1].data
);
};

View File

@@ -1,66 +0,0 @@
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
import { secp256k1 } from '@noble/curves/secp256k1';
import { bytesToNumber } from '../util/utils.js';
import { BlindSignature, IntRange, Keyset, MintKeys, Proof } from '../common/index.js';
import { createRandomPrivateKey, deriveKeysetId, hashToCurve } from '../common/index.js';
import { HDKey } from '@scure/bip32';
const DERIVATION_PATH = "m/0'/0'/0'";
export type KeysetPair = {
keysetId: string;
pubKeys: MintKeys;
privKeys: MintKeys;
};
export type KeysetWithKeys = Keyset & {
pubKeys: MintKeys;
};
export function createBlindSignature(
B_: ProjPointType<bigint>,
privateKey: Uint8Array,
amount: number,
id: string
): BlindSignature {
const C_: ProjPointType<bigint> = B_.multiply(bytesToNumber(privateKey));
return { C_, amount, id };
}
export function getPubKeyFromPrivKey(privKey: Uint8Array) {
return secp256k1.getPublicKey(privKey, true);
}
export function createNewMintKeys(pow2height: IntRange<0, 65>, seed?: Uint8Array): KeysetPair {
let counter = 0n;
const pubKeys: MintKeys = {};
const privKeys: MintKeys = {};
let masterKey;
if (seed) {
masterKey = HDKey.fromMasterSeed(seed);
}
while (counter < pow2height) {
const index: string = (2n ** counter).toString();
if (masterKey) {
const k = masterKey.derive(`${DERIVATION_PATH}/${counter}`).privateKey;
if (k) {
privKeys[index] = k;
} else {
throw new Error(`Could not derive Private key from: ${DERIVATION_PATH}/${counter}`);
}
} else {
privKeys[index] = createRandomPrivateKey();
}
pubKeys[index] = getPubKeyFromPrivKey(privKeys[index]);
counter++;
}
const keysetId = deriveKeysetId(pubKeys);
return { pubKeys, privKeys, keysetId };
}
export function verifyProof(proof: Proof, privKey: Uint8Array): boolean {
const Y: ProjPointType<bigint> = hashToCurve(proof.secret);
const aY: ProjPointType<bigint> = Y.multiply(bytesToNumber(privKey));
return aY.equals(proof.C);
}

View File

@@ -1,14 +0,0 @@
import { bytesToHex } from '@noble/curves/abstract/utils';
import { Buffer } from 'buffer/';
export function bytesToNumber(bytes: Uint8Array): bigint {
return hexToNumber(bytesToHex(bytes));
}
export function hexToNumber(hex: string): bigint {
return BigInt(`0x${hex}`);
}
export function encodeBase64toUint8(base64String: string): Uint8Array {
return Buffer.from(base64String, 'base64');
}

View File

@@ -1,6 +1,6 @@
{
"name": "applesauce-accounts",
"version": "3.1.0",
"version": "4.0.0",
"description": "A simple nostr account management system",
"type": "module",
"main": "dist/index.js",
@@ -33,21 +33,23 @@
},
"dependencies": {
"@noble/hashes": "^1.7.1",
"applesauce-signers": "^3.1.0",
"applesauce-core": "^3.1.0",
"applesauce-core": "^4.0.0",
"applesauce-signers": "^4.0.0",
"nanoid": "^5.1.5",
"nostr-tools": "~2.15",
"nostr-tools": "~2.17",
"rxjs": "^7.8.1"
},
"devDependencies": {
"rimraf": "^6.0.1",
"typescript": "^5.8.3",
"vitest": "^3.2.3"
"vitest": "^3.2.4"
},
"funding": {
"type": "lightning",
"url": "lightning:nostrudel@geyser.fund"
},
"scripts": {
"prebuild": "rimraf dist",
"build": "tsc",
"watch:build": "tsc --watch > /dev/null",
"test": "vitest run --passWithNoTests",

View File

@@ -1,3 +1,4 @@
export * from "./app-data.js";
export * from "./blocked-relays.js";
export * from "./blossom.js";
export * from "./bookmarks.js";

View File

@@ -1,3 +1,4 @@
export * from "./app-data.js";
export * from "./blocked-relays.js";
export * from "./blossom.js";
export * from "./bookmarks.js";

View File

@@ -1,6 +1,6 @@
{
"name": "applesauce-actions",
"version": "3.1.0",
"version": "4.0.0",
"description": "A package for performing common nostr actions",
"type": "module",
"main": "dist/index.js",
@@ -32,24 +32,26 @@
}
},
"dependencies": {
"applesauce-core": "^3.1.0",
"applesauce-factory": "^3.1.0",
"nostr-tools": "~2.15",
"applesauce-core": "^4.0.0",
"applesauce-factory": "^4.0.0",
"nostr-tools": "~2.17",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@hirez_io/observer-spy": "^2.2.0",
"@types/debug": "^4.1.12",
"applesauce-signers": "^3.1.0",
"applesauce-signers": "^4.0.0",
"nanoid": "^5.1.5",
"rimraf": "^6.0.1",
"typescript": "^5.8.3",
"vitest": "^3.2.3"
"vitest": "^3.2.4"
},
"funding": {
"type": "lightning",
"url": "lightning:nostrudel@geyser.fund"
},
"scripts": {
"prebuild": "rimraf dist",
"build": "tsc",
"watch:build": "tsc --watch > /dev/null",
"test": "vitest run --passWithNoTests",

View File

@@ -1,84 +0,0 @@
import { Filter, NostrEvent } from "nostr-tools";
import { Subject } from "rxjs";
import { LRU } from "../helpers/lru.js";
import { IEventSet } from "./interface.js";
/**
* A set of nostr events that can be queried and subscribed to
* NOTE: does not handle replaceable events or any deletion logic
*/
export declare class EventSet implements IEventSet {
protected log: import("debug").Debugger;
/** Indexes */
protected kinds: Map<number, Set<import("nostr-tools").Event>>;
protected authors: Map<string, Set<import("nostr-tools").Event>>;
protected tags: LRU<Set<import("nostr-tools").Event>>;
protected created_at: NostrEvent[];
/** LRU cache of last events touched */
events: LRU<import("nostr-tools").Event>;
/** A sorted array of replaceable events by address */
protected replaceable: Map<string, import("nostr-tools").Event[]>;
/** A stream of events inserted into the database */
insert$: Subject<import("nostr-tools").Event>;
/** A stream of events that have been updated */
update$: Subject<import("nostr-tools").Event>;
/** A stream of events removed from the database */
remove$: Subject<import("nostr-tools").Event>;
/** A method thats called before a new event is inserted */
onBeforeInsert?: (event: NostrEvent) => boolean;
/** The number of events in the event set */
get size(): number;
/** Moves an event to the top of the LRU cache */
touch(event: NostrEvent): void;
/** Checks if the database contains an event without touching it */
hasEvent(id: string): boolean;
/** Gets a single event based on id */
getEvent(id: string): NostrEvent | undefined;
/** Checks if the event set has a replaceable event */
hasReplaceable(kind: number, pubkey: string, identifier?: string): boolean;
/** Gets the latest replaceable event */
getReplaceable(kind: number, pubkey: string, identifier?: string): NostrEvent | undefined;
/** Gets the history of a replaceable event */
getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined;
/** Gets all events that match the filters */
getByFilters(filters: Filter | Filter[]): Set<NostrEvent>;
/** Gets a timeline of events that match the filters */
getTimeline(filters: Filter | Filter[]): NostrEvent[];
/** Inserts an event into the database and notifies all subscriptions */
add(event: NostrEvent): NostrEvent | null;
/** Inserts and event into the database and notifies all subscriptions that the event has updated */
update(event: NostrEvent): boolean;
/** Removes an event from the database and notifies all subscriptions */
remove(eventOrId: string | NostrEvent): boolean;
/** A weak map of events that are claimed by other things */
protected claims: WeakMap<import("nostr-tools").Event, any>;
/** Sets the claim on the event and touches it */
claim(event: NostrEvent, claim: any): void;
/** Checks if an event is claimed by anything */
isClaimed(event: NostrEvent): boolean;
/** Removes a claim from an event */
removeClaim(event: NostrEvent, claim: any): void;
/** Removes all claims on an event */
clearClaim(event: NostrEvent): void;
/** Index helper methods */
protected getKindIndex(kind: number): Set<import("nostr-tools").Event>;
protected getAuthorsIndex(author: string): Set<import("nostr-tools").Event>;
protected getTagIndex(tagAndValue: string): Set<import("nostr-tools").Event>;
/** Iterates over all events by author */
iterateAuthors(authors: Iterable<string>): Generator<NostrEvent>;
/** Iterates over all events by indexable tag and value */
iterateTag(tag: string, values: Iterable<string>): Generator<NostrEvent>;
/** Iterates over all events by kind */
iterateKinds(kinds: Iterable<number>): Generator<NostrEvent>;
/** Iterates over all events by time */
iterateTime(since: number | undefined, until: number | undefined): Generator<NostrEvent>;
/** Iterates over all events by id */
iterateIds(ids: Iterable<string>): Generator<NostrEvent>;
/** Returns all events that match the filter */
getEventsForFilter(filter: Filter): Set<NostrEvent>;
/** Returns all events that match the filters */
getEventsForFilters(filters: Filter[]): Set<NostrEvent>;
/** Remove the oldest events that are not claimed */
prune(limit?: number): number;
/** Resets the event set */
reset(): void;
}

View File

@@ -1,359 +0,0 @@
import { binarySearch, insertEventIntoDescendingList } from "nostr-tools/utils";
import { Subject } from "rxjs";
import { getIndexableTags, INDEXABLE_TAGS } from "../helpers/event-tags.js";
import { createReplaceableAddress, isReplaceable } from "../helpers/event.js";
import { LRU } from "../helpers/lru.js";
import { logger } from "../logger.js";
/**
* A set of nostr events that can be queried and subscribed to
* NOTE: does not handle replaceable events or any deletion logic
*/
export class EventSet {
log = logger.extend("EventSet");
/** Indexes */
kinds = new Map();
authors = new Map();
tags = new LRU();
created_at = [];
/** LRU cache of last events touched */
events = new LRU();
/** A sorted array of replaceable events by address */
replaceable = new Map();
/** A stream of events inserted into the database */
insert$ = new Subject();
/** A stream of events that have been updated */
update$ = new Subject();
/** A stream of events removed from the database */
remove$ = new Subject();
/** A method thats called before a new event is inserted */
onBeforeInsert;
/** The number of events in the event set */
get size() {
return this.events.size;
}
/** Moves an event to the top of the LRU cache */
touch(event) {
this.events.set(event.id, event);
}
/** Checks if the database contains an event without touching it */
hasEvent(id) {
return this.events.has(id);
}
/** Gets a single event based on id */
getEvent(id) {
return this.events.get(id);
}
/** Checks if the event set has a replaceable event */
hasReplaceable(kind, pubkey, identifier) {
const events = this.replaceable.get(createReplaceableAddress(kind, pubkey, identifier));
return !!events && events.length > 0;
}
/** Gets the latest replaceable event */
getReplaceable(kind, pubkey, identifier) {
const address = createReplaceableAddress(kind, pubkey, identifier);
const events = this.replaceable.get(address);
return events?.[0];
}
/** Gets the history of a replaceable event */
getReplaceableHistory(kind, pubkey, identifier) {
const address = createReplaceableAddress(kind, pubkey, identifier);
return this.replaceable.get(address);
}
/** Gets all events that match the filters */
getByFilters(filters) {
return this.getEventsForFilters(Array.isArray(filters) ? filters : [filters]);
}
/** Gets a timeline of events that match the filters */
getTimeline(filters) {
const timeline = [];
const events = this.getEventsForFilters(Array.isArray(filters) ? filters : [filters]);
for (const event of events)
insertEventIntoDescendingList(timeline, event);
return timeline;
}
/** Inserts an event into the database and notifies all subscriptions */
add(event) {
const id = event.id;
const current = this.events.get(id);
if (current)
return current;
// Ignore events if before insert returns false
if (this.onBeforeInsert?.(event) === false)
return null;
this.events.set(id, event);
this.getKindIndex(event.kind).add(event);
this.getAuthorsIndex(event.pubkey).add(event);
// Add the event to the tag indexes if they exist
for (const tag of getIndexableTags(event)) {
if (this.tags.has(tag))
this.getTagIndex(tag).add(event);
}
// Insert into time index
insertEventIntoDescendingList(this.created_at, event);
// Insert into replaceable index
if (isReplaceable(event.kind)) {
const identifier = event.tags.find((t) => t[0] === "d")?.[1];
const address = createReplaceableAddress(event.kind, event.pubkey, identifier);
let array = this.replaceable.get(address);
if (!this.replaceable.has(address)) {
// add an empty array if there is no array
array = [];
this.replaceable.set(address, array);
}
// insert the event into the sorted array
insertEventIntoDescendingList(array, event);
}
// Notify subscribers that the event was inserted
this.insert$.next(event);
return event;
}
/** Inserts and event into the database and notifies all subscriptions that the event has updated */
update(event) {
const inserted = this.add(event);
if (inserted)
this.update$.next(inserted);
return inserted !== null;
}
/** Removes an event from the database and notifies all subscriptions */
remove(eventOrId) {
let event = typeof eventOrId === "string" ? this.events.get(eventOrId) : eventOrId;
if (!event)
throw new Error("Missing event");
const id = event.id;
// only remove events that are known
if (!this.events.has(id))
return false;
this.getAuthorsIndex(event.pubkey).delete(event);
this.getKindIndex(event.kind).delete(event);
for (const tag of getIndexableTags(event)) {
if (this.tags.has(tag)) {
this.getTagIndex(tag).delete(event);
}
}
// remove from created_at index
const i = this.created_at.indexOf(event);
this.created_at.splice(i, 1);
this.events.delete(id);
// remove from replaceable index
if (isReplaceable(event.kind)) {
const identifier = event.tags.find((t) => t[0] === "d")?.[1];
const address = createReplaceableAddress(event.kind, event.pubkey, identifier);
const array = this.replaceable.get(address);
if (array && array.includes(event)) {
const idx = array.indexOf(event);
array.splice(idx, 1);
}
}
// remove any claims this event has
this.claims.delete(event);
// notify subscribers this event was removed
this.remove$.next(event);
return true;
}
/** A weak map of events that are claimed by other things */
claims = new WeakMap();
/** Sets the claim on the event and touches it */
claim(event, claim) {
if (!this.claims.has(event)) {
this.claims.set(event, claim);
}
// always touch event
this.touch(event);
}
/** Checks if an event is claimed by anything */
isClaimed(event) {
return this.claims.has(event);
}
/** Removes a claim from an event */
removeClaim(event, claim) {
const current = this.claims.get(event);
if (current === claim)
this.claims.delete(event);
}
/** Removes all claims on an event */
clearClaim(event) {
this.claims.delete(event);
}
/** Index helper methods */
getKindIndex(kind) {
if (!this.kinds.has(kind))
this.kinds.set(kind, new Set());
return this.kinds.get(kind);
}
getAuthorsIndex(author) {
if (!this.authors.has(author))
this.authors.set(author, new Set());
return this.authors.get(author);
}
getTagIndex(tagAndValue) {
if (!this.tags.has(tagAndValue)) {
// build new tag index from existing events
const events = new Set();
const ts = Date.now();
for (const event of this.events.values()) {
if (getIndexableTags(event).has(tagAndValue)) {
events.add(event);
}
}
const took = Date.now() - ts;
if (took > 100)
this.log(`Built index ${tagAndValue} took ${took}ms`);
this.tags.set(tagAndValue, events);
}
return this.tags.get(tagAndValue);
}
/** Iterates over all events by author */
*iterateAuthors(authors) {
for (const author of authors) {
const events = this.authors.get(author);
if (events) {
for (const event of events)
yield event;
}
}
}
/** Iterates over all events by indexable tag and value */
*iterateTag(tag, values) {
for (const value of values) {
const events = this.getTagIndex(tag + ":" + value);
if (events) {
for (const event of events)
yield event;
}
}
}
/** Iterates over all events by kind */
*iterateKinds(kinds) {
for (const kind of kinds) {
const events = this.kinds.get(kind);
if (events) {
for (const event of events)
yield event;
}
}
}
/** Iterates over all events by time */
*iterateTime(since, until) {
let untilIndex = 0;
let sinceIndex = this.created_at.length - 1;
let start = until
? binarySearch(this.created_at, (mid) => {
return mid.created_at - until;
})
: undefined;
if (start)
untilIndex = start[0];
const end = since
? binarySearch(this.created_at, (mid) => {
return mid.created_at - since;
})
: undefined;
if (end)
sinceIndex = end[0];
for (let i = untilIndex; i < sinceIndex; i++) {
yield this.created_at[i];
}
}
/** Iterates over all events by id */
*iterateIds(ids) {
for (const id of ids) {
if (this.events.has(id))
yield this.events.get(id);
}
}
/** Returns all events that match the filter */
getEventsForFilter(filter) {
// search is not supported, return an empty set
if (filter.search)
return new Set();
let first = true;
let events = new Set();
const and = (iterable) => {
const set = iterable instanceof Set ? iterable : new Set(iterable);
if (first) {
events = set;
first = false;
}
else {
for (const event of events) {
if (!set.has(event))
events.delete(event);
}
}
return events;
};
if (filter.ids)
and(this.iterateIds(filter.ids));
let time = null;
// query for time first if since is set
if (filter.since !== undefined) {
time = Array.from(this.iterateTime(filter.since, filter.until));
and(time);
}
for (const t of INDEXABLE_TAGS) {
const key = `#${t}`;
const values = filter[key];
if (values?.length)
and(this.iterateTag(t, values));
}
if (filter.authors)
and(this.iterateAuthors(filter.authors));
if (filter.kinds)
and(this.iterateKinds(filter.kinds));
// query for time last if only until is set
if (filter.since === undefined && filter.until !== undefined) {
time = Array.from(this.iterateTime(filter.since, filter.until));
and(time);
}
// if the filter queried on time and has a limit. truncate the events now
if (filter.limit && time) {
const limited = new Set();
for (const event of time) {
if (limited.size >= filter.limit)
break;
if (events.has(event))
limited.add(event);
}
return limited;
}
return events;
}
/** Returns all events that match the filters */
getEventsForFilters(filters) {
if (filters.length === 0)
throw new Error("No Filters");
let events = new Set();
for (const filter of filters) {
const filtered = this.getEventsForFilter(filter);
for (const event of filtered)
events.add(event);
}
return events;
}
/** Remove the oldest events that are not claimed */
prune(limit = 1000) {
let removed = 0;
let cursor = this.events.first;
while (cursor) {
const event = cursor.value;
if (!this.isClaimed(event)) {
this.remove(event);
removed++;
if (removed >= limit)
break;
}
cursor = cursor.next;
}
return removed;
}
/** Resets the event set */
reset() {
this.events.clear();
this.kinds.clear();
this.authors.clear();
this.tags.clear();
this.created_at = [];
this.replaceable.clear();
this.claims = new WeakMap();
}
}

View File

@@ -1,12 +1,47 @@
import { Filter, NostrEvent } from "nostr-tools";
import { Observable } from "rxjs";
import { AddressPointer, EventPointer, ProfilePointer } from "nostr-tools/nip19";
import { AddressPointer, EventPointer } from "nostr-tools/nip19";
import { Observable, Subject } from "rxjs";
import { AddressPointerWithoutD } from "../helpers/pointers.js";
import { EventSet } from "./event-set.js";
import { IEventStore, ModelConstructor } from "./interface.js";
/** An extended {@link EventSet} that handles replaceable events, delets, and models */
export declare class EventStore implements IEventStore {
database: EventSet;
import { EventMemory } from "./event-memory.js";
import { IEventDatabase, IEventStore } from "./interface.js";
declare const EventStore_base: {
new (...args: any[]): {
[x: string]: any;
models: Map<import("./interface.js").ModelConstructor<any, any[], IEventStore | import("./interface.js").IAsyncEventStore>, Map<string, Observable<any>>>;
modelKeepWarm: number;
model<T extends unknown, Args extends Array<any>>(constructor: import("./interface.js").ModelConstructor<T, Args, IEventStore | import("./interface.js").IAsyncEventStore>, ...args: Args): Observable<T>;
filters(filters: Filter | Filter[], onlyNew?: boolean): Observable<NostrEvent>;
event(pointer: string | EventPointer): Observable<NostrEvent | undefined>;
replaceable(pointer: AddressPointer | AddressPointerWithoutD): Observable<NostrEvent | undefined>;
replaceable(kind: number, pubkey: string, identifier?: string): Observable<NostrEvent | undefined>;
addressable(pointer: AddressPointer): Observable<NostrEvent | undefined>;
timeline(filters: Filter | Filter[], includeOldVersion?: boolean): Observable<NostrEvent[]>;
profile(user: string | import("nostr-tools/nip19").ProfilePointer): Observable<import("../helpers/profile.js").ProfileContent | undefined>;
contacts(user: string | import("nostr-tools/nip19").ProfilePointer): Observable<import("nostr-tools/nip19").ProfilePointer[]>;
mutes(user: string | import("nostr-tools/nip19").ProfilePointer): Observable<import("../helpers/mutes.js").Mutes | undefined>;
mailboxes(user: string | import("nostr-tools/nip19").ProfilePointer): Observable<{
inboxes: string[];
outboxes: string[];
} | undefined>;
blossomServers(user: string | import("nostr-tools/nip19").ProfilePointer): Observable<URL[]>;
reactions(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
thread(root: string | EventPointer | AddressPointer): Observable<import("../models/thread.js").Thread>;
comments(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
events(ids: string[]): Observable<Record<string, NostrEvent | undefined>>;
replaceableSet(pointers: {
kind: number;
pubkey: string;
identifier?: string;
}[]): Observable<Record<string, NostrEvent | undefined>>;
};
} & {
new (): {};
};
/** A wrapper around an event database that handles replaceable events, deletes, and models */
export declare class EventStore extends EventStore_base implements IEventStore {
database: IEventDatabase;
/** Optional memory database for ensuring single event instances */
memory?: EventMemory;
/** Enable this to keep old versions of replaceable events */
keepOldVersions: boolean;
/** Enable this to keep expired events */
@@ -17,11 +52,11 @@ export declare class EventStore implements IEventStore {
*/
verifyEvent?: (event: NostrEvent) => boolean;
/** A stream of new events added to the store */
insert$: Observable<NostrEvent>;
insert$: Subject<import("nostr-tools").Event>;
/** A stream of events that have been updated */
update$: Observable<NostrEvent>;
update$: Subject<import("nostr-tools").Event>;
/** A stream of events that have been removed */
remove$: Observable<NostrEvent>;
remove$: Subject<import("nostr-tools").Event>;
/**
* A method that will be called when an event isn't found in the store
* @experimental
@@ -37,7 +72,9 @@ export declare class EventStore implements IEventStore {
* @experimental
*/
addressableLoader?: (pointer: AddressPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
constructor();
constructor(database?: IEventDatabase);
/** A method to add all events to memory to ensure there is only ever a single instance of an event */
private mapToMemory;
protected deletedIds: Set<string>;
protected deletedCoords: Map<string, number>;
protected checkDeleted(event: string | NostrEvent): boolean;
@@ -57,12 +94,10 @@ export declare class EventStore implements IEventStore {
* @returns The existing event or the event that was added, if it was ignored returns null
*/
add(event: NostrEvent, fromRelay?: string): NostrEvent | null;
/** Removes an event from the database and updates subscriptions */
/** Removes an event from the store and updates subscriptions */
remove(event: string | NostrEvent): boolean;
/** Add an event to the store and notifies all subscribes it has updated */
update(event: NostrEvent): boolean;
/** Removes any event that is not being used by a subscription */
prune(max?: number): number;
/** Check if the store has an event by id */
hasEvent(id: string): boolean;
/** Get an event by id from the store */
@@ -74,9 +109,11 @@ export declare class EventStore implements IEventStore {
/** Returns all versions of a replaceable event */
getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined;
/** Get all events matching a filter */
getByFilters(filters: Filter | Filter[]): Set<NostrEvent>;
getByFilters(filters: Filter | Filter[]): NostrEvent[];
/** Returns a timeline of events that match filters */
getTimeline(filters: Filter | Filter[]): NostrEvent[];
/** Passthrough method for the database.touch */
touch(event: NostrEvent): void | undefined;
/** Sets the claim on the event and touches it */
claim(event: NostrEvent, claim: any): void;
/** Checks if an event is claimed by anything */
@@ -85,56 +122,13 @@ export declare class EventStore implements IEventStore {
removeClaim(event: NostrEvent, claim: any): void;
/** Removes all claims on an event */
clearClaim(event: NostrEvent): void;
/** A directory of all active models */
protected models: Map<ModelConstructor<any, any[]>, Map<string, Observable<any>>>;
/** How long a model should be kept "warm" while nothing is subscribed to it */
modelKeepWarm: number;
/** Get or create a model on the event store */
model<T extends unknown, Args extends Array<any>>(constructor: ModelConstructor<T, Args>, ...args: Args): Observable<T>;
/**
* Creates an observable that streams all events that match the filter
* @param filters
* @param [onlyNew=false] Only subscribe to new events
*/
filters(filters: Filter | Filter[], onlyNew?: boolean): Observable<NostrEvent>;
/** Pass through method for the database.unclaimed */
unclaimed(): Generator<NostrEvent>;
/** Removes any event that is not being used by a subscription */
prune(limit?: number): number;
/** Returns an observable that completes when an event is removed */
removed(id: string): Observable<never>;
/** Creates an observable that emits when event is updated */
updated(event: string | NostrEvent): Observable<NostrEvent>;
/** Creates a {@link EventModel} */
event(pointer: string | EventPointer): Observable<NostrEvent | undefined>;
/** Creates a {@link ReplaceableModel} */
replaceable(pointer: AddressPointer | AddressPointerWithoutD): Observable<NostrEvent | undefined>;
replaceable(kind: number, pubkey: string, identifier?: string): Observable<NostrEvent | undefined>;
/** Subscribe to an addressable event by pointer */
addressable(pointer: AddressPointer): Observable<NostrEvent | undefined>;
/** Creates a {@link TimelineModel} */
timeline(filters: Filter | Filter[], includeOldVersion?: boolean): Observable<NostrEvent[]>;
/** Subscribe to a users profile */
profile(user: string | ProfilePointer): Observable<import("../helpers/profile.js").ProfileContent | undefined>;
/** Subscribe to a users contacts */
contacts(user: string | ProfilePointer): Observable<ProfilePointer[]>;
/** Subscribe to a users mutes */
mutes(user: string | ProfilePointer): Observable<import("../helpers/mutes.js").Mutes | undefined>;
/** Subscribe to a users NIP-65 mailboxes */
mailboxes(user: string | ProfilePointer): Observable<{
inboxes: string[];
outboxes: string[];
} | undefined>;
/** Subscribe to a users blossom servers */
blossomServers(user: string | ProfilePointer): Observable<URL[]>;
/** Subscribe to an event's reactions */
reactions(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
/** Subscribe to a thread */
thread(root: string | EventPointer | AddressPointer): Observable<import("../models/thread.js").Thread>;
/** Subscribe to a event's comments */
comments(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
/** @deprecated use multiple {@link EventModel} instead */
events(ids: string[]): Observable<Record<string, NostrEvent | undefined>>;
/** @deprecated use multiple {@link ReplaceableModel} instead */
replaceableSet(pointers: {
kind: number;
pubkey: string;
identifier?: string;
}[]): Observable<Record<string, NostrEvent | undefined>>;
}
export {};

View File

@@ -1,26 +1,20 @@
import { kinds } from "nostr-tools";
import { isAddressableKind } from "nostr-tools/kinds";
import { EMPTY, filter, finalize, from, merge, mergeMap, ReplaySubject, share, take, timer } from "rxjs";
import hash_sum from "hash-sum";
import { EMPTY, filter, mergeMap, Subject, take } from "rxjs";
import { getDeleteCoordinates, getDeleteIds } from "../helpers/delete.js";
import { createReplaceableAddress, EventStoreSymbol, FromCacheSymbol, isReplaceable } from "../helpers/event.js";
import { getExpirationTimestamp } from "../helpers/expiration.js";
import { matchFilters } from "../helpers/filter.js";
import { parseCoordinate } from "../helpers/pointers.js";
import { addSeenRelay, getSeenRelays } from "../helpers/relays.js";
import { unixNow } from "../helpers/time.js";
import { UserBlossomServersModel } from "../models/blossom.js";
import { EventModel, EventsModel, ReplaceableModel, ReplaceableSetModel, TimelineModel } from "../models/common.js";
import { ContactsModel } from "../models/contacts.js";
import { CommentsModel, ThreadModel } from "../models/index.js";
import { MailboxesModel } from "../models/mailboxes.js";
import { MuteModel } from "../models/mutes.js";
import { ProfileModel } from "../models/profile.js";
import { ReactionsModel } from "../models/reactions.js";
import { EventSet } from "./event-set.js";
/** An extended {@link EventSet} that handles replaceable events, delets, and models */
export class EventStore {
import { EventMemory } from "./event-memory.js";
import { EventStoreModelMixin } from "./model-mixin.js";
/** A wrapper around an event database that handles replaceable events, deletes, and models */
export class EventStore extends EventStoreModelMixin(class {
}) {
database;
/** Optional memory database for ensuring single event instances */
memory;
/** Enable this to keep old versions of replaceable events */
keepOldVersions = false;
/** Enable this to keep expired events */
@@ -31,11 +25,11 @@ export class EventStore {
*/
verifyEvent;
/** A stream of new events added to the store */
insert$;
insert$ = new Subject();
/** A stream of events that have been updated */
update$;
update$ = new Subject();
/** A stream of events that have been removed */
remove$;
remove$ = new Subject();
/**
* A method that will be called when an event isn't found in the store
* @experimental
@@ -51,27 +45,31 @@ export class EventStore {
* @experimental
*/
addressableLoader;
constructor() {
this.database = new EventSet();
// verify events before they are added to the database
this.database.onBeforeInsert = (event) => {
// Ignore events that are invalid
if (this.verifyEvent && this.verifyEvent(event) === false)
return false;
else
return true;
};
constructor(database = new EventMemory()) {
super();
if (database) {
this.database = database;
this.memory = new EventMemory();
}
else {
// If no database is provided, its the same as having a memory database
this.database = this.memory = new EventMemory();
}
// when events are added to the database, add the symbol
this.database.insert$.subscribe((event) => {
this.insert$.subscribe((event) => {
Reflect.set(event, EventStoreSymbol, this);
});
// when events are removed from the database, remove the symbol
this.database.remove$.subscribe((event) => {
this.remove$.subscribe((event) => {
Reflect.deleteProperty(event, EventStoreSymbol);
});
this.insert$ = this.database.insert$;
this.update$ = this.database.update$;
this.remove$ = this.database.remove$;
}
mapToMemory(event) {
if (event === undefined)
return undefined;
if (!this.memory)
return event;
return this.memory.add(event);
}
// delete state
deletedIds = new Set();
@@ -137,9 +135,7 @@ export class EventStore {
for (const id of ids) {
this.deletedIds.add(id);
// remove deleted events in the database
const event = this.database.getEvent(id);
if (event)
this.database.remove(event);
this.remove(id);
}
const coords = getDeleteCoordinates(deleteEvent);
for (const coord of coords) {
@@ -152,7 +148,7 @@ export class EventStore {
const events = this.database.getReplaceableHistory(parsed.kind, parsed.pubkey, parsed.identifier) ?? [];
for (const event of events) {
if (event.created_at < deleteEvent.created_at)
this.database.remove(event);
this.remove(event);
}
}
}
@@ -173,6 +169,7 @@ export class EventStore {
* @returns The existing event or the event that was added, if it was ignored returns null
*/
add(event, fromRelay) {
// Handle delete events differently
if (event.kind === kinds.EventDeletion)
this.handleDeleteEvent(event);
// Ignore if the event was deleted
@@ -193,32 +190,38 @@ export class EventStore {
return existing[0];
}
}
else if (this.database.hasEvent(event.id)) {
// Duplicate event, copy symbols and return existing event
const existing = this.database.getEvent(event.id);
if (existing) {
// Verify event before inserting into the database
if (this.verifyEvent && this.verifyEvent(event) === false)
return null;
// Always add event to memory
const existing = this.memory?.add(event);
// If the memory returned a different instance, this is a duplicate event
if (existing && existing !== event) {
// Copy cached symbols and return existing event
EventStore.mergeDuplicateEvent(event, existing);
// attach relay this event was from
if (fromRelay)
addSeenRelay(existing, fromRelay);
return existing;
}
}
// Insert event into database
const inserted = this.database.add(event);
// If the event was ignored, return null
if (inserted === null)
return null;
const inserted = this.mapToMemory(this.database.add(event));
// Copy cached data if its a duplicate event
if (event !== inserted)
EventStore.mergeDuplicateEvent(event, inserted);
// attach relay this event was from
if (fromRelay)
addSeenRelay(inserted, fromRelay);
// Emit insert$ signal
if (inserted === event)
this.insert$.next(inserted);
// remove all old version of the replaceable event
if (!this.keepOldVersions && isReplaceable(event.kind)) {
const existing = this.database.getReplaceableHistory(event.kind, event.pubkey, identifier);
if (existing) {
if (existing && existing.length > 0) {
const older = Array.from(existing).filter((e) => e.created_at < event.created_at);
for (const old of older)
this.database.remove(old);
this.remove(old);
// return the newest version of the replaceable event
// most of the time this will be === event, but not always
if (existing.length !== older.length)
@@ -230,108 +233,103 @@ export class EventStore {
this.handleExpiringEvent(inserted);
return inserted;
}
/** Removes an event from the database and updates subscriptions */
/** Removes an event from the store and updates subscriptions */
remove(event) {
return this.database.remove(event);
let instance = this.memory?.getEvent(typeof event === "string" ? event : event.id);
// Remove from memory if available
if (this.memory)
this.memory.remove(event);
// Remove the event from the database
const removed = this.database.remove(event);
// If the event was removed, notify the subscriptions
if (removed && instance) {
this.remove$.next(instance);
}
return removed;
}
/** Add an event to the store and notifies all subscribes it has updated */
update(event) {
return this.database.update(event);
}
/** Removes any event that is not being used by a subscription */
prune(max) {
return this.database.prune(max);
// Map the event to the current instance in the database
const e = this.database.add(event);
if (!e)
return false;
// Notify the database that the event has updated
this.database.update?.(event);
this.update$.next(event);
return true;
}
/** Check if the store has an event by id */
hasEvent(id) {
return this.database.hasEvent(id);
// Check if the event exists in memory first, then in the database
return this.memory?.hasEvent(id) || this.database.hasEvent(id);
}
/** Get an event by id from the store */
getEvent(id) {
return this.database.getEvent(id);
// Get the event from memory first, then from the database
return this.memory?.getEvent(id) ?? this.mapToMemory(this.database.getEvent(id));
}
/** Check if the store has a replaceable event */
hasReplaceable(kind, pubkey, d) {
return this.database.hasReplaceable(kind, pubkey, d);
// Check if the event exists in memory first, then in the database
return this.memory?.hasReplaceable(kind, pubkey, d) || this.database.hasReplaceable(kind, pubkey, d);
}
/** Gets the latest version of a replaceable event */
getReplaceable(kind, pubkey, identifier) {
return this.database.getReplaceable(kind, pubkey, identifier);
// Get the event from memory first, then from the database
return (this.memory?.getReplaceable(kind, pubkey, identifier) ??
this.mapToMemory(this.database.getReplaceable(kind, pubkey, identifier)));
}
/** Returns all versions of a replaceable event */
getReplaceableHistory(kind, pubkey, identifier) {
return this.database.getReplaceableHistory(kind, pubkey, identifier);
// Get the events from memory first, then from the database
return (this.memory?.getReplaceableHistory(kind, pubkey, identifier) ??
this.database.getReplaceableHistory(kind, pubkey, identifier)?.map((e) => this.mapToMemory(e) ?? e));
}
/** Get all events matching a filter */
getByFilters(filters) {
return this.database.getByFilters(filters);
// NOTE: no way to read from memory since memory won't have the full set of events
const events = this.database.getByFilters(filters);
// Map events to memory if available for better performance
if (this.memory)
return events.map((e) => this.mapToMemory(e));
else
return events;
}
/** Returns a timeline of events that match filters */
getTimeline(filters) {
return this.database.getTimeline(filters);
const events = this.database.getTimeline(filters);
if (this.memory)
return events.map((e) => this.mapToMemory(e));
else
return events;
}
/** Passthrough method for the database.touch */
touch(event) {
return this.memory?.touch(event);
}
/** Sets the claim on the event and touches it */
claim(event, claim) {
this.database.claim(event, claim);
return this.memory?.claim(event, claim);
}
/** Checks if an event is claimed by anything */
isClaimed(event) {
return this.database.isClaimed(event);
return this.memory?.isClaimed(event) ?? false;
}
/** Removes a claim from an event */
removeClaim(event, claim) {
this.database.removeClaim(event, claim);
return this.memory?.removeClaim(event, claim);
}
/** Removes all claims on an event */
clearClaim(event) {
this.database.clearClaim(event);
return this.memory?.clearClaim(event);
}
/** A directory of all active models */
models = new Map();
/** How long a model should be kept "warm" while nothing is subscribed to it */
modelKeepWarm = 60_000;
/** Get or create a model on the event store */
model(constructor, ...args) {
let models = this.models.get(constructor);
if (!models) {
models = new Map();
this.models.set(constructor, models);
/** Pass through method for the database.unclaimed */
unclaimed() {
return this.memory?.unclaimed() || (function* () { })();
}
const key = constructor.getKey ? constructor.getKey(...args) : hash_sum(args);
let model = models.get(key);
// Create the model if it does not exist
if (!model) {
const cleanup = () => {
// Remove the model from the cache if its the same one
if (models.get(key) === model)
models.delete(key);
};
model = constructor(...args)(this).pipe(
// remove the model when its unsubscribed
finalize(cleanup),
// only subscribe to models once for all subscriptions
share({
connector: () => new ReplaySubject(1),
resetOnComplete: () => timer(this.modelKeepWarm),
resetOnRefCountZero: () => timer(this.modelKeepWarm),
}));
// Add the model to the cache
models.set(key, model);
}
return model;
}
/**
* Creates an observable that streams all events that match the filter
* @param filters
* @param [onlyNew=false] Only subscribe to new events
*/
filters(filters, onlyNew = false) {
filters = Array.isArray(filters) ? filters : [filters];
return merge(
// merge existing events
onlyNew ? EMPTY : from(this.getByFilters(filters)),
// subscribe to future events
this.insert$.pipe(filter((e) => matchFilters(filters, e))));
/** Removes any event that is not being used by a subscription */
prune(limit) {
return this.memory?.prune(limit) ?? 0;
}
/** Returns an observable that completes when an event is removed */
removed(id) {
@@ -348,83 +346,6 @@ export class EventStore {
}
/** Creates an observable that emits when event is updated */
updated(event) {
return this.database.update$.pipe(filter((e) => e.id === event || e === event));
}
// Helper methods for creating models
/** Creates a {@link EventModel} */
event(pointer) {
if (typeof pointer === "string")
pointer = { id: pointer };
return this.model(EventModel, pointer);
}
replaceable(...args) {
let pointer;
// Parse arguments
if (args.length === 1) {
pointer = args[0];
}
else if (args.length === 3 || args.length === 2) {
let [kind, pubkey, identifier] = args;
pointer = { kind, pubkey, identifier };
}
if (!pointer)
throw new Error("Invalid arguments, expected address pointer or kind, pubkey, identifier");
return this.model(ReplaceableModel, pointer);
}
/** Subscribe to an addressable event by pointer */
addressable(pointer) {
return this.model(ReplaceableModel, pointer);
}
/** Creates a {@link TimelineModel} */
timeline(filters, includeOldVersion = false) {
return this.model(TimelineModel, filters, includeOldVersion);
}
/** Subscribe to a users profile */
profile(user) {
return this.model(ProfileModel, user);
}
/** Subscribe to a users contacts */
contacts(user) {
if (typeof user === "string")
user = { pubkey: user };
return this.model(ContactsModel, user);
}
/** Subscribe to a users mutes */
mutes(user) {
if (typeof user === "string")
user = { pubkey: user };
return this.model(MuteModel, user);
}
/** Subscribe to a users NIP-65 mailboxes */
mailboxes(user) {
if (typeof user === "string")
user = { pubkey: user };
return this.model(MailboxesModel, user);
}
/** Subscribe to a users blossom servers */
blossomServers(user) {
if (typeof user === "string")
user = { pubkey: user };
return this.model(UserBlossomServersModel, user);
}
/** Subscribe to an event's reactions */
reactions(event) {
return this.model(ReactionsModel, event);
}
/** Subscribe to a thread */
thread(root) {
return this.model(ThreadModel, root);
}
/** Subscribe to a event's comments */
comments(event) {
return this.model(CommentsModel, event);
}
/** @deprecated use multiple {@link EventModel} instead */
events(ids) {
return this.model(EventsModel, ids);
}
/** @deprecated use multiple {@link ReplaceableModel} instead */
replaceableSet(pointers) {
return this.model(ReplaceableSetModel, pointers);
return this.update$.pipe(filter((e) => e.id === event || e === event));
}
}

View File

@@ -1,3 +1,4 @@
export * from "./async-event-store.js";
export * from "./event-memory.js";
export * from "./event-store.js";
export * from "./event-set.js";
export * from "./interface.js";

View File

@@ -1,3 +1,4 @@
export * from "./async-event-store.js";
export * from "./event-memory.js";
export * from "./event-store.js";
export * from "./event-set.js";
export * from "./interface.js";

View File

@@ -1,28 +1,44 @@
import { Filter, NostrEvent } from "nostr-tools";
import { AddressPointer, EventPointer, ProfilePointer } from "nostr-tools/nip19";
import { Observable } from "rxjs";
import { LRU } from "../helpers/lru.js";
import { Mutes } from "../helpers/mutes.js";
import { ProfileContent } from "../helpers/profile.js";
import { Thread } from "../models/thread.js";
import { AddressPointerWithoutD } from "../helpers/pointers.js";
import { ProfileContent } from "../helpers/profile.js";
import { Mutes } from "../helpers/mutes.js";
import { Thread } from "../models/thread.js";
/** The read interface for an event store */
export interface IEventStoreRead {
/** Check if the event store has an event with id */
hasEvent(id: string): boolean;
/** Check if the event store has a replaceable event */
hasReplaceable(kind: number, pubkey: string, identifier?: string): boolean;
/** Get an event by id */
getEvent(id: string): NostrEvent | undefined;
/** Check if the event store has a replaceable event */
hasReplaceable(kind: number, pubkey: string, identifier?: string): boolean;
/** Get a replaceable event */
getReplaceable(kind: number, pubkey: string, identifier?: string): NostrEvent | undefined;
/** Get the history of a replaceable event */
getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined;
/** Get all events that match the filters */
getByFilters(filters: Filter | Filter[]): Set<NostrEvent>;
getByFilters(filters: Filter | Filter[]): NostrEvent[];
/** Get a timeline of events that match the filters */
getTimeline(filters: Filter | Filter[]): NostrEvent[];
}
/** The async read interface for an event store */
export interface IAsyncEventStoreRead {
/** Check if the event store has an event with id */
hasEvent(id: string): Promise<boolean>;
/** Get an event by id */
getEvent(id: string): Promise<NostrEvent | undefined>;
/** Check if the event store has a replaceable event */
hasReplaceable(kind: number, pubkey: string, identifier?: string): Promise<boolean>;
/** Get a replaceable event */
getReplaceable(kind: number, pubkey: string, identifier?: string): Promise<NostrEvent | undefined>;
/** Get the history of a replaceable event */
getReplaceableHistory(kind: number, pubkey: string, identifier?: string): Promise<NostrEvent[] | undefined>;
/** Get all events that match the filters */
getByFilters(filters: Filter | Filter[]): Promise<NostrEvent[]>;
/** Get a timeline of events that match the filters */
getTimeline(filters: Filter | Filter[]): Promise<NostrEvent[]>;
}
/** The stream interface for an event store */
export interface IEventStoreStreams {
/** A stream of new events added to the store */
@@ -41,8 +57,19 @@ export interface IEventStoreActions {
/** Notify the store that an event has updated */
update(event: NostrEvent): void;
}
/** The async actions for an event store */
export interface IAsyncEventStoreActions {
/** Add an event to the store */
add(event: NostrEvent): Promise<NostrEvent | null>;
/** Remove an event from the store */
remove(event: string | NostrEvent): Promise<boolean>;
/** Notify the store that an event has updated */
update(event: NostrEvent): Promise<void>;
}
/** The claim interface for an event store */
export interface IEventClaims {
/** Tell the store that this event was used */
touch(event: NostrEvent): void;
/** Sets the claim on the event and touches it */
claim(event: NostrEvent, claim: any): void;
/** Checks if an event is claimed by anything */
@@ -51,59 +78,105 @@ export interface IEventClaims {
removeClaim(event: NostrEvent, claim: any): void;
/** Removes all claims on an event */
clearClaim(event: NostrEvent): void;
/** Returns a generator of unclaimed events in order of least used */
unclaimed(): Generator<NostrEvent>;
}
/** An event store that can be subscribed to */
export interface IEventStoreSubscriptions {
/** Susbscribe to an event by id */
export interface IEventSubscriptions {
/** Subscribe to an event by id */
event(id: string | EventPointer): Observable<NostrEvent | undefined>;
/** Subscribe to a replaceable event by pointer */
replaceable(pointer: AddressPointerWithoutD): Observable<NostrEvent | undefined>;
/** Subscribe to a replaceable event with legacy arguments */
replaceable(kind: number, pubkey: string, identifier?: string): Observable<NostrEvent | undefined>;
/** Subscribe to an addressable event by pointer */
addressable(pointer: AddressPointer): Observable<NostrEvent | undefined>;
/** Subscribe to a batch of events that match the filters */
filter(filters: Filter | Filter[]): Observable<NostrEvent[]>;
filters(filters: Filter | Filter[], onlyNew?: boolean): Observable<NostrEvent>;
/** Subscribe to a sorted timeline of events that match the filters */
timeline(filters: Filter | Filter[], onlyNew?: boolean): Observable<NostrEvent[]>;
}
/** @deprecated use {@link IEventSubscriptions} instead */
export interface IEventStoreSubscriptions extends IEventSubscriptions {
}
/** Methods for creating common models */
export interface IEventStoreModels {
model<T extends unknown, Args extends Array<any>>(constructor: ModelConstructor<T, Args>, ...args: Args): Observable<T>;
event(id: string): Observable<NostrEvent | undefined>;
replaceable(pointer: AddressPointerWithoutD): Observable<NostrEvent | undefined>;
addressable(pointer: AddressPointer): Observable<NostrEvent | undefined>;
timeline(filters: Filter | Filter[], includeOldVersion?: boolean): Observable<NostrEvent[]>;
export interface IEventModelMixin<TStore extends IEventStore | IAsyncEventStore> {
model<T extends unknown, Args extends Array<any>>(constructor: ModelConstructor<T, Args, TStore>, ...args: Args): Observable<T>;
/** @deprecated use multiple {@link EventModel} instead */
events(ids: string[]): Observable<Record<string, NostrEvent | undefined>>;
/** @deprecated use multiple {@link ReplaceableModel} instead */
replaceableSet(pointers: (AddressPointer | AddressPointerWithoutD)[]): Observable<Record<string, NostrEvent | undefined>>;
}
/** A computed view of an event set or event store */
export type Model<T extends unknown> = (events: IEventStore) => Observable<T>;
/** A constructor for a {@link Model} */
export type ModelConstructor<T extends unknown, Args extends Array<any>> = ((...args: Args) => Model<T>) & {
getKey?: (...args: Args) => string;
};
/** The base interface for a set of events */
export interface IEventSet extends IEventStoreRead, IEventStoreStreams, IEventStoreActions, IEventClaims {
events: LRU<NostrEvent>;
}
export interface IEventStore extends IEventStoreRead, IEventStoreStreams, IEventStoreActions, IEventStoreModels, IEventClaims {
/** Enable this to keep old versions of replaceable events */
keepOldVersions: boolean;
/** Enable this to keep expired events */
keepExpired: boolean;
filters(filters: Filter | Filter[]): Observable<NostrEvent>;
updated(id: string | NostrEvent): Observable<NostrEvent>;
removed(id: string): Observable<never>;
replaceable(kind: number, pubkey: string, identifier?: string): Observable<NostrEvent | undefined>;
replaceable(pointer: AddressPointerWithoutD): Observable<NostrEvent | undefined>;
eventLoader?: (pointer: EventPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
replaceableLoader?: (pointer: AddressPointerWithoutD) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
addressableLoader?: (pointer: AddressPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
/** Methods for creating helpful models */
export interface IEventHelpfulSubscriptions {
/** Subscribe to a users profile */
profile(user: string | ProfilePointer): Observable<ProfileContent | undefined>;
/** Subscribe to a users contacts */
contacts(user: string | ProfilePointer): Observable<ProfilePointer[]>;
/** Subscribe to a users mutes */
mutes(user: string | ProfilePointer): Observable<Mutes | undefined>;
/** Subscribe to a users NIP-65 mailboxes */
mailboxes(user: string | ProfilePointer): Observable<{
inboxes: string[];
outboxes: string[];
} | undefined>;
/** Subscribe to a users blossom servers */
blossomServers(user: string | ProfilePointer): Observable<URL[]>;
/** Subscribe to an event's reactions */
reactions(event: NostrEvent): Observable<NostrEvent[]>;
/** Subscribe to a thread */
thread(root: string | EventPointer | AddressPointer): Observable<Thread>;
/** Subscribe to a event's comments */
comments(event: NostrEvent): Observable<NostrEvent[]>;
}
/** @deprecated use {@link IEventModelMixin} instead */
export interface IEventStoreModels extends IEventModelMixin<IEventStore> {
}
/** The interface that is passed to the model for creating subscriptions */
export type ModelEventStore<TStore extends IEventStore | IAsyncEventStore> = IEventStoreStreams & IEventSubscriptions & IEventModelMixin<TStore> & IEventFallbackLoaders & TStore;
/** A computed view of an event set or event store */
export type Model<T extends unknown, TStore extends IEventStore | IAsyncEventStore = IEventStore | IAsyncEventStore> = (events: ModelEventStore<TStore>) => Observable<T>;
/** A constructor for a {@link Model} */
export type ModelConstructor<T extends unknown, Args extends Array<any>, TStore extends IEventStore | IAsyncEventStore = IEventStore> = ((...args: Args) => Model<T, TStore>) & {
getKey?: (...args: Args) => string;
};
/** The base interface for a database of events */
export interface IEventDatabase extends IEventStoreRead {
/** Add an event to the database */
add(event: NostrEvent): NostrEvent;
/** Remove an event from the database */
remove(event: string | NostrEvent): boolean;
/** Notifies the database that an event has updated */
update?: (event: NostrEvent) => void;
}
/** The async base interface for a set of events */
export interface IAsyncEventDatabase extends IAsyncEventStoreRead {
/** Add an event to the database */
add(event: NostrEvent): Promise<NostrEvent>;
/** Remove an event from the database */
remove(event: string | NostrEvent): Promise<boolean>;
/** Notifies the database that an event has updated */
update?: (event: NostrEvent) => void;
}
/** The base interface for the in-memory database of events */
export interface IEventMemory extends IEventStoreRead, IEventClaims {
/** Add an event to the store */
add(event: NostrEvent): NostrEvent;
/** Remove an event from the store */
remove(event: string | NostrEvent): boolean;
}
/** A set of methods that an event store will use to load single events it does not have */
export interface IEventFallbackLoaders {
/** A method that will be called when an event isn't found in the store */
eventLoader?: (pointer: EventPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
/** A method that will be called when a replaceable event isn't found in the store */
replaceableLoader?: (pointer: AddressPointerWithoutD) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
/** A method that will be called when an addressable event isn't found in the store */
addressableLoader?: (pointer: AddressPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>;
}
/** The async event store interface */
export interface IAsyncEventStore extends IAsyncEventStoreRead, IEventStoreStreams, IEventSubscriptions, IAsyncEventStoreActions, IEventModelMixin<IAsyncEventStore>, IEventHelpfulSubscriptions, IEventClaims, IEventFallbackLoaders {
}
/** The sync event store interface */
export interface IEventStore extends IEventStoreRead, IEventStoreStreams, IEventSubscriptions, IEventStoreActions, IEventModelMixin<IEventStore>, IEventHelpfulSubscriptions, IEventClaims, IEventFallbackLoaders {
}

View File

@@ -1,7 +1,12 @@
import { NostrEvent } from "nostr-tools";
import { AddressPointer, EventPointer } from "nostr-tools/nip19";
import { HiddenContentSigner } from "./index.js";
export declare const BookmarkPublicSymbol: unique symbol;
export declare const BookmarkHiddenSymbol: unique symbol;
/** Type for unlocked bookmarks events */
export type UnlockedBookmarks = {
[BookmarkHiddenSymbol]: Bookmarks;
};
export type Bookmarks = {
notes: EventPointer[];
articles: AddressPointer[];
@@ -16,5 +21,10 @@ export declare function mergeBookmarks(...bookmarks: (Bookmarks | undefined)[]):
export declare function getBookmarks(bookmark: NostrEvent): Bookmarks;
/** Returns the public bookmarks of the event */
export declare function getPublicBookmarks(bookmark: NostrEvent): Bookmarks;
/** Checks if the hidden bookmarks are unlocked */
export declare function isHiddenBookmarksUnlocked<T extends NostrEvent>(bookmark: T): bookmark is T & UnlockedBookmarks;
/** Returns the bookmarks of the event if its unlocked */
export declare function getHiddenBookmarks(bookmark: NostrEvent): Bookmarks | undefined;
export declare function getHiddenBookmarks<T extends NostrEvent & UnlockedBookmarks>(bookmark: T): Bookmarks;
export declare function getHiddenBookmarks<T extends NostrEvent>(bookmark: T): Bookmarks | undefined;
/** Unlocks the hidden bookmarks on a bookmarks event */
export declare function unlockHiddenBookmarks(bookmark: NostrEvent, signer: HiddenContentSigner): Promise<Bookmarks>;

View File

@@ -1,6 +1,6 @@
import { kinds } from "nostr-tools";
import { getOrComputeCachedValue } from "./cache.js";
import { getHiddenTags, isHiddenTagsLocked } from "./index.js";
import { getHiddenTags, isHiddenTagsUnlocked, notifyEventUpdate, unlockHiddenTags, } from "./index.js";
import { getAddressPointerFromATag, getCoordinateFromAddressPointer, getEventPointerFromETag, mergeAddressPointers, mergeEventPointers, } from "./pointers.js";
export const BookmarkPublicSymbol = Symbol.for("bookmark-public");
export const BookmarkHiddenSymbol = Symbol.for("bookmark-hidden");
@@ -63,9 +63,34 @@ export function getBookmarks(bookmark) {
export function getPublicBookmarks(bookmark) {
return getOrComputeCachedValue(bookmark, BookmarkPublicSymbol, () => parseBookmarkTags(bookmark.tags));
}
/** Returns the bookmarks of the event if its unlocked */
export function getHiddenBookmarks(bookmark) {
if (isHiddenTagsLocked(bookmark))
return undefined;
return getOrComputeCachedValue(bookmark, BookmarkHiddenSymbol, () => parseBookmarkTags(getHiddenTags(bookmark)));
/** Checks if the hidden bookmarks are unlocked */
export function isHiddenBookmarksUnlocked(bookmark) {
return isHiddenTagsUnlocked(bookmark) && Reflect.has(bookmark, BookmarkHiddenSymbol);
}
export function getHiddenBookmarks(bookmark) {
if (isHiddenBookmarksUnlocked(bookmark))
return bookmark[BookmarkHiddenSymbol];
//get hidden tags
const tags = getHiddenTags(bookmark);
if (!tags)
return undefined;
// parse bookmarks
const bookmarks = parseBookmarkTags(tags);
// set cached value
Reflect.set(bookmark, BookmarkHiddenSymbol, bookmarks);
return bookmarks;
}
/** Unlocks the hidden bookmarks on a bookmarks event */
export async function unlockHiddenBookmarks(bookmark, signer) {
if (isHiddenBookmarksUnlocked(bookmark))
return bookmark[BookmarkHiddenSymbol];
// unlock hidden tags
await unlockHiddenTags(bookmark, signer);
// get hidden bookmarks
const bookmarks = getHiddenBookmarks(bookmark);
if (!bookmarks)
throw new Error("Failed to unlock hidden bookmarks");
// notify event store
notifyEventUpdate(bookmark);
return bookmarks;
}

View File

@@ -1,6 +1,9 @@
import { NostrEvent } from "nostr-tools";
import { ExternalPointer, ExternalIdentifiers } from "./external-id.js";
import { KnownEvent } from "./index.js";
export declare const COMMENT_KIND = 1111;
/** Type for validated comment events */
export type CommentEvent = KnownEvent<typeof COMMENT_KIND>;
export type CommentEventPointer = {
type: "event";
id: string;
@@ -22,30 +25,20 @@ export type CommentExternalPointer<T extends keyof ExternalIdentifiers> = Extern
export type CommentPointer = CommentEventPointer | CommentAddressPointer | CommentExternalPointer<keyof ExternalIdentifiers>;
export declare const CommentRootPointerSymbol: unique symbol;
export declare const CommentReplyPointerSymbol: unique symbol;
/**
* Gets the EventPointer from an array of tags
* @throws
*/
/** Gets the EventPointer from an array of tags */
export declare function getCommentEventPointer(tags: string[][], root?: boolean): CommentEventPointer | null;
/**
* Gets the AddressPointer from an array of tags
* @throws
*/
/** Gets the AddressPointer from an array of tags */
export declare function getCommentAddressPointer(tags: string[][], root?: boolean): CommentAddressPointer | null;
/**
* Gets the ExternalPointer from an array of tags
* @throws
*/
/** Gets the ExternalPointer from an array of tags */
export declare function getCommentExternalPointer(tags: string[][], root?: boolean): CommentExternalPointer<keyof ExternalIdentifiers> | null;
/**
* Returns the root pointer for a comment
* @throws
*/
/** Returns the root pointer for a comment */
export declare function getCommentRootPointer(comment: CommentEvent): CommentPointer;
export declare function getCommentRootPointer(comment: NostrEvent): CommentPointer | null;
/**
* Returns the reply pointer for a comment
* @throws
*/
/** Returns the reply pointer for a comment */
export declare function getCommentReplyPointer(comment: NostrEvent): CommentPointer | null;
/** Checks if a pointer is a {@link CommentEventPointer} */
export declare function isCommentEventPointer(pointer: any): pointer is CommentEventPointer;
/** Checks if a pointer is a {@link CommentAddressPointer} */
export declare function isCommentAddressPointer(pointer: any): pointer is CommentAddressPointer;
/** Checks if a comment event is valid */
export declare function isValidComment(comment: NostrEvent): comment is CommentEvent;

View File

@@ -5,16 +5,14 @@ import { isSafeRelayURL } from "./relays.js";
export const COMMENT_KIND = 1111;
export const CommentRootPointerSymbol = Symbol.for("comment-root-pointer");
export const CommentReplyPointerSymbol = Symbol.for("comment-reply-pointer");
/**
* Gets the EventPointer from an array of tags
* @throws
*/
/** Gets the EventPointer from an array of tags */
export function getCommentEventPointer(tags, root = false) {
const eTag = tags.find((t) => t[0] === (root ? "E" : "e"));
const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
if (eTag) {
// Missing kind tag, return null
if (!kind)
throw new Error("Missing kind tag");
return null;
// only the root pubkey can be gotten from the tags, since due to quotes and mentions there will be many "p" tags for replies
const rootPubkey = root ? tags.find((t) => t[0] === "P")?.[1] : undefined;
const pointer = {
@@ -28,17 +26,15 @@ export function getCommentEventPointer(tags, root = false) {
}
return null;
}
/**
* Gets the AddressPointer from an array of tags
* @throws
*/
/** Gets the AddressPointer from an array of tags */
export function getCommentAddressPointer(tags, root = false) {
const aTag = tags.find((t) => t[0] === (root ? "A" : "a"));
const eTag = tags.find((t) => t[0] === (root ? "E" : "e"));
const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
if (aTag) {
// Missing kind tag, return null
if (!kind)
throw new Error("Missing kind tag");
return null;
const addressPointer = getAddressPointerFromATag(aTag);
const pointer = {
type: "address",
@@ -52,16 +48,10 @@ export function getCommentAddressPointer(tags, root = false) {
}
return null;
}
/**
* Gets the ExternalPointer from an array of tags
* @throws
*/
/** Gets the ExternalPointer from an array of tags */
export function getCommentExternalPointer(tags, root = false) {
const iTag = tags.find((t) => t[0] === (root ? "I" : "i"));
const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
if (iTag) {
if (!kind)
throw new Error("Missing kind tag");
return {
type: "external",
...getExternalPointerFromTag(iTag),
@@ -69,13 +59,9 @@ export function getCommentExternalPointer(tags, root = false) {
}
return null;
}
/**
* Returns the root pointer for a comment
* @throws
*/
export function getCommentRootPointer(comment) {
if (comment.kind !== COMMENT_KIND)
throw new Error("Event is not a comment");
return null;
return getOrComputeCachedValue(comment, CommentRootPointerSymbol, () => {
// check for address pointer first since it can also have E tags
const A = getCommentAddressPointer(comment.tags, true);
@@ -90,13 +76,10 @@ export function getCommentRootPointer(comment) {
return null;
});
}
/**
* Returns the reply pointer for a comment
* @throws
*/
/** Returns the reply pointer for a comment */
export function getCommentReplyPointer(comment) {
if (comment.kind !== COMMENT_KIND)
throw new Error("Event is not a comment");
return null;
return getOrComputeCachedValue(comment, CommentReplyPointerSymbol, () => {
// check for address pointer first since it can also have E tags
const A = getCommentAddressPointer(comment.tags, false);
@@ -111,15 +94,21 @@ export function getCommentReplyPointer(comment) {
return null;
});
}
/** Checks if a pointer is a {@link CommentEventPointer} */
export function isCommentEventPointer(pointer) {
return (Reflect.has(pointer, "id") &&
Reflect.has(pointer, "kind") &&
!Reflect.has(pointer, "identifier") &&
typeof pointer.kind === "number");
}
/** Checks if a pointer is a {@link CommentAddressPointer} */
export function isCommentAddressPointer(pointer) {
return (Reflect.has(pointer, "identifier") &&
Reflect.has(pointer, "pubkey") &&
Reflect.has(pointer, "kind") &&
typeof pointer.kind === "number");
}
/** Checks if a comment event is valid */
export function isValidComment(comment) {
return (comment.kind === COMMENT_KIND && getCommentRootPointer(comment) !== null && getCommentReplyPointer(comment) !== null);
}

View File

@@ -1,14 +1,23 @@
import { NostrEvent } from "nostr-tools";
import { ProfilePointer } from "nostr-tools/nip19";
import { HiddenContentSigner } from "./hidden-content.js";
export declare const ContactsRelaysSymbol: unique symbol;
export declare const PublicContactsSymbol: unique symbol;
export declare const HiddenContactsSymbol: unique symbol;
export declare function getRelaysFromContactsEvent(event: NostrEvent): Map<string, "all" | "inbox" | "outbox"> | null;
/** Type for contact events with unlocked hidden tags */
export type UnlockedContacts = {
[HiddenContactsSymbol]: ProfilePointer[];
};
export declare function getRelaysFromContactsEvent(event: NostrEvent): Map<string, "inbox" | "outbox" | "all"> | null;
/** Merges any number of contact lists into a single list */
export declare function mergeContacts(...pointers: (ProfilePointer | undefined | (ProfilePointer | undefined)[])[]): ProfilePointer[];
/** Returns all public and hidden contacts from a contacts list event */
export declare function getContacts(event: NostrEvent): ProfilePointer[];
/** Returns only the public contacts from a contacts list event */
export declare function getPublicContacts(event: NostrEvent): ProfilePointer[];
/** Checks if the hidden contacts are unlocked */
export declare function isHiddenContactsUnlocked<T extends NostrEvent>(event: T): event is T & UnlockedContacts;
/** Returns only the hidden contacts from a contacts list event */
export declare function getHiddenContacts(event: NostrEvent): ProfilePointer[] | undefined;
/** Unlocks the hidden contacts */
export declare function unlockHiddenContacts(event: NostrEvent, signer: HiddenContentSigner): Promise<ProfilePointer[]>;

View File

@@ -2,7 +2,8 @@ import { getOrComputeCachedValue } from "./cache.js";
import { isSafeRelayURL } from "./relays.js";
import { isPTag, processTags } from "./tags.js";
import { getProfilePointerFromPTag } from "./pointers.js";
import { getHiddenTags, isHiddenTagsLocked } from "./hidden-tags.js";
import { getHiddenTags, isHiddenTagsUnlocked, unlockHiddenTags } from "./hidden-tags.js";
import { notifyEventUpdate } from "./index.js";
export const ContactsRelaysSymbol = Symbol.for("contacts-relays");
export const PublicContactsSymbol = Symbol.for("public-contacts");
export const HiddenContactsSymbol = Symbol.for("hidden-contacts");
@@ -51,9 +52,35 @@ export function getContacts(event) {
export function getPublicContacts(event) {
return getOrComputeCachedValue(event, PublicContactsSymbol, () => processTags(event.tags, (t) => (isPTag(t) ? t : undefined), getProfilePointerFromPTag));
}
/** Checks if the hidden contacts are unlocked */
export function isHiddenContactsUnlocked(event) {
return isHiddenTagsUnlocked(event) && Reflect.has(event, HiddenContactsSymbol);
}
/** Returns only the hidden contacts from a contacts list event */
export function getHiddenContacts(event) {
if (isHiddenTagsLocked(event))
if (isHiddenContactsUnlocked(event))
return event[HiddenContactsSymbol];
// Get hidden tags
const tags = getHiddenTags(event);
if (!tags)
return undefined;
return getOrComputeCachedValue(event, HiddenContactsSymbol, () => processTags(getHiddenTags(event), (t) => (isPTag(t) ? t : undefined), getProfilePointerFromPTag));
// Parse tags
const contacts = processTags(tags, (t) => (isPTag(t) ? t : undefined), getProfilePointerFromPTag);
// Set cache and notify event store
Reflect.set(event, HiddenContactsSymbol, contacts);
return contacts;
}
/** Unlocks the hidden contacts */
export async function unlockHiddenContacts(event, signer) {
if (isHiddenContactsUnlocked(event))
return event[HiddenContactsSymbol];
// Unlock hidden tags
await unlockHiddenTags(event, signer);
// Get hidden contacts
const contacts = getHiddenContacts(event);
if (!contacts)
throw new Error("Failed to unlock hidden contacts");
// Set cache and notify event store
notifyEventUpdate(event);
return contacts;
}

View File

@@ -1,7 +1,7 @@
import { kinds } from "nostr-tools";
import { catchError, combineLatest, distinct, EMPTY, filter, isObservable, map, merge, mergeMap, of, switchMap, } from "rxjs";
import { logger } from "../logger.js";
import { canHaveEncryptedContent, getEncryptedContent, isEncryptedContentLocked, setEncryptedContentCache, } from "./encrypted-content.js";
import { canHaveEncryptedContent, getEncryptedContent, isEncryptedContentUnlocked, setEncryptedContentCache, } from "./encrypted-content.js";
import { notifyEventUpdate } from "./event.js";
import { getGiftWrapSeal, getSealGiftWrap, getSealRumor } from "./gift-wraps.js";
/** A symbol that is used to mark encrypted content as being from a cache */
@@ -32,7 +32,7 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
const restore = eventStore.insert$
.pipe(
// Look for events that support encrypted content and are locked
filter((e) => canHaveEncryptedContent(e.kind) && isEncryptedContentLocked(e)),
filter((e) => canHaveEncryptedContent(e.kind) && isEncryptedContentUnlocked(e) === false),
// Get the encrypted content from storage
mergeMap((event) =>
// Wait for storage to be available
@@ -52,11 +52,11 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
const restoreSeals = eventStore.update$
.pipe(
// Look for gift wraps that are unlocked
filter((e) => e.kind === kinds.GiftWrap && !isEncryptedContentLocked(e)),
filter((e) => e.kind === kinds.GiftWrap && isEncryptedContentUnlocked(e)),
// Get the seal event
map((gift) => getGiftWrapSeal(gift)),
// Look for gift wraps with locked seals
filter((seal) => seal !== undefined && isEncryptedContentLocked(seal)),
filter((seal) => seal !== undefined && isEncryptedContentUnlocked(seal) === false),
// Only attempt to unlock seals once
distinct((seal) => seal.id),
// Get encrypted content from storage
@@ -84,7 +84,7 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
.pipe(
// Look for events that support encrypted content and are unlocked and not from the cache
filter(([event]) => canHaveEncryptedContent(event.kind) &&
!isEncryptedContentLocked(event) &&
isEncryptedContentUnlocked(event) &&
!isEncryptedContentFromCache(event)),
// Only persist the encrypted content once
distinct(([event]) => event.id))
@@ -106,13 +106,13 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
const persistSeals = combineLatest([merge(eventStore.update$, eventStore.insert$), storage$])
.pipe(
// Look for gift wraps that are unlocked
filter(([event]) => event.kind === kinds.GiftWrap && !isEncryptedContentLocked(event)),
filter(([event]) => event.kind === kinds.GiftWrap && isEncryptedContentUnlocked(event)),
// Get the seal event
map(([gift, storage]) => [getGiftWrapSeal(gift), storage]),
// Make sure the seal is defined
filter(([seal]) => seal !== undefined),
// Make sure seal is unlocked and not from cache
filter(([seal]) => !isEncryptedContentLocked(seal) && !isEncryptedContentFromCache(seal)),
filter(([seal]) => isEncryptedContentUnlocked(seal) && !isEncryptedContentFromCache(seal)),
// Only persist the seal once
distinct(([seal]) => seal.id))
.subscribe(async ([seal, storage]) => {

View File

@@ -10,7 +10,12 @@ export interface EncryptedContentSigner {
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
};
}
/** Encryption method types */
export type EncryptionMethod = "nip04" | "nip44";
/** Type for an event who's encrypted content is unlocked */
export type UnlockedEncryptedContent = {
[EncryptedContentSymbol]: string;
};
/** A pair of encryption methods for encrypting and decrypting event content */
export interface EncryptionMethods {
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
@@ -37,14 +42,16 @@ export declare function hasEncryptedContent<T extends {
content: string;
}>(event: T): boolean;
/** Returns the encrypted content for an event if it is unlocked */
export declare function getEncryptedContent<T extends UnlockedEncryptedContent>(event: T): string;
export declare function getEncryptedContent<T extends object>(event: T): string | undefined;
/** Checks if the encrypted content is locked */
export declare function isEncryptedContentLocked<T extends object>(event: T): boolean;
/** Checks if the encrypted content is unlocked and casts it to the {@link UnlockedEncryptedContent} type */
export declare function isEncryptedContentUnlocked<T extends object>(event: T): event is T & UnlockedEncryptedContent;
/**
* Unlocks the encrypted content in an event and caches it
* @param event The event with content to decrypt
* @param pubkey The other pubkey that encrypted the content
* @param signer A signer to use to decrypt the content
* @throws If the event kind does not support encrypted content
*/
export declare function unlockEncryptedContent<T extends {
kind: number;

View File

@@ -1,5 +1,5 @@
import { kinds } from "nostr-tools";
import { isEvent, notifyEventUpdate } from "./event.js";
import { notifyEventUpdate } from "./event.js";
/** A symbol use to store the encrypted content of an event in memory */
export const EncryptedContentSymbol = Symbol.for("encrypted-content");
/** Various event kinds that can have encrypted content and which encryption method they use */
@@ -39,37 +39,40 @@ export function canHaveEncryptedContent(kind) {
export function hasEncryptedContent(event) {
return event.content.length > 0;
}
/** Returns the encrypted content for an event if it is unlocked */
export function getEncryptedContent(event) {
return Reflect.get(event, EncryptedContentSymbol);
}
/** Checks if the encrypted content is locked */
export function isEncryptedContentLocked(event) {
return Reflect.has(event, EncryptedContentSymbol) === false;
/** Checks if the encrypted content is unlocked and casts it to the {@link UnlockedEncryptedContent} type */
export function isEncryptedContentUnlocked(event) {
return Reflect.has(event, EncryptedContentSymbol) === true;
}
/**
* Unlocks the encrypted content in an event and caches it
* @param event The event with content to decrypt
* @param pubkey The other pubkey that encrypted the content
* @param signer A signer to use to decrypt the content
* @throws If the event kind does not support encrypted content
*/
export async function unlockEncryptedContent(event, pubkey, signer) {
if (!canHaveEncryptedContent(event.kind))
throw new Error("Event kind does not support encrypted content");
// Get the encryption methods from the signer
const encryption = getEncryptedContentEncryptionMethods(event.kind, signer);
const plaintext = await encryption.decrypt(pubkey, event.content);
// Set the cached value and trigger update
setEncryptedContentCache(event, plaintext);
// Return the decrypted content
return plaintext;
}
/** Sets the encrypted content on an event and updates it if its part of an event store */
export function setEncryptedContentCache(event, plaintext) {
Reflect.set(event, EncryptedContentSymbol, plaintext);
// if the event has been added to an event store, notify it
if (isEvent(event))
notifyEventUpdate(event);
}
/** Removes the encrypted content cache on an event */
export function lockEncryptedContent(event) {
Reflect.deleteProperty(event, EncryptedContentSymbol);
// if the event has been added to an event store, notify it
if (isEvent(event))
notifyEventUpdate(event);
}

View File

@@ -9,7 +9,9 @@ import { IEventStoreStreams } from "../event-store/interface.js";
* @param opts.maxBatchSize - The maximum number of events to write in a batch
* @returns A function to stop the process
*/
export declare function presistEventsToCache(eventStore: IEventStoreStreams, write: (events: NostrEvent[]) => Promise<void>, opts?: {
export declare function persistEventsToCache(eventStore: IEventStoreStreams, write: (events: NostrEvent[]) => Promise<void>, opts?: {
maxBatchSize?: number;
batchTime?: number;
}): () => void;
/** @deprecated Use persistEventsToCache instead */
export declare const presistEventsToCache: typeof persistEventsToCache;

View File

@@ -11,7 +11,7 @@ const log = logger.extend("event-cache");
* @param opts.maxBatchSize - The maximum number of events to write in a batch
* @returns A function to stop the process
*/
export function presistEventsToCache(eventStore, write, opts) {
export function persistEventsToCache(eventStore, write, opts) {
const time = opts?.batchTime ?? 5_000;
// Save all new events to the cache
const sub = eventStore.insert$
@@ -30,3 +30,5 @@ export function presistEventsToCache(eventStore, write, opts) {
});
return () => sub.unsubscribe();
}
/** @deprecated Use persistEventsToCache instead */
export const presistEventsToCache = persistEventsToCache;

View File

@@ -10,5 +10,11 @@ export declare function getTagValue<T extends {
tags: string[][];
content: string;
}>(event: T, name: string): string | undefined;
/** Checks if an event has a public name / value tag*/
export declare function hasNameValueTag<T extends {
kind: number;
tags: string[][];
content: string;
}>(event: T, name: string, value: string): boolean;
/** Returns a Set of tag names and values that are indexable */
export declare function getIndexableTags(event: NostrEvent): Set<string>;

View File

@@ -13,6 +13,10 @@ export function getTagValue(event, name) {
return hiddenValue;
return event.tags.find((t) => t[0] === name)?.[1];
}
/** Checks if an event has a public name / value tag*/
export function hasNameValueTag(event, name, value) {
return event.tags.some((t) => t[0] === name && t[1] === value);
}
/** Returns a Set of tag names and values that are indexable */
export function getIndexableTags(event) {
let indexable = Reflect.get(event, EventIndexableTagsSymbol);

Some files were not shown because too many files have changed in this diff Show More