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", "name": "boris",
"version": "0.1.1", "version": "0.1.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
@@ -320,32 +320,6 @@
"node": ">=22.4.0" "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": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.21.5", "version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
@@ -1914,16 +1888,16 @@
} }
}, },
"node_modules/applesauce-accounts": { "node_modules/applesauce-accounts": {
"version": "3.1.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-accounts/-/applesauce-accounts-3.1.0.tgz", "resolved": "https://registry.npmjs.org/applesauce-accounts/-/applesauce-accounts-4.0.0.tgz",
"integrity": "sha512-F0xFi4CU5Ak917VcUap+6YIxyvbjP2BJejWBjVurFMY6tCK3kWWPQnoPc+GjU6LkMsL8BfQcOQ6wKIX4MvTB7Q==", "integrity": "sha512-JHv63cRSSWXqbId6anNfOlPCE+sr3zJeHT6Jt9p+3X+hZkHrlNy5sLBK+fBHvKc5w/eQFsmnIWYfuLxPdqZcrA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@noble/hashes": "^1.7.1", "@noble/hashes": "^1.7.1",
"applesauce-core": "^3.1.0", "applesauce-core": "^4.0.0",
"applesauce-signers": "^3.1.0", "applesauce-signers": "^4.0.0",
"nanoid": "^5.1.5", "nanoid": "^5.1.5",
"nostr-tools": "~2.15", "nostr-tools": "~2.17",
"rxjs": "^7.8.1" "rxjs": "^7.8.1"
}, },
"funding": { "funding": {
@@ -1932,14 +1906,14 @@
} }
}, },
"node_modules/applesauce-actions": { "node_modules/applesauce-actions": {
"version": "3.1.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-actions/-/applesauce-actions-3.1.0.tgz", "resolved": "https://registry.npmjs.org/applesauce-actions/-/applesauce-actions-4.0.0.tgz",
"integrity": "sha512-wNZQr1qAmlQCjOQQXK0sHyNtcEbYbcUmA97bYg4KctSwlEQAI14fUTltYHdLZ3Zw4xJ9F0Xq4O2YCUJA4UOYlA==", "integrity": "sha512-oYAjrazKGDINeVwypNDnV9eNSv7ZDTjNeV3azo5jeUU1haEQ0t+zwVWzGxk9/VutT1yWQHFsCZBInYZIegfLhQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"applesauce-core": "^3.1.0", "applesauce-core": "^4.0.0",
"applesauce-factory": "^3.1.0", "applesauce-factory": "^4.0.0",
"nostr-tools": "~2.15", "nostr-tools": "~2.17",
"rxjs": "^7.8.1" "rxjs": "^7.8.1"
}, },
"funding": { "funding": {
@@ -1970,90 +1944,7 @@
"url": "lightning:nostrudel@geyser.fund" "url": "lightning:nostrudel@geyser.fund"
} }
}, },
"node_modules/applesauce-content/node_modules/@noble/curves": { "node_modules/applesauce-core": {
"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": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-core/-/applesauce-core-4.0.0.tgz", "resolved": "https://registry.npmjs.org/applesauce-core/-/applesauce-core-4.0.0.tgz",
"integrity": "sha512-cXg9lDU0PKpAeVw7FGN3jzKB/k9kX5YOI7uwzrZhibYB5PfpHAYRiVexxBuFdT2RNaDgQogXl75gV2hag5uLuw==", "integrity": "sha512-cXg9lDU0PKpAeVw7FGN3jzKB/k9kX5YOI7uwzrZhibYB5PfpHAYRiVexxBuFdT2RNaDgQogXl75gV2hag5uLuw==",
@@ -2074,82 +1965,14 @@
"url": "lightning:nostrudel@geyser.fund" "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": { "node_modules/applesauce-factory": {
"version": "3.1.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-factory/-/applesauce-factory-3.1.0.tgz", "resolved": "https://registry.npmjs.org/applesauce-factory/-/applesauce-factory-4.0.0.tgz",
"integrity": "sha512-K9onWy8yvWnQp2c+a227IFHv65ToDH4B4yoLEOuLs2xOp7BwtZCoIdVZbWVk93X1KvA7pBTtXCjDbwTcH7LCjQ==", "integrity": "sha512-Sqsg+bC7CkRXMxXLkO6YGoKxy/Aqtia9YenasS5qjPOQFmyFMwKRxaHCu6vX6KdpNSABusw0b9Tnn4gTh6CxLw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"applesauce-content": "^3.1.0", "applesauce-content": "^4.0.0",
"applesauce-core": "^3.1.0", "applesauce-core": "^4.0.0",
"nanoid": "^5.0.9", "nanoid": "^5.0.9",
"nostr-tools": "^2.13" "nostr-tools": "^2.13"
}, },
@@ -2158,51 +1981,15 @@
"url": "lightning:nostrudel@geyser.fund" "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": { "node_modules/applesauce-loaders": {
"version": "3.1.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-loaders/-/applesauce-loaders-3.1.0.tgz", "resolved": "https://registry.npmjs.org/applesauce-loaders/-/applesauce-loaders-4.0.0.tgz",
"integrity": "sha512-9IY5RYqoXcIgAAdJNuMjMBr+CI85z1yj708C92UiP9YMQ4mrIIEvZbMmWLApBzRn5XsmmEa00a/iNlXpkRg9Sw==", "integrity": "sha512-yNlpzCeUlkpq1jehyHPre0C3ey9anMDTpPysqdV9Rca+U2QnAWduzQ+Eo8FVS1X9jZRD6s/gkLSnCnW1+kX7uQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"applesauce-core": "^3.1.0", "applesauce-core": "^4.0.0",
"nanoid": "^5.0.9", "nanoid": "^5.0.9",
"nostr-tools": "~2.15", "nostr-tools": "~2.17",
"rxjs": "^7.8.1" "rxjs": "^7.8.1"
}, },
"funding": { "funding": {
@@ -2211,18 +1998,18 @@
} }
}, },
"node_modules/applesauce-react": { "node_modules/applesauce-react": {
"version": "3.1.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-react/-/applesauce-react-3.1.0.tgz", "resolved": "https://registry.npmjs.org/applesauce-react/-/applesauce-react-4.0.0.tgz",
"integrity": "sha512-9zKbHOkXTGBLRe2uZvwAyPLTVPfelQQ7BvVTYHb2hg1JbhphMQqDSas3kaHBltt9ZCVh0xdv8JupmKWTUeCzag==", "integrity": "sha512-eVDUf3GL1j4bsL1Y8GsC/2sywajLu1oJioCNajUsm68hf5+zIR0rLHWaA4y0o5Rcctf/O4UbYkFztj1XHcuHgg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"applesauce-accounts": "^3.1.0", "applesauce-accounts": "^4.0.0",
"applesauce-actions": "^3.1.0", "applesauce-actions": "^4.0.0",
"applesauce-content": "^3.1.0", "applesauce-content": "^4.0.0",
"applesauce-core": "^3.1.0", "applesauce-core": "^4.0.0",
"applesauce-factory": "^3.1.0", "applesauce-factory": "^4.0.0",
"hash-sum": "^2.0.0", "hash-sum": "^2.0.0",
"nostr-tools": "~2.15", "nostr-tools": "~2.17",
"observable-hooks": "^4.2.4", "observable-hooks": "^4.2.4",
"react": "^18.3.1", "react": "^18.3.1",
"rxjs": "^7.8.1" "rxjs": "^7.8.1"
@@ -2232,52 +2019,16 @@
"url": "lightning:nostrudel@geyser.fund" "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": { "node_modules/applesauce-relay": {
"version": "3.1.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-relay/-/applesauce-relay-3.1.0.tgz", "resolved": "https://registry.npmjs.org/applesauce-relay/-/applesauce-relay-4.0.0.tgz",
"integrity": "sha512-YseV51O3pc9IIX9MoP4XrVmkUq6a0u8h7n/B4zg0Y3bCQEs415LbM3UwIwZzF1DAEnphOu2xXgkH/9QCg5HvFg==", "integrity": "sha512-qoWjh9dABdL7AuSe4cmKiyZhNvrVZBRXA1GQgWiKSynm+rNYP+6Rc4SDT3vndNMqx9jRKbL4jwvKeU5vkVpFRA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@noble/hashes": "^1.7.1", "@noble/hashes": "^1.7.1",
"applesauce-core": "^3.1.0", "applesauce-core": "^4.0.0",
"nanoid": "^5.0.9", "nanoid": "^5.0.9",
"nostr-tools": "~2.15", "nostr-tools": "~2.17",
"rxjs": "^7.8.1" "rxjs": "^7.8.1"
}, },
"funding": { "funding": {
@@ -2286,18 +2037,18 @@
} }
}, },
"node_modules/applesauce-signers": { "node_modules/applesauce-signers": {
"version": "3.1.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/applesauce-signers/-/applesauce-signers-3.1.0.tgz", "resolved": "https://registry.npmjs.org/applesauce-signers/-/applesauce-signers-4.0.0.tgz",
"integrity": "sha512-7loxZ3hKSAhDRDs9rtVP/MPBZRmSbutE7g8/ALnXe5ihytbKSdF+0L5NBd0fxpSLMvmd8wEFOha6LsT03I5RCQ==", "integrity": "sha512-AHrPtH1Oy0l1OS7jwd/DfseUpOhTE8JhZIUIElcQgAlqE7Cgg5FO+LF7dWRPUNb3zn8P+mzEQN2wJhby84SNcA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@noble/hashes": "^1.7.1", "@noble/hashes": "^1.7.1",
"@noble/secp256k1": "^1.7.1", "@noble/secp256k1": "^1.7.1",
"@scure/base": "^1.2.4", "@scure/base": "^1.2.4",
"applesauce-core": "^3.1.0", "applesauce-core": "^4.0.0",
"debug": "^4.4.0", "debug": "^4.4.0",
"nanoid": "^5.0.9", "nanoid": "^5.0.9",
"nostr-tools": "~2.15", "nostr-tools": "~2.17",
"rxjs": "^7.8.2" "rxjs": "^7.8.2"
}, },
"funding": { "funding": {
@@ -2339,26 +2090,6 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/baseline-browser-mapping": {
"version": "2.8.10", "version": "2.8.10",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz", "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": "^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": { "node_modules/callsites": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -3317,26 +3024,6 @@
"url": "https://opencollective.com/unified" "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": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -4605,9 +4292,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/nostr-tools": { "node_modules/nostr-tools": {
"version": "2.15.2", "version": "2.17.0",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.15.2.tgz", "resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.17.0.tgz",
"integrity": "sha512-utmqVVS4HMDiwhIgI6Cr+KqA4aUhF3Sb755iO/qCiqxc5H9JW/9Z3N1RO/jKWpjP6q/Vx0lru7IYuiPvk+2/ng==", "integrity": "sha512-lrvHM7cSaGhz7F0YuBvgHMoU2s8/KuThihDoOYk8w5gpVHTy0DeUCAgCN8uLGeuSl5MAWekJr9Dkfo5HClqO9w==",
"license": "Unlicense", "license": "Unlicense",
"dependencies": { "dependencies": {
"@noble/ciphers": "^0.5.1", "@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", "name": "applesauce-accounts",
"version": "3.1.0", "version": "4.0.0",
"description": "A simple nostr account management system", "description": "A simple nostr account management system",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",
@@ -33,21 +33,23 @@
}, },
"dependencies": { "dependencies": {
"@noble/hashes": "^1.7.1", "@noble/hashes": "^1.7.1",
"applesauce-signers": "^3.1.0", "applesauce-core": "^4.0.0",
"applesauce-core": "^3.1.0", "applesauce-signers": "^4.0.0",
"nanoid": "^5.1.5", "nanoid": "^5.1.5",
"nostr-tools": "~2.15", "nostr-tools": "~2.17",
"rxjs": "^7.8.1" "rxjs": "^7.8.1"
}, },
"devDependencies": { "devDependencies": {
"rimraf": "^6.0.1",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vitest": "^3.2.3" "vitest": "^3.2.4"
}, },
"funding": { "funding": {
"type": "lightning", "type": "lightning",
"url": "lightning:nostrudel@geyser.fund" "url": "lightning:nostrudel@geyser.fund"
}, },
"scripts": { "scripts": {
"prebuild": "rimraf dist",
"build": "tsc", "build": "tsc",
"watch:build": "tsc --watch > /dev/null", "watch:build": "tsc --watch > /dev/null",
"test": "vitest run --passWithNoTests", "test": "vitest run --passWithNoTests",

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{ {
"name": "applesauce-actions", "name": "applesauce-actions",
"version": "3.1.0", "version": "4.0.0",
"description": "A package for performing common nostr actions", "description": "A package for performing common nostr actions",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",
@@ -32,24 +32,26 @@
} }
}, },
"dependencies": { "dependencies": {
"applesauce-core": "^3.1.0", "applesauce-core": "^4.0.0",
"applesauce-factory": "^3.1.0", "applesauce-factory": "^4.0.0",
"nostr-tools": "~2.15", "nostr-tools": "~2.17",
"rxjs": "^7.8.1" "rxjs": "^7.8.1"
}, },
"devDependencies": { "devDependencies": {
"@hirez_io/observer-spy": "^2.2.0", "@hirez_io/observer-spy": "^2.2.0",
"@types/debug": "^4.1.12", "@types/debug": "^4.1.12",
"applesauce-signers": "^3.1.0", "applesauce-signers": "^4.0.0",
"nanoid": "^5.1.5", "nanoid": "^5.1.5",
"rimraf": "^6.0.1",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vitest": "^3.2.3" "vitest": "^3.2.4"
}, },
"funding": { "funding": {
"type": "lightning", "type": "lightning",
"url": "lightning:nostrudel@geyser.fund" "url": "lightning:nostrudel@geyser.fund"
}, },
"scripts": { "scripts": {
"prebuild": "rimraf dist",
"build": "tsc", "build": "tsc",
"watch:build": "tsc --watch > /dev/null", "watch:build": "tsc --watch > /dev/null",
"test": "vitest run --passWithNoTests", "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 { Filter, NostrEvent } from "nostr-tools";
import { Observable } from "rxjs"; import { AddressPointer, EventPointer } from "nostr-tools/nip19";
import { AddressPointer, EventPointer, ProfilePointer } from "nostr-tools/nip19"; import { Observable, Subject } from "rxjs";
import { AddressPointerWithoutD } from "../helpers/pointers.js"; import { AddressPointerWithoutD } from "../helpers/pointers.js";
import { EventSet } from "./event-set.js"; import { EventMemory } from "./event-memory.js";
import { IEventStore, ModelConstructor } from "./interface.js"; import { IEventDatabase, IEventStore } from "./interface.js";
/** An extended {@link EventSet} that handles replaceable events, delets, and models */ declare const EventStore_base: {
export declare class EventStore implements IEventStore { new (...args: any[]): {
database: EventSet; [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 */ /** Enable this to keep old versions of replaceable events */
keepOldVersions: boolean; keepOldVersions: boolean;
/** Enable this to keep expired events */ /** Enable this to keep expired events */
@@ -17,11 +52,11 @@ export declare class EventStore implements IEventStore {
*/ */
verifyEvent?: (event: NostrEvent) => boolean; verifyEvent?: (event: NostrEvent) => boolean;
/** A stream of new events added to the store */ /** 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 */ /** 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 */ /** 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 * A method that will be called when an event isn't found in the store
* @experimental * @experimental
@@ -37,7 +72,9 @@ export declare class EventStore implements IEventStore {
* @experimental * @experimental
*/ */
addressableLoader?: (pointer: AddressPointer) => Observable<NostrEvent> | Promise<NostrEvent | undefined>; 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 deletedIds: Set<string>;
protected deletedCoords: Map<string, number>; protected deletedCoords: Map<string, number>;
protected checkDeleted(event: string | NostrEvent): boolean; 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 * @returns The existing event or the event that was added, if it was ignored returns null
*/ */
add(event: NostrEvent, fromRelay?: string): NostrEvent | 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; remove(event: string | NostrEvent): boolean;
/** Add an event to the store and notifies all subscribes it has updated */ /** Add an event to the store and notifies all subscribes it has updated */
update(event: NostrEvent): boolean; 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 */ /** Check if the store has an event by id */
hasEvent(id: string): boolean; hasEvent(id: string): boolean;
/** Get an event by id from the store */ /** 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 */ /** Returns all versions of a replaceable event */
getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined; getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined;
/** Get all events matching a filter */ /** 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 */ /** Returns a timeline of events that match filters */
getTimeline(filters: Filter | Filter[]): NostrEvent[]; 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 */ /** Sets the claim on the event and touches it */
claim(event: NostrEvent, claim: any): void; claim(event: NostrEvent, claim: any): void;
/** Checks if an event is claimed by anything */ /** Checks if an event is claimed by anything */
@@ -85,56 +122,13 @@ export declare class EventStore implements IEventStore {
removeClaim(event: NostrEvent, claim: any): void; removeClaim(event: NostrEvent, claim: any): void;
/** Removes all claims on an event */ /** Removes all claims on an event */
clearClaim(event: NostrEvent): void; clearClaim(event: NostrEvent): void;
/** A directory of all active models */ /** Pass through method for the database.unclaimed */
protected models: Map<ModelConstructor<any, any[]>, Map<string, Observable<any>>>; unclaimed(): Generator<NostrEvent>;
/** How long a model should be kept "warm" while nothing is subscribed to it */ /** Removes any event that is not being used by a subscription */
modelKeepWarm: number; prune(limit?: number): 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>;
/** Returns an observable that completes when an event is removed */ /** Returns an observable that completes when an event is removed */
removed(id: string): Observable<never>; removed(id: string): Observable<never>;
/** Creates an observable that emits when event is updated */ /** Creates an observable that emits when event is updated */
updated(event: string | NostrEvent): Observable<NostrEvent>; 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 { kinds } from "nostr-tools";
import { isAddressableKind } from "nostr-tools/kinds"; import { isAddressableKind } from "nostr-tools/kinds";
import { EMPTY, filter, finalize, from, merge, mergeMap, ReplaySubject, share, take, timer } from "rxjs"; import { EMPTY, filter, mergeMap, Subject, take } from "rxjs";
import hash_sum from "hash-sum";
import { getDeleteCoordinates, getDeleteIds } from "../helpers/delete.js"; import { getDeleteCoordinates, getDeleteIds } from "../helpers/delete.js";
import { createReplaceableAddress, EventStoreSymbol, FromCacheSymbol, isReplaceable } from "../helpers/event.js"; import { createReplaceableAddress, EventStoreSymbol, FromCacheSymbol, isReplaceable } from "../helpers/event.js";
import { getExpirationTimestamp } from "../helpers/expiration.js"; import { getExpirationTimestamp } from "../helpers/expiration.js";
import { matchFilters } from "../helpers/filter.js";
import { parseCoordinate } from "../helpers/pointers.js"; import { parseCoordinate } from "../helpers/pointers.js";
import { addSeenRelay, getSeenRelays } from "../helpers/relays.js"; import { addSeenRelay, getSeenRelays } from "../helpers/relays.js";
import { unixNow } from "../helpers/time.js"; import { unixNow } from "../helpers/time.js";
import { UserBlossomServersModel } from "../models/blossom.js"; import { EventMemory } from "./event-memory.js";
import { EventModel, EventsModel, ReplaceableModel, ReplaceableSetModel, TimelineModel } from "../models/common.js"; import { EventStoreModelMixin } from "./model-mixin.js";
import { ContactsModel } from "../models/contacts.js"; /** A wrapper around an event database that handles replaceable events, deletes, and models */
import { CommentsModel, ThreadModel } from "../models/index.js"; export class EventStore extends EventStoreModelMixin(class {
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 {
database; database;
/** Optional memory database for ensuring single event instances */
memory;
/** Enable this to keep old versions of replaceable events */ /** Enable this to keep old versions of replaceable events */
keepOldVersions = false; keepOldVersions = false;
/** Enable this to keep expired events */ /** Enable this to keep expired events */
@@ -31,11 +25,11 @@ export class EventStore {
*/ */
verifyEvent; verifyEvent;
/** A stream of new events added to the store */ /** A stream of new events added to the store */
insert$; insert$ = new Subject();
/** A stream of events that have been updated */ /** A stream of events that have been updated */
update$; update$ = new Subject();
/** A stream of events that have been removed */ /** 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 * A method that will be called when an event isn't found in the store
* @experimental * @experimental
@@ -51,27 +45,31 @@ export class EventStore {
* @experimental * @experimental
*/ */
addressableLoader; addressableLoader;
constructor() { constructor(database = new EventMemory()) {
this.database = new EventSet(); super();
// verify events before they are added to the database if (database) {
this.database.onBeforeInsert = (event) => { this.database = database;
// Ignore events that are invalid this.memory = new EventMemory();
if (this.verifyEvent && this.verifyEvent(event) === false) }
return false; else {
else // If no database is provided, its the same as having a memory database
return true; this.database = this.memory = new EventMemory();
}; }
// when events are added to the database, add the symbol // when events are added to the database, add the symbol
this.database.insert$.subscribe((event) => { this.insert$.subscribe((event) => {
Reflect.set(event, EventStoreSymbol, this); Reflect.set(event, EventStoreSymbol, this);
}); });
// when events are removed from the database, remove the symbol // when events are removed from the database, remove the symbol
this.database.remove$.subscribe((event) => { this.remove$.subscribe((event) => {
Reflect.deleteProperty(event, EventStoreSymbol); Reflect.deleteProperty(event, EventStoreSymbol);
}); });
this.insert$ = this.database.insert$; }
this.update$ = this.database.update$; mapToMemory(event) {
this.remove$ = this.database.remove$; if (event === undefined)
return undefined;
if (!this.memory)
return event;
return this.memory.add(event);
} }
// delete state // delete state
deletedIds = new Set(); deletedIds = new Set();
@@ -137,9 +135,7 @@ export class EventStore {
for (const id of ids) { for (const id of ids) {
this.deletedIds.add(id); this.deletedIds.add(id);
// remove deleted events in the database // remove deleted events in the database
const event = this.database.getEvent(id); this.remove(id);
if (event)
this.database.remove(event);
} }
const coords = getDeleteCoordinates(deleteEvent); const coords = getDeleteCoordinates(deleteEvent);
for (const coord of coords) { for (const coord of coords) {
@@ -152,7 +148,7 @@ export class EventStore {
const events = this.database.getReplaceableHistory(parsed.kind, parsed.pubkey, parsed.identifier) ?? []; const events = this.database.getReplaceableHistory(parsed.kind, parsed.pubkey, parsed.identifier) ?? [];
for (const event of events) { for (const event of events) {
if (event.created_at < deleteEvent.created_at) 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 * @returns The existing event or the event that was added, if it was ignored returns null
*/ */
add(event, fromRelay) { add(event, fromRelay) {
// Handle delete events differently
if (event.kind === kinds.EventDeletion) if (event.kind === kinds.EventDeletion)
this.handleDeleteEvent(event); this.handleDeleteEvent(event);
// Ignore if the event was deleted // Ignore if the event was deleted
@@ -193,32 +190,38 @@ export class EventStore {
return existing[0]; return existing[0];
} }
} }
else if (this.database.hasEvent(event.id)) { // Verify event before inserting into the database
// Duplicate event, copy symbols and return existing event if (this.verifyEvent && this.verifyEvent(event) === false)
const existing = this.database.getEvent(event.id); return null;
if (existing) { // Always add event to memory
EventStore.mergeDuplicateEvent(event, existing); const existing = this.memory?.add(event);
return existing; // 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 // Insert event into database
const inserted = this.database.add(event); const inserted = this.mapToMemory(this.database.add(event));
// If the event was ignored, return null
if (inserted === null)
return null;
// Copy cached data if its a duplicate event // Copy cached data if its a duplicate event
if (event !== inserted) if (event !== inserted)
EventStore.mergeDuplicateEvent(event, inserted); EventStore.mergeDuplicateEvent(event, inserted);
// attach relay this event was from // attach relay this event was from
if (fromRelay) if (fromRelay)
addSeenRelay(inserted, fromRelay); addSeenRelay(inserted, fromRelay);
// Emit insert$ signal
if (inserted === event)
this.insert$.next(inserted);
// remove all old version of the replaceable event // remove all old version of the replaceable event
if (!this.keepOldVersions && isReplaceable(event.kind)) { if (!this.keepOldVersions && isReplaceable(event.kind)) {
const existing = this.database.getReplaceableHistory(event.kind, event.pubkey, identifier); 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); const older = Array.from(existing).filter((e) => e.created_at < event.created_at);
for (const old of older) for (const old of older)
this.database.remove(old); this.remove(old);
// return the newest version of the replaceable event // return the newest version of the replaceable event
// most of the time this will be === event, but not always // most of the time this will be === event, but not always
if (existing.length !== older.length) if (existing.length !== older.length)
@@ -230,108 +233,103 @@ export class EventStore {
this.handleExpiringEvent(inserted); this.handleExpiringEvent(inserted);
return inserted; return inserted;
} }
/** Removes an event from the database and updates subscriptions */ /** Removes an event from the store and updates subscriptions */
remove(event) { 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 */ /** Add an event to the store and notifies all subscribes it has updated */
update(event) { update(event) {
return this.database.update(event); // Map the event to the current instance in the database
} const e = this.database.add(event);
/** Removes any event that is not being used by a subscription */ if (!e)
prune(max) { return false;
return this.database.prune(max); // 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 */ /** Check if the store has an event by id */
hasEvent(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 */ /** Get an event by id from the store */
getEvent(id) { 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 */ /** Check if the store has a replaceable event */
hasReplaceable(kind, pubkey, d) { 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 */ /** Gets the latest version of a replaceable event */
getReplaceable(kind, pubkey, identifier) { 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 */ /** Returns all versions of a replaceable event */
getReplaceableHistory(kind, pubkey, identifier) { 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 */ /** Get all events matching a filter */
getByFilters(filters) { 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 */ /** Returns a timeline of events that match filters */
getTimeline(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 */ /** Sets the claim on the event and touches it */
claim(event, claim) { claim(event, claim) {
this.database.claim(event, claim); return this.memory?.claim(event, claim);
} }
/** Checks if an event is claimed by anything */ /** Checks if an event is claimed by anything */
isClaimed(event) { isClaimed(event) {
return this.database.isClaimed(event); return this.memory?.isClaimed(event) ?? false;
} }
/** Removes a claim from an event */ /** Removes a claim from an event */
removeClaim(event, claim) { removeClaim(event, claim) {
this.database.removeClaim(event, claim); return this.memory?.removeClaim(event, claim);
} }
/** Removes all claims on an event */ /** Removes all claims on an event */
clearClaim(event) { clearClaim(event) {
this.database.clearClaim(event); return this.memory?.clearClaim(event);
} }
/** A directory of all active models */ /** Pass through method for the database.unclaimed */
models = new Map(); unclaimed() {
/** How long a model should be kept "warm" while nothing is subscribed to it */ return this.memory?.unclaimed() || (function* () { })();
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);
}
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;
} }
/** /** Removes any event that is not being used by a subscription */
* Creates an observable that streams all events that match the filter prune(limit) {
* @param filters return this.memory?.prune(limit) ?? 0;
* @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))));
} }
/** Returns an observable that completes when an event is removed */ /** Returns an observable that completes when an event is removed */
removed(id) { removed(id) {
@@ -348,83 +346,6 @@ export class EventStore {
} }
/** Creates an observable that emits when event is updated */ /** Creates an observable that emits when event is updated */
updated(event) { updated(event) {
return this.database.update$.pipe(filter((e) => e.id === event || e === event)); return this.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);
} }
} }

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-store.js";
export * from "./event-set.js";
export * from "./interface.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-store.js";
export * from "./event-set.js";
export * from "./interface.js"; export * from "./interface.js";

View File

@@ -1,28 +1,44 @@
import { Filter, NostrEvent } from "nostr-tools"; import { Filter, NostrEvent } from "nostr-tools";
import { AddressPointer, EventPointer, ProfilePointer } from "nostr-tools/nip19"; import { AddressPointer, EventPointer, ProfilePointer } from "nostr-tools/nip19";
import { Observable } from "rxjs"; 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 { 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 */ /** The read interface for an event store */
export interface IEventStoreRead { export interface IEventStoreRead {
/** Check if the event store has an event with id */ /** Check if the event store has an event with id */
hasEvent(id: string): boolean; 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 */ /** Get an event by id */
getEvent(id: string): NostrEvent | undefined; 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 */ /** Get a replaceable event */
getReplaceable(kind: number, pubkey: string, identifier?: string): NostrEvent | undefined; getReplaceable(kind: number, pubkey: string, identifier?: string): NostrEvent | undefined;
/** Get the history of a replaceable event */ /** Get the history of a replaceable event */
getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined; getReplaceableHistory(kind: number, pubkey: string, identifier?: string): NostrEvent[] | undefined;
/** Get all events that match the filters */ /** 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 */ /** Get a timeline of events that match the filters */
getTimeline(filters: Filter | Filter[]): NostrEvent[]; 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 */ /** The stream interface for an event store */
export interface IEventStoreStreams { export interface IEventStoreStreams {
/** A stream of new events added to the store */ /** A stream of new events added to the store */
@@ -41,8 +57,19 @@ export interface IEventStoreActions {
/** Notify the store that an event has updated */ /** Notify the store that an event has updated */
update(event: NostrEvent): void; 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 */ /** The claim interface for an event store */
export interface IEventClaims { export interface IEventClaims {
/** Tell the store that this event was used */
touch(event: NostrEvent): void;
/** Sets the claim on the event and touches it */ /** Sets the claim on the event and touches it */
claim(event: NostrEvent, claim: any): void; claim(event: NostrEvent, claim: any): void;
/** Checks if an event is claimed by anything */ /** Checks if an event is claimed by anything */
@@ -51,59 +78,105 @@ export interface IEventClaims {
removeClaim(event: NostrEvent, claim: any): void; removeClaim(event: NostrEvent, claim: any): void;
/** Removes all claims on an event */ /** Removes all claims on an event */
clearClaim(event: NostrEvent): void; 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 */ /** An event store that can be subscribed to */
export interface IEventStoreSubscriptions { export interface IEventSubscriptions {
/** Susbscribe to an event by id */ /** Subscribe to an event by id */
event(id: string | EventPointer): Observable<NostrEvent | undefined>; event(id: string | EventPointer): Observable<NostrEvent | undefined>;
/** Subscribe to a replaceable event by pointer */ /** Subscribe to a replaceable event by pointer */
replaceable(pointer: AddressPointerWithoutD): Observable<NostrEvent | undefined>; 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 */ /** Subscribe to an addressable event by pointer */
addressable(pointer: AddressPointer): Observable<NostrEvent | undefined>; addressable(pointer: AddressPointer): Observable<NostrEvent | undefined>;
/** Subscribe to a batch of events that match the filters */ /** 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 */ /** Methods for creating common models */
export interface IEventStoreModels { export interface IEventModelMixin<TStore extends IEventStore | IAsyncEventStore> {
model<T extends unknown, Args extends Array<any>>(constructor: ModelConstructor<T, Args>, ...args: Args): Observable<T>; model<T extends unknown, Args extends Array<any>>(constructor: ModelConstructor<T, Args, TStore>, ...args: Args): Observable<T>;
event(id: string): Observable<NostrEvent | undefined>; /** @deprecated use multiple {@link EventModel} instead */
replaceable(pointer: AddressPointerWithoutD): Observable<NostrEvent | undefined>;
addressable(pointer: AddressPointer): Observable<NostrEvent | undefined>;
timeline(filters: Filter | Filter[], includeOldVersion?: boolean): Observable<NostrEvent[]>;
events(ids: string[]): Observable<Record<string, NostrEvent | undefined>>; events(ids: string[]): Observable<Record<string, NostrEvent | undefined>>;
/** @deprecated use multiple {@link ReplaceableModel} instead */
replaceableSet(pointers: (AddressPointer | AddressPointerWithoutD)[]): Observable<Record<string, NostrEvent | undefined>>; replaceableSet(pointers: (AddressPointer | AddressPointerWithoutD)[]): Observable<Record<string, NostrEvent | undefined>>;
} }
/** A computed view of an event set or event store */ /** Methods for creating helpful models */
export type Model<T extends unknown> = (events: IEventStore) => Observable<T>; export interface IEventHelpfulSubscriptions {
/** A constructor for a {@link Model} */ /** Subscribe to a users profile */
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>;
profile(user: string | ProfilePointer): Observable<ProfileContent | undefined>; profile(user: string | ProfilePointer): Observable<ProfileContent | undefined>;
/** Subscribe to a users contacts */
contacts(user: string | ProfilePointer): Observable<ProfilePointer[]>; contacts(user: string | ProfilePointer): Observable<ProfilePointer[]>;
/** Subscribe to a users mutes */
mutes(user: string | ProfilePointer): Observable<Mutes | undefined>; mutes(user: string | ProfilePointer): Observable<Mutes | undefined>;
/** Subscribe to a users NIP-65 mailboxes */
mailboxes(user: string | ProfilePointer): Observable<{ mailboxes(user: string | ProfilePointer): Observable<{
inboxes: string[]; inboxes: string[];
outboxes: string[]; outboxes: string[];
} | undefined>; } | undefined>;
/** Subscribe to a users blossom servers */
blossomServers(user: string | ProfilePointer): Observable<URL[]>; blossomServers(user: string | ProfilePointer): Observable<URL[]>;
/** Subscribe to an event's reactions */
reactions(event: NostrEvent): Observable<NostrEvent[]>; reactions(event: NostrEvent): Observable<NostrEvent[]>;
/** Subscribe to a thread */
thread(root: string | EventPointer | AddressPointer): Observable<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 { NostrEvent } from "nostr-tools";
import { AddressPointer, EventPointer } from "nostr-tools/nip19"; import { AddressPointer, EventPointer } from "nostr-tools/nip19";
import { HiddenContentSigner } from "./index.js";
export declare const BookmarkPublicSymbol: unique symbol; export declare const BookmarkPublicSymbol: unique symbol;
export declare const BookmarkHiddenSymbol: unique symbol; export declare const BookmarkHiddenSymbol: unique symbol;
/** Type for unlocked bookmarks events */
export type UnlockedBookmarks = {
[BookmarkHiddenSymbol]: Bookmarks;
};
export type Bookmarks = { export type Bookmarks = {
notes: EventPointer[]; notes: EventPointer[];
articles: AddressPointer[]; articles: AddressPointer[];
@@ -16,5 +21,10 @@ export declare function mergeBookmarks(...bookmarks: (Bookmarks | undefined)[]):
export declare function getBookmarks(bookmark: NostrEvent): Bookmarks; export declare function getBookmarks(bookmark: NostrEvent): Bookmarks;
/** Returns the public bookmarks of the event */ /** Returns the public bookmarks of the event */
export declare function getPublicBookmarks(bookmark: NostrEvent): Bookmarks; 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 */ /** 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 { kinds } from "nostr-tools";
import { getOrComputeCachedValue } from "./cache.js"; 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"; import { getAddressPointerFromATag, getCoordinateFromAddressPointer, getEventPointerFromETag, mergeAddressPointers, mergeEventPointers, } from "./pointers.js";
export const BookmarkPublicSymbol = Symbol.for("bookmark-public"); export const BookmarkPublicSymbol = Symbol.for("bookmark-public");
export const BookmarkHiddenSymbol = Symbol.for("bookmark-hidden"); export const BookmarkHiddenSymbol = Symbol.for("bookmark-hidden");
@@ -63,9 +63,34 @@ export function getBookmarks(bookmark) {
export function getPublicBookmarks(bookmark) { export function getPublicBookmarks(bookmark) {
return getOrComputeCachedValue(bookmark, BookmarkPublicSymbol, () => parseBookmarkTags(bookmark.tags)); return getOrComputeCachedValue(bookmark, BookmarkPublicSymbol, () => parseBookmarkTags(bookmark.tags));
} }
/** Returns the bookmarks of the event if its unlocked */ /** Checks if the hidden bookmarks are unlocked */
export function getHiddenBookmarks(bookmark) { export function isHiddenBookmarksUnlocked(bookmark) {
if (isHiddenTagsLocked(bookmark)) return isHiddenTagsUnlocked(bookmark) && Reflect.has(bookmark, BookmarkHiddenSymbol);
return undefined; }
return getOrComputeCachedValue(bookmark, BookmarkHiddenSymbol, () => parseBookmarkTags(getHiddenTags(bookmark))); 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 { NostrEvent } from "nostr-tools";
import { ExternalPointer, ExternalIdentifiers } from "./external-id.js"; import { ExternalPointer, ExternalIdentifiers } from "./external-id.js";
import { KnownEvent } from "./index.js";
export declare const COMMENT_KIND = 1111; export declare const COMMENT_KIND = 1111;
/** Type for validated comment events */
export type CommentEvent = KnownEvent<typeof COMMENT_KIND>;
export type CommentEventPointer = { export type CommentEventPointer = {
type: "event"; type: "event";
id: string; id: string;
@@ -22,30 +25,20 @@ export type CommentExternalPointer<T extends keyof ExternalIdentifiers> = Extern
export type CommentPointer = CommentEventPointer | CommentAddressPointer | CommentExternalPointer<keyof ExternalIdentifiers>; export type CommentPointer = CommentEventPointer | CommentAddressPointer | CommentExternalPointer<keyof ExternalIdentifiers>;
export declare const CommentRootPointerSymbol: unique symbol; export declare const CommentRootPointerSymbol: unique symbol;
export declare const CommentReplyPointerSymbol: unique symbol; export declare const CommentReplyPointerSymbol: unique symbol;
/** /** Gets the EventPointer from an array of tags */
* Gets the EventPointer from an array of tags
* @throws
*/
export declare function getCommentEventPointer(tags: string[][], root?: boolean): CommentEventPointer | null; export declare function getCommentEventPointer(tags: string[][], root?: boolean): CommentEventPointer | null;
/** /** Gets the AddressPointer from an array of tags */
* Gets the AddressPointer from an array of tags
* @throws
*/
export declare function getCommentAddressPointer(tags: string[][], root?: boolean): CommentAddressPointer | null; export declare function getCommentAddressPointer(tags: string[][], root?: boolean): CommentAddressPointer | null;
/** /** Gets the ExternalPointer from an array of tags */
* Gets the ExternalPointer from an array of tags
* @throws
*/
export declare function getCommentExternalPointer(tags: string[][], root?: boolean): CommentExternalPointer<keyof ExternalIdentifiers> | null; export declare function getCommentExternalPointer(tags: string[][], root?: boolean): CommentExternalPointer<keyof ExternalIdentifiers> | null;
/** /** Returns the root pointer for a comment */
* Returns the root pointer for a comment export declare function getCommentRootPointer(comment: CommentEvent): CommentPointer;
* @throws
*/
export declare function getCommentRootPointer(comment: NostrEvent): CommentPointer | null; export declare function getCommentRootPointer(comment: NostrEvent): CommentPointer | null;
/** /** Returns the reply pointer for a comment */
* Returns the reply pointer for a comment
* @throws
*/
export declare function getCommentReplyPointer(comment: NostrEvent): CommentPointer | null; 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; 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; 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 COMMENT_KIND = 1111;
export const CommentRootPointerSymbol = Symbol.for("comment-root-pointer"); export const CommentRootPointerSymbol = Symbol.for("comment-root-pointer");
export const CommentReplyPointerSymbol = Symbol.for("comment-reply-pointer"); export const CommentReplyPointerSymbol = Symbol.for("comment-reply-pointer");
/** /** Gets the EventPointer from an array of tags */
* Gets the EventPointer from an array of tags
* @throws
*/
export function getCommentEventPointer(tags, root = false) { export function getCommentEventPointer(tags, root = false) {
const eTag = tags.find((t) => t[0] === (root ? "E" : "e")); const eTag = tags.find((t) => t[0] === (root ? "E" : "e"));
const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1]; const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
if (eTag) { if (eTag) {
// Missing kind tag, return null
if (!kind) 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 // 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 rootPubkey = root ? tags.find((t) => t[0] === "P")?.[1] : undefined;
const pointer = { const pointer = {
@@ -28,17 +26,15 @@ export function getCommentEventPointer(tags, root = false) {
} }
return null; return null;
} }
/** /** Gets the AddressPointer from an array of tags */
* Gets the AddressPointer from an array of tags
* @throws
*/
export function getCommentAddressPointer(tags, root = false) { export function getCommentAddressPointer(tags, root = false) {
const aTag = tags.find((t) => t[0] === (root ? "A" : "a")); const aTag = tags.find((t) => t[0] === (root ? "A" : "a"));
const eTag = tags.find((t) => t[0] === (root ? "E" : "e")); const eTag = tags.find((t) => t[0] === (root ? "E" : "e"));
const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1]; const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
if (aTag) { if (aTag) {
// Missing kind tag, return null
if (!kind) if (!kind)
throw new Error("Missing kind tag"); return null;
const addressPointer = getAddressPointerFromATag(aTag); const addressPointer = getAddressPointerFromATag(aTag);
const pointer = { const pointer = {
type: "address", type: "address",
@@ -52,16 +48,10 @@ export function getCommentAddressPointer(tags, root = false) {
} }
return null; return null;
} }
/** /** Gets the ExternalPointer from an array of tags */
* Gets the ExternalPointer from an array of tags
* @throws
*/
export function getCommentExternalPointer(tags, root = false) { export function getCommentExternalPointer(tags, root = false) {
const iTag = tags.find((t) => t[0] === (root ? "I" : "i")); const iTag = tags.find((t) => t[0] === (root ? "I" : "i"));
const kind = tags.find((t) => t[0] === (root ? "K" : "k"))?.[1];
if (iTag) { if (iTag) {
if (!kind)
throw new Error("Missing kind tag");
return { return {
type: "external", type: "external",
...getExternalPointerFromTag(iTag), ...getExternalPointerFromTag(iTag),
@@ -69,13 +59,9 @@ export function getCommentExternalPointer(tags, root = false) {
} }
return null; return null;
} }
/**
* Returns the root pointer for a comment
* @throws
*/
export function getCommentRootPointer(comment) { export function getCommentRootPointer(comment) {
if (comment.kind !== COMMENT_KIND) if (comment.kind !== COMMENT_KIND)
throw new Error("Event is not a comment"); return null;
return getOrComputeCachedValue(comment, CommentRootPointerSymbol, () => { return getOrComputeCachedValue(comment, CommentRootPointerSymbol, () => {
// check for address pointer first since it can also have E tags // check for address pointer first since it can also have E tags
const A = getCommentAddressPointer(comment.tags, true); const A = getCommentAddressPointer(comment.tags, true);
@@ -90,13 +76,10 @@ export function getCommentRootPointer(comment) {
return null; return null;
}); });
} }
/** /** Returns the reply pointer for a comment */
* Returns the reply pointer for a comment
* @throws
*/
export function getCommentReplyPointer(comment) { export function getCommentReplyPointer(comment) {
if (comment.kind !== COMMENT_KIND) if (comment.kind !== COMMENT_KIND)
throw new Error("Event is not a comment"); return null;
return getOrComputeCachedValue(comment, CommentReplyPointerSymbol, () => { return getOrComputeCachedValue(comment, CommentReplyPointerSymbol, () => {
// check for address pointer first since it can also have E tags // check for address pointer first since it can also have E tags
const A = getCommentAddressPointer(comment.tags, false); const A = getCommentAddressPointer(comment.tags, false);
@@ -111,15 +94,21 @@ export function getCommentReplyPointer(comment) {
return null; return null;
}); });
} }
/** Checks if a pointer is a {@link CommentEventPointer} */
export function isCommentEventPointer(pointer) { export function isCommentEventPointer(pointer) {
return (Reflect.has(pointer, "id") && return (Reflect.has(pointer, "id") &&
Reflect.has(pointer, "kind") && Reflect.has(pointer, "kind") &&
!Reflect.has(pointer, "identifier") && !Reflect.has(pointer, "identifier") &&
typeof pointer.kind === "number"); typeof pointer.kind === "number");
} }
/** Checks if a pointer is a {@link CommentAddressPointer} */
export function isCommentAddressPointer(pointer) { export function isCommentAddressPointer(pointer) {
return (Reflect.has(pointer, "identifier") && return (Reflect.has(pointer, "identifier") &&
Reflect.has(pointer, "pubkey") && Reflect.has(pointer, "pubkey") &&
Reflect.has(pointer, "kind") && Reflect.has(pointer, "kind") &&
typeof pointer.kind === "number"); 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 { NostrEvent } from "nostr-tools";
import { ProfilePointer } from "nostr-tools/nip19"; import { ProfilePointer } from "nostr-tools/nip19";
import { HiddenContentSigner } from "./hidden-content.js";
export declare const ContactsRelaysSymbol: unique symbol; export declare const ContactsRelaysSymbol: unique symbol;
export declare const PublicContactsSymbol: unique symbol; export declare const PublicContactsSymbol: unique symbol;
export declare const HiddenContactsSymbol: 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 */ /** Merges any number of contact lists into a single list */
export declare function mergeContacts(...pointers: (ProfilePointer | undefined | (ProfilePointer | undefined)[])[]): ProfilePointer[]; export declare function mergeContacts(...pointers: (ProfilePointer | undefined | (ProfilePointer | undefined)[])[]): ProfilePointer[];
/** Returns all public and hidden contacts from a contacts list event */ /** Returns all public and hidden contacts from a contacts list event */
export declare function getContacts(event: NostrEvent): ProfilePointer[]; export declare function getContacts(event: NostrEvent): ProfilePointer[];
/** Returns only the public contacts from a contacts list event */ /** Returns only the public contacts from a contacts list event */
export declare function getPublicContacts(event: NostrEvent): ProfilePointer[]; 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 */ /** Returns only the hidden contacts from a contacts list event */
export declare function getHiddenContacts(event: NostrEvent): ProfilePointer[] | undefined; 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 { isSafeRelayURL } from "./relays.js";
import { isPTag, processTags } from "./tags.js"; import { isPTag, processTags } from "./tags.js";
import { getProfilePointerFromPTag } from "./pointers.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 ContactsRelaysSymbol = Symbol.for("contacts-relays");
export const PublicContactsSymbol = Symbol.for("public-contacts"); export const PublicContactsSymbol = Symbol.for("public-contacts");
export const HiddenContactsSymbol = Symbol.for("hidden-contacts"); export const HiddenContactsSymbol = Symbol.for("hidden-contacts");
@@ -51,9 +52,35 @@ export function getContacts(event) {
export function getPublicContacts(event) { export function getPublicContacts(event) {
return getOrComputeCachedValue(event, PublicContactsSymbol, () => processTags(event.tags, (t) => (isPTag(t) ? t : undefined), getProfilePointerFromPTag)); 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 */ /** Returns only the hidden contacts from a contacts list event */
export function getHiddenContacts(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 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 { kinds } from "nostr-tools";
import { catchError, combineLatest, distinct, EMPTY, filter, isObservable, map, merge, mergeMap, of, switchMap, } from "rxjs"; import { catchError, combineLatest, distinct, EMPTY, filter, isObservable, map, merge, mergeMap, of, switchMap, } from "rxjs";
import { logger } from "../logger.js"; 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 { notifyEventUpdate } from "./event.js";
import { getGiftWrapSeal, getSealGiftWrap, getSealRumor } from "./gift-wraps.js"; import { getGiftWrapSeal, getSealGiftWrap, getSealRumor } from "./gift-wraps.js";
/** A symbol that is used to mark encrypted content as being from a cache */ /** 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$ const restore = eventStore.insert$
.pipe( .pipe(
// Look for events that support encrypted content and are locked // 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 // Get the encrypted content from storage
mergeMap((event) => mergeMap((event) =>
// Wait for storage to be available // Wait for storage to be available
@@ -52,11 +52,11 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
const restoreSeals = eventStore.update$ const restoreSeals = eventStore.update$
.pipe( .pipe(
// Look for gift wraps that are unlocked // 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 // Get the seal event
map((gift) => getGiftWrapSeal(gift)), map((gift) => getGiftWrapSeal(gift)),
// Look for gift wraps with locked seals // 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 // Only attempt to unlock seals once
distinct((seal) => seal.id), distinct((seal) => seal.id),
// Get encrypted content from storage // Get encrypted content from storage
@@ -84,7 +84,7 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
.pipe( .pipe(
// Look for events that support encrypted content and are unlocked and not from the cache // Look for events that support encrypted content and are unlocked and not from the cache
filter(([event]) => canHaveEncryptedContent(event.kind) && filter(([event]) => canHaveEncryptedContent(event.kind) &&
!isEncryptedContentLocked(event) && isEncryptedContentUnlocked(event) &&
!isEncryptedContentFromCache(event)), !isEncryptedContentFromCache(event)),
// Only persist the encrypted content once // Only persist the encrypted content once
distinct(([event]) => event.id)) distinct(([event]) => event.id))
@@ -106,13 +106,13 @@ export function persistEncryptedContent(eventStore, storage, fallback) {
const persistSeals = combineLatest([merge(eventStore.update$, eventStore.insert$), storage$]) const persistSeals = combineLatest([merge(eventStore.update$, eventStore.insert$), storage$])
.pipe( .pipe(
// Look for gift wraps that are unlocked // 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 // Get the seal event
map(([gift, storage]) => [getGiftWrapSeal(gift), storage]), map(([gift, storage]) => [getGiftWrapSeal(gift), storage]),
// Make sure the seal is defined // Make sure the seal is defined
filter(([seal]) => seal !== undefined), filter(([seal]) => seal !== undefined),
// Make sure seal is unlocked and not from cache // 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 // Only persist the seal once
distinct(([seal]) => seal.id)) distinct(([seal]) => seal.id))
.subscribe(async ([seal, storage]) => { .subscribe(async ([seal, storage]) => {

View File

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

View File

@@ -1,5 +1,5 @@
import { kinds } from "nostr-tools"; 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 */ /** A symbol use to store the encrypted content of an event in memory */
export const EncryptedContentSymbol = Symbol.for("encrypted-content"); export const EncryptedContentSymbol = Symbol.for("encrypted-content");
/** Various event kinds that can have encrypted content and which encryption method they use */ /** 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) { export function hasEncryptedContent(event) {
return event.content.length > 0; return event.content.length > 0;
} }
/** Returns the encrypted content for an event if it is unlocked */
export function getEncryptedContent(event) { export function getEncryptedContent(event) {
return Reflect.get(event, EncryptedContentSymbol); return Reflect.get(event, EncryptedContentSymbol);
} }
/** Checks if the encrypted content is locked */ /** Checks if the encrypted content is unlocked and casts it to the {@link UnlockedEncryptedContent} type */
export function isEncryptedContentLocked(event) { export function isEncryptedContentUnlocked(event) {
return Reflect.has(event, EncryptedContentSymbol) === false; return Reflect.has(event, EncryptedContentSymbol) === true;
} }
/** /**
* Unlocks the encrypted content in an event and caches it * Unlocks the encrypted content in an event and caches it
* @param event The event with content to decrypt * @param event The event with content to decrypt
* @param pubkey The other pubkey that encrypted the content * @param pubkey The other pubkey that encrypted the content
* @param signer A signer to use to decrypt 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) { 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 encryption = getEncryptedContentEncryptionMethods(event.kind, signer);
const plaintext = await encryption.decrypt(pubkey, event.content); const plaintext = await encryption.decrypt(pubkey, event.content);
// Set the cached value and trigger update
setEncryptedContentCache(event, plaintext); setEncryptedContentCache(event, plaintext);
// Return the decrypted content
return plaintext; return plaintext;
} }
/** Sets the encrypted content on an event and updates it if its part of an event store */ /** Sets the encrypted content on an event and updates it if its part of an event store */
export function setEncryptedContentCache(event, plaintext) { export function setEncryptedContentCache(event, plaintext) {
Reflect.set(event, EncryptedContentSymbol, plaintext); Reflect.set(event, EncryptedContentSymbol, plaintext);
// if the event has been added to an event store, notify it // if the event has been added to an event store, notify it
if (isEvent(event)) notifyEventUpdate(event);
notifyEventUpdate(event);
} }
/** Removes the encrypted content cache on an event */ /** Removes the encrypted content cache on an event */
export function lockEncryptedContent(event) { export function lockEncryptedContent(event) {
Reflect.deleteProperty(event, EncryptedContentSymbol); Reflect.deleteProperty(event, EncryptedContentSymbol);
// if the event has been added to an event store, notify it // if the event has been added to an event store, notify it
if (isEvent(event)) notifyEventUpdate(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 * @param opts.maxBatchSize - The maximum number of events to write in a batch
* @returns A function to stop the process * @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; maxBatchSize?: number;
batchTime?: number; batchTime?: number;
}): () => void; }): () => 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 * @param opts.maxBatchSize - The maximum number of events to write in a batch
* @returns A function to stop the process * @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; const time = opts?.batchTime ?? 5_000;
// Save all new events to the cache // Save all new events to the cache
const sub = eventStore.insert$ const sub = eventStore.insert$
@@ -30,3 +30,5 @@ export function presistEventsToCache(eventStore, write, opts) {
}); });
return () => sub.unsubscribe(); 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[][]; tags: string[][];
content: string; content: string;
}>(event: T, name: string): string | undefined; }>(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 */ /** Returns a Set of tag names and values that are indexable */
export declare function getIndexableTags(event: NostrEvent): Set<string>; export declare function getIndexableTags(event: NostrEvent): Set<string>;

View File

@@ -13,6 +13,10 @@ export function getTagValue(event, name) {
return hiddenValue; return hiddenValue;
return event.tags.find((t) => t[0] === name)?.[1]; 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 */ /** Returns a Set of tag names and values that are indexable */
export function getIndexableTags(event) { export function getIndexableTags(event) {
let indexable = Reflect.get(event, EventIndexableTagsSymbol); let indexable = Reflect.get(event, EventIndexableTagsSymbol);

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