0.1.4 - Android bundle with tor, patch cashu-ts

This commit is contained in:
minibits-cash
2023-11-09 23:22:37 +01:00
parent bc35c75440
commit 6b336c7afd
408 changed files with 420 additions and 55490 deletions
+2 -2
View File
@@ -102,8 +102,8 @@ android {
applicationId "com.minibits_wallet"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 22
versionName "0.1.3-tor.2"
versionCode 23
versionName "0.1.4"
ndk {
abiFilters 'arm64-v8a', 'x86_64', 'x86', 'armeabi-v7a'
} // react-native-tor build
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "minibits_wallet",
"version": "0.1.3-tor.2-beta.16",
"version": "0.1.4-beta",
"private": true,
"scripts": {
"android:clean": "cd android && ./gradlew clean",
@@ -15,7 +15,7 @@
"postinstall": "patch-package"
},
"dependencies": {
"@cashu/cashu-ts": "file:src/lib/cashu-ts",
"@cashu/cashu-ts": "^0.8.2-rc.7",
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
+385
View File
@@ -0,0 +1,385 @@
diff --git a/node_modules/@cashu/cashu-ts/package.json b/node_modules/@cashu/cashu-ts/package.json
index af7649e..91a54c4 100644
--- a/node_modules/@cashu/cashu-ts/package.json
+++ b/node_modules/@cashu/cashu-ts/package.json
@@ -2,8 +2,7 @@
"name": "@cashu/cashu-ts",
"version": "0.8.2-rc.7",
"description": "cashu library for communicating with a cashu mint",
- "main": "dist/lib/es5/index.js",
- "module": "dist/lib/es6/index.js",
+ "main": "src/index.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/cashubtc/cashu-ts"
diff --git a/node_modules/@cashu/cashu-ts/src/CashuMint.ts b/node_modules/@cashu/cashu-ts/src/CashuMint.ts
index f6bc3b1..0bfe1dc 100644
--- a/node_modules/@cashu/cashu-ts/src/CashuMint.ts
+++ b/node_modules/@cashu/cashu-ts/src/CashuMint.ts
@@ -6,14 +6,14 @@ import {
MeltResponse,
MintKeys,
PostRestoreResponse,
- RequestMintResponse,
+ RequestMintResponse,
SerializedBlindedMessage,
SerializedBlindedSignature,
SplitPayload,
SplitResponse
-} from './model/types/index.js';
-import request from './request.js';
-import { isObj, joinUrls } from './utils.js';
+} from './model/types/index';
+import request from './request';
+import { isObj, joinUrls } from './utils';
/**
* Class represents Cashu Mint API. This class contains Lower level functions that are implemented by CashuWallet.
@@ -21,24 +21,33 @@ import { isObj, joinUrls } from './utils.js';
class CashuMint {
/**
* @param _mintUrl requires mint URL to create this object
+ * @param _customRequest if passed, use custom request implementation for network communication with the mint
*/
- constructor(private _mintUrl: string) {}
+ constructor(
+ private _mintUrl: string,
+ private _customRequest?: typeof request
+ ) {}
get mintUrl() {
return this._mintUrl;
}
+
/**
* fetches mints info at the /info endpoint
* @param mintUrl
*/
- public static async getInfo(mintUrl: string): Promise<GetInfoResponse> {
- return request<GetInfoResponse>({ endpoint: joinUrls(mintUrl, 'info') });
+ public static async getInfo(
+ mintUrl: string,
+ customRequest?: typeof request
+ ): Promise<GetInfoResponse> {
+ const requestInstance = customRequest || request;
+ return requestInstance<GetInfoResponse>({ endpoint: joinUrls(mintUrl, 'info') });
}
/**
* fetches mints info at the /info endpoint
*/
async getInfo(): Promise<GetInfoResponse> {
- return CashuMint.getInfo(this._mintUrl);
+ return CashuMint.getInfo(this._mintUrl, this._customRequest);
}
/**
* Starts a minting process by requesting an invoice from the mint
@@ -46,8 +55,13 @@ class CashuMint {
* @param amount Amount requesting for mint.
* @returns the mint will create and return a Lightning invoice for the specified amount
*/
- public static async requestMint(mintUrl: string, amount: number): Promise<RequestMintResponse> {
- return request<RequestMintResponse>({
+ public static async requestMint(
+ mintUrl: string,
+ amount: number,
+ customRequest?: typeof request
+ ): Promise<RequestMintResponse> {
+ const requestInstance = customRequest || request;
+ return requestInstance<RequestMintResponse>({
endpoint: `${joinUrls(mintUrl, 'mint')}?amount=${amount}`
});
}
@@ -58,7 +72,7 @@ class CashuMint {
* @returns the mint will create and return a Lightning invoice for the specified amount
*/
async requestMint(amount: number): Promise<RequestMintResponse> {
- return CashuMint.requestMint(this._mintUrl, amount);
+ return CashuMint.requestMint(this._mintUrl, amount, this._customRequest);
}
/**
* Requests the mint to perform token minting after the LN invoice has been paid
@@ -70,9 +84,11 @@ class CashuMint {
public static async mint(
mintUrl: string,
payloads: { outputs: Array<SerializedBlindedMessage> },
- hash: string
+ hash: string,
+ customRequest?: typeof request
) {
- const data = await request<{ promises: Array<SerializedBlindedSignature> }>({
+ const requestInstance = customRequest || request;
+ const data = await requestInstance<{ promises: Array<SerializedBlindedSignature> }>({
endpoint: `${joinUrls(mintUrl, 'mint')}?hash=${hash}`,
method: 'POST',
requestBody: payloads
@@ -91,7 +107,7 @@ class CashuMint {
* @returns serialized blinded signatures
*/
async mint(payloads: { outputs: Array<SerializedBlindedMessage> }, hash: string) {
- return CashuMint.mint(this._mintUrl, payloads, hash);
+ return CashuMint.mint(this._mintUrl, payloads, hash, this._customRequest);
}
/**
* Get the mints public keys
@@ -99,12 +115,17 @@ class CashuMint {
* @param keysetId optional param to get the keys for a specific keyset. If not specified, the keys from the active keyset are fetched
* @returns
*/
- public static async getKeys(mintUrl: string, keysetId?: string): Promise<MintKeys> {
+ public static async getKeys(
+ mintUrl: string,
+ keysetId?: string,
+ customRequest?: typeof request
+ ): Promise<MintKeys> {
if (keysetId) {
// make the keysetId url safe
keysetId = keysetId.replace(/\//g, '_').replace(/\+/g, '-');
}
- return request<MintKeys>({
+ const requestInstance = customRequest || request;
+ return requestInstance<MintKeys>({
endpoint: keysetId ? joinUrls(mintUrl, 'keys', keysetId) : joinUrls(mintUrl, 'keys')
});
}
@@ -114,15 +135,19 @@ class CashuMint {
* @returns the mints public keys
*/
async getKeys(keysetId?: string): Promise<MintKeys> {
- return CashuMint.getKeys(this._mintUrl, keysetId);
+ return CashuMint.getKeys(this._mintUrl, keysetId, this._customRequest);
}
/**
* Get the mints keysets in no specific order
* @param mintUrl
* @returns all the mints past and current keysets.
*/
- public static async getKeySets(mintUrl: string): Promise<{ keysets: Array<string> }> {
- return request<{ keysets: Array<string> }>({ endpoint: joinUrls(mintUrl, 'keysets') });
+ public static async getKeySets(
+ mintUrl: string,
+ customRequest?: typeof request
+ ): Promise<{ keysets: Array<string> }> {
+ const requestInstance = customRequest || request;
+ return requestInstance<{ keysets: Array<string> }>({ endpoint: joinUrls(mintUrl, 'keysets') });
}
/**
@@ -130,7 +155,7 @@ class CashuMint {
* @returns all the mints past and current keysets.
*/
async getKeySets(): Promise<{ keysets: Array<string> }> {
- return CashuMint.getKeySets(this._mintUrl);
+ return CashuMint.getKeySets(this._mintUrl, this._customRequest);
}
/**
@@ -139,8 +164,13 @@ class CashuMint {
* @param splitPayload data needed for performing a token split
* @returns split tokens
*/
- public static async split(mintUrl: string, splitPayload: SplitPayload): Promise<SplitResponse> {
- const data = await request<SplitResponse>({
+ public static async split(
+ mintUrl: string,
+ splitPayload: SplitPayload,
+ customRequest?: typeof request
+ ): Promise<SplitResponse> {
+ const requestInstance = customRequest || request;
+ const data = await requestInstance<SplitResponse>({
endpoint: joinUrls(mintUrl, 'split'),
method: 'POST',
requestBody: splitPayload
@@ -158,7 +188,7 @@ class CashuMint {
* @returns split tokens
*/
async split(splitPayload: SplitPayload): Promise<SplitResponse> {
- return CashuMint.split(this._mintUrl, splitPayload);
+ return CashuMint.split(this._mintUrl, splitPayload, this._customRequest);
}
/**
* Ask mint to perform a melt operation. This pays a lightning invoice and destroys tokens matching its amount + fees
@@ -166,8 +196,13 @@ class CashuMint {
* @param meltPayload
* @returns
*/
- public static async melt(mintUrl: string, meltPayload: MeltPayload): Promise<MeltResponse> {
- const data = await request<MeltResponse>({
+ public static async melt(
+ mintUrl: string,
+ meltPayload: MeltPayload,
+ customRequest?: typeof request
+ ): Promise<MeltResponse> {
+ const requestInstance = customRequest || request;
+ const data = await requestInstance<MeltResponse>({
endpoint: joinUrls(mintUrl, 'melt'),
method: 'POST',
requestBody: meltPayload
@@ -189,7 +224,7 @@ class CashuMint {
* @returns
*/
async melt(meltPayload: MeltPayload): Promise<MeltResponse> {
- return CashuMint.melt(this._mintUrl, meltPayload);
+ return CashuMint.melt(this._mintUrl, meltPayload, this._customRequest);
}
/**
* Estimate fees for a given LN invoice
@@ -199,9 +234,11 @@ class CashuMint {
*/
public static async checkFees(
mintUrl: string,
- checkfeesPayload: { pr: string }
+ checkfeesPayload: { pr: string },
+ customRequest?: typeof request
): Promise<{ fee: number }> {
- const data = await request<{ fee: number }>({
+ const requestInstance = customRequest || request;
+ const data = await requestInstance<{ fee: number }>({
endpoint: joinUrls(mintUrl, 'checkfees'),
method: 'POST',
requestBody: checkfeesPayload
@@ -220,7 +257,7 @@ class CashuMint {
* @returns estimated Fee
*/
async checkFees(checkfeesPayload: { pr: string }): Promise<{ fee: number }> {
- return CashuMint.checkFees(this._mintUrl, checkfeesPayload);
+ return CashuMint.checkFees(this._mintUrl, checkfeesPayload, this._customRequest);
}
/**
* Checks if specific proofs have already been redeemed
@@ -230,9 +267,11 @@ class CashuMint {
*/
public static async check(
mintUrl: string,
- checkPayload: CheckSpendablePayload
+ checkPayload: CheckSpendablePayload,
+ customRequest?: typeof request
): Promise<CheckSpendableResponse> {
- const data = await request<CheckSpendableResponse>({
+ const requestInstance = customRequest || request;
+ const data = await requestInstance<CheckSpendableResponse>({
endpoint: joinUrls(mintUrl, 'check'),
method: 'POST',
requestBody: checkPayload
@@ -250,14 +289,16 @@ class CashuMint {
* @returns redeemed and unredeemed ordered list of booleans
*/
async check(checkPayload: CheckSpendablePayload): Promise<CheckSpendableResponse> {
- return CashuMint.check(this._mintUrl, checkPayload);
+ return CashuMint.check(this._mintUrl, checkPayload, this._customRequest);
}
public static async restore(
mintUrl: string,
- restorePayload: { outputs: Array<SerializedBlindedMessage> }
+ restorePayload: { outputs: Array<SerializedBlindedMessage> },
+ customRequest?: typeof request
): Promise<PostRestoreResponse> {
- const data = await request<PostRestoreResponse>({
+ const requestInstance = customRequest || request;
+ const data = await requestInstance<PostRestoreResponse>({
endpoint: joinUrls(mintUrl, 'restore'),
method: 'POST',
requestBody: restorePayload
diff --git a/node_modules/@cashu/cashu-ts/src/CashuWallet.ts b/node_modules/@cashu/cashu-ts/src/CashuWallet.ts
index b7cf354..d3e9f0a 100644
--- a/node_modules/@cashu/cashu-ts/src/CashuWallet.ts
+++ b/node_modules/@cashu/cashu-ts/src/CashuWallet.ts
@@ -1,7 +1,7 @@
import { randomBytes } from '@noble/hashes/utils';
-import { CashuMint } from './CashuMint.js';
-import * as dhke from './DHKE.js';
-import { BlindedMessage } from './model/BlindedMessage.js';
+import { CashuMint } from './CashuMint';
+import * as dhke from './DHKE';
+import { BlindedMessage } from './model/BlindedMessage';
import {
AmountPreference,
BlindedMessageData,
@@ -17,7 +17,7 @@ import {
SerializedBlindedSignature,
SplitPayload,
TokenEntry
-} from './model/types/index.js';
+} from './model/types/index';
import {
bytesToNumber,
cleanToken,
@@ -25,8 +25,8 @@ import {
getDecodedToken,
getDefaultAmountPreference,
splitAmount
-} from './utils.js';
-import { deriveBlindingFactor, deriveSecret, deriveSeedFromMnemonic } from './secrets.js';
+} from './utils';
+import { deriveBlindingFactor, deriveSecret, deriveSeedFromMnemonic } from './secrets';
/**
* Class that represents a Cashu wallet.
diff --git a/node_modules/@cashu/cashu-ts/src/DHKE.ts b/node_modules/@cashu/cashu-ts/src/DHKE.ts
index ca37559..02843b5 100644
--- a/node_modules/@cashu/cashu-ts/src/DHKE.ts
+++ b/node_modules/@cashu/cashu-ts/src/DHKE.ts
@@ -1,8 +1,8 @@
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
import { secp256k1 } from '@noble/curves/secp256k1';
-import { encodeUint8toBase64 } from './base64.js';
-import { MintKeys, Proof, SerializedBlindedSignature } from './model/types/index.js';
-import { bytesToNumber } from './utils.js';
+import { encodeUint8toBase64 } from './base64';
+import { MintKeys, Proof, SerializedBlindedSignature } from './model/types/index';
+import { bytesToNumber } from './utils';
import { sha256 } from '@noble/hashes/sha256';
import { bytesToHex } from '@noble/curves/abstract/utils';
diff --git a/node_modules/@cashu/cashu-ts/src/index.ts b/node_modules/@cashu/cashu-ts/src/index.ts
index b1f2c00..138c251 100644
--- a/node_modules/@cashu/cashu-ts/src/index.ts
+++ b/node_modules/@cashu/cashu-ts/src/index.ts
@@ -1,10 +1,10 @@
-import { CashuMint } from './CashuMint.js';
-import { CashuWallet } from './CashuWallet.js';
-import { setGlobalRequestOptions } from './request.js';
-import { getEncodedToken, getDecodedToken, deriveKeysetId } from './utils.js';
+import { CashuMint } from './CashuMint';
+import { CashuWallet } from './CashuWallet';
+import { setGlobalRequestOptions } from './request';
+import { getEncodedToken, getDecodedToken, deriveKeysetId } from './utils';
import { decode as getDecodedLnInvoice } from '@gandlaf21/bolt11-decode';
-import { generateNewMnemonic } from './secrets.js';
-export * from './model/types/index.js';
+import { generateNewMnemonic } from './secrets';
+export * from './model/types/index';
export {
CashuMint,
diff --git a/node_modules/@cashu/cashu-ts/src/secrets.ts b/node_modules/@cashu/cashu-ts/src/secrets.ts
index ce70d91..4ebe676 100644
--- a/node_modules/@cashu/cashu-ts/src/secrets.ts
+++ b/node_modules/@cashu/cashu-ts/src/secrets.ts
@@ -4,6 +4,7 @@ import { wordlist } from '@scure/bip39/wordlists/english';
import { encodeBase64toUint8 } from './base64';
import { bytesToNumber } from './utils';
import { hexToNumber } from '@noble/curves/abstract/utils';
+
export const generateNewMnemonic = (): string => {
const mnemonic = generateMnemonic(wordlist, 128);
if (!validateMnemonic(mnemonic, wordlist)) {
diff --git a/node_modules/@cashu/cashu-ts/src/utils.ts b/node_modules/@cashu/cashu-ts/src/utils.ts
index 541a94f..ba835d1 100644
--- a/node_modules/@cashu/cashu-ts/src/utils.ts
+++ b/node_modules/@cashu/cashu-ts/src/utils.ts
@@ -1,4 +1,4 @@
-import { encodeBase64ToJson, encodeJsonToBase64 } from './base64.js';
+import { encodeBase64ToJson, encodeJsonToBase64 } from './base64';
import {
AmountPreference,
MintKeys,
@@ -7,7 +7,7 @@ import {
TokenEntry,
TokenV2
} from './model/types/index.js';
-import { TOKEN_PREFIX, TOKEN_VERSION } from './utils/Constants.js';
+import { TOKEN_PREFIX, TOKEN_VERSION } from './utils/Constants';
import { bytesToHex } from '@noble/curves/abstract/utils';
import { sha256 } from '@noble/hashes/sha256';
import { Buffer } from 'buffer/';
-6
View File
@@ -1,6 +0,0 @@
/node_modules
/dist
/test
/coverage
/.jest
jest.config.ts
-28
View File
@@ -1,28 +0,0 @@
// .eslintrc
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module",
"project": "tsconfig.json"
},
"plugins": ["@typescript-eslint"],
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"rules": {
"@typescript-eslint/no-unused-vars": "warn",
// to enforce using type for object type definitions, can be type or interface
"@typescript-eslint/consistent-type-definitions": ["warn", "type"],
"@typescript-eslint/array-type": ["warn", { "default": "generic" }],
"require-await": "off",
"@typescript-eslint/require-await": "warn",
"@typescript-eslint/await-thenable": "warn",
"@typescript-eslint/consistent-type-exports": "warn",
"no-else-return": "warn"
},
"env": {
"browser": true,
"es2021": true
}
}
-16
View File
@@ -1,16 +0,0 @@
# Fixes: #[issue]
## Description
...
## Changes
- ...
- ...
## PR Tasks
- [x] Open PR
- [ ] run `npm run test` --> no failing unit tests
- [ ] run `npm run format`
-26
View File
@@ -1,26 +0,0 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
name: Node.js CI
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x, 16.x, 18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run compile
- run: npm test
-31
View File
@@ -1,31 +0,0 @@
name: Generate and Deploy Typedoc
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm install
- name: Generate Typedoc
run: npm run typedoc
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs
-3
View File
@@ -1,3 +0,0 @@
coverage
docs
!node_modules/
-4
View File
@@ -1,4 +0,0 @@
/test
/coverage
/.github
tsconfig.json
-3
View File
@@ -1,3 +0,0 @@
dist
node_modules
package-lock.json
-7
View File
@@ -1,7 +0,0 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"pluginSearchDirs": ["."]
}
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 Cashu
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.
-80
View File
@@ -1,80 +0,0 @@
# Cashu TS
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/cashubtc/cashu-ts/node.js.yml)
![GitHub issues](https://img.shields.io/github/issues/cashubtc/cashu-ts)
![GitHub package.json version](https://img.shields.io/github/package-json/v/cashubtc/cashu-ts)
![npm](https://img.shields.io/npm/v/@cashu/cashu-ts)
![npm type definitions](https://img.shields.io/npm/types/@cashu/cashu-ts)
![npm bundle size](https://img.shields.io/bundlephobia/min/@cashu/cashu-ts)
⚠️ **Don't be reckless:** This project is in early development, it does however work with real sats! Always use amounts you don't mind losing.
Cashu TS is a JavaScript library for [Cashu](https://github.com/cashubtc) wallets written in Typescript.
Wallet Features:
- [x] connect to mint (load keys)
- [x] request minting tokens
- [x] minting tokens
- [x] sending tokens (get encoded token for chosen value)
- [x] receiving tokens
- [x] melting tokens
- [x] check if tokens are spent
- [ ] ...
Implemented [NUTs](https://github.com/cashubtc/nuts/):
- [x] [NUT-00](https://github.com/cashubtc/nuts/blob/main/00.md)
- [x] [NUT-01](https://github.com/cashubtc/nuts/blob/main/01.md)
- [x] [NUT-02](https://github.com/cashubtc/nuts/blob/main/02.md)
- [x] [NUT-03](https://github.com/cashubtc/nuts/blob/main/03.md)
- [x] [NUT-04](https://github.com/cashubtc/nuts/blob/main/04.md)
- [x] [NUT-05](https://github.com/cashubtc/nuts/blob/main/05.md)
- [x] [NUT-06](https://github.com/cashubtc/nuts/blob/main/06.md)
- [x] [NUT-07](https://github.com/cashubtc/nuts/blob/main/07.md)
- [x] [NUT-08](https://github.com/cashubtc/nuts/blob/main/08.md)
- [x] [NUT-09](https://github.com/cashubtc/nuts/blob/main/09.md)
Supported token formats:
- [x] v1 read
- [x] v2 read (deprecated)
- [x] v3 read/write
## Usage
Go to the [docs](https://cashubtc.github.io/cashu-ts/) for detailed usage.
### Install
```shell
npm i @cashu/cashu-ts
```
### Import
```typescript
import { CashuMint, CashuWallet, getEncodedToken } from '@cashu/cashu-ts';
const wallet = new CashuWallet(new CashuMint('{MINT_URL}'));
const { pr, hash } = await wallet.requestMint(200);
//pay this LN invoice
console.log({ pr }, { hash });
async function invoiceHasBeenPaid() {
const { proofs } = await wallet.requestTokens(200, hash);
//Encoded proofs can be spent at the mint
const encoded = getEncodedToken({
token: [{ mint: '{MINT_URL}', proofs }]
});
console.log(encoded);
}
```
## Contribute
Contributions are very welcome.
If you want to contribute, please open an Issue or a PR.
-150
View File
@@ -1,150 +0,0 @@
import { CheckSpendablePayload, CheckSpendableResponse, GetInfoResponse, MeltPayload, MeltResponse, MintKeys, RequestMintResponse, SerializedBlindedMessage, SerializedBlindedSignature, SplitPayload, SplitResponse } from './model/types/index.js';
import request from './request.js';
/**
* Class represents Cashu Mint API. This class contains Lower level functions that are implemented by CashuWallet.
*/
declare class CashuMint {
private _mintUrl;
private _customRequest?;
/**
* @param _mintUrl requires mint URL to create this object
* @param _customRequest if passed, use custom request implementation for network communication with the mint
*/
constructor(_mintUrl: string, _customRequest?: typeof request | undefined);
get mintUrl(): string;
/**
* fetches mints info at the /info endpoint
* @param mintUrl
*/
static getInfo(mintUrl: string, customRequest?: typeof request): Promise<GetInfoResponse>;
/**
* fetches mints info at the /info endpoint
*/
getInfo(): Promise<GetInfoResponse>;
/**
* Starts a minting process by requesting an invoice from the mint
* @param mintUrl
* @param amount Amount requesting for mint.
* @returns the mint will create and return a Lightning invoice for the specified amount
*/
static requestMint(mintUrl: string, amount: number, customRequest?: typeof request): Promise<RequestMintResponse>;
/**
* Starts a minting process by requesting an invoice from the mint
* @param amount Amount requesting for mint.
* @returns the mint will create and return a Lightning invoice for the specified amount
*/
requestMint(amount: number): Promise<RequestMintResponse>;
/**
* Requests the mint to perform token minting after the LN invoice has been paid
* @param mintUrl
* @param payloads outputs (Blinded messages) that can be written
* @param hash hash (id) used for by the mint to keep track of wether the invoice has been paid yet
* @returns serialized blinded signatures
*/
static mint(mintUrl: string, payloads: {
outputs: Array<SerializedBlindedMessage>;
}, hash: string, customRequest?: typeof request): Promise<{
promises: Array<SerializedBlindedSignature>;
}>;
/**
* Requests the mint to perform token minting after the LN invoice has been paid
* @param payloads outputs (Blinded messages) that can be written
* @param hash hash (id) used for by the mint to keep track of wether the invoice has been paid yet
* @returns serialized blinded signatures
*/
mint(payloads: {
outputs: Array<SerializedBlindedMessage>;
}, hash: string): Promise<{
promises: SerializedBlindedSignature[];
}>;
/**
* Get the mints public keys
* @param mintUrl
* @param keysetId optional param to get the keys for a specific keyset. If not specified, the keys from the active keyset are fetched
* @returns
*/
static getKeys(mintUrl: string, keysetId?: string, customRequest?: typeof request): Promise<MintKeys>;
/**
* Get the mints public keys
* @param keysetId optional param to get the keys for a specific keyset. If not specified, the keys from the active keyset are fetched
* @returns the mints public keys
*/
getKeys(keysetId?: string): Promise<MintKeys>;
/**
* Get the mints keysets in no specific order
* @param mintUrl
* @returns all the mints past and current keysets.
*/
static getKeySets(mintUrl: string, customRequest?: typeof request): Promise<{
keysets: Array<string>;
}>;
/**
* Get the mints keysets in no specific order
* @returns all the mints past and current keysets.
*/
getKeySets(): Promise<{
keysets: Array<string>;
}>;
/**
* Ask mint to perform a split operation
* @param mintUrl
* @param splitPayload data needed for performing a token split
* @returns split tokens
*/
static split(mintUrl: string, splitPayload: SplitPayload, customRequest?: typeof request): Promise<SplitResponse>;
/**
* Ask mint to perform a split operation
* @param splitPayload data needed for performing a token split
* @returns split tokens
*/
split(splitPayload: SplitPayload): Promise<SplitResponse>;
/**
* Ask mint to perform a melt operation. This pays a lightning invoice and destroys tokens matching its amount + fees
* @param mintUrl
* @param meltPayload
* @returns
*/
static melt(mintUrl: string, meltPayload: MeltPayload, customRequest?: typeof request): Promise<MeltResponse>;
/**
* Ask mint to perform a melt operation. This pays a lightning invoice and destroys tokens matching its amount + fees
* @param meltPayload
* @returns
*/
melt(meltPayload: MeltPayload): Promise<MeltResponse>;
/**
* Estimate fees for a given LN invoice
* @param mintUrl
* @param checkfeesPayload Payload containing LN invoice that needs to get a fee estimate
* @returns estimated Fee
*/
static checkFees(mintUrl: string, checkfeesPayload: {
pr: string;
}, customRequest?: typeof request): Promise<{
fee: number;
}>;
/**
* Estimate fees for a given LN invoice
* @param mintUrl
* @param checkfeesPayload Payload containing LN invoice that needs to get a fee estimate
* @returns estimated Fee
*/
checkFees(checkfeesPayload: {
pr: string;
}): Promise<{
fee: number;
}>;
/**
* Checks if specific proofs have already been redeemed
* @param mintUrl
* @param checkPayload
* @returns redeemed and unredeemed ordered list of booleans
*/
static check(mintUrl: string, checkPayload: CheckSpendablePayload, customRequest?: typeof request): Promise<CheckSpendableResponse>;
/**
* Checks if specific proofs have already been redeemed
* @param checkPayload
* @returns redeemed and unredeemed ordered list of booleans
*/
check(checkPayload: CheckSpendablePayload): Promise<CheckSpendableResponse>;
}
export { CashuMint };
-376
View File
@@ -1,376 +0,0 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
import request from './request.js';
import { isObj, joinUrls } from './utils.js';
/**
* Class represents Cashu Mint API. This class contains Lower level functions that are implemented by CashuWallet.
*/
var CashuMint = /** @class */ (function () {
/**
* @param _mintUrl requires mint URL to create this object
* @param _customRequest if passed, use custom request implementation for network communication with the mint
*/
function CashuMint(_mintUrl, _customRequest) {
this._mintUrl = _mintUrl;
this._customRequest = _customRequest;
}
Object.defineProperty(CashuMint.prototype, "mintUrl", {
get: function () {
return this._mintUrl;
},
enumerable: false,
configurable: true
});
/**
* fetches mints info at the /info endpoint
* @param mintUrl
*/
CashuMint.getInfo = function (mintUrl, customRequest) {
return __awaiter(this, void 0, void 0, function () {
var requestInstance;
return __generator(this, function (_a) {
requestInstance = customRequest || request;
return [2 /*return*/, requestInstance({ endpoint: joinUrls(mintUrl, 'info') })];
});
});
};
/**
* fetches mints info at the /info endpoint
*/
CashuMint.prototype.getInfo = function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, CashuMint.getInfo(this._mintUrl, this._customRequest)];
});
});
};
/**
* Starts a minting process by requesting an invoice from the mint
* @param mintUrl
* @param amount Amount requesting for mint.
* @returns the mint will create and return a Lightning invoice for the specified amount
*/
CashuMint.requestMint = function (mintUrl, amount, customRequest) {
return __awaiter(this, void 0, void 0, function () {
var requestInstance;
return __generator(this, function (_a) {
requestInstance = customRequest || request;
return [2 /*return*/, requestInstance({
endpoint: "".concat(joinUrls(mintUrl, 'mint'), "?amount=").concat(amount)
})];
});
});
};
/**
* Starts a minting process by requesting an invoice from the mint
* @param amount Amount requesting for mint.
* @returns the mint will create and return a Lightning invoice for the specified amount
*/
CashuMint.prototype.requestMint = function (amount) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, CashuMint.requestMint(this._mintUrl, amount, this._customRequest)];
});
});
};
/**
* Requests the mint to perform token minting after the LN invoice has been paid
* @param mintUrl
* @param payloads outputs (Blinded messages) that can be written
* @param hash hash (id) used for by the mint to keep track of wether the invoice has been paid yet
* @returns serialized blinded signatures
*/
CashuMint.mint = function (mintUrl, payloads, hash, customRequest) {
return __awaiter(this, void 0, void 0, function () {
var requestInstance, data;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
requestInstance = customRequest || request;
return [4 /*yield*/, requestInstance({
endpoint: "".concat(joinUrls(mintUrl, 'mint'), "?hash=").concat(hash),
method: 'POST',
requestBody: payloads
})];
case 1:
data = _a.sent();
if (!isObj(data) || !Array.isArray(data === null || data === void 0 ? void 0 : data.promises)) {
throw new Error('bad response');
}
return [2 /*return*/, data];
}
});
});
};
/**
* Requests the mint to perform token minting after the LN invoice has been paid
* @param payloads outputs (Blinded messages) that can be written
* @param hash hash (id) used for by the mint to keep track of wether the invoice has been paid yet
* @returns serialized blinded signatures
*/
CashuMint.prototype.mint = function (payloads, hash) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, CashuMint.mint(this._mintUrl, payloads, hash, this._customRequest)];
});
});
};
/**
* Get the mints public keys
* @param mintUrl
* @param keysetId optional param to get the keys for a specific keyset. If not specified, the keys from the active keyset are fetched
* @returns
*/
CashuMint.getKeys = function (mintUrl, keysetId, customRequest) {
return __awaiter(this, void 0, void 0, function () {
var requestInstance;
return __generator(this, function (_a) {
if (keysetId) {
// make the keysetId url safe
keysetId = keysetId.replace(/\//g, '_').replace(/\+/g, '-');
}
requestInstance = customRequest || request;
return [2 /*return*/, requestInstance({
endpoint: keysetId ? joinUrls(mintUrl, 'keys', keysetId) : joinUrls(mintUrl, 'keys')
})];
});
});
};
/**
* Get the mints public keys
* @param keysetId optional param to get the keys for a specific keyset. If not specified, the keys from the active keyset are fetched
* @returns the mints public keys
*/
CashuMint.prototype.getKeys = function (keysetId) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, CashuMint.getKeys(this._mintUrl, keysetId, this._customRequest)];
});
});
};
/**
* Get the mints keysets in no specific order
* @param mintUrl
* @returns all the mints past and current keysets.
*/
CashuMint.getKeySets = function (mintUrl, customRequest) {
return __awaiter(this, void 0, void 0, function () {
var requestInstance;
return __generator(this, function (_a) {
requestInstance = customRequest || request;
return [2 /*return*/, requestInstance({ endpoint: joinUrls(mintUrl, 'keysets') })];
});
});
};
/**
* Get the mints keysets in no specific order
* @returns all the mints past and current keysets.
*/
CashuMint.prototype.getKeySets = function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, CashuMint.getKeySets(this._mintUrl, this._customRequest)];
});
});
};
/**
* Ask mint to perform a split operation
* @param mintUrl
* @param splitPayload data needed for performing a token split
* @returns split tokens
*/
CashuMint.split = function (mintUrl, splitPayload, customRequest) {
return __awaiter(this, void 0, void 0, function () {
var requestInstance, data;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
requestInstance = customRequest || request;
return [4 /*yield*/, requestInstance({
endpoint: joinUrls(mintUrl, 'split'),
method: 'POST',
requestBody: splitPayload
})];
case 1:
data = _a.sent();
if (!isObj(data) || !Array.isArray(data === null || data === void 0 ? void 0 : data.promises)) {
throw new Error('bad response');
}
return [2 /*return*/, data];
}
});
});
};
/**
* Ask mint to perform a split operation
* @param splitPayload data needed for performing a token split
* @returns split tokens
*/
CashuMint.prototype.split = function (splitPayload) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, CashuMint.split(this._mintUrl, splitPayload, this._customRequest)];
});
});
};
/**
* Ask mint to perform a melt operation. This pays a lightning invoice and destroys tokens matching its amount + fees
* @param mintUrl
* @param meltPayload
* @returns
*/
CashuMint.melt = function (mintUrl, meltPayload, customRequest) {
return __awaiter(this, void 0, void 0, function () {
var requestInstance, data;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
requestInstance = customRequest || request;
return [4 /*yield*/, requestInstance({
endpoint: joinUrls(mintUrl, 'melt'),
method: 'POST',
requestBody: meltPayload
})];
case 1:
data = _a.sent();
if (!isObj(data) ||
typeof (data === null || data === void 0 ? void 0 : data.paid) !== 'boolean' ||
((data === null || data === void 0 ? void 0 : data.preimage) !== null && typeof (data === null || data === void 0 ? void 0 : data.preimage) !== 'string')) {
throw new Error('bad response');
}
return [2 /*return*/, data];
}
});
});
};
/**
* Ask mint to perform a melt operation. This pays a lightning invoice and destroys tokens matching its amount + fees
* @param meltPayload
* @returns
*/
CashuMint.prototype.melt = function (meltPayload) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, CashuMint.melt(this._mintUrl, meltPayload, this._customRequest)];
});
});
};
/**
* Estimate fees for a given LN invoice
* @param mintUrl
* @param checkfeesPayload Payload containing LN invoice that needs to get a fee estimate
* @returns estimated Fee
*/
CashuMint.checkFees = function (mintUrl, checkfeesPayload, customRequest) {
return __awaiter(this, void 0, void 0, function () {
var requestInstance, data;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
requestInstance = customRequest || request;
return [4 /*yield*/, requestInstance({
endpoint: joinUrls(mintUrl, 'checkfees'),
method: 'POST',
requestBody: checkfeesPayload
})];
case 1:
data = _a.sent();
if (!isObj(data) || typeof (data === null || data === void 0 ? void 0 : data.fee) !== 'number') {
throw new Error('bad response');
}
return [2 /*return*/, data];
}
});
});
};
/**
* Estimate fees for a given LN invoice
* @param mintUrl
* @param checkfeesPayload Payload containing LN invoice that needs to get a fee estimate
* @returns estimated Fee
*/
CashuMint.prototype.checkFees = function (checkfeesPayload) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, CashuMint.checkFees(this._mintUrl, checkfeesPayload, this._customRequest)];
});
});
};
/**
* Checks if specific proofs have already been redeemed
* @param mintUrl
* @param checkPayload
* @returns redeemed and unredeemed ordered list of booleans
*/
CashuMint.check = function (mintUrl, checkPayload, customRequest) {
return __awaiter(this, void 0, void 0, function () {
var requestInstance, data;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
requestInstance = customRequest || request;
return [4 /*yield*/, requestInstance({
endpoint: joinUrls(mintUrl, 'check'),
method: 'POST',
requestBody: checkPayload
})];
case 1:
data = _a.sent();
if (!isObj(data) || !Array.isArray(data === null || data === void 0 ? void 0 : data.spendable)) {
throw new Error('bad response');
}
return [2 /*return*/, data];
}
});
});
};
/**
* Checks if specific proofs have already been redeemed
* @param checkPayload
* @returns redeemed and unredeemed ordered list of booleans
*/
CashuMint.prototype.check = function (checkPayload) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, CashuMint.check(this._mintUrl, checkPayload, this._customRequest)];
});
});
};
return CashuMint;
}());
export { CashuMint };
//# sourceMappingURL=CashuMint.js.map
File diff suppressed because one or more lines are too long
-128
View File
@@ -1,128 +0,0 @@
import { CashuMint } from './CashuMint.js';
import { AmountPreference, MintKeys, PayLnInvoiceResponse, PaymentPayload, Proof, ReceiveResponse, ReceiveTokenEntryResponse, SendResponse, TokenEntry } from './model/types/index.js';
/**
* Class that represents a Cashu wallet.
* This class should act as the entry point for this library
*/
declare class CashuWallet {
private _keys;
private _keysetId;
mint: CashuMint;
/**
* @param keys public keys from the mint
* @param mint Cashu mint instance is used to make api calls
*/
constructor(mint: CashuMint, keys?: MintKeys);
get keys(): MintKeys;
set keys(keys: MintKeys);
get keysetId(): string;
/**
* returns proofs that are already spent (use for keeping wallet state clean)
* @param proofs (only the 'secret' field is required)
* @returns
*/
checkProofsSpent<T extends {
secret: string;
}>(proofs: Array<T>): Promise<Array<T>>;
/**
* Starts a minting process by requesting an invoice from the mint
* @param amount Amount requesting for mint.
* @returns the mint will create and return a Lightning invoice for the specified amount
*/
requestMint(amount: number): Promise<import("./model/types/index.js").RequestMintResponse>;
/**
* Executes a payment of an invoice on the Lightning network.
* The combined amount of Proofs has to match the payment amount including fees.
* @param invoice
* @param proofsToSend the exact amount to send including fees
* @param feeReserve? optionally set LN routing fee reserve. If not set, fee reserve will get fetched at mint
*/
payLnInvoice(invoice: string, proofsToSend: Array<Proof>, feeReserve?: number): Promise<PayLnInvoiceResponse>;
/**
* Estimate fees for a given LN invoice
* @param invoice LN invoice that needs to get a fee estimate
* @returns estimated Fee
*/
getFee(invoice: string): Promise<number>;
createPaymentPayload(invoice: string, proofs: Array<Proof>): PaymentPayload;
/**
* Use a cashu token to pay an ln invoice
* @param invoice Lightning invoice
* @param token cashu token
*/
payLnInvoiceWithToken(invoice: string, token: string): Promise<PayLnInvoiceResponse>;
/**
* Receive an encoded Cashu token
* @param encodedToken Cashu token
* @param preference optional preference for splitting proofs into specific amounts
* @returns New token with newly created proofs, token entries that had errors, and newKeys if they have changed
*/
receive(encodedToken: string, preference?: Array<AmountPreference>): Promise<ReceiveResponse>;
/**
* Receive a single cashu token entry
* @param tokenEntry a single entry of a cashu token
* @param preference optional preference for splitting proofs into specific amounts.
* @returns New token entry with newly created proofs, proofs that had errors, and newKeys if they have changed
*/
receiveTokenEntry(tokenEntry: TokenEntry, preference?: Array<AmountPreference>): Promise<ReceiveTokenEntryResponse>;
/**
* Splits and creates sendable tokens
* if no amount is specified, the amount is implied by the cumulative amount of all proofs
* if both amount and preference are set, but the preference cannot fulfill the amount, then we use the default split
* @param amount amount to send while performing the optimal split (least proofs possible). can be set to undefined if preference is set
* @param proofs proofs matching that amount
* @param preference optional preference for splitting proofs into specific amounts. overrides amount param
* @returns promise of the change- and send-proofs
*/
send(amount: number, proofs: Array<Proof>, preference?: Array<AmountPreference>): Promise<SendResponse>;
/**
* Request tokens from the mint
* @param amount amount to request
* @param hash hash to use to identify the request
* @returns proofs and newKeys if they have changed
*/
requestTokens(amount: number, hash: string, AmountPreference?: Array<AmountPreference>): Promise<{
proofs: Array<Proof>;
newKeys?: MintKeys;
}>;
/**
* Initialize the wallet with the mints public keys
*/
private initKeys;
/**
* Check if the keysetId has changed and return the new keys
* @param promises array of promises to check
* @returns new keys if they have changed
*/
private changedKeys;
/**
* Get the mint's public keys for a given set of proofs
* @param arr array of proofs
* @param mint optional mint url
* @returns keys
*/
private getKeys;
/**
* Creates a split payload
* @param amount1 amount to keep
* @param amount2 amount to send
* @param proofsToSend proofs to split
* @returns
*/
private createSplitPayload;
private splitReceive;
/**
* Creates blinded messages for a given amount
* @param amount amount to create blinded messages for
* @returns blinded messages, secrets, rs, and amounts
*/
private createRandomBlindedMessages;
/**
* Creates NUT-08 blank outputs (fee returns) for a given fee reserve
* See: https://github.com/cashubtc/nuts/blob/main/08.md
* @param feeReserve amount to cover with blank outputs
* @returns blinded messages, secrets, and rs
*/
private createBlankOutputs;
}
export { CashuWallet };
-590
View File
@@ -1,590 +0,0 @@
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
import { randomBytes } from '@noble/hashes/utils';
import { CashuMint } from './CashuMint.js';
import * as dhke from './DHKE.js';
import { BlindedMessage } from './model/BlindedMessage.js';
import { cleanToken, deriveKeysetId, getDecodedToken, getDefaultAmountPreference, splitAmount } from './utils.js';
/**
* Class that represents a Cashu wallet.
* This class should act as the entry point for this library
*/
var CashuWallet = /** @class */ (function () {
/**
* @param keys public keys from the mint
* @param mint Cashu mint instance is used to make api calls
*/
function CashuWallet(mint, keys) {
this._keysetId = '';
this._keys = keys || {};
this.mint = mint;
if (keys) {
this._keysetId = deriveKeysetId(this._keys);
}
}
Object.defineProperty(CashuWallet.prototype, "keys", {
get: function () {
return this._keys;
},
set: function (keys) {
this._keys = keys;
this._keysetId = deriveKeysetId(this._keys);
},
enumerable: false,
configurable: true
});
Object.defineProperty(CashuWallet.prototype, "keysetId", {
get: function () {
return this._keysetId;
},
enumerable: false,
configurable: true
});
/**
* returns proofs that are already spent (use for keeping wallet state clean)
* @param proofs (only the 'secret' field is required)
* @returns
*/
CashuWallet.prototype.checkProofsSpent = function (proofs) {
return __awaiter(this, void 0, void 0, function () {
var payload, spendable;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
payload = {
//send only the secret
proofs: proofs.map(function (p) { return ({ secret: p.secret }); })
};
return [4 /*yield*/, this.mint.check(payload)];
case 1:
spendable = (_a.sent()).spendable;
return [2 /*return*/, proofs.filter(function (_, i) { return !spendable[i]; })];
}
});
});
};
/**
* Starts a minting process by requesting an invoice from the mint
* @param amount Amount requesting for mint.
* @returns the mint will create and return a Lightning invoice for the specified amount
*/
CashuWallet.prototype.requestMint = function (amount) {
return this.mint.requestMint(amount);
};
/**
* Executes a payment of an invoice on the Lightning network.
* The combined amount of Proofs has to match the payment amount including fees.
* @param invoice
* @param proofsToSend the exact amount to send including fees
* @param feeReserve? optionally set LN routing fee reserve. If not set, fee reserve will get fetched at mint
*/
CashuWallet.prototype.payLnInvoice = function (invoice, proofsToSend, feeReserve) {
var _a;
return __awaiter(this, void 0, void 0, function () {
var paymentPayload, _b, blindedMessages, secrets, rs, payData, _c, _d, _e, _f;
var _g;
return __generator(this, function (_h) {
switch (_h.label) {
case 0:
paymentPayload = this.createPaymentPayload(invoice, proofsToSend);
if (!!feeReserve) return [3 /*break*/, 2];
return [4 /*yield*/, this.getFee(invoice)];
case 1:
feeReserve = _h.sent();
_h.label = 2;
case 2:
_b = this.createBlankOutputs(feeReserve), blindedMessages = _b.blindedMessages, secrets = _b.secrets, rs = _b.rs;
return [4 /*yield*/, this.mint.melt(__assign(__assign({}, paymentPayload), { outputs: blindedMessages }))];
case 3:
payData = _h.sent();
_g = {
isPaid: (_a = payData.paid) !== null && _a !== void 0 ? _a : false,
preimage: payData.preimage
};
if (!(payData === null || payData === void 0 ? void 0 : payData.change)) return [3 /*break*/, 5];
_e = (_d = dhke).constructProofs;
_f = [payData.change, rs, secrets];
return [4 /*yield*/, this.getKeys(payData.change)];
case 4:
_c = _e.apply(_d, _f.concat([_h.sent()]));
return [3 /*break*/, 6];
case 5:
_c = [];
_h.label = 6;
case 6:
_g.change = _c;
return [4 /*yield*/, this.changedKeys(payData === null || payData === void 0 ? void 0 : payData.change)];
case 7: return [2 /*return*/, (_g.newKeys = _h.sent(),
_g)];
}
});
});
};
/**
* Estimate fees for a given LN invoice
* @param invoice LN invoice that needs to get a fee estimate
* @returns estimated Fee
*/
CashuWallet.prototype.getFee = function (invoice) {
return __awaiter(this, void 0, void 0, function () {
var fee;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.mint.checkFees({ pr: invoice })];
case 1:
fee = (_a.sent()).fee;
return [2 /*return*/, fee];
}
});
});
};
CashuWallet.prototype.createPaymentPayload = function (invoice, proofs) {
return {
pr: invoice,
proofs: proofs
};
};
/**
* Use a cashu token to pay an ln invoice
* @param invoice Lightning invoice
* @param token cashu token
*/
CashuWallet.prototype.payLnInvoiceWithToken = function (invoice, token) {
var _this = this;
var decodedToken = getDecodedToken(token);
var proofs = decodedToken.token
.filter(function (x) { return x.mint === _this.mint.mintUrl; })
.flatMap(function (t) { return t.proofs; });
return this.payLnInvoice(invoice, proofs);
};
/**
* Receive an encoded Cashu token
* @param encodedToken Cashu token
* @param preference optional preference for splitting proofs into specific amounts
* @returns New token with newly created proofs, token entries that had errors, and newKeys if they have changed
*/
CashuWallet.prototype.receive = function (encodedToken, preference) {
var _a;
return __awaiter(this, void 0, void 0, function () {
var token, tokenEntries, tokenEntriesWithError, newKeys, _i, token_1, tokenEntry, _b, proofsWithError, proofs, newKeysFromReceive, error_1;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
token = cleanToken(getDecodedToken(encodedToken)).token;
tokenEntries = [];
tokenEntriesWithError = [];
_i = 0, token_1 = token;
_c.label = 1;
case 1:
if (!(_i < token_1.length)) return [3 /*break*/, 6];
tokenEntry = token_1[_i];
if (!((_a = tokenEntry === null || tokenEntry === void 0 ? void 0 : tokenEntry.proofs) === null || _a === void 0 ? void 0 : _a.length)) {
return [3 /*break*/, 5];
}
_c.label = 2;
case 2:
_c.trys.push([2, 4, , 5]);
return [4 /*yield*/, this.receiveTokenEntry(tokenEntry, preference)];
case 3:
_b = _c.sent(), proofsWithError = _b.proofsWithError, proofs = _b.proofs, newKeysFromReceive = _b.newKeys;
if (proofsWithError === null || proofsWithError === void 0 ? void 0 : proofsWithError.length) {
tokenEntriesWithError.push(tokenEntry);
return [3 /*break*/, 5];
}
tokenEntries.push({ mint: tokenEntry.mint, proofs: __spreadArray([], proofs, true) });
if (!newKeys) {
newKeys = newKeysFromReceive;
}
return [3 /*break*/, 5];
case 4:
error_1 = _c.sent();
console.error(error_1);
tokenEntriesWithError.push(tokenEntry);
return [3 /*break*/, 5];
case 5:
_i++;
return [3 /*break*/, 1];
case 6: return [2 /*return*/, {
token: { token: tokenEntries },
tokensWithErrors: tokenEntriesWithError.length ? { token: tokenEntriesWithError } : undefined,
newKeys: newKeys
}];
}
});
});
};
/**
* Receive a single cashu token entry
* @param tokenEntry a single entry of a cashu token
* @param preference optional preference for splitting proofs into specific amounts.
* @returns New token entry with newly created proofs, proofs that had errors, and newKeys if they have changed
*/
CashuWallet.prototype.receiveTokenEntry = function (tokenEntry, preference) {
return __awaiter(this, void 0, void 0, function () {
var proofsWithError, proofs, newKeys, amount, _a, payload, blindedMessages, _b, promises, error, newProofs, _c, _d, _e, _f, error_2;
return __generator(this, function (_g) {
switch (_g.label) {
case 0:
proofsWithError = [];
proofs = [];
_g.label = 1;
case 1:
_g.trys.push([1, 7, , 8]);
amount = tokenEntry.proofs.reduce(function (total, curr) { return total + curr.amount; }, 0);
if (!preference) {
preference = getDefaultAmountPreference(amount);
}
_a = this.createSplitPayload(amount, tokenEntry.proofs, preference), payload = _a.payload, blindedMessages = _a.blindedMessages;
return [4 /*yield*/, CashuMint.split(tokenEntry.mint, payload)];
case 2:
_b = _g.sent(), promises = _b.promises, error = _b.error;
_d = (_c = dhke).constructProofs;
_e = [promises,
blindedMessages.rs,
blindedMessages.secrets];
return [4 /*yield*/, this.getKeys(promises, tokenEntry.mint)];
case 3:
newProofs = _d.apply(_c, _e.concat([_g.sent()]));
proofs.push.apply(proofs, newProofs);
if (!(tokenEntry.mint === this.mint.mintUrl)) return [3 /*break*/, 5];
return [4 /*yield*/, this.changedKeys(__spreadArray([], (promises || []), true))];
case 4:
_f = _g.sent();
return [3 /*break*/, 6];
case 5:
_f = undefined;
_g.label = 6;
case 6:
newKeys = _f;
return [3 /*break*/, 8];
case 7:
error_2 = _g.sent();
console.error(error_2);
proofsWithError.push.apply(proofsWithError, tokenEntry.proofs);
return [3 /*break*/, 8];
case 8: return [2 /*return*/, {
proofs: proofs,
proofsWithError: proofsWithError.length ? proofsWithError : undefined,
newKeys: newKeys
}];
}
});
});
};
/**
* Splits and creates sendable tokens
* if no amount is specified, the amount is implied by the cumulative amount of all proofs
* if both amount and preference are set, but the preference cannot fulfill the amount, then we use the default split
* @param amount amount to send while performing the optimal split (least proofs possible). can be set to undefined if preference is set
* @param proofs proofs matching that amount
* @param preference optional preference for splitting proofs into specific amounts. overrides amount param
* @returns promise of the change- and send-proofs
*/
CashuWallet.prototype.send = function (amount, proofs, preference) {
return __awaiter(this, void 0, void 0, function () {
var amountAvailable, proofsToSend, proofsToKeep, _a, amountKeep_1, amountSend, _b, payload, blindedMessages, promises, proofs_1, _c, _d, _e, splitProofsToKeep_1, splitProofsToSend_1, amountKeepCounter_1;
var _f;
return __generator(this, function (_g) {
switch (_g.label) {
case 0:
if (preference) {
amount = preference === null || preference === void 0 ? void 0 : preference.reduce(function (acc, curr) { return acc + curr.amount * curr.count; }, 0);
}
amountAvailable = 0;
proofsToSend = [];
proofsToKeep = [];
proofs.forEach(function (proof) {
if (amountAvailable >= amount) {
proofsToKeep.push(proof);
return;
}
amountAvailable = amountAvailable + proof.amount;
proofsToSend.push(proof);
});
if (amount > amountAvailable) {
throw new Error('Not enough funds available');
}
if (!(amount < amountAvailable || preference)) return [3 /*break*/, 4];
_a = this.splitReceive(amount, amountAvailable), amountKeep_1 = _a.amountKeep, amountSend = _a.amountSend;
_b = this.createSplitPayload(amountSend, proofsToSend, preference), payload = _b.payload, blindedMessages = _b.blindedMessages;
return [4 /*yield*/, this.mint.split(payload)];
case 1:
promises = (_g.sent()).promises;
_d = (_c = dhke).constructProofs;
_e = [promises,
blindedMessages.rs,
blindedMessages.secrets];
return [4 /*yield*/, this.getKeys(promises)];
case 2:
proofs_1 = _d.apply(_c, _e.concat([_g.sent()]));
splitProofsToKeep_1 = [];
splitProofsToSend_1 = [];
amountKeepCounter_1 = 0;
proofs_1.forEach(function (proof) {
if (amountKeepCounter_1 < amountKeep_1) {
amountKeepCounter_1 += proof.amount;
splitProofsToKeep_1.push(proof);
return;
}
splitProofsToSend_1.push(proof);
});
_f = {
returnChange: __spreadArray(__spreadArray([], splitProofsToKeep_1, true), proofsToKeep, true),
send: splitProofsToSend_1
};
return [4 /*yield*/, this.changedKeys(__spreadArray([], (promises || []), true))];
case 3: return [2 /*return*/, (_f.newKeys = _g.sent(),
_f)];
case 4: return [2 /*return*/, { returnChange: proofsToKeep, send: proofsToSend }];
}
});
});
};
/**
* Request tokens from the mint
* @param amount amount to request
* @param hash hash to use to identify the request
* @returns proofs and newKeys if they have changed
*/
CashuWallet.prototype.requestTokens = function (amount, hash, AmountPreference) {
return __awaiter(this, void 0, void 0, function () {
var _a, blindedMessages, secrets, rs, payloads, promises, _b, _c, _d;
var _e;
return __generator(this, function (_f) {
switch (_f.label) {
case 0:
_a = this.createRandomBlindedMessages(amount, AmountPreference), blindedMessages = _a.blindedMessages, secrets = _a.secrets, rs = _a.rs;
payloads = { outputs: blindedMessages };
return [4 /*yield*/, this.mint.mint(payloads, hash)];
case 1:
promises = (_f.sent()).promises;
_e = {};
_c = (_b = dhke).constructProofs;
_d = [promises, rs, secrets];
return [4 /*yield*/, this.getKeys(promises)];
case 2:
_e.proofs = _c.apply(_b, _d.concat([_f.sent()]));
return [4 /*yield*/, this.changedKeys(promises)];
case 3: return [2 /*return*/, (_e.newKeys = _f.sent(),
_e)];
}
});
});
};
/**
* Initialize the wallet with the mints public keys
*/
CashuWallet.prototype.initKeys = function () {
return __awaiter(this, void 0, void 0, function () {
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (!(!this.keysetId || !Object.keys(this.keys).length)) return [3 /*break*/, 2];
_a = this;
return [4 /*yield*/, this.mint.getKeys()];
case 1:
_a.keys = _b.sent();
this._keysetId = deriveKeysetId(this.keys);
_b.label = 2;
case 2: return [2 /*return*/];
}
});
});
};
/**
* Check if the keysetId has changed and return the new keys
* @param promises array of promises to check
* @returns new keys if they have changed
*/
CashuWallet.prototype.changedKeys = function (promises) {
if (promises === void 0) { promises = []; }
return __awaiter(this, void 0, void 0, function () {
var maybeNewKeys, keysetId;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.initKeys()];
case 1:
_a.sent();
if (!(promises === null || promises === void 0 ? void 0 : promises.length)) {
return [2 /*return*/, undefined];
}
if (!promises.some(function (x) { return x.id !== _this.keysetId; })) {
return [2 /*return*/, undefined];
}
return [4 /*yield*/, this.mint.getKeys()];
case 2:
maybeNewKeys = _a.sent();
keysetId = deriveKeysetId(maybeNewKeys);
return [2 /*return*/, keysetId === this.keysetId ? undefined : maybeNewKeys];
}
});
});
};
/**
* Get the mint's public keys for a given set of proofs
* @param arr array of proofs
* @param mint optional mint url
* @returns keys
*/
CashuWallet.prototype.getKeys = function (arr, mint) {
var _a;
return __awaiter(this, void 0, void 0, function () {
var keysetId, keys, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0: return [4 /*yield*/, this.initKeys()];
case 1:
_c.sent();
if (!(arr === null || arr === void 0 ? void 0 : arr.length) || !((_a = arr[0]) === null || _a === void 0 ? void 0 : _a.id)) {
return [2 /*return*/, this.keys];
}
keysetId = arr[0].id;
if (this.keysetId === keysetId) {
return [2 /*return*/, this.keys];
}
if (!(!mint || mint === this.mint.mintUrl)) return [3 /*break*/, 3];
return [4 /*yield*/, this.mint.getKeys(arr[0].id)];
case 2:
_b = _c.sent();
return [3 /*break*/, 5];
case 3: return [4 /*yield*/, CashuMint.getKeys(mint, arr[0].id)];
case 4:
_b = _c.sent();
_c.label = 5;
case 5:
keys = _b;
return [2 /*return*/, keys];
}
});
});
};
/**
* Creates a split payload
* @param amount1 amount to keep
* @param amount2 amount to send
* @param proofsToSend proofs to split
* @returns
*/
CashuWallet.prototype.createSplitPayload = function (amount, proofsToSend, preference) {
var totalAmount = proofsToSend.reduce(function (total, curr) { return total + curr.amount; }, 0);
var keepBlindedMessages = this.createRandomBlindedMessages(totalAmount - amount);
var sendBlindedMessages = this.createRandomBlindedMessages(amount, preference);
// join keepBlindedMessages and sendBlindedMessages
var blindedMessages = {
blindedMessages: __spreadArray(__spreadArray([], keepBlindedMessages.blindedMessages, true), sendBlindedMessages.blindedMessages, true),
secrets: __spreadArray(__spreadArray([], keepBlindedMessages.secrets, true), sendBlindedMessages.secrets, true),
rs: __spreadArray(__spreadArray([], keepBlindedMessages.rs, true), sendBlindedMessages.rs, true),
amounts: __spreadArray(__spreadArray([], keepBlindedMessages.amounts, true), sendBlindedMessages.amounts, true)
};
var payload = {
proofs: proofsToSend,
outputs: __spreadArray([], blindedMessages.blindedMessages, true)
};
return { payload: payload, blindedMessages: blindedMessages };
};
CashuWallet.prototype.splitReceive = function (amount, amountAvailable) {
var amountKeep = amountAvailable - amount;
var amountSend = amount;
return { amountKeep: amountKeep, amountSend: amountSend };
};
/**
* Creates blinded messages for a given amount
* @param amount amount to create blinded messages for
* @returns blinded messages, secrets, rs, and amounts
*/
CashuWallet.prototype.createRandomBlindedMessages = function (amount, amountPreference) {
var blindedMessages = [];
var secrets = [];
var rs = [];
var amounts = splitAmount(amount, amountPreference);
for (var i = 0; i < amounts.length; i++) {
var secret = randomBytes(32);
secrets.push(secret);
var _a = dhke.blindMessage(secret), B_ = _a.B_, r = _a.r;
rs.push(r);
var blindedMessage = new BlindedMessage(amounts[i], B_);
blindedMessages.push(blindedMessage.getSerializedBlindedMessage());
}
return { blindedMessages: blindedMessages, secrets: secrets, rs: rs, amounts: amounts };
};
/**
* Creates NUT-08 blank outputs (fee returns) for a given fee reserve
* See: https://github.com/cashubtc/nuts/blob/main/08.md
* @param feeReserve amount to cover with blank outputs
* @returns blinded messages, secrets, and rs
*/
CashuWallet.prototype.createBlankOutputs = function (feeReserve) {
var blindedMessages = [];
var secrets = [];
var rs = [];
var count = Math.ceil(Math.log2(feeReserve)) || 1;
for (var i = 0; i < count; i++) {
var secret = randomBytes(32);
secrets.push(secret);
var _a = dhke.blindMessage(secret), B_ = _a.B_, r = _a.r;
rs.push(r);
var blindedMessage = new BlindedMessage(0, B_);
blindedMessages.push(blindedMessage.getSerializedBlindedMessage());
}
return { blindedMessages: blindedMessages, secrets: secrets, rs: rs };
};
return CashuWallet;
}());
export { CashuWallet };
//# sourceMappingURL=CashuWallet.js.map
File diff suppressed because one or more lines are too long
-11
View File
@@ -1,11 +0,0 @@
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
import { MintKeys, Proof, SerializedBlindedSignature } from './model/types/index.js';
declare function hashToCurve(secret: Uint8Array): ProjPointType<bigint>;
export declare function pointFromHex(hex: string): ProjPointType<bigint>;
declare function blindMessage(secret: Uint8Array, r?: bigint): {
B_: ProjPointType<bigint>;
r: bigint;
};
declare function unblindSignature(C_: ProjPointType<bigint>, r: bigint, A: ProjPointType<bigint>): ProjPointType<bigint>;
declare function constructProofs(promises: Array<SerializedBlindedSignature>, rs: Array<bigint>, secrets: Array<Uint8Array>, keys: MintKeys): Array<Proof>;
export { hashToCurve, blindMessage, unblindSignature, constructProofs };
-57
View File
@@ -1,57 +0,0 @@
import { secp256k1 } from '@noble/curves/secp256k1';
import { encodeUint8toBase64 } from './base64.js';
import { bytesToNumber } from './utils.js';
import { sha256 } from '@noble/hashes/sha256';
import { bytesToHex } from '@noble/curves/abstract/utils';
function hashToCurve(secret) {
var point;
while (!point) {
var hash = sha256(secret);
var hashHex = bytesToHex(hash);
var pointX = '02' + hashHex;
try {
point = pointFromHex(pointX);
}
catch (error) {
secret = sha256(secret);
}
}
return point;
}
export function pointFromHex(hex) {
return secp256k1.ProjectivePoint.fromHex(hex);
}
/* export function h2cToPoint(h2c: H2CPoint<bigint>): ProjPointType<bigint> {
return secp256k1.ProjectivePoint.fromAffine(h2c.toAffine());
} */
function blindMessage(secret, r) {
var secretMessageBase64 = encodeUint8toBase64(secret);
var secretMessage = new TextEncoder().encode(secretMessageBase64);
var Y = hashToCurve(secretMessage);
if (!r) {
r = bytesToNumber(secp256k1.utils.randomPrivateKey());
}
var rG = secp256k1.ProjectivePoint.BASE.multiply(r);
var B_ = Y.add(rG);
return { B_: B_, r: r };
}
function unblindSignature(C_, r, A) {
var C = C_.subtract(A.multiply(r));
return C;
}
function constructProofs(promises, rs, secrets, keys) {
return promises.map(function (p, i) {
var C_ = pointFromHex(p.C_);
var A = pointFromHex(keys[p.amount]);
var C = unblindSignature(C_, rs[i], A);
var proof = {
id: p.id,
amount: p.amount,
secret: encodeUint8toBase64(secrets[i]),
C: C.toHex(true)
};
return proof;
});
}
export { hashToCurve, blindMessage, unblindSignature, constructProofs };
//# sourceMappingURL=DHKE.js.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"DHKE.js","sourceRoot":"","sources":["../../../src/DHKE.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAE1D,SAAS,WAAW,CAAC,MAAkB;IACtC,IAAI,KAAwC,CAAC;IAC7C,OAAO,CAAC,KAAK,EAAE;QACd,IAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,IAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QACjC,IAAM,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;QAC9B,IAAI;YACH,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;SAC7B;QAAC,OAAO,KAAK,EAAE;YACf,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;SACxB;KACD;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AACD,MAAM,UAAU,YAAY,CAAC,GAAW;IACvC,OAAO,SAAS,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AAC/C,CAAC;AACD;;IAEI;AACJ,SAAS,YAAY,CAAC,MAAkB,EAAE,CAAU;IACnD,IAAM,mBAAmB,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACxD,IAAM,aAAa,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACpE,IAAM,CAAC,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;IACrC,IAAI,CAAC,CAAC,EAAE;QACP,CAAC,GAAG,aAAa,CAAC,SAAS,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC;KACtD;IACD,IAAM,EAAE,GAAG,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtD,IAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrB,OAAO,EAAE,EAAE,IAAA,EAAE,CAAC,GAAA,EAAE,CAAC;AAClB,CAAC;AAED,SAAS,gBAAgB,CACxB,EAAyB,EACzB,CAAS,EACT,CAAwB;IAExB,IAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,OAAO,CAAC,CAAC;AACV,CAAC;AAED,SAAS,eAAe,CACvB,QAA2C,EAC3C,EAAiB,EACjB,OAA0B,EAC1B,IAAc;IAEd,OAAO,QAAQ,CAAC,GAAG,CAAC,UAAC,CAA6B,EAAE,CAAS;QAC5D,IAAM,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAM,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QACvC,IAAM,CAAC,GAAG,gBAAgB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzC,IAAM,KAAK,GAAG;YACb,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,MAAM,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACvC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;SAChB,CAAC;QACF,OAAO,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC"}
-5
View File
@@ -1,5 +0,0 @@
declare function encodeUint8toBase64(uint8array: Uint8Array): string;
declare function encodeBase64toUint8(base64String: string): Uint8Array;
declare function encodeJsonToBase64(jsonObj: unknown): string;
declare function encodeBase64ToJson<T extends object>(base64String: string): T;
export { encodeUint8toBase64, encodeBase64toUint8, encodeJsonToBase64, encodeBase64ToJson };
-24
View File
@@ -1,24 +0,0 @@
import { Buffer } from 'buffer/';
function encodeUint8toBase64(uint8array) {
return Buffer.from(uint8array).toString('base64');
}
function encodeBase64toUint8(base64String) {
return Buffer.from(base64String, 'base64');
}
function encodeJsonToBase64(jsonObj) {
var jsonString = JSON.stringify(jsonObj);
return base64urlFromBase64(Buffer.from(jsonString).toString('base64'));
}
function encodeBase64ToJson(base64String) {
var jsonString = Buffer.from(base64urlToBase64(base64String), 'base64').toString();
var jsonObj = JSON.parse(jsonString);
return jsonObj;
}
function base64urlToBase64(str) {
return str.replace(/-/g, '+').replace(/_/g, '/');
}
function base64urlFromBase64(str) {
return str.replace(/\+/g, '-').replace(/\//g, '_').split('=')[0];
}
export { encodeUint8toBase64, encodeBase64toUint8, encodeJsonToBase64, encodeBase64ToJson };
//# sourceMappingURL=base64.js.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"base64.js","sourceRoot":"","sources":["../../../src/base64.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,SAAS,mBAAmB,CAAC,UAAsB;IAClD,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,mBAAmB,CAAC,YAAoB;IAChD,OAAO,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAgB;IAC3C,IAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC3C,OAAO,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,SAAS,kBAAkB,CAAmB,YAAoB;IACjE,IAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;IACrF,IAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAM,CAAC;IAC5C,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAW;IACrC,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAW;IACvC,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAC"}
-7
View File
@@ -1,7 +0,0 @@
import { CashuMint } from './CashuMint.js';
import { CashuWallet } from './CashuWallet.js';
import { setGlobalRequestOptions } from './request.js';
import { getEncodedToken, getDecodedToken, deriveKeysetId } from './utils.js';
import { decode as getDecodedLnInvoice } from '@gandlaf21/bolt11-decode';
export * from './model/types/index.js';
export { CashuMint, CashuWallet, getDecodedToken, getEncodedToken, deriveKeysetId, getDecodedLnInvoice, setGlobalRequestOptions };
-8
View File
@@ -1,8 +0,0 @@
import { CashuMint } from './CashuMint.js';
import { CashuWallet } from './CashuWallet.js';
import { setGlobalRequestOptions } from './request.js';
import { getEncodedToken, getDecodedToken, deriveKeysetId } from './utils.js';
import { decode as getDecodedLnInvoice } from '@gandlaf21/bolt11-decode';
export * from './model/types/index.js';
export { CashuMint, CashuWallet, getDecodedToken, getEncodedToken, deriveKeysetId, getDecodedLnInvoice, setGlobalRequestOptions };
//# sourceMappingURL=index.js.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,EAAE,MAAM,IAAI,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEzE,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EACN,SAAS,EACT,WAAW,EACX,eAAe,EACf,eAAe,EACf,cAAc,EACd,mBAAmB,EACnB,uBAAuB,EACvB,CAAC"}
@@ -1,9 +0,0 @@
import { SerializedBlindedMessage } from './types/index.js';
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
declare class BlindedMessage {
amount: number;
B_: ProjPointType<bigint>;
constructor(amount: number, B_: ProjPointType<bigint>);
getSerializedBlindedMessage(): SerializedBlindedMessage;
}
export { BlindedMessage };
-12
View File
@@ -1,12 +0,0 @@
var BlindedMessage = /** @class */ (function () {
function BlindedMessage(amount, B_) {
this.amount = amount;
this.B_ = B_;
}
BlindedMessage.prototype.getSerializedBlindedMessage = function () {
return { amount: this.amount, B_: this.B_.toHex(true) };
};
return BlindedMessage;
}());
export { BlindedMessage };
//# sourceMappingURL=BlindedMessage.js.map
@@ -1 +0,0 @@
{"version":3,"file":"BlindedMessage.js","sourceRoot":"","sources":["../../../../src/model/BlindedMessage.ts"],"names":[],"mappings":"AAGA;IAGC,wBAAY,MAAc,EAAE,EAAyB;QACpD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACd,CAAC;IACD,oDAA2B,GAA3B;QACC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;IACzD,CAAC;IACF,qBAAC;AAAD,CAAC,AAVD,IAUC;AACD,OAAO,EAAE,cAAc,EAAE,CAAC"}
@@ -1,10 +0,0 @@
import { ProjPointType } from '@noble/curves/abstract/weierstrass';
import { SerializedBlindedSignature } from './types/index.js';
declare class BlindedSignature {
id: string;
amount: number;
C_: ProjPointType<bigint>;
constructor(id: string, amount: number, C_: ProjPointType<bigint>);
getSerializedBlindedSignature(): SerializedBlindedSignature;
}
export { BlindedSignature };
-13
View File
@@ -1,13 +0,0 @@
var BlindedSignature = /** @class */ (function () {
function BlindedSignature(id, amount, C_) {
this.id = id;
this.amount = amount;
this.C_ = C_;
}
BlindedSignature.prototype.getSerializedBlindedSignature = function () {
return { id: this.id, amount: this.amount, C_: this.C_.toHex(true) };
};
return BlindedSignature;
}());
export { BlindedSignature };
//# sourceMappingURL=BlindedSignature.js.map
@@ -1 +0,0 @@
{"version":3,"file":"BlindedSignature.js","sourceRoot":"","sources":["../../../../src/model/BlindedSignature.ts"],"names":[],"mappings":"AAGA;IAKC,0BAAY,EAAU,EAAE,MAAc,EAAE,EAAyB;QAChE,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACd,CAAC;IAED,wDAA6B,GAA7B;QACC,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;IACtE,CAAC;IACF,uBAAC;AAAD,CAAC,AAdD,IAcC;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
-17
View File
@@ -1,17 +0,0 @@
import { BlindedMessage } from './BlindedMessage.js';
import { Proof } from './types/index.js';
declare class Split {
proofs: Array<Proof>;
amount: number;
outputs: Array<BlindedMessage>;
constructor(proofs: Array<Proof>, amount: number, outputs: Array<BlindedMessage>);
getSerializedSplit(): {
proofs: Proof[];
amount: number;
outputs: {
amount: number;
B_: string;
}[];
};
}
export { Split };
-19
View File
@@ -1,19 +0,0 @@
var Split = /** @class */ (function () {
function Split(proofs, amount, outputs) {
this.proofs = proofs;
this.amount = amount;
this.outputs = outputs;
}
Split.prototype.getSerializedSplit = function () {
return {
proofs: this.proofs,
amount: this.amount,
outputs: this.outputs.map(function (blindedMessage) {
return { amount: blindedMessage.amount, B_: blindedMessage.B_.toHex(true) };
})
};
};
return Split;
}());
export { Split };
//# sourceMappingURL=Split.js.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"Split.js","sourceRoot":"","sources":["../../../../src/model/Split.ts"],"names":[],"mappings":"AAGA;IAIC,eAAY,MAAoB,EAAE,MAAc,EAAE,OAA8B;QAC/E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC;IACD,kCAAkB,GAAlB;QACC,OAAO;YACN,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAC,cAA8B;gBACxD,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,EAAE,EAAE,EAAE,cAAc,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7E,CAAC,CAAC;SACF,CAAC;IACH,CAAC;IACF,YAAC;AAAD,CAAC,AAlBD,IAkBC;AAED,OAAO,EAAE,KAAK,EAAE,CAAC"}
-335
View File
@@ -1,335 +0,0 @@
/**
* represents a single Cashu proof.
*/
export type Proof = {
/**
* Keyset id, used to link proofs to a mint an its MintKeys.
*/
id: string;
/**
* Amount denominated in Satoshis. Has to match the amount of the mints signing key.
*/
amount: number;
/**
* The initial secret that was (randomly) chosen for the creation of this proof.
*/
secret: string;
/**
* The unblinded signature for this secret, signed by the mints private key.
*/
C: string;
};
/**
* A mints publickey-set.
*/
export type MintKeys = {
[k: number]: string;
};
/**
* response when after receiving a single TokenEntry
*/
export type ReceiveTokenEntryResponse = {
/**
* Received proofs
*/
proofs: Array<Proof>;
/**
* Proofs that could not be received. Doesn't throw an error, but if this field is populated it should be handled by the implementation accordingly
*/
proofsWithError: Array<Proof> | undefined;
/**
* If the mint has rotated keys, this field will be populated with the new keys.
*/
newKeys?: MintKeys;
};
/**
* response after sending
*/
export type SendResponse = {
/**
* Proofs that exceeded the needed amount
*/
returnChange: Array<Proof>;
/**
* Proofs to be sent, matching the chosen amount
*/
send: Array<Proof>;
/**
* If the mint has rotated keys, this field will be populated with the new keys.
*/
newKeys?: MintKeys;
};
/**
* Response when receiving a complete token.
*/
export type ReceiveResponse = {
/**
* Successfully received Cashu Token
*/
token: Token;
/**
* TokenEntries that had errors. No error will be thrown, but clients can choose to handle tokens with errors accordingly.
*/
tokensWithErrors: Token | undefined;
/**
* If the mint has rotated keys, this field will be populated with the new keys.
*/
newKeys?: MintKeys;
};
/**
* Payload that needs to be sent to the mint when paying a lightning invoice.
*/
export type PaymentPayload = {
/**
* Payment request/Lighting invoice that should get paid by the mint.
*/
pr: string;
/**
* Proofs, matching Lightning invoices amount + fees.
*/
proofs: Array<Proof>;
};
/**
* Payload that needs to be sent to the mint when melting. Includes Return for overpaid fees
*/
export type MeltPayload = {
/**
* Payment request/Lighting invoice that should get paid by the mint.
*/
pr: string;
/**
* Proofs, matching Lightning invoices amount + fees.
*/
proofs: Array<Proof>;
/**
* Blank outputs (blinded messages) that can be filled by the mint to return overpaid fees
*/
outputs: Array<SerializedBlindedMessage>;
};
/**
* Response from the mint after paying a lightning invoice (melt)
*/
export type MeltResponse = {
/**
* if false, the proofs have not been invalidated and the payment can be tried later again with the same proofs
*/
paid: boolean;
/**
* preimage of the paid invoice. can be null, depending on which LN-backend the mint uses
*/
preimage: string | null;
/**
* Return/Change from overpaid fees. This happens due to Lighting fee estimation being inaccurate
*/
change?: Array<SerializedBlindedSignature>;
} & ApiError;
/**
* Response after paying a Lightning invoice
*/
export type PayLnInvoiceResponse = {
/**
* if false, the proofs have not been invalidated and the payment can be tried later again with the same proofs
*/
isPaid: boolean;
/**
* preimage of the paid invoice. can be null, depending on which LN-backend the mint uses
*/
preimage: string | null;
/**
* Return/Change from overpaid fees. This happens due to Lighting fee estimation being inaccurate
*/
change: Array<Proof>;
/**
* If the mint has rotated keys, this field will be populated with the new keys.
*/
newKeys?: MintKeys;
};
/**
* Payload that needs to be sent to the mint when performing a split action
*/
export type SplitPayload = {
/**
* Proofs to be split
*/
proofs: Array<Proof>;
/**
* Fresh blinded messages to be signed by the mint to create the split proofs
*/
outputs: Array<SerializedBlindedMessage>;
};
/**
* Response from the mint after performing a split action
*/
export type SplitResponse = {
/**
* represents the outputs after the split
*/
promises: Array<SerializedBlindedSignature>;
} & ApiError;
/**
* Cashu api error
*/
export type ApiError = {
/**
* Error message
*/
error?: string;
/**
* HTTP error code
*/
code?: number;
/**
* Detailed error message
*/
detail?: string;
};
export type RequestMintResponse = {
pr: string;
hash: string;
} & ApiError;
/**
* Payload that needs to be sent to the mint when checking for spendable proofs
*/
export type CheckSpendablePayload = {
/**
* array of proofs. Only the secret is strictly needed.
* If the whole object is passed, it will be stripped of other objects before sending it to the mint.
*/
proofs: Array<{
secret: string;
}>;
};
/**
* Response when checking proofs if they are spendable. Should not rely on this for receiving, since it can be easily cheated.
*/
export type CheckSpendableResponse = {
/**
* Ordered list for checked proofs. True if the secret has not been redeemed at the mint before
*/
spendable: Array<boolean>;
} & ApiError;
/**
* blinded message for sending to the mint
*/
export type SerializedBlindedMessage = {
/**
* amount
*/
amount: number;
/**
* Blinded message
*/
B_: string;
};
/**
* Blinded signature as it is received from the mint
*/
export type SerializedBlindedSignature = {
/**
* keyset id for indicating which public key was used to sign the blinded message
*/
id: string;
/**
* Amount denominated in Satoshi
*/
amount: number;
/**
* Blinded signature
*/
C_: string;
};
/**
* A Cashu token
*/
export type Token = {
/**
* token entries
*/
token: Array<TokenEntry>;
/**
* a message to send along with the token
*/
memo?: string;
};
/**
* TokenEntry that stores proofs and mints
*/
export type TokenEntry = {
/**
* a list of proofs
*/
proofs: Array<Proof>;
/**
* the mints URL
*/
mint: string;
};
/**
* @deprecated Token V2
* should no longer be used
*/
export type TokenV2 = {
proofs: Array<Proof>;
mints: Array<{
url: string;
ids: Array<string>;
}>;
};
/**
* Data that the library needs to hold in memory while it awaits the blinded signatures for the mint. It is later used for unblinding the signatures.
*/
export type BlindedTransaction = {
/**
* Blinded messages sent to the mint for signing.
*/
blindedMessages: Array<SerializedBlindedMessage>;
/**
* secrets, kept client side for constructing proofs later.
*/
secrets: Array<Uint8Array>;
/**
* Blinding factor used for blinding messages and unblinding signatures after they are received from the mint.
*/
rs: Array<bigint>;
/**
* amounts denominated in Satoshi
*/
amounts: Array<number>;
};
/**
* Data that the library needs to hold in memory while it awaits the blinded signatures for the mint. It is later used for unblinding the signatures.
*/
export type BlindedMessageData = {
/**
* Blinded messages sent to the mint for signing.
*/
blindedMessages: Array<SerializedBlindedMessage>;
/**
* secrets, kept client side for constructing proofs later.
*/
secrets: Array<Uint8Array>;
/**
* Blinding factor used for blinding messages and unblinding signatures after they are received from the mint.
*/
rs: Array<bigint>;
};
/**
* Response from mint at /info endpoint
*/
export type GetInfoResponse = {
name: string;
pubkey: string;
version: string;
description?: string;
description_long?: string;
contact: Array<Array<string>>;
nuts: Array<string>;
motd?: string;
parameter: {
peg_out_only: boolean;
};
};
export type AmountPreference = {
amount: number;
count: number;
};
-2
View File
@@ -1,2 +0,0 @@
export {};
//# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/model/types/index.ts"],"names":[],"mappings":""}
-13
View File
@@ -1,13 +0,0 @@
type RequestArgs = {
endpoint: string;
requestBody?: Record<string, unknown>;
headers?: Record<string, string>;
};
type RequestOptions = RequestArgs & Omit<RequestInit, 'body' | 'headers'>;
/**
* An object containing any custom settings that you want to apply to the global fetch method.
* @param options See possible options here: https://developer.mozilla.org/en-US/docs/Web/API/fetch#options
*/
export declare function setGlobalRequestOptions(options: Partial<RequestOptions>): void;
export default function request<T>(options: RequestOptions): Promise<T>;
export {};
-108
View File
@@ -1,108 +0,0 @@
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { checkResponse } from './utils';
var globalRequestOptions = {};
/**
* An object containing any custom settings that you want to apply to the global fetch method.
* @param options See possible options here: https://developer.mozilla.org/en-US/docs/Web/API/fetch#options
*/
export function setGlobalRequestOptions(options) {
globalRequestOptions = options;
}
function _request(_a) {
var endpoint = _a.endpoint, requestBody = _a.requestBody, requestHeaders = _a.headers, options = __rest(_a, ["endpoint", "requestBody", "headers"]);
return __awaiter(this, void 0, void 0, function () {
var body, headers, response, _b, error, detail, message;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
body = requestBody ? JSON.stringify(requestBody) : undefined;
headers = __assign(__assign({ Accept: 'application/json, text/plain, */*' }, (body ? { 'Content-Type': 'application/json' } : undefined)), requestHeaders);
return [4 /*yield*/, fetch(endpoint, __assign({ body: body, headers: headers }, options))];
case 1:
response = _c.sent();
if (!!response.ok) return [3 /*break*/, 3];
return [4 /*yield*/, response.json()];
case 2:
_b = _c.sent(), error = _b.error, detail = _b.detail;
message = error || detail || 'bad response';
throw new Error(message);
case 3: return [2 /*return*/, response];
}
});
});
}
export default function request(options) {
return __awaiter(this, void 0, void 0, function () {
var response, data;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, _request(__assign(__assign({}, options), globalRequestOptions))];
case 1:
response = _a.sent();
return [4 /*yield*/, response.json().catch(function () { return ({ error: 'bad response' }); })];
case 2:
data = _a.sent();
checkResponse(data);
return [2 /*return*/, data];
}
});
});
}
//# sourceMappingURL=request.js.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../../src/request.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAUxC,IAAI,oBAAoB,GAA4B,EAAE,CAAC;AAEvD;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAgC;IACvE,oBAAoB,GAAG,OAAO,CAAC;AAChC,CAAC;AAED,SAAe,QAAQ,CAAC,EAKP;IAJhB,IAAA,QAAQ,cAAA,EACR,WAAW,iBAAA,EACF,cAAc,aAAA,EACpB,OAAO,cAJa,sCAKvB,CADU;;;;;;oBAEJ,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;oBAC7D,OAAO,qBACT,EAAE,MAAM,EAAE,mCAAmC,EAAE,EAC/C,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,GAC3D,cAAc,CACjB,CAAC;oBAEe,qBAAM,KAAK,CAAC,QAAQ,aAAI,IAAI,MAAA,EAAE,OAAO,SAAA,IAAK,OAAO,EAAG,EAAA;;oBAA/D,QAAQ,GAAG,SAAoD;yBAEjE,CAAC,QAAQ,CAAC,EAAE,EAAZ,wBAAY;oBACW,qBAAM,QAAQ,CAAC,IAAI,EAAE,EAAA;;oBAAzC,KAAoB,SAAqB,EAAvC,KAAK,WAAA,EAAE,MAAM,YAAA;oBACf,OAAO,GAAG,KAAK,IAAI,MAAM,IAAI,cAAc,CAAC;oBAClD,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;wBAG1B,sBAAO,QAAQ,EAAC;;;;CAChB;AAED,MAAM,CAAC,OAAO,UAAgB,OAAO,CAAI,OAAuB;;;;;wBAC9C,qBAAM,QAAQ,uBAAM,OAAO,GAAK,oBAAoB,EAAG,EAAA;;oBAAlE,QAAQ,GAAG,SAAuD;oBAC3D,qBAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,cAAM,OAAA,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,EAA3B,CAA2B,CAAC,EAAA;;oBAArE,IAAI,GAAG,SAA8D;oBAC3E,aAAa,CAAC,IAAI,CAAC,CAAC;oBACpB,sBAAO,IAAI,EAAC;;;;CACZ"}
-42
View File
@@ -1,42 +0,0 @@
import { AmountPreference, MintKeys, Proof, Token } from './model/types/index.js';
declare function splitAmount(value: number, amountPreference?: Array<AmountPreference>): Array<number>;
declare function getDefaultAmountPreference(amount: number): Array<AmountPreference>;
declare function bytesToNumber(bytes: Uint8Array): bigint;
declare function hexToNumber(hex: string): bigint;
declare function bigIntStringify<T>(_key: unknown, value: T): string | T;
/**
* Helper function to encode a v3 cashu token
* @param token
* @returns
*/
declare function getEncodedToken(token: Token): string;
/**
* Helper function to decode cashu tokens into object
* @param token an encoded cashu token (cashuAey...)
* @returns cashu token object
*/
declare function getDecodedToken(token: string): Token;
/**
* Returns the keyset id of a set of keys
* @param keys keys object to derive keyset id from
* @returns
*/
export declare function deriveKeysetId(keys: MintKeys): string;
/**
* merge proofs from same mint,
* removes TokenEntrys with no proofs or no mint field
* and sorts proofs by id
*
* @export
* @param {Token} token
* @return {*} {Token}
*/
export declare function cleanToken(token: Token): Token;
export declare function sortProofsById(proofs: Array<Proof>): Proof[];
export declare function isObj(v: unknown): v is object;
export declare function checkResponse(data: {
error?: string;
detail?: string;
}): void;
export declare function joinUrls(...parts: string[]): string;
export { bigIntStringify, bytesToNumber, getDecodedToken, getEncodedToken, hexToNumber, splitAmount, getDefaultAmountPreference };
-194
View File
@@ -1,194 +0,0 @@
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
import { encodeBase64ToJson, encodeJsonToBase64 } from './base64.js';
import { TOKEN_PREFIX, TOKEN_VERSION } from './utils/Constants.js';
import { bytesToHex } from '@noble/curves/abstract/utils';
import { sha256 } from '@noble/hashes/sha256';
import { Buffer } from 'buffer/';
function splitAmount(value, amountPreference) {
var chunks = [];
if (amountPreference) {
chunks.push.apply(chunks, getPreference(value, amountPreference));
value =
value -
chunks.reduce(function (curr, acc) {
return curr + acc;
}, 0);
}
for (var i = 0; i < 32; i++) {
var mask = 1 << i;
if ((value & mask) !== 0) {
chunks.push(Math.pow(2, i));
}
}
return chunks;
}
function isPowerOfTwo(number) {
return number && !(number & (number - 1));
}
function getPreference(amount, preferredAmounts) {
var chunks = [];
var accumulator = 0;
preferredAmounts.forEach(function (pa) {
if (!isPowerOfTwo(pa.amount)) {
throw new Error('Provided amount preferences contain non-power-of-2 numbers. Use only ^2 numbers');
}
for (var i = 1; i <= pa.count; i++) {
accumulator += pa.amount;
if (accumulator > amount) {
return;
}
chunks.push(pa.amount);
}
});
return chunks;
}
function getDefaultAmountPreference(amount) {
var amounts = splitAmount(amount);
return amounts.map(function (a) {
return { amount: a, count: 1 };
});
}
function bytesToNumber(bytes) {
return hexToNumber(bytesToHex(bytes));
}
function hexToNumber(hex) {
return BigInt("0x".concat(hex));
}
//used for json serialization
function bigIntStringify(_key, value) {
return typeof value === 'bigint' ? value.toString() : value;
}
/**
* Helper function to encode a v3 cashu token
* @param token
* @returns
*/
function getEncodedToken(token) {
return TOKEN_PREFIX + TOKEN_VERSION + encodeJsonToBase64(token);
}
/**
* Helper function to decode cashu tokens into object
* @param token an encoded cashu token (cashuAey...)
* @returns cashu token object
*/
function getDecodedToken(token) {
// remove prefixes
var uriPrefixes = ['web+cashu://', 'cashu://', 'cashu:', 'cashuA'];
uriPrefixes.forEach(function (prefix) {
if (!token.startsWith(prefix)) {
return;
}
token = token.slice(prefix.length);
});
return handleTokens(token);
}
/**
* @param token
* @returns
*/
function handleTokens(token) {
var _a, _b;
var obj = encodeBase64ToJson(token);
// check if v3
if ('token' in obj) {
return obj;
}
// check if v1
if (Array.isArray(obj)) {
return { token: [{ proofs: obj, mint: '' }] };
}
// if v2 token return v3 format
return { token: [{ proofs: obj.proofs, mint: (_b = (_a = obj === null || obj === void 0 ? void 0 : obj.mints[0]) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '' }] };
}
/**
* Returns the keyset id of a set of keys
* @param keys keys object to derive keyset id from
* @returns
*/
export function deriveKeysetId(keys) {
var pubkeysConcat = Object.entries(keys)
.sort(function (a, b) { return +a[0] - +b[0]; })
.map(function (_a) {
var pubKey = _a[1];
return pubKey;
})
.join('');
var hash = sha256(new TextEncoder().encode(pubkeysConcat));
return Buffer.from(hash).toString('base64').slice(0, 12);
}
/**
* merge proofs from same mint,
* removes TokenEntrys with no proofs or no mint field
* and sorts proofs by id
*
* @export
* @param {Token} token
* @return {*} {Token}
*/
export function cleanToken(token) {
var _a;
var _b;
var tokenEntryMap = {};
for (var _i = 0, _c = token.token; _i < _c.length; _i++) {
var tokenEntry = _c[_i];
if (!((_b = tokenEntry === null || tokenEntry === void 0 ? void 0 : tokenEntry.proofs) === null || _b === void 0 ? void 0 : _b.length) || !(tokenEntry === null || tokenEntry === void 0 ? void 0 : tokenEntry.mint)) {
continue;
}
if (tokenEntryMap[tokenEntry.mint]) {
(_a = tokenEntryMap[tokenEntry.mint].proofs).push.apply(_a, __spreadArray([], tokenEntry.proofs, true));
continue;
}
tokenEntryMap[tokenEntry.mint] = {
mint: tokenEntry.mint,
proofs: __spreadArray([], tokenEntry.proofs, true)
};
}
return {
memo: token === null || token === void 0 ? void 0 : token.memo,
token: Object.values(tokenEntryMap).map(function (x) { return (__assign(__assign({}, x), { proofs: sortProofsById(x.proofs) })); })
};
}
export function sortProofsById(proofs) {
return proofs.sort(function (a, b) { return a.id.localeCompare(b.id); });
}
export function isObj(v) {
return typeof v === 'object';
}
export function checkResponse(data) {
if (!isObj(data))
return;
if ('error' in data && data.error) {
throw new Error(data.error);
}
if ('detail' in data && data.detail) {
throw new Error(data.detail);
}
}
export function joinUrls() {
var parts = [];
for (var _i = 0; _i < arguments.length; _i++) {
parts[_i] = arguments[_i];
}
return parts.map(function (part) { return part.replace(/(^\/+|\/+$)/g, ''); }).join('/');
}
export { bigIntStringify, bytesToNumber, getDecodedToken, getEncodedToken, hexToNumber, splitAmount, getDefaultAmountPreference };
//# sourceMappingURL=utils.js.map
File diff suppressed because one or more lines are too long
-6
View File
@@ -1,6 +0,0 @@
/**
* per protocol definition since tokenV3 tokens have a prefix and a version number
*/
declare const TOKEN_VERSION = "A";
declare const TOKEN_PREFIX = "cashu";
export { TOKEN_VERSION, TOKEN_PREFIX };
-7
View File
@@ -1,7 +0,0 @@
/**
* per protocol definition since tokenV3 tokens have a prefix and a version number
*/
var TOKEN_VERSION = 'A';
var TOKEN_PREFIX = 'cashu';
export { TOKEN_VERSION, TOKEN_PREFIX };
//# sourceMappingURL=Constants.js.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"Constants.js","sourceRoot":"","sources":["../../../../src/utils/Constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,IAAM,aAAa,GAAG,GAAG,CAAC;AAC1B,IAAM,YAAY,GAAG,OAAO,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC"}
-8
View File
@@ -1,8 +0,0 @@
export default {
transform: { '^.+\\.ts?$': 'ts-jest' },
testEnvironment: 'node',
resolver: 'ts-jest-resolver',
testRegex: '/test/.*\\.(test|spec)?\\.(ts|tsx)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
setupFilesAfterEnv: ['./test/_replace-native-fetch.ts']
};
-114
View File
@@ -1,114 +0,0 @@
{
"name": "@cashu/cashu-ts",
"version": "0.8.2-rc.6",
"lockfileVersion": 2,
"requires": true,
"packages": {
"node_modules/@gandlaf21/bolt11-decode": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@gandlaf21/bolt11-decode/-/bolt11-decode-3.0.6.tgz",
"integrity": "sha512-KUcAK2b9or8J47hzNTM2A+xdU0jCGIL4oC4TDyUlRYMfS5dBVOh4ywg9r3TZD8C/eVx7r14Hp4F79CSDjyCWTQ==",
"dependencies": {
"bech32": "^1.1.2",
"bn.js": "^4.11.8",
"buffer": "^6.0.3"
}
},
"node_modules/@noble/curves": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz",
"integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"@noble/hashes": "1.3.0"
}
},
"node_modules/@noble/hashes": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz",
"integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
]
},
"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"
}
]
},
"node_modules/bech32": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
"integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
},
"node_modules/bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
},
"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"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"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"
}
]
}
}
}
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 bolt11-decoder contributors, fiatjaf, 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.
-203
View File
@@ -1,203 +0,0 @@
# bolt11-decode
A lightweight and naïve library for decoding lightning network payment requests as defined in [BOLT #11](https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md).
It doesn't recover payee from signature, doesn't check signature, doesn't parse fallback addresses and doesn't do any encoding -- therefore dependencies are very minimal (no libsecp256k1 here).
Code derived from [bolt11](https://npmjs.com/package/bolt11), which has the full functionality but it's a pain to run in browsers.
## Installation
```shell
npm i @gandlaf21/bolt11-decode
```
### Usage
```javascript
import { decode } from "@gandlaf21/bolt11-decode";
decode('{invoice}', 'optional: network')
```
Output example:
```json
{
"paymentRequest": "lnbc20u1p3y0x3hpp5743k2g0fsqqxj7n8qzuhns5gmkk4djeejk3wkp64ppevgekvc0jsdqcve5kzar2v9nr5gpqd4hkuetesp5ez2g297jduwc20t6lmqlsg3man0vf2jfd8ar9fh8fhn2g8yttfkqxqy9gcqcqzys9qrsgqrzjqtx3k77yrrav9hye7zar2rtqlfkytl094dsp0ms5majzth6gt7ca6uhdkxl983uywgqqqqlgqqqvx5qqjqrzjqd98kxkpyw0l9tyy8r8q57k7zpy9zjmh6sez752wj6gcumqnj3yxzhdsmg6qq56utgqqqqqqqqqqqeqqjq7jd56882gtxhrjm03c93aacyfy306m4fq0tskf83c0nmet8zc2lxyyg3saz8x6vwcp26xnrlagf9semau3qm2glysp7sv95693fphvsp54l567",
"sections": [
{
"name": "lightning_network",
"letters": "ln"
},
{
"name": "coin_network",
"letters": "bc",
"value": {
"bech32": "bc",
"pubKeyHash": 0,
"scriptHash": 5,
"validWitnessVersions": [
0
]
}
},
{
"name": "amount",
"letters": "20u",
"value": "2000000"
},
{
"name": "separator",
"letters": "1"
},
{
"name": "timestamp",
"letters": "p3y0x3h",
"value": 1648859703
},
{
"name": "payment_hash",
"tag": "p",
"letters": "pp5743k2g0fsqqxj7n8qzuhns5gmkk4djeejk3wkp64ppevgekvc0js",
"value": "f5636521e98000697a6700b979c288ddad56cb3995a2eb07550872c466ccc3e5"
},
{
"name": "description",
"tag": "d",
"letters": "dqcve5kzar2v9nr5gpqd4hkuete",
"value": "fiatjaf: money"
},
{
"name": "payment_secret",
"tag": "s",
"letters": "sp5ez2g297jduwc20t6lmqlsg3man0vf2jfd8ar9fh8fhn2g8yttfkq",
"value": "c8948517d26f1d853d7afec1f8223becdec4aa4969fa32a6e74de6a41c8b5a6c"
},
{
"name": "expiry",
"tag": "x",
"letters": "xqy9gcq",
"value": 172800
},
{
"name": "min_final_cltv_expiry",
"tag": "c",
"letters": "cqzys",
"value": 144
},
{
"name": "feature_bits",
"tag": "9",
"letters": "9qrsgq",
"value": {
"word_length": 3,
"option_data_loss_protect": {
"required": false,
"supported": false
},
"initial_routing_sync": {
"required": false,
"supported": false
},
"option_upfront_shutdown_script": {
"required": false,
"supported": false
},
"gossip_queries": {
"required": false,
"supported": false
},
"var_onion_optin": {
"required": true,
"supported": false
},
"gossip_queries_ex": {
"required": false,
"supported": false
},
"option_static_remotekey": {
"required": false,
"supported": false
},
"payment_secret": {
"required": true,
"supported": false
},
"basic_mpp": {
"required": false,
"supported": false
},
"option_support_large_channel": {
"required": false,
"supported": false
},
"extra_bits": {
"start_bit": 20,
"bits": [],
"has_required": false
}
}
},
{
"name": "route_hint",
"tag": "r",
"letters": "rzjqtx3k77yrrav9hye7zar2rtqlfkytl094dsp0ms5majzth6gt7ca6uhdkxl983uywgqqqqlgqqqvx5qqjq",
"value": [
{
"pubkey": "02cd1b7bc418fac2dc99f0ba350d60fa6c45fde5ab6017ee14df6425df485fb1dd",
"short_channel_id": "72edb1be53c78472",
"fee_base_msat": 1000,
"fee_proportional_millionths": 50000,
"cltv_expiry_delta": 144
}
]
},
{
"name": "route_hint",
"tag": "r",
"letters": "rzjqd98kxkpyw0l9tyy8r8q57k7zpy9zjmh6sez752wj6gcumqnj3yxzhdsmg6qq56utgqqqqqqqqqqqeqqjq",
"value": [
{
"pubkey": "034a7b1ac1239ff2ac8438ce0a7ade1048514b77d4322f514e96918e6c13944861",
"short_channel_id": "5db0da3400535c5a",
"fee_base_msat": 0,
"fee_proportional_millionths": 100,
"cltv_expiry_delta": 144
}
]
},
{
"name": "signature",
"letters": "7jd56882gtxhrjm03c93aacyfy306m4fq0tskf83c0nmet8zc2lxyyg3saz8x6vwcp26xnrlagf9semau3qm2glysp7sv95693fphvsp",
"value": "f49b4d1cea42cd71cb6f8e0b1ef7044922fd6ea903d70b24f1c3e7bcace2c2be621111874473698ec055a34c7fea1258677de441b523e4807d06169a2c521bb201"
},
{
"name": "checksum",
"letters": "54l567"
}
],
"expiry": 172800,
"route_hints": [
[
{
"pubkey": "02cd1b7bc418fac2dc99f0ba350d60fa6c45fde5ab6017ee14df6425df485fb1dd",
"short_channel_id": "72edb1be53c78472",
"fee_base_msat": 1000,
"fee_proportional_millionths": 50000,
"cltv_expiry_delta": 144
}
],
[
{
"pubkey": "034a7b1ac1239ff2ac8438ce0a7ade1048514b77d4322f514e96918e6c13944861",
"short_channel_id": "5db0da3400535c5a",
"fee_base_msat": 0,
"fee_proportional_millionths": 100,
"cltv_expiry_delta": 144
}
]
]
}
```
## LICENSE [MIT](LICENSE)
@@ -1,8 +0,0 @@
declare function hrpToMillisat(hrpString: any, outputString: any): any;
declare function decode(paymentRequest: string, network?: any): {
paymentRequest: string;
sections: any[];
readonly expiry: any;
readonly route_hints: any[];
};
export { decode, hrpToMillisat };
@@ -1,365 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hrpToMillisat = exports.decode = void 0;
var bech32 = require("bech32");
var buffer_1 = require("buffer");
var bn_js_1 = require("bn.js");
// defaults for encode; default timestamp is current time at call
var DEFAULTNETWORK = {
// default network is bitcoin
bech32: 'bc',
pubKeyHash: 0x00,
scriptHash: 0x05,
validWitnessVersions: [0]
};
var TESTNETWORK = {
bech32: 'tb',
pubKeyHash: 0x6f,
scriptHash: 0xc4,
validWitnessVersions: [0]
};
var REGTESTNETWORK = {
bech32: 'bcrt',
pubKeyHash: 0x6f,
scriptHash: 0xc4,
validWitnessVersions: [0]
};
var SIMNETWORK = {
bech32: 'sb',
pubKeyHash: 0x3f,
scriptHash: 0x7b,
validWitnessVersions: [0]
};
var FEATUREBIT_ORDER = [
'option_data_loss_protect',
'initial_routing_sync',
'option_upfront_shutdown_script',
'gossip_queries',
'var_onion_optin',
'gossip_queries_ex',
'option_static_remotekey',
'payment_secret',
'basic_mpp',
'option_support_large_channel'
];
var DIVISORS = {
m: new bn_js_1.BN(1e3, 10),
u: new bn_js_1.BN(1e6, 10),
n: new bn_js_1.BN(1e9, 10),
p: new bn_js_1.BN(1e12, 10)
};
var MAX_MILLISATS = new bn_js_1.BN('2100000000000000000', 10);
var MILLISATS_PER_BTC = new bn_js_1.BN(1e11, 10);
var TAGCODES = {
payment_hash: 1,
payment_secret: 16,
description: 13,
payee: 19,
description_hash: 23,
expiry: 6,
min_final_cltv_expiry: 24,
fallback_address: 9,
route_hint: 3,
feature_bits: 5,
metadata: 27
};
// reverse the keys and values of TAGCODES and insert into TAGNAMES
var TAGNAMES = {};
for (var i = 0, keys = Object.keys(TAGCODES); i < keys.length; i++) {
var currentName = keys[i];
var currentCode = TAGCODES[keys[i]].toString();
TAGNAMES[currentCode] = currentName;
}
var TAGPARSERS = {
1: function (words) { return wordsToBuffer(words, true); },
16: function (words) { return wordsToBuffer(words, true); },
13: function (words) { return wordsToBuffer(words, true).toString('utf8'); },
19: function (words) { return wordsToBuffer(words, true); },
23: function (words) { return wordsToBuffer(words, true); },
27: function (words) { return wordsToBuffer(words, true); },
6: wordsToIntBE,
24: wordsToIntBE,
3: routingInfoParser,
5: featureBitsParser // keep feature bits as array of 5 bit words
};
function getUnknownParser(tagCode) {
return function (words) { return ({
tagCode: parseInt(tagCode),
words: bech32.encode('unknown', words, Number.MAX_SAFE_INTEGER)
}); };
}
function wordsToIntBE(words) {
return words.reverse().reduce(function (total, item, index) {
return total + item * Math.pow(32, index);
}, 0);
}
function convert(data, inBits, outBits) {
var value = 0;
var bits = 0;
var maxV = (1 << outBits) - 1;
var result = [];
for (var i = 0; i < data.length; ++i) {
value = (value << inBits) | data[i];
bits += inBits;
while (bits >= outBits) {
bits -= outBits;
result.push((value >> bits) & maxV);
}
}
if (bits > 0) {
result.push((value << (outBits - bits)) & maxV);
}
return result;
}
function wordsToBuffer(words, trim) {
var buffer = buffer_1.Buffer.from(convert(words, 5, 8));
if (trim && (words.length * 5) % 8 !== 0) {
buffer = buffer.slice(0, -1);
}
return buffer;
}
// first convert from words to buffer, trimming padding where necessary
// parse in 51 byte chunks. See encoder for details.
function routingInfoParser(words) {
var routes = [];
var pubkey, shortChannelId, feeBaseMSats, feeProportionalMillionths, cltvExpiryDelta;
var routesBuffer = wordsToBuffer(words, true);
while (routesBuffer.length > 0) {
pubkey = routesBuffer.slice(0, 33).toString('hex'); // 33 bytes
shortChannelId = routesBuffer.slice(33, 41).toString('hex'); // 8 bytes
feeBaseMSats = parseInt(routesBuffer.slice(41, 45).toString('hex'), 16); // 4 bytes
feeProportionalMillionths = parseInt(routesBuffer.slice(45, 49).toString('hex'), 16); // 4 bytes
cltvExpiryDelta = parseInt(routesBuffer.slice(49, 51).toString('hex'), 16); // 2 bytes
routesBuffer = routesBuffer.slice(51);
routes.push({
pubkey: pubkey,
short_channel_id: shortChannelId,
fee_base_msat: feeBaseMSats,
fee_proportional_millionths: feeProportionalMillionths,
cltv_expiry_delta: cltvExpiryDelta
});
}
return routes;
}
function featureBitsParser(words) {
var bools = words
.slice()
.reverse()
.map(function (word) { return [
!!(word & 1),
!!(word & 2),
!!(word & 4),
!!(word & 8),
!!(word & 16)
]; })
.reduce(function (finalArr, itemArr) { return finalArr.concat(itemArr); }, []);
while (bools.length < FEATUREBIT_ORDER.length * 2) {
bools.push(false);
}
var featureBits = { extra_bits: {} };
FEATUREBIT_ORDER.forEach(function (featureName, index) {
var status;
if (bools[index * 2]) {
status = 'required';
}
else if (bools[index * 2 + 1]) {
status = 'supported';
}
featureBits[featureName] = status;
});
if (bools.length > FEATUREBIT_ORDER.length * 2) {
var extraBits = bools.slice(FEATUREBIT_ORDER.length * 2);
featureBits.extra_bits = {
start_bit: FEATUREBIT_ORDER.length * 2,
bits: extraBits,
required: extraBits.reduce(function (result, bit, index) {
return index % 2 !== 0 ? result || false : result || bit;
}, false)
};
}
return featureBits;
}
function hrpToMillisat(hrpString, outputString) {
var divisor, value;
if (hrpString.slice(-1).match(/^[munp]$/)) {
divisor = hrpString.slice(-1);
value = hrpString.slice(0, -1);
}
else if (hrpString.slice(-1).match(/^[^munp0-9]$/)) {
throw new Error('Not a valid multiplier for the amount');
}
else {
value = hrpString;
}
if (!value.match(/^\d+$/))
throw new Error('Not a valid human readable amount');
var valueBN = new bn_js_1.BN(value, 10);
var millisatoshisBN = divisor
? valueBN.mul(MILLISATS_PER_BTC).div(DIVISORS[divisor])
: valueBN.mul(MILLISATS_PER_BTC);
if ((divisor === 'p' && !valueBN.mod(new bn_js_1.BN(10, 10)).eq(new bn_js_1.BN(0, 10))) ||
millisatoshisBN.gt(MAX_MILLISATS)) {
throw new Error('Amount is outside of valid range');
}
return outputString ? millisatoshisBN.toString() : millisatoshisBN;
}
exports.hrpToMillisat = hrpToMillisat;
// decode will only have extra comments that aren't covered in encode comments.
// also if anything is hard to read I'll comment.
function decode(paymentRequest, network) {
if (typeof paymentRequest !== 'string')
throw new Error('Lightning Payment Request must be string');
if (paymentRequest.slice(0, 2).toLowerCase() !== 'ln')
throw new Error('Not a proper lightning payment request');
var sections = [];
var decoded = bech32.decode(paymentRequest, Number.MAX_SAFE_INTEGER);
paymentRequest = paymentRequest.toLowerCase();
var prefix = decoded.prefix;
var words = decoded.words;
var letters = paymentRequest.slice(prefix.length + 1);
var sigWords = words.slice(-104);
words = words.slice(0, -104);
// Without reverse lookups, can't say that the multipier at the end must
// have a number before it, so instead we parse, and if the second group
// doesn't have anything, there's a good chance the last letter of the
// coin type got captured by the third group, so just re-regex without
// the number.
var prefixMatches = prefix.match(/^ln(\S+?)(\d*)([a-zA-Z]?)$/);
if (prefixMatches && !prefixMatches[2])
prefixMatches = prefix.match(/^ln(\S+)$/);
if (!prefixMatches) {
throw new Error('Not a proper lightning payment request');
}
// "ln" section
sections.push({
name: 'lightning_network',
letters: 'ln'
});
// "bc" section
var bech32Prefix = prefixMatches[1];
var coinNetwork;
if (!network) {
switch (bech32Prefix) {
case DEFAULTNETWORK.bech32:
coinNetwork = DEFAULTNETWORK;
break;
case TESTNETWORK.bech32:
coinNetwork = TESTNETWORK;
break;
case REGTESTNETWORK.bech32:
coinNetwork = REGTESTNETWORK;
break;
case SIMNETWORK.bech32:
coinNetwork = SIMNETWORK;
break;
}
}
else {
if (network.bech32 === undefined ||
network.pubKeyHash === undefined ||
network.scriptHash === undefined ||
!Array.isArray(network.validWitnessVersions))
throw new Error('Invalid network');
coinNetwork = network;
}
if (!coinNetwork || coinNetwork.bech32 !== bech32Prefix) {
throw new Error('Unknown coin bech32 prefix');
}
sections.push({
name: 'coin_network',
letters: bech32Prefix,
value: coinNetwork
});
// amount section
var value = prefixMatches[2];
var millisatoshis;
if (value) {
var divisor = prefixMatches[3];
millisatoshis = hrpToMillisat(value + divisor, true);
sections.push({
name: 'amount',
letters: prefixMatches[2] + prefixMatches[3],
value: millisatoshis
});
}
else {
millisatoshis = null;
}
// "1" separator
sections.push({
name: 'separator',
letters: '1'
});
// timestamp
var timestamp = wordsToIntBE(words.slice(0, 7));
words = words.slice(7); // trim off the left 7 words
sections.push({
name: 'timestamp',
letters: letters.slice(0, 7),
value: timestamp
});
letters = letters.slice(7);
var tagName, parser, tagLength, tagWords;
// we have no tag count to go on, so just keep hacking off words
// until we have none.
while (words.length > 0) {
var tagCode = words[0].toString();
tagName = TAGNAMES[tagCode] || 'unknown_tag';
parser = TAGPARSERS[tagCode] || getUnknownParser(tagCode);
words = words.slice(1);
tagLength = wordsToIntBE(words.slice(0, 2));
words = words.slice(2);
tagWords = words.slice(0, tagLength);
words = words.slice(tagLength);
sections.push({
name: tagName,
tag: letters[0],
letters: letters.slice(0, 1 + 2 + tagLength),
value: parser(tagWords) // see: parsers for more comments
});
letters = letters.slice(1 + 2 + tagLength);
}
// signature
sections.push({
name: 'signature',
letters: letters.slice(0, 104),
value: wordsToBuffer(sigWords, true)
});
letters = letters.slice(104);
// checksum
sections.push({
name: 'checksum',
letters: letters
});
var result = {
paymentRequest: paymentRequest,
sections: sections,
get expiry() {
var exp = sections.find(function (s) { return s.name === 'expiry'; });
if (exp)
return getValue('timestamp') + exp.value;
},
get route_hints() {
return sections.filter(function (s) { return s.name === 'route_hint'; }).map(function (s) { return s.value; });
}
};
var _loop_1 = function (name_1) {
if (name_1 === 'route_hint') {
return "continue";
}
Object.defineProperty(result, name_1, {
get: function () {
return getValue(name_1);
}
});
};
for (var name_1 in TAGCODES) {
_loop_1(name_1);
}
return result;
function getValue(name) {
var section = sections.find(function (s) { return s.name === name; });
return section ? section.value : undefined;
}
}
exports.decode = decode;
@@ -1,8 +0,0 @@
declare function hrpToMillisat(hrpString: any, outputString: any): any;
declare function decode(paymentRequest: string, network?: any): {
paymentRequest: string;
sections: any[];
readonly expiry: any;
readonly route_hints: any[];
};
export { decode, hrpToMillisat };
@@ -1,361 +0,0 @@
import * as bech32 from "bech32";
import { Buffer } from "buffer";
import { BN } from 'bn.js';
// defaults for encode; default timestamp is current time at call
var DEFAULTNETWORK = {
// default network is bitcoin
bech32: 'bc',
pubKeyHash: 0x00,
scriptHash: 0x05,
validWitnessVersions: [0]
};
var TESTNETWORK = {
bech32: 'tb',
pubKeyHash: 0x6f,
scriptHash: 0xc4,
validWitnessVersions: [0]
};
var REGTESTNETWORK = {
bech32: 'bcrt',
pubKeyHash: 0x6f,
scriptHash: 0xc4,
validWitnessVersions: [0]
};
var SIMNETWORK = {
bech32: 'sb',
pubKeyHash: 0x3f,
scriptHash: 0x7b,
validWitnessVersions: [0]
};
var FEATUREBIT_ORDER = [
'option_data_loss_protect',
'initial_routing_sync',
'option_upfront_shutdown_script',
'gossip_queries',
'var_onion_optin',
'gossip_queries_ex',
'option_static_remotekey',
'payment_secret',
'basic_mpp',
'option_support_large_channel'
];
var DIVISORS = {
m: new BN(1e3, 10),
u: new BN(1e6, 10),
n: new BN(1e9, 10),
p: new BN(1e12, 10)
};
var MAX_MILLISATS = new BN('2100000000000000000', 10);
var MILLISATS_PER_BTC = new BN(1e11, 10);
var TAGCODES = {
payment_hash: 1,
payment_secret: 16,
description: 13,
payee: 19,
description_hash: 23,
expiry: 6,
min_final_cltv_expiry: 24,
fallback_address: 9,
route_hint: 3,
feature_bits: 5,
metadata: 27
};
// reverse the keys and values of TAGCODES and insert into TAGNAMES
var TAGNAMES = {};
for (var i = 0, keys = Object.keys(TAGCODES); i < keys.length; i++) {
var currentName = keys[i];
var currentCode = TAGCODES[keys[i]].toString();
TAGNAMES[currentCode] = currentName;
}
var TAGPARSERS = {
1: function (words) { return wordsToBuffer(words, true); },
16: function (words) { return wordsToBuffer(words, true); },
13: function (words) { return wordsToBuffer(words, true).toString('utf8'); },
19: function (words) { return wordsToBuffer(words, true); },
23: function (words) { return wordsToBuffer(words, true); },
27: function (words) { return wordsToBuffer(words, true); },
6: wordsToIntBE,
24: wordsToIntBE,
3: routingInfoParser,
5: featureBitsParser // keep feature bits as array of 5 bit words
};
function getUnknownParser(tagCode) {
return function (words) { return ({
tagCode: parseInt(tagCode),
words: bech32.encode('unknown', words, Number.MAX_SAFE_INTEGER)
}); };
}
function wordsToIntBE(words) {
return words.reverse().reduce(function (total, item, index) {
return total + item * Math.pow(32, index);
}, 0);
}
function convert(data, inBits, outBits) {
var value = 0;
var bits = 0;
var maxV = (1 << outBits) - 1;
var result = [];
for (var i = 0; i < data.length; ++i) {
value = (value << inBits) | data[i];
bits += inBits;
while (bits >= outBits) {
bits -= outBits;
result.push((value >> bits) & maxV);
}
}
if (bits > 0) {
result.push((value << (outBits - bits)) & maxV);
}
return result;
}
function wordsToBuffer(words, trim) {
var buffer = Buffer.from(convert(words, 5, 8));
if (trim && (words.length * 5) % 8 !== 0) {
buffer = buffer.slice(0, -1);
}
return buffer;
}
// first convert from words to buffer, trimming padding where necessary
// parse in 51 byte chunks. See encoder for details.
function routingInfoParser(words) {
var routes = [];
var pubkey, shortChannelId, feeBaseMSats, feeProportionalMillionths, cltvExpiryDelta;
var routesBuffer = wordsToBuffer(words, true);
while (routesBuffer.length > 0) {
pubkey = routesBuffer.slice(0, 33).toString('hex'); // 33 bytes
shortChannelId = routesBuffer.slice(33, 41).toString('hex'); // 8 bytes
feeBaseMSats = parseInt(routesBuffer.slice(41, 45).toString('hex'), 16); // 4 bytes
feeProportionalMillionths = parseInt(routesBuffer.slice(45, 49).toString('hex'), 16); // 4 bytes
cltvExpiryDelta = parseInt(routesBuffer.slice(49, 51).toString('hex'), 16); // 2 bytes
routesBuffer = routesBuffer.slice(51);
routes.push({
pubkey: pubkey,
short_channel_id: shortChannelId,
fee_base_msat: feeBaseMSats,
fee_proportional_millionths: feeProportionalMillionths,
cltv_expiry_delta: cltvExpiryDelta
});
}
return routes;
}
function featureBitsParser(words) {
var bools = words
.slice()
.reverse()
.map(function (word) { return [
!!(word & 1),
!!(word & 2),
!!(word & 4),
!!(word & 8),
!!(word & 16)
]; })
.reduce(function (finalArr, itemArr) { return finalArr.concat(itemArr); }, []);
while (bools.length < FEATUREBIT_ORDER.length * 2) {
bools.push(false);
}
var featureBits = { extra_bits: {} };
FEATUREBIT_ORDER.forEach(function (featureName, index) {
var status;
if (bools[index * 2]) {
status = 'required';
}
else if (bools[index * 2 + 1]) {
status = 'supported';
}
featureBits[featureName] = status;
});
if (bools.length > FEATUREBIT_ORDER.length * 2) {
var extraBits = bools.slice(FEATUREBIT_ORDER.length * 2);
featureBits.extra_bits = {
start_bit: FEATUREBIT_ORDER.length * 2,
bits: extraBits,
required: extraBits.reduce(function (result, bit, index) {
return index % 2 !== 0 ? result || false : result || bit;
}, false)
};
}
return featureBits;
}
function hrpToMillisat(hrpString, outputString) {
var divisor, value;
if (hrpString.slice(-1).match(/^[munp]$/)) {
divisor = hrpString.slice(-1);
value = hrpString.slice(0, -1);
}
else if (hrpString.slice(-1).match(/^[^munp0-9]$/)) {
throw new Error('Not a valid multiplier for the amount');
}
else {
value = hrpString;
}
if (!value.match(/^\d+$/))
throw new Error('Not a valid human readable amount');
var valueBN = new BN(value, 10);
var millisatoshisBN = divisor
? valueBN.mul(MILLISATS_PER_BTC).div(DIVISORS[divisor])
: valueBN.mul(MILLISATS_PER_BTC);
if ((divisor === 'p' && !valueBN.mod(new BN(10, 10)).eq(new BN(0, 10))) ||
millisatoshisBN.gt(MAX_MILLISATS)) {
throw new Error('Amount is outside of valid range');
}
return outputString ? millisatoshisBN.toString() : millisatoshisBN;
}
// decode will only have extra comments that aren't covered in encode comments.
// also if anything is hard to read I'll comment.
function decode(paymentRequest, network) {
if (typeof paymentRequest !== 'string')
throw new Error('Lightning Payment Request must be string');
if (paymentRequest.slice(0, 2).toLowerCase() !== 'ln')
throw new Error('Not a proper lightning payment request');
var sections = [];
var decoded = bech32.decode(paymentRequest, Number.MAX_SAFE_INTEGER);
paymentRequest = paymentRequest.toLowerCase();
var prefix = decoded.prefix;
var words = decoded.words;
var letters = paymentRequest.slice(prefix.length + 1);
var sigWords = words.slice(-104);
words = words.slice(0, -104);
// Without reverse lookups, can't say that the multipier at the end must
// have a number before it, so instead we parse, and if the second group
// doesn't have anything, there's a good chance the last letter of the
// coin type got captured by the third group, so just re-regex without
// the number.
var prefixMatches = prefix.match(/^ln(\S+?)(\d*)([a-zA-Z]?)$/);
if (prefixMatches && !prefixMatches[2])
prefixMatches = prefix.match(/^ln(\S+)$/);
if (!prefixMatches) {
throw new Error('Not a proper lightning payment request');
}
// "ln" section
sections.push({
name: 'lightning_network',
letters: 'ln'
});
// "bc" section
var bech32Prefix = prefixMatches[1];
var coinNetwork;
if (!network) {
switch (bech32Prefix) {
case DEFAULTNETWORK.bech32:
coinNetwork = DEFAULTNETWORK;
break;
case TESTNETWORK.bech32:
coinNetwork = TESTNETWORK;
break;
case REGTESTNETWORK.bech32:
coinNetwork = REGTESTNETWORK;
break;
case SIMNETWORK.bech32:
coinNetwork = SIMNETWORK;
break;
}
}
else {
if (network.bech32 === undefined ||
network.pubKeyHash === undefined ||
network.scriptHash === undefined ||
!Array.isArray(network.validWitnessVersions))
throw new Error('Invalid network');
coinNetwork = network;
}
if (!coinNetwork || coinNetwork.bech32 !== bech32Prefix) {
throw new Error('Unknown coin bech32 prefix');
}
sections.push({
name: 'coin_network',
letters: bech32Prefix,
value: coinNetwork
});
// amount section
var value = prefixMatches[2];
var millisatoshis;
if (value) {
var divisor = prefixMatches[3];
millisatoshis = hrpToMillisat(value + divisor, true);
sections.push({
name: 'amount',
letters: prefixMatches[2] + prefixMatches[3],
value: millisatoshis
});
}
else {
millisatoshis = null;
}
// "1" separator
sections.push({
name: 'separator',
letters: '1'
});
// timestamp
var timestamp = wordsToIntBE(words.slice(0, 7));
words = words.slice(7); // trim off the left 7 words
sections.push({
name: 'timestamp',
letters: letters.slice(0, 7),
value: timestamp
});
letters = letters.slice(7);
var tagName, parser, tagLength, tagWords;
// we have no tag count to go on, so just keep hacking off words
// until we have none.
while (words.length > 0) {
var tagCode = words[0].toString();
tagName = TAGNAMES[tagCode] || 'unknown_tag';
parser = TAGPARSERS[tagCode] || getUnknownParser(tagCode);
words = words.slice(1);
tagLength = wordsToIntBE(words.slice(0, 2));
words = words.slice(2);
tagWords = words.slice(0, tagLength);
words = words.slice(tagLength);
sections.push({
name: tagName,
tag: letters[0],
letters: letters.slice(0, 1 + 2 + tagLength),
value: parser(tagWords) // see: parsers for more comments
});
letters = letters.slice(1 + 2 + tagLength);
}
// signature
sections.push({
name: 'signature',
letters: letters.slice(0, 104),
value: wordsToBuffer(sigWords, true)
});
letters = letters.slice(104);
// checksum
sections.push({
name: 'checksum',
letters: letters
});
var result = {
paymentRequest: paymentRequest,
sections: sections,
get expiry() {
var exp = sections.find(function (s) { return s.name === 'expiry'; });
if (exp)
return getValue('timestamp') + exp.value;
},
get route_hints() {
return sections.filter(function (s) { return s.name === 'route_hint'; }).map(function (s) { return s.value; });
}
};
var _loop_1 = function (name_1) {
if (name_1 === 'route_hint') {
return "continue";
}
Object.defineProperty(result, name_1, {
get: function () {
return getValue(name_1);
}
});
};
for (var name_1 in TAGCODES) {
_loop_1(name_1);
}
return result;
function getValue(name) {
var section = sections.find(function (s) { return s.name === name; });
return section ? section.value : undefined;
}
}
export { decode, hrpToMillisat };
-52
View File
@@ -1,52 +0,0 @@
{
"name": "@gandlaf21/bolt11-decode",
"version": "3.0.6",
"description": "",
"main": "dist/lib/es5/bolt11.js",
"module": "dist/lib/es6/bolt11.js",
"files": [
"dist",
"src"
],
"scripts": {
"compile": "rm -rf dist/lib && tsc && tsc --build tsconfig.es5.json",
"test": "jest --coverage --maxWorkers=1",
"dev": "tsc --watch",
"lint": "eslint --ext .js,.ts ."
},
"repository": {
"type": "git",
"url": "git+https://github.com/gandlaf21/bolt11-decode.git"
},
"keywords": [
"invoice",
"payment",
"request",
"lightning",
"network",
"bolt11",
"bech32",
"browser"
],
"author": "gandlaf21",
"license": "MIT",
"dependencies": {
"bech32": "^1.1.2",
"bn.js": "^4.11.8",
"buffer": "^6.0.3"
},
"devDependencies": {
"prettier": "^2.4.1",
"@types/jest": "^29.2.4",
"@typescript-eslint/eslint-plugin": "^5.47.1",
"@typescript-eslint/parser": "^5.47.1",
"eslint": "^8.31.0",
"eslint-config-standard-with-typescript": "^24.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.6.0",
"eslint-plugin-promise": "^6.1.1",
"jest": "^29.3.1",
"ts-jest": "^29.0.3",
"typescript": "^4.9.4"
}
}
-425
View File
@@ -1,425 +0,0 @@
import * as bech32 from "bech32";
import { Buffer } from "buffer";
import { BN } from 'bn.js'
// defaults for encode; default timestamp is current time at call
const DEFAULTNETWORK = {
// default network is bitcoin
bech32: 'bc',
pubKeyHash: 0x00,
scriptHash: 0x05,
validWitnessVersions: [0]
}
const TESTNETWORK = {
bech32: 'tb',
pubKeyHash: 0x6f,
scriptHash: 0xc4,
validWitnessVersions: [0]
}
const REGTESTNETWORK = {
bech32: 'bcrt',
pubKeyHash: 0x6f,
scriptHash: 0xc4,
validWitnessVersions: [0]
}
const SIMNETWORK = {
bech32: 'sb',
pubKeyHash: 0x3f,
scriptHash: 0x7b,
validWitnessVersions: [0]
}
const FEATUREBIT_ORDER = [
'option_data_loss_protect',
'initial_routing_sync',
'option_upfront_shutdown_script',
'gossip_queries',
'var_onion_optin',
'gossip_queries_ex',
'option_static_remotekey',
'payment_secret',
'basic_mpp',
'option_support_large_channel'
]
const DIVISORS = {
m: new BN(1e3, 10),
u: new BN(1e6, 10),
n: new BN(1e9, 10),
p: new BN(1e12, 10)
}
const MAX_MILLISATS = new BN('2100000000000000000', 10)
const MILLISATS_PER_BTC = new BN(1e11, 10)
const TAGCODES = {
payment_hash: 1,
payment_secret: 16,
description: 13,
payee: 19,
description_hash: 23, // commit to longer descriptions (used by lnurl-pay)
expiry: 6, // default: 3600 (1 hour)
min_final_cltv_expiry: 24, // default: 9
fallback_address: 9,
route_hint: 3, // for extra routing info (private etc.)
feature_bits: 5,
metadata: 27
}
// reverse the keys and values of TAGCODES and insert into TAGNAMES
const TAGNAMES = {}
for (let i = 0, keys = Object.keys(TAGCODES); i < keys.length; i++) {
const currentName = keys[i]
const currentCode = TAGCODES[keys[i]].toString()
TAGNAMES[currentCode] = currentName
}
const TAGPARSERS = {
1: words => wordsToBuffer(words, true), // 256 bits
16: words => wordsToBuffer(words, true), // 256 bits
13: words => wordsToBuffer(words, true).toString('utf8'), // string variable length
19: words => wordsToBuffer(words, true), // 264 bits
23: words => wordsToBuffer(words, true), // 256 bits
27: words => wordsToBuffer(words, true), // variable
6: wordsToIntBE, // default: 3600 (1 hour)
24: wordsToIntBE, // default: 9
3: routingInfoParser, // for extra routing info (private etc.)
5: featureBitsParser // keep feature bits as array of 5 bit words
}
function getUnknownParser(tagCode) {
return words => ({
tagCode: parseInt(tagCode),
words: bech32.encode('unknown', words, Number.MAX_SAFE_INTEGER)
})
}
function wordsToIntBE(words) {
return words.reverse().reduce((total, item, index) => {
return total + item * Math.pow(32, index)
}, 0)
}
function convert(data, inBits, outBits) {
let value = 0
let bits = 0
const maxV = (1 << outBits) - 1
const result = []
for (let i = 0; i < data.length; ++i) {
value = (value << inBits) | data[i]
bits += inBits
while (bits >= outBits) {
bits -= outBits
result.push((value >> bits) & maxV)
}
}
if (bits > 0) {
result.push((value << (outBits - bits)) & maxV)
}
return result
}
function wordsToBuffer(words, trim) {
let buffer = Buffer.from(convert(words, 5, 8))
if (trim && (words.length * 5) % 8 !== 0) {
buffer = buffer.slice(0, -1)
}
return buffer
}
// first convert from words to buffer, trimming padding where necessary
// parse in 51 byte chunks. See encoder for details.
function routingInfoParser(words) {
const routes = []
let pubkey,
shortChannelId,
feeBaseMSats,
feeProportionalMillionths,
cltvExpiryDelta
let routesBuffer = wordsToBuffer(words, true)
while (routesBuffer.length > 0) {
pubkey = routesBuffer.slice(0, 33).toString('hex') // 33 bytes
shortChannelId = routesBuffer.slice(33, 41).toString('hex') // 8 bytes
feeBaseMSats = parseInt(routesBuffer.slice(41, 45).toString('hex'), 16) // 4 bytes
feeProportionalMillionths = parseInt(
routesBuffer.slice(45, 49).toString('hex'),
16
) // 4 bytes
cltvExpiryDelta = parseInt(routesBuffer.slice(49, 51).toString('hex'), 16) // 2 bytes
routesBuffer = routesBuffer.slice(51)
routes.push({
pubkey,
short_channel_id: shortChannelId,
fee_base_msat: feeBaseMSats,
fee_proportional_millionths: feeProportionalMillionths,
cltv_expiry_delta: cltvExpiryDelta
})
}
return routes
}
function featureBitsParser(words) {
const bools = words
.slice()
.reverse()
.map(word => [
!!(word & 0b1),
!!(word & 0b10),
!!(word & 0b100),
!!(word & 0b1000),
!!(word & 0b10000)
])
.reduce((finalArr, itemArr) => finalArr.concat(itemArr), [])
while (bools.length < FEATUREBIT_ORDER.length * 2) {
bools.push(false)
}
const featureBits = { extra_bits: {} }
FEATUREBIT_ORDER.forEach((featureName, index) => {
let status
if (bools[index * 2]) {
status = 'required'
} else if (bools[index * 2 + 1]) {
status = 'supported'
}
featureBits[featureName] = status
})
if (bools.length > FEATUREBIT_ORDER.length * 2) {
const extraBits = bools.slice(FEATUREBIT_ORDER.length * 2)
featureBits.extra_bits = {
start_bit: FEATUREBIT_ORDER.length * 2,
bits: extraBits,
required: extraBits.reduce(
(result, bit, index) =>
index % 2 !== 0 ? result || false : result || bit,
false
)
}
}
return featureBits
}
function hrpToMillisat(hrpString, outputString) {
let divisor, value
if (hrpString.slice(-1).match(/^[munp]$/)) {
divisor = hrpString.slice(-1)
value = hrpString.slice(0, -1)
} else if (hrpString.slice(-1).match(/^[^munp0-9]$/)) {
throw new Error('Not a valid multiplier for the amount')
} else {
value = hrpString
}
if (!value.match(/^\d+$/))
throw new Error('Not a valid human readable amount')
const valueBN = new BN(value, 10)
const millisatoshisBN = divisor
? valueBN.mul(MILLISATS_PER_BTC).div(DIVISORS[divisor])
: valueBN.mul(MILLISATS_PER_BTC)
if (
(divisor === 'p' && !valueBN.mod(new BN(10, 10)).eq(new BN(0, 10))) ||
millisatoshisBN.gt(MAX_MILLISATS)
) {
throw new Error('Amount is outside of valid range')
}
return outputString ? millisatoshisBN.toString() : millisatoshisBN
}
// decode will only have extra comments that aren't covered in encode comments.
// also if anything is hard to read I'll comment.
function decode(paymentRequest: string, network?: any): {
paymentRequest: string;
sections: any[];
readonly expiry: any;
readonly route_hints: any[];
} {
if (typeof paymentRequest !== 'string')
throw new Error('Lightning Payment Request must be string')
if (paymentRequest.slice(0, 2).toLowerCase() !== 'ln')
throw new Error('Not a proper lightning payment request')
const sections = []
const decoded = bech32.decode(paymentRequest, Number.MAX_SAFE_INTEGER)
paymentRequest = paymentRequest.toLowerCase()
const prefix = decoded.prefix
let words = decoded.words
let letters = paymentRequest.slice(prefix.length + 1)
let sigWords = words.slice(-104)
words = words.slice(0, -104)
// Without reverse lookups, can't say that the multipier at the end must
// have a number before it, so instead we parse, and if the second group
// doesn't have anything, there's a good chance the last letter of the
// coin type got captured by the third group, so just re-regex without
// the number.
let prefixMatches = prefix.match(/^ln(\S+?)(\d*)([a-zA-Z]?)$/)
if (prefixMatches && !prefixMatches[2])
prefixMatches = prefix.match(/^ln(\S+)$/)
if (!prefixMatches) {
throw new Error('Not a proper lightning payment request')
}
// "ln" section
sections.push({
name: 'lightning_network',
letters: 'ln'
})
// "bc" section
const bech32Prefix = prefixMatches[1]
let coinNetwork
if (!network) {
switch (bech32Prefix) {
case DEFAULTNETWORK.bech32:
coinNetwork = DEFAULTNETWORK
break
case TESTNETWORK.bech32:
coinNetwork = TESTNETWORK
break
case REGTESTNETWORK.bech32:
coinNetwork = REGTESTNETWORK
break
case SIMNETWORK.bech32:
coinNetwork = SIMNETWORK
break
}
} else {
if (
network.bech32 === undefined ||
network.pubKeyHash === undefined ||
network.scriptHash === undefined ||
!Array.isArray(network.validWitnessVersions)
)
throw new Error('Invalid network')
coinNetwork = network
}
if (!coinNetwork || coinNetwork.bech32 !== bech32Prefix) {
throw new Error('Unknown coin bech32 prefix')
}
sections.push({
name: 'coin_network',
letters: bech32Prefix,
value: coinNetwork
})
// amount section
const value = prefixMatches[2]
let millisatoshis
if (value) {
const divisor = prefixMatches[3]
millisatoshis = hrpToMillisat(value + divisor, true)
sections.push({
name: 'amount',
letters: prefixMatches[2] + prefixMatches[3],
value: millisatoshis
})
} else {
millisatoshis = null
}
// "1" separator
sections.push({
name: 'separator',
letters: '1'
})
// timestamp
const timestamp = wordsToIntBE(words.slice(0, 7))
words = words.slice(7) // trim off the left 7 words
sections.push({
name: 'timestamp',
letters: letters.slice(0, 7),
value: timestamp
})
letters = letters.slice(7)
let tagName, parser, tagLength, tagWords
// we have no tag count to go on, so just keep hacking off words
// until we have none.
while (words.length > 0) {
const tagCode = words[0].toString()
tagName = TAGNAMES[tagCode] || 'unknown_tag'
parser = TAGPARSERS[tagCode] || getUnknownParser(tagCode)
words = words.slice(1)
tagLength = wordsToIntBE(words.slice(0, 2))
words = words.slice(2)
tagWords = words.slice(0, tagLength)
words = words.slice(tagLength)
sections.push({
name: tagName,
tag: letters[0],
letters: letters.slice(0, 1 + 2 + tagLength),
value: parser(tagWords) // see: parsers for more comments
})
letters = letters.slice(1 + 2 + tagLength)
}
// signature
sections.push({
name: 'signature',
letters: letters.slice(0, 104),
value: wordsToBuffer(sigWords, true)
})
letters = letters.slice(104)
// checksum
sections.push({
name: 'checksum',
letters: letters
})
let result = {
paymentRequest,
sections,
get expiry() {
let exp = sections.find(s => s.name === 'expiry')
if (exp) return getValue('timestamp') + exp.value
},
get route_hints() {
return sections.filter(s => s.name === 'route_hint').map(s => s.value)
}
}
for (let name in TAGCODES) {
if (name === 'route_hint') {
// route hints can be multiple, so this won't work for them
continue
}
Object.defineProperty(result, name, {
get() {
return getValue(name)
}
})
}
return result
function getValue(name) {
let section = sections.find(s => s.name === name)
return section ? section.value : undefined
}
}
export {
decode,
hrpToMillisat
}
-21
View File
@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
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.
-922
View File
@@ -1,922 +0,0 @@
# noble-curves
Audited & minimal JS implementation of elliptic curve cryptography.
- 🔒 [**Audited**](#security) by an independent security firm
- 🔻 Tree-shaking-friendly: use only what's necessary, other code won't be included
- 🏎 Ultra-fast, hand-optimized for caveats of JS engines
- 🔍 Unique tests ensure correctness: property-based, cross-library and Wycheproof vectors, fuzzing
- ➰ Short Weierstrass, Edwards, Montgomery curves
- ✍️ ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement
- #️⃣ Hash-to-curve
for encoding or hashing an arbitrary string to an elliptic curve point
- 🧜‍♂️ Poseidon ZK-friendly hash
Check out [Upgrading](#upgrading) if you've previously used single-feature noble
packages. See [Resources](#resources) for articles and real-world software that uses curves.
### This library belongs to _noble_ crypto
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
- No dependencies, protection against supply chain attacks
- Auditable TypeScript / JS code
- Supported in all major browsers and stable node.js versions
- All releases are signed with PGP keys
- Check out [homepage](https://paulmillr.com/noble/) & all libraries:
[curves](https://github.com/paulmillr/noble-curves)
(4kb versions [secp256k1](https://github.com/paulmillr/noble-secp256k1),
[ed25519](https://github.com/paulmillr/noble-ed25519)),
[hashes](https://github.com/paulmillr/noble-hashes)
## Usage
Browser, deno and node.js are supported:
> npm install @noble/curves
For [Deno](https://deno.land), use it with
[npm specifier](https://deno.land/manual@v1.28.0/node/npm_specifiers).
In browser, you could also include the single file from
[GitHub's releases page](https://github.com/paulmillr/noble-curves/releases).
The library is tree-shaking-friendly and does NOT expose root entry point as
`import c from '@noble/curves'`. Instead, you need to import specific primitives.
This is done to ensure small size of your apps.
Package consists of two parts:
1. [Implementations](#implementations), utilizing one dependency [noble-hashes](https://github.com/paulmillr/noble-hashes),
providing ready-to-use:
- NIST curves secp256r1 / p256, secp384r1 / p384, secp521r1 / p521
- SECG curve secp256k1
- ed25519 / curve25519 / x25519 / ristretto255,
edwards448 / curve448 / x448
implementing
[RFC7748](https://www.rfc-editor.org/rfc/rfc7748) /
[RFC8032](https://www.rfc-editor.org/rfc/rfc8032) /
[FIPS 186-5](https://csrc.nist.gov/publications/detail/fips/186/5/final) /
[ZIP215](https://zips.z.cash/zip-0215) standards
- pairing-friendly curves bls12-381, bn254
- [pasta](https://electriccoin.co/blog/the-pasta-curves-for-halo-2-and-beyond/) curves
2. [Abstract](#abstract-api), zero-dependency EC algorithms
### Implementations
Each curve can be used in the following way:
```ts
import { secp256k1 } from '@noble/curves/secp256k1'; // ESM and Common.js
// import { secp256k1 } from 'npm:@noble/curves@1.2.0/secp256k1'; // Deno
const priv = secp256k1.utils.randomPrivateKey();
const pub = secp256k1.getPublicKey(priv);
const msg = new Uint8Array(32).fill(1);
const sig = secp256k1.sign(msg, priv);
const isValid = secp256k1.verify(sig, msg, pub) === true;
// hex strings are also supported besides Uint8Arrays:
const privHex = '46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236';
const pub2 = secp256k1.getPublicKey(privHex);
```
All curves:
```typescript
import { secp256k1, schnorr } from '@noble/curves/secp256k1';
import { ed25519, ed25519ph, ed25519ctx, x25519, RistrettoPoint } from '@noble/curves/ed25519';
import { ed448, ed448ph, ed448ctx, x448 } from '@noble/curves/ed448';
import { p256 } from '@noble/curves/p256';
import { p384 } from '@noble/curves/p384';
import { p521 } from '@noble/curves/p521';
import { pallas, vesta } from '@noble/curves/pasta';
import { bls12_381 } from '@noble/curves/bls12-381';
import { bn254 } from '@noble/curves/bn254';
import { jubjub } from '@noble/curves/jubjub';
```
Recovering public keys from weierstrass ECDSA signatures; using ECDH:
```ts
// extraEntropy https://moderncrypto.org/mail-archive/curves/2017/000925.html
const sigImprovedSecurity = secp256k1.sign(msg, priv, { extraEntropy: true });
sig.recoverPublicKey(msg) === pub; // public key recovery
const someonesPub = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
const shared = secp256k1.getSharedSecret(priv, someonesPub); // ECDH
```
Schnorr signatures over secp256k1 following
[BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki):
```ts
import { schnorr } from '@noble/curves/secp256k1';
const priv = schnorr.utils.randomPrivateKey();
const pub = schnorr.getPublicKey(priv);
const msg = new TextEncoder().encode('hello');
const sig = schnorr.sign(msg, priv);
const isValid = schnorr.verify(sig, msg, pub);
```
ed25519 module has ed25519ctx / ed25519ph variants,
x25519 ECDH and [ristretto255](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448).
Default `verify` behavior follows [ZIP215](https://zips.z.cash/zip-0215) and
[can be used in consensus-critical applications](https://hdevalence.ca/blog/2020-10-04-its-25519am).
`zip215: false` option switches verification criteria to RFC8032 / FIPS 186-5.
```ts
import { ed25519 } from '@noble/curves/ed25519';
const priv = ed25519.utils.randomPrivateKey();
const pub = ed25519.getPublicKey(priv);
const msg = new TextEncoder().encode('hello');
const sig = ed25519.sign(msg, priv);
ed25519.verify(sig, msg, pub); // Default mode: follows ZIP215
ed25519.verify(sig, msg, pub, { zip215: false }); // RFC8032 / FIPS 186-5
// Variants from RFC8032: with context, prehashed
import { ed25519ctx, ed25519ph } from '@noble/curves/ed25519';
// ECDH using curve25519 aka x25519
import { x25519 } from '@noble/curves/ed25519';
const priv = 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4';
const pub = 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c';
x25519.getSharedSecret(priv, pub) === x25519.scalarMult(priv, pub); // aliases
x25519.getPublicKey(priv) === x25519.scalarMultBase(priv);
// hash-to-curve
import { hashToCurve, encodeToCurve } from '@noble/curves/ed25519';
import { RistrettoPoint } from '@noble/curves/ed25519';
const rp = RistrettoPoint.fromHex(
'6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919'
);
RistrettoPoint.hashToCurve('Ristretto is traditionally a short shot of espresso coffee');
// also has add(), equals(), multiply(), toRawBytes() methods
```
ed448 is similar:
```ts
import { ed448, ed448ph, ed448ctx, x448 } from '@noble/curves/ed448';
import { hashToCurve, encodeToCurve } from '@noble/curves/ed448';
ed448.getPublicKey(ed448.utils.randomPrivateKey());
```
Every curve has `CURVE` object that contains its parameters, field, and others:
```ts
import { secp256k1 } from '@noble/curves/secp256k1'; // ESM and Common.js
console.log(secp256k1.CURVE.p, secp256k1.CURVE.n, secp256k1.CURVE.a, secp256k1.CURVE.b);
```
## Abstract API
Abstract API allows to define custom curves. All arithmetics is done with JS
bigints over finite fields, which is defined from `modular` sub-module. For
scalar multiplication, we use
[precomputed tables with w-ary non-adjacent form (wNAF)](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/).
Precomputes are enabled for weierstrass and edwards BASE points of a curve. You
could precompute any other point (e.g. for ECDH) using `utils.precompute()`
method: check out examples.
There are following zero-dependency algorithms:
- [abstract/weierstrass: Short Weierstrass curve](#abstractweierstrass-short-weierstrass-curve)
- [abstract/edwards: Twisted Edwards curve](#abstractedwards-twisted-edwards-curve)
- [abstract/montgomery: Montgomery curve](#abstractmontgomery-montgomery-curve)
- [abstract/bls: Barreto-Lynn-Scott curves](#abstractbls-barreto-lynn-scott-curves)
- [abstract/hash-to-curve: Hashing strings to curve points](#abstracthash-to-curve-hashing-strings-to-curve-points)
- [abstract/poseidon: Poseidon hash](#abstractposeidon-poseidon-hash)
- [abstract/modular: Modular arithmetics utilities](#abstractmodular-modular-arithmetics-utilities)
- [abstract/utils: General utilities](#abstractutils-general-utilities)
### abstract/weierstrass: Short Weierstrass curve
```ts
import { weierstrass } from '@noble/curves/abstract/weierstrass';
import { Field } from '@noble/curves/abstract/modular'; // finite field for mod arithmetics
import { sha256 } from '@noble/hashes/sha256'; // 3rd-party sha256() of type utils.CHash
import { hmac } from '@noble/hashes/hmac'; // 3rd-party hmac() that will accept sha256()
import { concatBytes, randomBytes } from '@noble/hashes/utils'; // 3rd-party utilities
const secq256k1 = weierstrass({
// secq256k1: cycle of secp256k1 with Fp/N flipped.
// https://personaelabs.org/posts/spartan-ecdsa
// https://zcash.github.io/halo2/background/curves.html#cycles-of-curves
a: 0n,
b: 7n,
Fp: Field(2n ** 256n - 432420386565659656852420866394968145599n),
n: 2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n,
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
hash: sha256,
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
randomBytes,
});
// Replace weierstrass with weierstrassPoints if you don't need ECDSA, hash, hmac, randomBytes
```
Short Weierstrass curve's formula is `y² = x³ + ax + b`. `weierstrass`
expects arguments `a`, `b`, field `Fp`, curve order `n`, cofactor `h`
and coordinates `Gx`, `Gy` of generator point.
**`k` generation** is done deterministically, following
[RFC6979](https://www.rfc-editor.org/rfc/rfc6979). For this you will need
`hmac` & `hash`, which in our implementations is provided by noble-hashes. If
you're using different hashing library, make sure to wrap it in the following interface:
```ts
type CHash = {
(message: Uint8Array): Uint8Array;
blockLen: number;
outputLen: number;
create(): any;
};
```
**Weierstrass points:**
1. Exported as `ProjectivePoint`
2. Represented in projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
3. Use complete exception-free formulas for addition and doubling
4. Can be decoded/encoded from/to Uint8Array / hex strings using
`ProjectivePoint.fromHex` and `ProjectivePoint#toRawBytes()`
5. Have `assertValidity()` which checks for being on-curve
6. Have `toAffine()` and `x` / `y` getters which convert to 2d xy affine coordinates
```ts
// `weierstrassPoints()` returns `CURVE` and `ProjectivePoint`
// `weierstrass()` returns `CurveFn`
type SignOpts = { lowS?: boolean; prehash?: boolean; extraEntropy: boolean | Uint8Array };
type CurveFn = {
CURVE: ReturnType<typeof validateOpts>;
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
verify: (
signature: Hex | SignatureType,
msgHash: Hex,
publicKey: Hex,
opts?: { lowS?: boolean; prehash?: boolean }
) => boolean;
ProjectivePoint: ProjectivePointConstructor;
Signature: SignatureConstructor;
utils: {
normPrivateKeyToScalar: (key: PrivKey) => bigint;
isValidPrivateKey(key: PrivKey): boolean;
randomPrivateKey: () => Uint8Array;
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
};
};
// T is usually bigint, but can be something else like complex numbers in BLS curves
interface ProjPointType<T> extends Group<ProjPointType<T>> {
readonly px: T;
readonly py: T;
readonly pz: T;
get x(): bigint;
get y(): bigint;
multiply(scalar: bigint): ProjPointType<T>;
multiplyUnsafe(scalar: bigint): ProjPointType<T>;
multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
toAffine(iz?: T): AffinePoint<T>;
isTorsionFree(): boolean;
clearCofactor(): ProjPointType<T>;
assertValidity(): void;
hasEvenY(): boolean;
toRawBytes(isCompressed?: boolean): Uint8Array;
toHex(isCompressed?: boolean): string;
}
// Static methods for 3d XYZ points
interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
new (x: T, y: T, z: T): ProjPointType<T>;
fromAffine(p: AffinePoint<T>): ProjPointType<T>;
fromHex(hex: Hex): ProjPointType<T>;
fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
}
```
**ECDSA signatures** are represented by `Signature` instances and can be
described by the interface:
```ts
interface SignatureType {
readonly r: bigint;
readonly s: bigint;
readonly recovery?: number;
assertValidity(): void;
addRecoveryBit(recovery: number): SignatureType;
hasHighS(): boolean;
normalizeS(): SignatureType;
recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
toCompactRawBytes(): Uint8Array;
toCompactHex(): string;
// DER-encoded
toDERRawBytes(): Uint8Array;
toDERHex(): string;
}
type SignatureConstructor = {
new (r: bigint, s: bigint): SignatureType;
fromCompact(hex: Hex): SignatureType;
fromDER(hex: Hex): SignatureType;
};
```
More examples:
```typescript
// All curves expose same generic interface.
const priv = secq256k1.utils.randomPrivateKey();
secq256k1.getPublicKey(priv); // Convert private key to public.
const sig = secq256k1.sign(msg, priv); // Sign msg with private key.
secq256k1.verify(sig, msg, priv); // Verify if sig is correct.
const Point = secq256k1.ProjectivePoint;
const point = Point.BASE; // Elliptic curve Point class and BASE point static var.
point.add(point).equals(point.double()); // add(), equals(), double() methods
point.subtract(point).equals(Point.ZERO); // subtract() method, ZERO static var
point.negate(); // Flips point over x/y coordinate.
point.multiply(31415n); // Multiplication of Point by scalar.
point.assertValidity(); // Checks for being on-curve
point.toAffine(); // Converts to 2d affine xy coordinates
secq256k1.CURVE.n;
secq256k1.CURVE.p;
secq256k1.CURVE.Fp.mod();
secq256k1.CURVE.hash();
// precomputes
const fast = secq256k1.utils.precompute(8, Point.fromHex(someonesPubKey));
fast.multiply(privKey); // much faster ECDH now
```
### abstract/edwards: Twisted Edwards curve
```ts
import { twistedEdwards } from '@noble/curves/abstract/edwards';
import { Field } from '@noble/curves/abstract/modular';
import { sha512 } from '@noble/hashes/sha512';
import { randomBytes } from '@noble/hashes/utils';
const Fp = Field(2n ** 255n - 19n);
const ed25519 = twistedEdwards({
a: Fp.create(-1n),
d: Fp.div(-121665n, 121666n), // -121665n/121666n mod p
Fp: Fp,
n: 2n ** 252n + 27742317777372353535851937790883648493n,
h: 8n,
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
Gy: 46316835694926478169428394003475163141307993866256225615783033603165251855960n,
hash: sha512,
randomBytes,
adjustScalarBytes(bytes) {
// optional; but mandatory in ed25519
bytes[0] &= 248;
bytes[31] &= 127;
bytes[31] |= 64;
return bytes;
},
} as const);
```
Twisted Edwards curve's formula is `ax² + y² = 1 + dx²y²`. You must specify `a`, `d`, field `Fp`, order `n`, cofactor `h`
and coordinates `Gx`, `Gy` of generator point.
For EdDSA signatures, `hash` param required. `adjustScalarBytes` which instructs how to change private scalars could be specified.
**Edwards points:**
1. Exported as `ExtendedPoint`
2. Represented in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z)
3. Use complete exception-free formulas for addition and doubling
4. Can be decoded/encoded from/to Uint8Array / hex strings using `ExtendedPoint.fromHex` and `ExtendedPoint#toRawBytes()`
5. Have `assertValidity()` which checks for being on-curve
6. Have `toAffine()` and `x` / `y` getters which convert to 2d xy affine coordinates
7. Have `isTorsionFree()`, `clearCofactor()` and `isSmallOrder()` utilities to handle torsions
```ts
// `twistedEdwards()` returns `CurveFn` of following type:
type CurveFn = {
CURVE: ReturnType<typeof validateOpts>;
getPublicKey: (privateKey: Hex) => Uint8Array;
sign: (message: Hex, privateKey: Hex, context?: Hex) => Uint8Array;
verify: (sig: SigType, message: Hex, publicKey: Hex, context?: Hex) => boolean;
ExtendedPoint: ExtPointConstructor;
utils: {
randomPrivateKey: () => Uint8Array;
getExtendedPublicKey: (key: PrivKey) => {
head: Uint8Array;
prefix: Uint8Array;
scalar: bigint;
point: PointType;
pointBytes: Uint8Array;
};
};
};
interface ExtPointType extends Group<ExtPointType> {
readonly ex: bigint;
readonly ey: bigint;
readonly ez: bigint;
readonly et: bigint;
get x(): bigint;
get y(): bigint;
assertValidity(): void;
multiply(scalar: bigint): ExtPointType;
multiplyUnsafe(scalar: bigint): ExtPointType;
isSmallOrder(): boolean;
isTorsionFree(): boolean;
clearCofactor(): ExtPointType;
toAffine(iz?: bigint): AffinePoint<bigint>;
toRawBytes(isCompressed?: boolean): Uint8Array;
toHex(isCompressed?: boolean): string;
}
// Static methods of Extended Point with coordinates in X, Y, Z, T
interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
fromAffine(p: AffinePoint<bigint>): ExtPointType;
fromHex(hex: Hex): ExtPointType;
fromPrivateKey(privateKey: Hex): ExtPointType;
}
```
### abstract/montgomery: Montgomery curve
```typescript
import { montgomery } from '@noble/curves/abstract/montgomery';
import { Field } from '@noble/curves/abstract/modular';
const x25519 = montgomery({
a: 486662n,
Gu: 9n,
Fp: Field(2n ** 255n - 19n),
montgomeryBits: 255,
nByteLength: 32,
// Optional param
adjustScalarBytes(bytes) {
bytes[0] &= 248;
bytes[31] &= 127;
bytes[31] |= 64;
return bytes;
},
});
```
The module contains methods for x-only ECDH on Curve25519 / Curve448 from RFC7748.
Proper Elliptic Curve Points are not implemented yet.
You must specify curve params `Fp`, `a`, `Gu` coordinate of u, `montgomeryBits` and `nByteLength`.
### abstract/bls: Barreto-Lynn-Scott curves
The module abstracts BLS (Barreto-Lynn-Scott) pairing-friendly elliptic curve construction.
They allow to construct [zk-SNARKs](https://z.cash/technology/zksnarks/) and
use aggregated, batch-verifiable
[threshold signatures](https://medium.com/snigirev.stepan/bls-signatures-better-than-schnorr-5a7fe30ea716),
using Boneh-Lynn-Shacham signature scheme.
Main methods and properties are:
- `getPublicKey(privateKey)`
- `sign(message, privateKey)`
- `verify(signature, message, publicKey)`
- `aggregatePublicKeys(publicKeys)`
- `aggregateSignatures(signatures)`
- `G1` and `G2` curves containing `CURVE` and `ProjectivePoint`
- `Signature` property with `fromHex`, `toHex` methods
- `fields` containing `Fp`, `Fp2`, `Fp6`, `Fp12`, `Fr`
Right now we only implement BLS12-381 (compatible with ETH and others),
but in theory defining BLS12-377, BLS24 should be straightforward. An example:
```ts
import { bls12_381 as bls } from '@noble/curves/bls12-381';
const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
const message = '64726e3da8';
const publicKey = bls.getPublicKey(privateKey);
const signature = bls.sign(message, privateKey);
const isValid = bls.verify(signature, message, publicKey);
console.log({ publicKey, signature, isValid });
// Sign 1 msg with 3 keys
const privateKeys = [
'18f020b98eb798752a50ed0563b079c125b0db5dd0b1060d1c1b47d4a193e1e4',
'ed69a8c50cf8c9836be3b67c7eeff416612d45ba39a5c099d48fa668bf558c9c',
'16ae669f3be7a2121e17d0c68c05a8f3d6bef21ec0f2315f1d7aec12484e4cf5',
];
const messages = ['d2', '0d98', '05caf3'];
const publicKeys = privateKeys.map(bls.getPublicKey);
const signatures2 = privateKeys.map((p) => bls.sign(message, p));
const aggPubKey2 = bls.aggregatePublicKeys(publicKeys);
const aggSignature2 = bls.aggregateSignatures(signatures2);
const isValid2 = bls.verify(aggSignature2, message, aggPubKey2);
console.log({ signatures2, aggSignature2, isValid2 });
// Sign 3 msgs with 3 keys
const signatures3 = privateKeys.map((p, i) => bls.sign(messages[i], p));
const aggSignature3 = bls.aggregateSignatures(signatures3);
const isValid3 = bls.verifyBatch(aggSignature3, messages, publicKeys);
console.log({ publicKeys, signatures3, aggSignature3, isValid3 });
// bls.pairing(PointG1, PointG2) // pairings
// bls.G1.ProjectivePoint.BASE, bls.G2.ProjectivePoint.BASE
// bls.fields.Fp, bls.fields.Fp2, bls.fields.Fp12, bls.fields.Fr
// hash-to-curve examples can be seen below
```
Full types:
```ts
getPublicKey: (privateKey: PrivKey) => Uint8Array;
sign: {
(message: Hex, privateKey: PrivKey): Uint8Array;
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
};
verify: (
signature: Hex | ProjPointType<Fp2>,
message: Hex | ProjPointType<Fp2>,
publicKey: Hex | ProjPointType<Fp>
) => boolean;
verifyBatch: (
signature: Hex | ProjPointType<Fp2>,
messages: (Hex | ProjPointType<Fp2>)[],
publicKeys: (Hex | ProjPointType<Fp>)[]
) => boolean;
aggregatePublicKeys: {
(publicKeys: Hex[]): Uint8Array;
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
};
aggregateSignatures: {
(signatures: Hex[]): Uint8Array;
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
};
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
Signature: SignatureCoder<Fp2>;
params: {
x: bigint;
r: bigint;
G1b: bigint;
G2b: Fp2;
};
fields: {
Fp: IField<Fp>;
Fp2: IField<Fp2>;
Fp6: IField<Fp6>;
Fp12: IField<Fp12>;
Fr: IField<bigint>;
};
utils: {
randomPrivateKey: () => Uint8Array;
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
};
```
### abstract/hash-to-curve: Hashing strings to curve points
The module allows to hash arbitrary strings to elliptic curve points. Implements [hash-to-curve v16](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16).
Every curve has exported `hashToCurve` and `encodeToCurve` methods. You should always prefer `hashToCurve` for security:
```ts
import { hashToCurve, encodeToCurve } from '@noble/curves/secp256k1';
import { randomBytes } from '@noble/hashes/utils';
hashToCurve('0102abcd');
console.log(hashToCurve(randomBytes()));
console.log(encodeToCurve(randomBytes()));
import { bls12_381 } from '@noble/curves/bls12-381';
bls12_381.G1.hashToCurve(randomBytes(), { DST: 'another' });
bls12_381.G2.hashToCurve(randomBytes(), { DST: 'custom' });
```
If you need low-level methods from spec:
`expand_message_xmd` [(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1) produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
Hash must conform to `CHash` interface (see [weierstrass section](#abstractweierstrass-short-weierstrass-curve)).
```ts
function expand_message_xmd(
msg: Uint8Array,
DST: Uint8Array,
lenInBytes: number,
H: CHash
): Uint8Array;
function expand_message_xof(
msg: Uint8Array,
DST: Uint8Array,
lenInBytes: number,
k: number,
H: CHash
): Uint8Array;
```
`hash_to_field(msg, count, options)`
[(spec)](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3)
hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
```ts
/**
* * `DST` is a domain separation tag, defined in section 2.2.5
* * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m
* * `m` is extension degree (1 for prime fields)
* * `k` is the target security target in bits (e.g. 128), from section 5.1
* * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
* * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
*/
type UnicodeOrBytes = string | Uint8Array;
type Opts = {
DST: UnicodeOrBytes;
p: bigint;
m: number;
k: number;
expand?: 'xmd' | 'xof';
hash: CHash;
};
/**
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
* @param msg a byte string containing the message to hash
* @param count the number of elements of F to output
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
*/
function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][];
```
### abstract/poseidon: Poseidon hash
Implements [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash.
There are many poseidon variants with different constants.
We don't provide them: you should construct them manually.
Check out [micro-starknet](https://github.com/paulmillr/micro-starknet) package for a proper example.
```ts
import { poseidon } from '@noble/curves/abstract/poseidon';
type PoseidonOpts = {
Fp: Field<bigint>;
t: number;
roundsFull: number;
roundsPartial: number;
sboxPower?: number;
reversePartialPowIdx?: boolean;
mds: bigint[][];
roundConstants: bigint[][];
};
const instance = poseidon(opts: PoseidonOpts);
```
### abstract/modular: Modular arithmetics utilities
```ts
import * as mod from '@noble/curves/abstract/modular';
const fp = mod.Field(2n ** 255n - 19n); // Finite field over 2^255-19
fp.mul(591n, 932n); // multiplication
fp.pow(481n, 11024858120n); // exponentiation
fp.div(5n, 17n); // division: 5/17 mod 2^255-19 == 5 * invert(17)
fp.sqrt(21n); // square root
// Generic non-FP utils are also available
mod.mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
mod.invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
mod.invertBatch([1n, 2n, 4n], 21n); // => [1n, 11n, 16n] in one inversion
```
#### Creating private keys from hashes
Suppose you have `sha256(something)` (e.g. from HMAC) and you want to make a private key from it.
Even though p256 or secp256k1 may have 32-byte private keys,
and sha256 output is also 32-byte, you can't just use it and reduce it modulo `CURVE.n`.
Doing so will make the result key [biased](https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/).
To avoid the bias, we implement FIPS 186 B.4.1, which allows to take arbitrary
byte array and produce valid scalars / private keys with bias being neglible.
Use [hash-to-curve](#abstracthash-to-curve-hashing-strings-to-curve-points) if you need
hashing to **public keys**; the function in the module instead operates on **private keys**.
```ts
import { p256 } from '@noble/curves/p256';
import { sha256 } from '@noble/hashes/sha256';
import { hkdf } from '@noble/hashes/hkdf';
const someKey = new Uint8Array(32).fill(2); // Needs to actually be random, not .fill(2)
const derived = hkdf(sha256, someKey, undefined, 'application', 40); // 40 bytes
const validPrivateKey = mod.hashToPrivateScalar(derived, p256.CURVE.n);
```
### abstract/utils: General utilities
```ts
import * as utils from '@noble/curves/abstract/utils';
utils.bytesToHex(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
utils.hexToBytes('deadbeef');
utils.numberToHexUnpadded(123n);
utils.hexToNumber();
utils.bytesToNumberBE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
utils.bytesToNumberLE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
utils.numberToBytesBE(123n, 32);
utils.numberToBytesLE(123n, 64);
utils.concatBytes(Uint8Array.from([0xde, 0xad]), Uint8Array.from([0xbe, 0xef]));
utils.nLength(255n);
utils.equalBytes(Uint8Array.from([0xde]), Uint8Array.from([0xde]));
```
## Security
1. The library has been audited during Jan-Feb 2023 by an independent security firm [Trail of Bits](https://www.trailofbits.com):
[PDF](https://github.com/trailofbits/publications/blob/master/reviews/2023-01-ryanshea-noblecurveslibrary-securityreview.pdf).
The audit has been funded by Ryan Shea. Audit scope was abstract modules `curve`, `hash-to-curve`, `modular`, `poseidon`, `utils`, `weierstrass`, and top-level modules `_shortw_utils` and `secp256k1`. See [changes since audit](https://github.com/paulmillr/noble-curves/compare/0.7.3..main).
2. The library has been fuzzed by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz). You can run the fuzzer by yourself to check it.
3. [Timing attack](https://en.wikipedia.org/wiki/Timing_attack) considerations: _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to achieve in a scripting language. Which means _any other JS library can't have constant-timeness_. Even statically typed Rust, a language without GC, [makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security) for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
We consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading malware with every `npm install`. Our goal is to minimize this attack vector. As for devDependencies used by the library:
- `@scure` base, bip32, bip39 (used in tests), micro-bmark (benchmark), micro-should (testing) are developed by us
and follow the same practices such as: minimal library size, auditability, signed releases
- prettier (linter), fast-check (property-based testing),
typescript versions are locked and rarely updated. Every update is checked with `npm-diff`.
The packages are big, which makes it hard to audit their source code thoroughly and fully.
- They are only used if you clone the git repo and want to add some feature to it. End-users won't use them.
## Speed
Benchmark results on Apple M2 with node v19:
```
secp256k1
init x 58 ops/sec @ 17ms/op
getPublicKey x 5,640 ops/sec @ 177μs/op
sign x 4,471 ops/sec @ 223μs/op
verify x 780 ops/sec @ 1ms/op
getSharedSecret x 465 ops/sec @ 2ms/op
recoverPublicKey x 740 ops/sec @ 1ms/op
schnorr.sign x 597 ops/sec @ 1ms/op
schnorr.verify x 775 ops/sec @ 1ms/op
P256
init x 31 ops/sec @ 31ms/op
getPublicKey x 5,607 ops/sec @ 178μs/op
sign x 4,583 ops/sec @ 218μs/op
verify x 540 ops/sec @ 1ms/op
P384
init x 15 ops/sec @ 63ms/op
getPublicKey x 2,622 ops/sec @ 381μs/op
sign x 2,106 ops/sec @ 474μs/op
verify x 222 ops/sec @ 4ms/op
P521
init x 8 ops/sec @ 119ms/op
getPublicKey x 1,371 ops/sec @ 729μs/op
sign x 1,164 ops/sec @ 858μs/op
verify x 118 ops/sec @ 8ms/op
ed25519
init x 47 ops/sec @ 20ms/op
getPublicKey x 9,414 ops/sec @ 106μs/op
sign x 4,516 ops/sec @ 221μs/op
verify x 912 ops/sec @ 1ms/op
ed448
init x 17 ops/sec @ 56ms/op
getPublicKey x 3,363 ops/sec @ 297μs/op
sign x 1,615 ops/sec @ 619μs/op
verify x 319 ops/sec @ 3ms/op
ecdh
├─x25519 x 1,337 ops/sec @ 747μs/op
├─secp256k1 x 461 ops/sec @ 2ms/op
├─P256 x 441 ops/sec @ 2ms/op
├─P384 x 179 ops/sec @ 5ms/op
├─P521 x 93 ops/sec @ 10ms/op
└─x448 x 496 ops/sec @ 2ms/op
bls12-381
init x 32 ops/sec @ 30ms/op
getPublicKey 1-bit x 858 ops/sec @ 1ms/op
getPublicKey x 858 ops/sec @ 1ms/op
sign x 49 ops/sec @ 20ms/op
verify x 34 ops/sec @ 28ms/op
pairing x 94 ops/sec @ 10ms/op
aggregatePublicKeys/8 x 116 ops/sec @ 8ms/op
aggregatePublicKeys/32 x 31 ops/sec @ 31ms/op
aggregatePublicKeys/128 x 7 ops/sec @ 125ms/op
aggregateSignatures/8 x 45 ops/sec @ 22ms/op
aggregateSignatures/32 x 11 ops/sec @ 84ms/op
aggregateSignatures/128 x 3 ops/sec @ 332ms/opp
hash-to-curve
hash_to_field x 850,340 ops/sec @ 1μs/op
secp256k1 x 2,143 ops/sec @ 466μs/op
P256 x 3,861 ops/sec @ 258μs/op
P384 x 1,526 ops/sec @ 655μs/op
P521 x 748 ops/sec @ 1ms/op
ed25519 x 2,772 ops/sec @ 360μs/op
ed448 x 1,146 ops/sec @ 871μs/op
```
## Contributing & testing
1. Clone the repository
2. `npm install` to install build dependencies like TypeScript
3. `npm run build` to compile TypeScript code
4. `npm run test` will execute all main tests
## Upgrading
Previously, the library was split into single-feature packages
noble-secp256k1 and noble-ed25519. curves can be thought as a continuation of their
original work. The libraries now changed their direction towards providing
minimal 4kb implementations of cryptography and are not as feature-complete.
Upgrading from @noble/secp256k1 2.0 or @noble/ed25519 2.0: no changes, libraries are compatible.
Upgrading from [@noble/secp256k1](https://github.com/paulmillr/noble-secp256k1) 1.7:
- `getPublicKey`
- now produce 33-byte compressed signatures by default
- to use old behavior, which produced 65-byte uncompressed keys, set
argument `isCompressed` to `false`: `getPublicKey(priv, false)`
- `sign`
- is now sync; use `signAsync` for async version
- now returns `Signature` instance with `{ r, s, recovery }` properties
- `canonical` option was renamed to `lowS`
- `recovered` option has been removed because recovery bit is always returned now
- `der` option has been removed. There are 2 options:
1. Use compact encoding: `fromCompact`, `toCompactRawBytes`, `toCompactHex`.
Compact encoding is simply a concatenation of 32-byte r and 32-byte s.
2. If you must use DER encoding, switch to noble-curves (see above).
- `verify`
- `strict` option was renamed to `lowS`
- `getSharedSecret`
- now produce 33-byte compressed signatures by default
- to use old behavior, which produced 65-byte uncompressed keys, set
argument `isCompressed` to `false`: `getSharedSecret(a, b, false)`
- `recoverPublicKey(msg, sig, rec)` was changed to `sig.recoverPublicKey(msg)`
- `number` type for private keys have been removed: use `bigint` instead
- `Point` (2d xy) has been changed to `ProjectivePoint` (3d xyz)
- `utils` were split into `utils` (same api as in noble-curves) and
`etc` (`hmacSha256Sync` and others)
Upgrading from [@noble/ed25519](https://github.com/paulmillr/noble-ed25519) 1.7:
- Methods are now sync by default
- `bigint` is no longer allowed in `getPublicKey`, `sign`, `verify`. Reason: ed25519 is LE, can lead to bugs
- `Point` (2d xy) has been changed to `ExtendedPoint` (xyzt)
- `Signature` was removed: just use raw bytes or hex now
- `utils` were split into `utils` (same api as in noble-curves) and
`etc` (`sha512Sync` and others)
- `getSharedSecret` was moved to `x25519` module
Upgrading from [@noble/bls12-381](https://github.com/paulmillr/noble-bls12-381):
- Methods and classes were renamed:
- PointG1 -> G1.Point, PointG2 -> G2.Point
- PointG2.fromSignature -> Signature.decode, PointG2.toSignature -> Signature.encode
- Fp2 ORDER was corrected
## Resources
Useful articles about the library or its primitives:
- [Learning fast elliptic-curve cryptography](https://paulmillr.com/posts/noble-secp256k1-fast-ecc/)
- Pairings and BLS
- [BLS12-381 for the rest of us](https://hackmd.io/@benjaminion/bls12-381)
- [Key concepts of pairings](https://medium.com/@alonmuroch_65570/bls-signatures-part-2-key-concepts-of-pairings-27a8a9533d0c)
- Pairing over bls12-381:
[part 1](https://research.nccgroup.com/2020/07/06/pairing-over-bls12-381-part-1-fields/),
[part 2](https://research.nccgroup.com/2020/07/13/pairing-over-bls12-381-part-2-curves/),
[part 3](https://research.nccgroup.com/2020/08/13/pairing-over-bls12-381-part-3-pairing/)
- [Estimating the bit security of pairing-friendly curves](https://research.nccgroup.com/2022/02/03/estimating-the-bit-security-of-pairing-friendly-curves/)
Real-world software that uses curves:
- [Elliptic Curve Calculator](https://paulmillr.com/noble) online demo: add / multiply points, sign messages
- Signers for web3 projects:
[btc-signer](https://github.com/paulmillr/scure-btc-signer), [eth-signer](https://github.com/paulmillr/micro-eth-signer),
[sol-signer](https://github.com/paulmillr/micro-sol-signer) for Solana
- [scure-bip32](https://github.com/paulmillr/scure-bip32) and separate [bip32](https://github.com/bitcoinjs/bip32) HDkey libraries
- [ed25519-keygen](https://github.com/paulmillr/ed25519-keygen) SSH, PGP, TOR key generation
- [micro-starknet](https://github.com/paulmillr/micro-starknet) stark-friendly elliptic curve algorithms.
- BLS threshold sigs demo [genthresh.com](https://genthresh.com)
- BLS BBS signatures [github.com/Wind4Greg/BBS-Draft-Checks](https://github.com/Wind4Greg/BBS-Draft-Checks) following [draft-irtf-cfrg-bbs-signatures-latest](https://identity.foundation/bbs-signature/draft-irtf-cfrg-bbs-signatures.html)
- [KZG trusted setup ceremony](https://github.com/dsrvlabs/czg-keremony)
## License
The MIT License (MIT)
Copyright (c) 2022 Paul Miller [(https://paulmillr.com)](https://paulmillr.com)
See LICENSE file.
-62
View File
@@ -1,62 +0,0 @@
import { randomBytes } from '@noble/hashes/utils';
import { CurveType } from './abstract/weierstrass.js';
import { CHash } from './abstract/utils.js';
export declare function getHash(hash: CHash): {
hash: CHash;
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => Uint8Array;
randomBytes: typeof randomBytes;
};
type CurveDef = Readonly<Omit<CurveType, 'hash' | 'hmac' | 'randomBytes'>>;
export declare function createCurve(curveDef: CurveDef, defHash: CHash): Readonly<{
create: (hash: CHash) => import("./abstract/weierstrass.js").CurveFn;
CURVE: Readonly<{
readonly nBitLength: number;
readonly nByteLength: number;
readonly Fp: import("./abstract/modular.js").IField<bigint>;
readonly n: bigint;
readonly h: bigint;
readonly hEff?: bigint | undefined;
readonly Gx: bigint;
readonly Gy: bigint;
readonly allowInfinityPoint?: boolean | undefined;
readonly a: bigint;
readonly b: bigint;
readonly allowedPrivateKeyLengths?: readonly number[] | undefined;
readonly wrapPrivateKey?: boolean | undefined;
readonly endo?: {
beta: bigint;
splitScalar: (k: bigint) => {
k1neg: boolean;
k1: bigint;
k2neg: boolean;
k2: bigint;
};
} | undefined;
readonly isTorsionFree?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => boolean) | undefined;
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined;
readonly hash: CHash;
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array;
lowS: boolean;
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined;
readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined;
readonly p: bigint;
}>;
getPublicKey: (privateKey: import("./abstract/utils.js").PrivKey, isCompressed?: boolean | undefined) => Uint8Array;
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/utils.js").Hex, isCompressed?: boolean | undefined) => Uint8Array;
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: import("./abstract/weierstrass.js").SignOpts | undefined) => import("./abstract/weierstrass.js").SignatureType;
verify: (signature: import("./abstract/utils.js").Hex | {
r: bigint;
s: bigint;
}, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/utils.js").Hex, opts?: import("./abstract/weierstrass.js").VerOpts | undefined) => boolean;
ProjectivePoint: import("./abstract/weierstrass.js").ProjConstructor<bigint>;
Signature: import("./abstract/weierstrass.js").SignatureConstructor;
utils: {
normPrivateKeyToScalar: (key: import("./abstract/utils.js").PrivKey) => bigint;
isValidPrivateKey(privateKey: import("./abstract/utils.js").PrivKey): boolean;
randomPrivateKey: () => Uint8Array;
precompute: (windowSize?: number | undefined, point?: import("./abstract/weierstrass.js").ProjPointType<bigint> | undefined) => import("./abstract/weierstrass.js").ProjPointType<bigint>;
};
}>;
export {};
//# sourceMappingURL=_shortw_utils.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"_shortw_utils.d.ts","sourceRoot":"","sources":["src/_shortw_utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAe,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAG5C,wBAAgB,OAAO,CAAC,IAAI,EAAE,KAAK;;gBAGnB,UAAU,WAAW,UAAU,EAAE;;EAGhD;AAED,KAAK,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC;AAC3E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK;mBACtC,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAE5B"}
-22
View File
@@ -1,22 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createCurve = exports.getHash = void 0;
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
const hmac_1 = require("@noble/hashes/hmac");
const utils_1 = require("@noble/hashes/utils");
const weierstrass_js_1 = require("./abstract/weierstrass.js");
// connects noble-curves to noble-hashes
function getHash(hash) {
return {
hash,
hmac: (key, ...msgs) => (0, hmac_1.hmac)(hash, key, (0, utils_1.concatBytes)(...msgs)),
randomBytes: utils_1.randomBytes,
};
}
exports.getHash = getHash;
function createCurve(curveDef, defHash) {
const create = (hash) => (0, weierstrass_js_1.weierstrass)({ ...curveDef, ...getHash(hash) });
return Object.freeze({ ...create(defHash), create });
}
exports.createCurve = createCurve;
//# sourceMappingURL=_shortw_utils.js.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"_shortw_utils.js","sourceRoot":"","sources":["src/_shortw_utils.ts"],"names":[],"mappings":";;;AAAA,sEAAsE;AACtE,6CAA0C;AAC1C,+CAA+D;AAC/D,8DAAmE;AAGnE,wCAAwC;AACxC,SAAgB,OAAO,CAAC,IAAW;IACjC,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,CAAC,GAAe,EAAE,GAAG,IAAkB,EAAE,EAAE,CAAC,IAAA,WAAI,EAAC,IAAI,EAAE,GAAG,EAAE,IAAA,mBAAW,EAAC,GAAG,IAAI,CAAC,CAAC;QACvF,WAAW,EAAX,mBAAW;KACZ,CAAC;AACJ,CAAC;AAND,0BAMC;AAGD,SAAgB,WAAW,CAAC,QAAkB,EAAE,OAAc;IAC5D,MAAM,MAAM,GAAG,CAAC,IAAW,EAAE,EAAE,CAAC,IAAA,4BAAW,EAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/E,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AACvD,CAAC;AAHD,kCAGC"}
-103
View File
@@ -1,103 +0,0 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
/**
* BLS (Barreto-Lynn-Scott) family of pairing-friendly curves.
* Implements BLS (Boneh-Lynn-Shacham) signatures.
* Consists of two curves: G1 and G2:
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4.
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1
* - Gt, created by bilinear (ate) pairing e(G1, G2), consists of p-th roots of unity in
* Fq^k where k is embedding degree. Only degree 12 is currently supported, 24 is not.
* Pairing is used to aggregate and verify signatures.
* We are using Fp for private keys (shorter) and Fp₂ for signatures (longer).
* Some projects may prefer to swap this relation, it is not supported for now.
*/
import { AffinePoint } from './curve.js';
import { IField } from './modular.js';
import { Hex, PrivKey, CHash } from './utils.js';
import * as htf from './hash-to-curve.js';
import { CurvePointsType, ProjPointType as ProjPointType, CurvePointsRes } from './weierstrass.js';
type Fp = bigint;
export type SignatureCoder<Fp2> = {
fromHex(hex: Hex): ProjPointType<Fp2>;
toRawBytes(point: ProjPointType<Fp2>): Uint8Array;
toHex(point: ProjPointType<Fp2>): string;
};
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
G1: Omit<CurvePointsType<Fp>, 'n'> & {
mapToCurve: htf.MapToCurve<Fp>;
htfDefaults: htf.Opts;
};
G2: Omit<CurvePointsType<Fp2>, 'n'> & {
Signature: SignatureCoder<Fp2>;
mapToCurve: htf.MapToCurve<Fp2>;
htfDefaults: htf.Opts;
};
fields: {
Fp: IField<Fp>;
Fr: IField<bigint>;
Fp2: IField<Fp2> & {
reim: (num: Fp2) => {
re: bigint;
im: bigint;
};
multiplyByB: (num: Fp2) => Fp2;
frobeniusMap(num: Fp2, power: number): Fp2;
};
Fp6: IField<Fp6>;
Fp12: IField<Fp12> & {
frobeniusMap(num: Fp12, power: number): Fp12;
multiplyBy014(num: Fp12, o0: Fp2, o1: Fp2, o4: Fp2): Fp12;
conjugate(num: Fp12): Fp12;
finalExponentiate(num: Fp12): Fp12;
};
};
params: {
x: bigint;
r: bigint;
};
htfDefaults: htf.Opts;
hash: CHash;
randomBytes: (bytesLength?: number) => Uint8Array;
};
export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
getPublicKey: (privateKey: PrivKey) => Uint8Array;
sign: {
(message: Hex, privateKey: PrivKey): Uint8Array;
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
};
verify: (signature: Hex | ProjPointType<Fp2>, message: Hex | ProjPointType<Fp2>, publicKey: Hex | ProjPointType<Fp>) => boolean;
verifyBatch: (signature: Hex | ProjPointType<Fp2>, messages: (Hex | ProjPointType<Fp2>)[], publicKeys: (Hex | ProjPointType<Fp>)[]) => boolean;
aggregatePublicKeys: {
(publicKeys: Hex[]): Uint8Array;
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
};
aggregateSignatures: {
(signatures: Hex[]): Uint8Array;
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
};
millerLoop: (ell: [Fp2, Fp2, Fp2][], g1: [Fp, Fp]) => Fp12;
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
G1: CurvePointsRes<Fp> & ReturnType<typeof htf.createHasher<Fp>>;
G2: CurvePointsRes<Fp2> & ReturnType<typeof htf.createHasher<Fp2>>;
Signature: SignatureCoder<Fp2>;
params: {
x: bigint;
r: bigint;
G1b: bigint;
G2b: Fp2;
};
fields: {
Fp: IField<Fp>;
Fp2: IField<Fp2>;
Fp6: IField<Fp6>;
Fp12: IField<Fp12>;
Fr: IField<bigint>;
};
utils: {
randomPrivateKey: () => Uint8Array;
calcPairingPrecomputes: (p: AffinePoint<Fp2>) => [Fp2, Fp2, Fp2][];
};
};
export declare function bls<Fp2, Fp6, Fp12>(CURVE: CurveType<Fp, Fp2, Fp6, Fp12>): CurveFn<Fp, Fp2, Fp6, Fp12>;
export {};
//# sourceMappingURL=bls.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"bls.d.ts","sourceRoot":"","sources":["../src/abstract/bls.ts"],"names":[],"mappings":"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"}
-239
View File
@@ -1,239 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.bls = void 0;
const modular_js_1 = require("./modular.js");
const utils_js_1 = require("./utils.js");
const htf = require("./hash-to-curve.js");
const weierstrass_js_1 = require("./weierstrass.js");
// prettier-ignore
const _2n = BigInt(2), _3n = BigInt(3);
function bls(CURVE) {
// Fields are specific for curve, so for now we'll need to pass them with opts
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE.fields;
const BLS_X_LEN = (0, utils_js_1.bitLen)(CURVE.params.x);
const groupLen = 32; // TODO: calculate; hardcoded for now
// Pre-compute coefficients for sparse multiplication
// Point addition and point double calculations is reused for coefficients
function calcPairingPrecomputes(p) {
const { x, y } = p;
// prettier-ignore
const Qx = x, Qy = y, Qz = Fp2.ONE;
// prettier-ignore
let Rx = Qx, Ry = Qy, Rz = Qz;
let ell_coeff = [];
for (let i = BLS_X_LEN - 2; i >= 0; i--) {
// Double
let t0 = Fp2.sqr(Ry); // Ry²
let t1 = Fp2.sqr(Rz); // Rz²
let t2 = Fp2.multiplyByB(Fp2.mul(t1, _3n)); // 3 * T1 * B
let t3 = Fp2.mul(t2, _3n); // 3 * T2
let t4 = Fp2.sub(Fp2.sub(Fp2.sqr(Fp2.add(Ry, Rz)), t1), t0); // (Ry + Rz)² - T1 - T0
ell_coeff.push([
Fp2.sub(t2, t0),
Fp2.mul(Fp2.sqr(Rx), _3n),
Fp2.neg(t4), // -T4
]);
Rx = Fp2.div(Fp2.mul(Fp2.mul(Fp2.sub(t0, t3), Rx), Ry), _2n); // ((T0 - T3) * Rx * Ry) / 2
Ry = Fp2.sub(Fp2.sqr(Fp2.div(Fp2.add(t0, t3), _2n)), Fp2.mul(Fp2.sqr(t2), _3n)); // ((T0 + T3) / 2)² - 3 * T2²
Rz = Fp2.mul(t0, t4); // T0 * T4
if ((0, utils_js_1.bitGet)(CURVE.params.x, i)) {
// Addition
let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz
let t1 = Fp2.sub(Rx, Fp2.mul(Qx, Rz)); // Rx - Qx * Rz
ell_coeff.push([
Fp2.sub(Fp2.mul(t0, Qx), Fp2.mul(t1, Qy)),
Fp2.neg(t0),
t1, // T1
]);
let t2 = Fp2.sqr(t1); // T1²
let t3 = Fp2.mul(t2, t1); // T2 * T1
let t4 = Fp2.mul(t2, Rx); // T2 * Rx
let t5 = Fp2.add(Fp2.sub(t3, Fp2.mul(t4, _2n)), Fp2.mul(Fp2.sqr(t0), Rz)); // T3 - 2 * T4 + T0² * Rz
Rx = Fp2.mul(t1, t5); // T1 * T5
Ry = Fp2.sub(Fp2.mul(Fp2.sub(t4, t5), t0), Fp2.mul(t3, Ry)); // (T4 - T5) * T0 - T3 * Ry
Rz = Fp2.mul(Rz, t3); // Rz * T3
}
}
return ell_coeff;
}
function millerLoop(ell, g1) {
const { x } = CURVE.params;
const Px = g1[0];
const Py = g1[1];
let f12 = Fp12.ONE;
for (let j = 0, i = BLS_X_LEN - 2; i >= 0; i--, j++) {
const E = ell[j];
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py));
if ((0, utils_js_1.bitGet)(x, i)) {
j += 1;
const F = ell[j];
f12 = Fp12.multiplyBy014(f12, F[0], Fp2.mul(F[1], Px), Fp2.mul(F[2], Py));
}
if (i !== 0)
f12 = Fp12.sqr(f12);
}
return Fp12.conjugate(f12);
}
const utils = {
randomPrivateKey: () => {
return Fr.toBytes((0, modular_js_1.hashToPrivateScalar)(CURVE.randomBytes(groupLen + 8), CURVE.params.r));
},
calcPairingPrecomputes,
};
// Point on G1 curve: (x, y)
const G1_ = (0, weierstrass_js_1.weierstrassPoints)({ n: Fr.ORDER, ...CURVE.G1 });
const G1 = Object.assign(G1_, htf.createHasher(G1_.ProjectivePoint, CURVE.G1.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G1.htfDefaults,
}));
function pairingPrecomputes(point) {
const p = point;
if (p._PPRECOMPUTES)
return p._PPRECOMPUTES;
p._PPRECOMPUTES = calcPairingPrecomputes(point.toAffine());
return p._PPRECOMPUTES;
}
// TODO: export
// function clearPairingPrecomputes(point: G2) {
// const p = point as G2 & withPairingPrecomputes;
// p._PPRECOMPUTES = undefined;
// }
// Point on G2 curve (complex numbers): (x₁, x₂+i), (y₁, y₂+i)
const G2_ = (0, weierstrass_js_1.weierstrassPoints)({ n: Fr.ORDER, ...CURVE.G2 });
const G2 = Object.assign(G2_, htf.createHasher(G2_.ProjectivePoint, CURVE.G2.mapToCurve, {
...CURVE.htfDefaults,
...CURVE.G2.htfDefaults,
}));
const { Signature } = CURVE.G2;
// Calculates bilinear pairing
function pairing(Q, P, withFinalExponent = true) {
if (Q.equals(G1.ProjectivePoint.ZERO) || P.equals(G2.ProjectivePoint.ZERO))
throw new Error('pairing is not available for ZERO point');
Q.assertValidity();
P.assertValidity();
// Performance: 9ms for millerLoop and ~14ms for exp.
const Qa = Q.toAffine();
const looped = millerLoop(pairingPrecomputes(P), [Qa.x, Qa.y]);
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
}
function normP1(point) {
return point instanceof G1.ProjectivePoint ? point : G1.ProjectivePoint.fromHex(point);
}
function normP2(point) {
return point instanceof G2.ProjectivePoint ? point : Signature.fromHex(point);
}
function normP2Hash(point, htfOpts) {
return point instanceof G2.ProjectivePoint
? point
: G2.hashToCurve((0, utils_js_1.ensureBytes)('point', point), htfOpts);
}
// Multiplies generator by private key.
// P = pk x G
function getPublicKey(privateKey) {
return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
}
function sign(message, privateKey, htfOpts) {
const msgPoint = normP2Hash(message, htfOpts);
msgPoint.assertValidity();
const sigPoint = msgPoint.multiply(G1.normPrivateKeyToScalar(privateKey));
if (message instanceof G2.ProjectivePoint)
return sigPoint;
return Signature.toRawBytes(sigPoint);
}
// Checks if pairing of public key & hash is equal to pairing of generator & signature.
// e(P, H(m)) == e(G, S)
function verify(signature, message, publicKey, htfOpts) {
const P = normP1(publicKey);
const Hm = normP2Hash(message, htfOpts);
const G = G1.ProjectivePoint.BASE;
const S = normP2(signature);
// Instead of doing 2 exponentiations, we use property of billinear maps
// and do one exp after multiplying 2 points.
const ePHm = pairing(P.negate(), Hm, false);
const eGS = pairing(G, S, false);
const exp = Fp12.finalExponentiate(Fp12.mul(eGS, ePHm));
return Fp12.eql(exp, Fp12.ONE);
}
function aggregatePublicKeys(publicKeys) {
if (!publicKeys.length)
throw new Error('Expected non-empty array');
const agg = publicKeys.map(normP1).reduce((sum, p) => sum.add(p), G1.ProjectivePoint.ZERO);
const aggAffine = agg; //.toAffine();
if (publicKeys[0] instanceof G1.ProjectivePoint) {
aggAffine.assertValidity();
return aggAffine;
}
// toRawBytes ensures point validity
return aggAffine.toRawBytes(true);
}
function aggregateSignatures(signatures) {
if (!signatures.length)
throw new Error('Expected non-empty array');
const agg = signatures.map(normP2).reduce((sum, s) => sum.add(s), G2.ProjectivePoint.ZERO);
const aggAffine = agg; //.toAffine();
if (signatures[0] instanceof G2.ProjectivePoint) {
aggAffine.assertValidity();
return aggAffine;
}
return Signature.toRawBytes(aggAffine);
}
// https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
// e(G, S) = e(G, SUM(n)(Si)) = MUL(n)(e(G, Si))
function verifyBatch(signature, messages, publicKeys, htfOpts) {
// @ts-ignore
// console.log('verifyBatch', bytesToHex(signature as any), messages, publicKeys.map(bytesToHex));
if (!messages.length)
throw new Error('Expected non-empty messages array');
if (publicKeys.length !== messages.length)
throw new Error('Pubkey count should equal msg count');
const sig = normP2(signature);
const nMessages = messages.map((i) => normP2Hash(i, htfOpts));
const nPublicKeys = publicKeys.map(normP1);
try {
const paired = [];
for (const message of new Set(nMessages)) {
const groupPublicKey = nMessages.reduce((groupPublicKey, subMessage, i) => subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey, G1.ProjectivePoint.ZERO);
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
// Possible to batch pairing for same msg with different groupPublicKey here
paired.push(pairing(groupPublicKey, message, false));
}
paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
const exp = Fp12.finalExponentiate(product);
return Fp12.eql(exp, Fp12.ONE);
}
catch {
return false;
}
}
G1.ProjectivePoint.BASE._setWindowSize(4);
return {
getPublicKey,
sign,
verify,
verifyBatch,
aggregatePublicKeys,
aggregateSignatures,
millerLoop,
pairing,
G1,
G2,
Signature,
fields: {
Fr,
Fp,
Fp2,
Fp6,
Fp12,
},
params: {
x: CURVE.params.x,
r: CURVE.params.r,
G1b: CURVE.G1.b,
G2b: CURVE.G2.b,
},
utils,
};
}
exports.bls = bls;
//# sourceMappingURL=bls.js.map
File diff suppressed because one or more lines are too long
-70
View File
@@ -1,70 +0,0 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { IField } from './modular.js';
export type AffinePoint<T> = {
x: T;
y: T;
} & {
z?: never;
t?: never;
};
export interface Group<T extends Group<T>> {
double(): T;
negate(): T;
add(other: T): T;
subtract(other: T): T;
equals(other: T): boolean;
multiply(scalar: bigint): T;
}
export type GroupConstructor<T> = {
BASE: T;
ZERO: T;
};
export type Mapper<T> = (i: T[]) => T[];
export declare function wNAF<T extends Group<T>>(c: GroupConstructor<T>, bits: number): {
constTimeNegate: (condition: boolean, item: T) => T;
unsafeLadder(elm: T, n: bigint): T;
/**
* Creates a wNAF precomputation window. Used for caching.
* Default window size is set by `utils.precompute()` and is equal to 8.
* Number of precomputed points depends on the curve size:
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
* - 𝑊 is the window size
* - 𝑛 is the bitlength of the curve order.
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
* @returns precomputed point tables flattened to a single array
*/
precomputeWindow(elm: T, W: number): Group<T>[];
/**
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
* @param W window size
* @param precomputes precomputed tables
* @param n scalar (we don't check here, but should be less than curve order)
* @returns real and fake (for const-time) points
*/
wNAF(W: number, precomputes: T[], n: bigint): {
p: T;
f: T;
};
wNAFCached(P: T, precomputesMap: Map<T, T[]>, n: bigint, transform: Mapper<T>): {
p: T;
f: T;
};
};
export type BasicCurve<T> = {
Fp: IField<T>;
n: bigint;
nBitLength?: number;
nByteLength?: number;
h: bigint;
hEff?: bigint;
Gx: T;
Gy: T;
allowInfinityPoint?: boolean;
};
export declare function validateBasic<FP, T>(curve: BasicCurve<FP> & T): Readonly<{
readonly nBitLength: number;
readonly nByteLength: number;
} & BasicCurve<FP> & T & {
p: bigint;
}>;
//# sourceMappingURL=curve.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"curve.d.ts","sourceRoot":"","sources":["../src/abstract/curve.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EAAE,MAAM,EAA0B,MAAM,cAAc,CAAC;AAK9D,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,CAAC,EAAE,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,CAAC;CACN,GAAG;IAAE,CAAC,CAAC,EAAE,KAAK,CAAC;IAAC,CAAC,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAE7B,MAAM,WAAW,KAAK,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC;IACvC,MAAM,IAAI,CAAC,CAAC;IACZ,MAAM,IAAI,CAAC,CAAC;IACZ,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IACjB,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;IACtB,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC;CAC7B;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAChC,IAAI,EAAE,CAAC,CAAC;IACR,IAAI,EAAE,CAAC,CAAC;CACT,CAAC;AACF,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;AAaxC,wBAAgB,IAAI,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM;iCACvC,OAAO,QAAQ,CAAC,KAAG,CAAC;sBAYpC,CAAC,KAAK,MAAM;IAW9B;;;;;;;;;OASG;0BACmB,CAAC,KAAK,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE;IAkB/C;;;;;;OAMG;YACK,MAAM,eAAe,CAAC,EAAE,KAAK,MAAM;WAAQ,CAAC;WAAK,CAAC;;kBAsD5C,CAAC,kBAAkB,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,aAAa,OAAO,CAAC,CAAC,GAAG;QAAE,CAAC,EAAE,CAAC,CAAC;QAAC,CAAC,EAAE,CAAC,CAAA;KAAE;EAcjG;AAID,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI;IAC1B,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,CAAC,CAAC;IACN,EAAE,EAAE,CAAC,CAAC;IACN,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,wBAAgB,aAAa,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,CAAC,GAAG,CAAC;;;;;GAqB7D"}
-161
View File
@@ -1,161 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateBasic = exports.wNAF = void 0;
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Abelian group utilities
const modular_js_1 = require("./modular.js");
const utils_js_1 = require("./utils.js");
const _0n = BigInt(0);
const _1n = BigInt(1);
// Elliptic curve multiplication of Point by scalar. Fragile.
// Scalars should always be less than curve order: this should be checked inside of a curve itself.
// Creates precomputation tables for fast multiplication:
// - private scalar is split by fixed size windows of W bits
// - every window point is collected from window's table & added to accumulator
// - since windows are different, same point inside tables won't be accessed more than once per calc
// - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar)
// - +1 window is neccessary for wNAF
// - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication
// TODO: Research returning 2d JS array of windows, instead of a single window. This would allow
// windows to be in different memory locations
function wNAF(c, bits) {
const constTimeNegate = (condition, item) => {
const neg = item.negate();
return condition ? neg : item;
};
const opts = (W) => {
const windows = Math.ceil(bits / W) + 1; // +1, because
const windowSize = 2 ** (W - 1); // -1 because we skip zero
return { windows, windowSize };
};
return {
constTimeNegate,
// non-const time multiplication ladder
unsafeLadder(elm, n) {
let p = c.ZERO;
let d = elm;
while (n > _0n) {
if (n & _1n)
p = p.add(d);
d = d.double();
n >>= _1n;
}
return p;
},
/**
* Creates a wNAF precomputation window. Used for caching.
* Default window size is set by `utils.precompute()` and is equal to 8.
* Number of precomputed points depends on the curve size:
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
* - 𝑊 is the window size
* - 𝑛 is the bitlength of the curve order.
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
* @returns precomputed point tables flattened to a single array
*/
precomputeWindow(elm, W) {
const { windows, windowSize } = opts(W);
const points = [];
let p = elm;
let base = p;
for (let window = 0; window < windows; window++) {
base = p;
points.push(base);
// =1, because we skip zero
for (let i = 1; i < windowSize; i++) {
base = base.add(p);
points.push(base);
}
p = base.double();
}
return points;
},
/**
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
* @param W window size
* @param precomputes precomputed tables
* @param n scalar (we don't check here, but should be less than curve order)
* @returns real and fake (for const-time) points
*/
wNAF(W, precomputes, n) {
// TODO: maybe check that scalar is less than group order? wNAF behavious is undefined otherwise
// But need to carefully remove other checks before wNAF. ORDER == bits here
const { windows, windowSize } = opts(W);
let p = c.ZERO;
let f = c.BASE;
const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc.
const maxNumber = 2 ** W;
const shiftBy = BigInt(W);
for (let window = 0; window < windows; window++) {
const offset = window * windowSize;
// Extract W bits.
let wbits = Number(n & mask);
// Shift number by W bits.
n >>= shiftBy;
// If the bits are bigger than max size, we'll split those.
// +224 => 256 - 32
if (wbits > windowSize) {
wbits -= maxNumber;
n += _1n;
}
// This code was first written with assumption that 'f' and 'p' will never be infinity point:
// since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
// there is negate now: it is possible that negated element from low value
// would be the same as high element, which will create carry into next window.
// It's not obvious how this can fail, but still worth investigating later.
// Check if we're onto Zero point.
// Add random point inside current window to f.
const offset1 = offset;
const offset2 = offset + Math.abs(wbits) - 1; // -1 because we skip zero
const cond1 = window % 2 !== 0;
const cond2 = wbits < 0;
if (wbits === 0) {
// The most important part for const-time getPublicKey
f = f.add(constTimeNegate(cond1, precomputes[offset1]));
}
else {
p = p.add(constTimeNegate(cond2, precomputes[offset2]));
}
}
// JIT-compiler should not eliminate f here, since it will later be used in normalizeZ()
// Even if the variable is still unused, there are some checks which will
// throw an exception, so compiler needs to prove they won't happen, which is hard.
// At this point there is a way to F be infinity-point even if p is not,
// which makes it less const-time: around 1 bigint multiply.
return { p, f };
},
wNAFCached(P, precomputesMap, n, transform) {
// @ts-ignore
const W = P._WINDOW_SIZE || 1;
// Calculate precomputes on a first run, reuse them after
let comp = precomputesMap.get(P);
if (!comp) {
comp = this.precomputeWindow(P, W);
if (W !== 1) {
precomputesMap.set(P, transform(comp));
}
}
return this.wNAF(W, comp, n);
},
};
}
exports.wNAF = wNAF;
function validateBasic(curve) {
(0, modular_js_1.validateField)(curve.Fp);
(0, utils_js_1.validateObject)(curve, {
n: 'bigint',
h: 'bigint',
Gx: 'field',
Gy: 'field',
}, {
nBitLength: 'isSafeInteger',
nByteLength: 'isSafeInteger',
});
// Set defaults
return Object.freeze({
...(0, modular_js_1.nLength)(curve.n, curve.nBitLength),
...curve,
...{ p: curve.Fp.ORDER },
});
}
exports.validateBasic = validateBasic;
//# sourceMappingURL=curve.js.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"curve.js","sourceRoot":"","sources":["../src/abstract/curve.ts"],"names":[],"mappings":"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"}
-84
View File
@@ -1,84 +0,0 @@
import * as ut from './utils.js';
import { FHash, Hex } from './utils.js';
import { Group, GroupConstructor, BasicCurve, AffinePoint } from './curve.js';
export type CurveType = BasicCurve<bigint> & {
a: bigint;
d: bigint;
hash: FHash;
randomBytes: (bytesLength?: number) => Uint8Array;
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
uvRatio?: (u: bigint, v: bigint) => {
isValid: boolean;
value: bigint;
};
prehash?: FHash;
mapToCurve?: (scalar: bigint[]) => AffinePoint<bigint>;
};
declare function validateOpts(curve: CurveType): Readonly<{
readonly nBitLength: number;
readonly nByteLength: number;
readonly Fp: import("./modular.js").IField<bigint>;
readonly n: bigint;
readonly h: bigint;
readonly hEff?: bigint | undefined;
readonly Gx: bigint;
readonly Gy: bigint;
readonly allowInfinityPoint?: boolean | undefined;
readonly a: bigint;
readonly d: bigint;
readonly hash: ut.FHash;
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array;
readonly adjustScalarBytes?: ((bytes: Uint8Array) => Uint8Array) | undefined;
readonly domain?: ((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array) | undefined;
readonly uvRatio?: ((u: bigint, v: bigint) => {
isValid: boolean;
value: bigint;
}) | undefined;
readonly prehash?: ut.FHash | undefined;
readonly mapToCurve?: ((scalar: bigint[]) => AffinePoint<bigint>) | undefined;
readonly p: bigint;
}>;
export interface ExtPointType extends Group<ExtPointType> {
readonly ex: bigint;
readonly ey: bigint;
readonly ez: bigint;
readonly et: bigint;
get x(): bigint;
get y(): bigint;
assertValidity(): void;
multiply(scalar: bigint): ExtPointType;
multiplyUnsafe(scalar: bigint): ExtPointType;
isSmallOrder(): boolean;
isTorsionFree(): boolean;
clearCofactor(): ExtPointType;
toAffine(iz?: bigint): AffinePoint<bigint>;
toRawBytes(isCompressed?: boolean): Uint8Array;
toHex(isCompressed?: boolean): string;
}
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
fromAffine(p: AffinePoint<bigint>): ExtPointType;
fromHex(hex: Hex): ExtPointType;
fromPrivateKey(privateKey: Hex): ExtPointType;
}
export type CurveFn = {
CURVE: ReturnType<typeof validateOpts>;
getPublicKey: (privateKey: Hex) => Uint8Array;
sign: (message: Hex, privateKey: Hex) => Uint8Array;
verify: (sig: Hex, message: Hex, publicKey: Hex) => boolean;
ExtendedPoint: ExtPointConstructor;
utils: {
randomPrivateKey: () => Uint8Array;
getExtendedPublicKey: (key: Hex) => {
head: Uint8Array;
prefix: Uint8Array;
scalar: bigint;
point: ExtPointType;
pointBytes: Uint8Array;
};
};
};
export declare function twistedEdwards(curveDef: CurveType): CurveFn;
export {};
//# sourceMappingURL=edwards.d.ts.map
@@ -1 +0,0 @@
{"version":3,"file":"edwards.d.ts","sourceRoot":"","sources":["../src/abstract/edwards.ts"],"names":[],"mappings":"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"}
-424
View File
@@ -1,424 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.twistedEdwards = void 0;
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
const modular_js_1 = require("./modular.js");
const ut = require("./utils.js");
const utils_js_1 = require("./utils.js");
const curve_js_1 = require("./curve.js");
// Be friendly to bad ECMAScript parsers by not using bigint literals
// prettier-ignore
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
// verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
const VERIFY_DEFAULT = { zip215: true };
function validateOpts(curve) {
const opts = (0, curve_js_1.validateBasic)(curve);
ut.validateObject(curve, {
hash: 'function',
a: 'bigint',
d: 'bigint',
randomBytes: 'function',
}, {
adjustScalarBytes: 'function',
domain: 'function',
uvRatio: 'function',
mapToCurve: 'function',
});
// Set defaults
return Object.freeze({ ...opts });
}
// It is not generic twisted curve for now, but ed25519/ed448 generic implementation
function twistedEdwards(curveDef) {
const CURVE = validateOpts(curveDef);
const { Fp, n: CURVE_ORDER, prehash: prehash, hash: cHash, randomBytes, nByteLength, h: cofactor, } = CURVE;
const MASK = _2n ** BigInt(nByteLength * 8);
const modP = Fp.create; // Function overrides
// sqrt(u/v)
const uvRatio = CURVE.uvRatio ||
((u, v) => {
try {
return { isValid: true, value: Fp.sqrt(u * Fp.inv(v)) };
}
catch (e) {
return { isValid: false, value: _0n };
}
});
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes); // NOOP
const domain = CURVE.domain ||
((data, ctx, phflag) => {
if (ctx.length || phflag)
throw new Error('Contexts/pre-hash are not supported');
return data;
}); // NOOP
const inBig = (n) => typeof n === 'bigint' && _0n < n; // n in [1..]
const inRange = (n, max) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
const in0MaskRange = (n) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
function assertInRange(n, max) {
// n in [1..max-1]
if (inRange(n, max))
return n;
throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`);
}
function assertGE0(n) {
// n in [0..CURVE_ORDER-1]
return n === _0n ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
}
const pointPrecomputes = new Map();
function isPoint(other) {
if (!(other instanceof Point))
throw new Error('ExtendedPoint expected');
}
// Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
// https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
class Point {
constructor(ex, ey, ez, et) {
this.ex = ex;
this.ey = ey;
this.ez = ez;
this.et = et;
if (!in0MaskRange(ex))
throw new Error('x required');
if (!in0MaskRange(ey))
throw new Error('y required');
if (!in0MaskRange(ez))
throw new Error('z required');
if (!in0MaskRange(et))
throw new Error('t required');
}
get x() {
return this.toAffine().x;
}
get y() {
return this.toAffine().y;
}
static fromAffine(p) {
if (p instanceof Point)
throw new Error('extended point not allowed');
const { x, y } = p || {};
if (!in0MaskRange(x) || !in0MaskRange(y))
throw new Error('invalid affine point');
return new Point(x, y, _1n, modP(x * y));
}
static normalizeZ(points) {
const toInv = Fp.invertBatch(points.map((p) => p.ez));
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
}
// "Private method", don't use it directly
_setWindowSize(windowSize) {
this._WINDOW_SIZE = windowSize;
pointPrecomputes.delete(this);
}
// Not required for fromHex(), which always creates valid points.
// Could be useful for fromAffine().
assertValidity() {
const { a, d } = CURVE;
if (this.is0())
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
const { ex: X, ey: Y, ez: Z, et: T } = this;
const X2 = modP(X * X); // X²
const Y2 = modP(Y * Y); // Y²
const Z2 = modP(Z * Z); // Z²
const Z4 = modP(Z2 * Z2); // Z⁴
const aX2 = modP(X2 * a); // aX²
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
if (left !== right)
throw new Error('bad point: equation left != right (1)');
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
const XY = modP(X * Y);
const ZT = modP(Z * T);
if (XY !== ZT)
throw new Error('bad point: equation left != right (2)');
}
// Compare one point to another.
equals(other) {
isPoint(other);
const { ex: X1, ey: Y1, ez: Z1 } = this;
const { ex: X2, ey: Y2, ez: Z2 } = other;
const X1Z2 = modP(X1 * Z2);
const X2Z1 = modP(X2 * Z1);
const Y1Z2 = modP(Y1 * Z2);
const Y2Z1 = modP(Y2 * Z1);
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
}
is0() {
return this.equals(Point.ZERO);
}
negate() {
// Flips point sign to a negative one (-x, y in affine coords)
return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et));
}
// Fast algo for doubling Extended Point.
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
// Cost: 4M + 4S + 1*a + 6add + 1*2.
double() {
const { a } = CURVE;
const { ex: X1, ey: Y1, ez: Z1 } = this;
const A = modP(X1 * X1); // A = X12
const B = modP(Y1 * Y1); // B = Y12
const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
const D = modP(a * A); // D = a*A
const x1y1 = X1 + Y1;
const E = modP(modP(x1y1 * x1y1) - A - B); // E = (X1+Y1)2-A-B
const G = D + B; // G = D+B
const F = G - C; // F = G-C
const H = D - B; // H = D-B
const X3 = modP(E * F); // X3 = E*F
const Y3 = modP(G * H); // Y3 = G*H
const T3 = modP(E * H); // T3 = E*H
const Z3 = modP(F * G); // Z3 = F*G
return new Point(X3, Y3, Z3, T3);
}
// Fast algo for adding 2 Extended Points.
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
// Cost: 9M + 1*a + 1*d + 7add.
add(other) {
isPoint(other);
const { a, d } = CURVE;
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
// Faster algo for adding 2 Extended Points when curve's a=-1.
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
// Cost: 8M + 8add + 2*2.
// Note: It does not check whether the `other` point is valid.
if (a === BigInt(-1)) {
const A = modP((Y1 - X1) * (Y2 + X2));
const B = modP((Y1 + X1) * (Y2 - X2));
const F = modP(B - A);
if (F === _0n)
return this.double(); // Same point. Tests say it doesn't affect timing
const C = modP(Z1 * _2n * T2);
const D = modP(T1 * _2n * Z2);
const E = D + C;
const G = B + A;
const H = D - C;
const X3 = modP(E * F);
const Y3 = modP(G * H);
const T3 = modP(E * H);
const Z3 = modP(F * G);
return new Point(X3, Y3, Z3, T3);
}
const A = modP(X1 * X2); // A = X1*X2
const B = modP(Y1 * Y2); // B = Y1*Y2
const C = modP(T1 * d * T2); // C = T1*d*T2
const D = modP(Z1 * Z2); // D = Z1*Z2
const E = modP((X1 + Y1) * (X2 + Y2) - A - B); // E = (X1+Y1)*(X2+Y2)-A-B
const F = D - C; // F = D-C
const G = D + C; // G = D+C
const H = modP(B - a * A); // H = B-a*A
const X3 = modP(E * F); // X3 = E*F
const Y3 = modP(G * H); // Y3 = G*H
const T3 = modP(E * H); // T3 = E*H
const Z3 = modP(F * G); // Z3 = F*G
return new Point(X3, Y3, Z3, T3);
}
subtract(other) {
return this.add(other.negate());
}
wNAF(n) {
return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
}
// Constant-time multiplication.
multiply(scalar) {
const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
return Point.normalizeZ([p, f])[0];
}
// Non-constant-time multiplication. Uses double-and-add algorithm.
// It's faster, but should only be used when you don't care about
// an exposed private key e.g. sig verification.
// Does NOT allow scalars higher than CURVE.n.
multiplyUnsafe(scalar) {
let n = assertGE0(scalar); // 0 <= scalar < CURVE.n
if (n === _0n)
return I;
if (this.equals(I) || n === _1n)
return this;
if (this.equals(G))
return this.wNAF(n).p;
return wnaf.unsafeLadder(this, n);
}
// Checks if point is of small order.
// If you add something to small order point, you will have "dirty"
// point with torsion component.
// Multiplies point by cofactor and checks if the result is 0.
isSmallOrder() {
return this.multiplyUnsafe(cofactor).is0();
}
// Multiplies point by curve order and checks if the result is 0.
// Returns `false` is the point is dirty.
isTorsionFree() {
return wnaf.unsafeLadder(this, CURVE_ORDER).is0();
}
// Converts Extended point to default (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
toAffine(iz) {
const { ex: x, ey: y, ez: z } = this;
const is0 = this.is0();
if (iz == null)
iz = is0 ? _8n : Fp.inv(z); // 8 was chosen arbitrarily
const ax = modP(x * iz);
const ay = modP(y * iz);
const zz = modP(z * iz);
if (is0)
return { x: _0n, y: _1n };
if (zz !== _1n)
throw new Error('invZ was invalid');
return { x: ax, y: ay };
}
clearCofactor() {
const { h: cofactor } = CURVE;
if (cofactor === _1n)
return this;
return this.multiplyUnsafe(cofactor);
}
// Converts hash string or Uint8Array to Point.
// Uses algo from RFC8032 5.1.3.
static fromHex(hex, zip215 = false) {
const { d, a } = CURVE;
const len = Fp.BYTES;
hex = (0, utils_js_1.ensureBytes)('pointHex', hex, len); // copy hex to a new array
const normed = hex.slice(); // copy again, we'll manipulate it
const lastByte = hex[len - 1]; // select last byte
normed[len - 1] = lastByte & ~0x80; // clear last bit
const y = ut.bytesToNumberLE(normed);
if (y === _0n) {
// y=0 is allowed
}
else {
// RFC8032 prohibits >= p, but ZIP215 doesn't
if (zip215)
assertInRange(y, MASK); // zip215=true [1..P-1] (2^255-19-1 for ed25519)
else
assertInRange(y, Fp.ORDER); // zip215=false [1..MASK-1] (2^256-1 for ed25519)
}
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
// ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
const y2 = modP(y * y); // denominator is always non-0 mod p.
const u = modP(y2 - _1n); // u = y² - 1
const v = modP(d * y2 - a); // v = d y² + 1.
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
if (!isValid)
throw new Error('Point.fromHex: invalid y coordinate');
const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
const isLastByteOdd = (lastByte & 0x80) !== 0; // if x=0 and x_0 = 1, fail
if (isLastByteOdd !== isXOdd)
x = modP(-x); // if x_0 != x mod 2, set x = p-x
return Point.fromAffine({ x, y });
}
static fromPrivateKey(privKey) {
return getExtendedPublicKey(privKey).point;
}
toRawBytes() {
const { x, y } = this.toAffine();
const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
return bytes; // and use the last byte to encode sign of x
}
toHex() {
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
}
}
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
Point.ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
const { BASE: G, ZERO: I } = Point;
const wnaf = (0, curve_js_1.wNAF)(Point, nByteLength * 8);
function modN(a) {
return (0, modular_js_1.mod)(a, CURVE_ORDER);
}
// Little-endian SHA512 with modulo n
function modN_LE(hash) {
return modN(ut.bytesToNumberLE(hash));
}
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
function getExtendedPublicKey(key) {
const len = nByteLength;
key = (0, utils_js_1.ensureBytes)('private key', key, len);
// Hash private key with curve's hash function to produce uniformingly random input
// Check byte lengths: ensure(64, h(ensure(32, key)))
const hashed = (0, utils_js_1.ensureBytes)('hashed private key', cHash(key), 2 * len);
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
const scalar = modN_LE(head); // The actual private scalar
const point = G.multiply(scalar); // Point on Edwards curve aka public key
const pointBytes = point.toRawBytes(); // Uint8Array representation
return { head, prefix, scalar, point, pointBytes };
}
// Calculates EdDSA pub key. RFC8032 5.1.5. Privkey is hashed. Use first half with 3 bits cleared
function getPublicKey(privKey) {
return getExtendedPublicKey(privKey).pointBytes;
}
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
function hashDomainToScalar(context = new Uint8Array(), ...msgs) {
const msg = ut.concatBytes(...msgs);
return modN_LE(cHash(domain(msg, (0, utils_js_1.ensureBytes)('context', context), !!prehash)));
}
/** Signs message with privateKey. RFC8032 5.1.6 */
function sign(msg, privKey, options = {}) {
msg = (0, utils_js_1.ensureBytes)('message', msg);
if (prehash)
msg = prehash(msg); // for ed25519ph etc.
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
const R = G.multiply(r).toRawBytes(); // R = rG
const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
const s = modN(r + k * scalar); // S = (r + k * s) mod L
assertGE0(s); // 0 <= s < l
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
return (0, utils_js_1.ensureBytes)('result', res, nByteLength * 2); // 64-byte signature
}
const verifyOpts = VERIFY_DEFAULT;
function verify(sig, msg, publicKey, options = verifyOpts) {
const { context, zip215 } = options;
const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
sig = (0, utils_js_1.ensureBytes)('signature', sig, 2 * len); // An extended group equation is checked.
msg = (0, utils_js_1.ensureBytes)('message', msg);
if (prehash)
msg = prehash(msg); // for ed25519ph, etc
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len));
// zip215: true is good for consensus-critical apps and allows points < 2^256
// zip215: false follows RFC8032 / NIST186-5 and restricts points to CURVE.p
let A, R, SB;
try {
A = Point.fromHex(publicKey, zip215);
R = Point.fromHex(sig.slice(0, len), zip215);
SB = G.multiplyUnsafe(s); // 0 <= s < l is done inside
}
catch (error) {
return false;
}
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
const RkA = R.add(A.multiplyUnsafe(k));
// [8][S]B = [8]R + [8][k]A'
return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
}
G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
const utils = {
getExtendedPublicKey,
// ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1.
randomPrivateKey: () => randomBytes(Fp.BYTES),
/**
* We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
* values. This slows down first getPublicKey() by milliseconds (see Speed section),
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
* @param windowSize 2, 4, 8, 16
*/
precompute(windowSize = 8, point = Point.BASE) {
point._setWindowSize(windowSize);
point.multiply(BigInt(3));
return point;
},
};
return {
CURVE,
getPublicKey,
sign,
verify,
ExtendedPoint: Point,
utils,
};
}
exports.twistedEdwards = twistedEdwards;
//# sourceMappingURL=edwards.js.map
File diff suppressed because one or more lines are too long
@@ -1,57 +0,0 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import type { Group, GroupConstructor, AffinePoint } from './curve.js';
import { IField } from './modular.js';
import { CHash } from './utils.js';
/**
* * `DST` is a domain separation tag, defined in section 2.2.5
* * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m
* * `m` is extension degree (1 for prime fields)
* * `k` is the target security target in bits (e.g. 128), from section 5.1
* * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
* * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
*/
type UnicodeOrBytes = string | Uint8Array;
export type Opts = {
DST: UnicodeOrBytes;
p: bigint;
m: number;
k: number;
expand: 'xmd' | 'xof';
hash: CHash;
};
export declare function expand_message_xmd(msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H: CHash): Uint8Array;
export declare function expand_message_xof(msg: Uint8Array, DST: Uint8Array, lenInBytes: number, k: number, H: CHash): Uint8Array;
/**
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
* @param msg a byte string containing the message to hash
* @param count the number of elements of F to output
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
*/
export declare function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][];
export declare function isogenyMap<T, F extends IField<T>>(field: F, map: [T[], T[], T[], T[]]): (x: T, y: T) => {
x: T;
y: T;
};
export interface H2CPoint<T> extends Group<H2CPoint<T>> {
add(rhs: H2CPoint<T>): H2CPoint<T>;
toAffine(iz?: bigint): AffinePoint<T>;
clearCofactor(): H2CPoint<T>;
assertValidity(): void;
}
export interface H2CPointConstructor<T> extends GroupConstructor<H2CPoint<T>> {
fromAffine(ap: AffinePoint<T>): H2CPoint<T>;
}
export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
export type htfBasicOpts = {
DST: UnicodeOrBytes;
};
export declare function createHasher<T>(Point: H2CPointConstructor<T>, mapToCurve: MapToCurve<T>, def: Opts & {
encodeDST?: UnicodeOrBytes;
}): {
hashToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T>;
encodeToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T>;
};
export {};
//# sourceMappingURL=hash-to-curve.d.ts.map
@@ -1 +0,0 @@
{"version":3,"file":"hash-to-curve.d.ts","sourceRoot":"","sources":["../src/abstract/hash-to-curve.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,OAAO,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAAO,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAmB,KAAK,EAA4C,MAAM,YAAY,CAAC;AAE9F;;;;;;;GAOG;AACH,KAAK,cAAc,GAAG,MAAM,GAAG,UAAU,CAAC;AAC1C,MAAM,MAAM,IAAI,GAAG;IACjB,GAAG,EAAE,cAAc,CAAC;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC;IACtB,IAAI,EAAE,KAAK,CAAC;CACb,CAAC;AAyCF,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,UAAU,EACf,UAAU,EAAE,MAAM,EAClB,CAAC,EAAE,KAAK,GACP,UAAU,CAqBZ;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,UAAU,EACf,UAAU,EAAE,MAAM,EAClB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,KAAK,GACP,UAAU,CAqBZ;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,MAAM,EAAE,EAAE,CAqCvF;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,OAGzE,CAAC,KAAK,CAAC;;;EAQnB;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC,CAAE,SAAQ,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACrD,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IACtC,aAAa,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC7B,cAAc,IAAI,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB,CAAC,CAAC,CAAE,SAAQ,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC3E,UAAU,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;CAC7C;AAED,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC;AAIjE,MAAM,MAAM,YAAY,GAAG;IAAE,GAAG,EAAE,cAAc,CAAA;CAAE,CAAC;AAEnD,wBAAgB,YAAY,CAAC,CAAC,EAC5B,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC,EAC7B,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,GAAG,EAAE,IAAI,GAAG;IAAE,SAAS,CAAC,EAAE,cAAc,CAAA;CAAE;qBAMvB,UAAU,YAAY,YAAY;uBAUhC,UAAU,YAAY,YAAY;EAOxD"}
-175
View File
@@ -1,175 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createHasher = exports.isogenyMap = exports.hash_to_field = exports.expand_message_xof = exports.expand_message_xmd = void 0;
const modular_js_1 = require("./modular.js");
const utils_js_1 = require("./utils.js");
function validateDST(dst) {
if (dst instanceof Uint8Array)
return dst;
if (typeof dst === 'string')
return (0, utils_js_1.utf8ToBytes)(dst);
throw new Error('DST must be Uint8Array or string');
}
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
const os2ip = utils_js_1.bytesToNumberBE;
// Integer to Octet Stream (numberToBytesBE)
function i2osp(value, length) {
if (value < 0 || value >= 1 << (8 * length)) {
throw new Error(`bad I2OSP call: value=${value} length=${length}`);
}
const res = Array.from({ length }).fill(0);
for (let i = length - 1; i >= 0; i--) {
res[i] = value & 0xff;
value >>>= 8;
}
return new Uint8Array(res);
}
function strxor(a, b) {
const arr = new Uint8Array(a.length);
for (let i = 0; i < a.length; i++) {
arr[i] = a[i] ^ b[i];
}
return arr;
}
function isBytes(item) {
if (!(item instanceof Uint8Array))
throw new Error('Uint8Array expected');
}
function isNum(item) {
if (!Number.isSafeInteger(item))
throw new Error('number expected');
}
// Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.4.1
function expand_message_xmd(msg, DST, lenInBytes, H) {
isBytes(msg);
isBytes(DST);
isNum(lenInBytes);
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
if (DST.length > 255)
DST = H((0, utils_js_1.concatBytes)((0, utils_js_1.utf8ToBytes)('H2C-OVERSIZE-DST-'), DST));
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
const ell = Math.ceil(lenInBytes / b_in_bytes);
if (ell > 255)
throw new Error('Invalid xmd length');
const DST_prime = (0, utils_js_1.concatBytes)(DST, i2osp(DST.length, 1));
const Z_pad = i2osp(0, r_in_bytes);
const l_i_b_str = i2osp(lenInBytes, 2); // len_in_bytes_str
const b = new Array(ell);
const b_0 = H((0, utils_js_1.concatBytes)(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
b[0] = H((0, utils_js_1.concatBytes)(b_0, i2osp(1, 1), DST_prime));
for (let i = 1; i <= ell; i++) {
const args = [strxor(b_0, b[i - 1]), i2osp(i + 1, 1), DST_prime];
b[i] = H((0, utils_js_1.concatBytes)(...args));
}
const pseudo_random_bytes = (0, utils_js_1.concatBytes)(...b);
return pseudo_random_bytes.slice(0, lenInBytes);
}
exports.expand_message_xmd = expand_message_xmd;
function expand_message_xof(msg, DST, lenInBytes, k, H) {
isBytes(msg);
isBytes(DST);
isNum(lenInBytes);
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-5.3.3
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
if (DST.length > 255) {
const dkLen = Math.ceil((2 * k) / 8);
DST = H.create({ dkLen }).update((0, utils_js_1.utf8ToBytes)('H2C-OVERSIZE-DST-')).update(DST).digest();
}
if (lenInBytes > 65535 || DST.length > 255)
throw new Error('expand_message_xof: invalid lenInBytes');
return (H.create({ dkLen: lenInBytes })
.update(msg)
.update(i2osp(lenInBytes, 2))
// 2. DST_prime = DST || I2OSP(len(DST), 1)
.update(DST)
.update(i2osp(DST.length, 1))
.digest());
}
exports.expand_message_xof = expand_message_xof;
/**
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-5.3
* @param msg a byte string containing the message to hash
* @param count the number of elements of F to output
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
*/
function hash_to_field(msg, count, options) {
(0, utils_js_1.validateObject)(options, {
DST: 'string',
p: 'bigint',
m: 'isSafeInteger',
k: 'isSafeInteger',
hash: 'hash',
});
const { p, k, m, hash, expand, DST: _DST } = options;
isBytes(msg);
isNum(count);
const DST = validateDST(_DST);
const log2p = p.toString(2).length;
const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
const len_in_bytes = count * m * L;
let prb; // pseudo_random_bytes
if (expand === 'xmd') {
prb = expand_message_xmd(msg, DST, len_in_bytes, hash);
}
else if (expand === 'xof') {
prb = expand_message_xof(msg, DST, len_in_bytes, k, hash);
}
else if (expand === '_internal_pass') {
// for internal tests only
prb = msg;
}
else {
throw new Error('expand must be "xmd" or "xof"');
}
const u = new Array(count);
for (let i = 0; i < count; i++) {
const e = new Array(m);
for (let j = 0; j < m; j++) {
const elm_offset = L * (j + i * m);
const tv = prb.subarray(elm_offset, elm_offset + L);
e[j] = (0, modular_js_1.mod)(os2ip(tv), p);
}
u[i] = e;
}
return u;
}
exports.hash_to_field = hash_to_field;
function isogenyMap(field, map) {
// Make same order as in spec
const COEFF = map.map((i) => Array.from(i).reverse());
return (x, y) => {
const [xNum, xDen, yNum, yDen] = COEFF.map((val) => val.reduce((acc, i) => field.add(field.mul(acc, x), i)));
x = field.div(xNum, xDen); // xNum / xDen
y = field.mul(y, field.div(yNum, yDen)); // y * (yNum / yDev)
return { x, y };
};
}
exports.isogenyMap = isogenyMap;
function createHasher(Point, mapToCurve, def) {
if (typeof mapToCurve !== 'function')
throw new Error('mapToCurve() must be defined');
return {
// Encodes byte string to elliptic curve
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
hashToCurve(msg, options) {
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options });
const u0 = Point.fromAffine(mapToCurve(u[0]));
const u1 = Point.fromAffine(mapToCurve(u[1]));
const P = u0.add(u1).clearCofactor();
P.assertValidity();
return P;
},
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
encodeToCurve(msg, options) {
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options });
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
P.assertValidity();
return P;
},
};
}
exports.createHasher = createHasher;
//# sourceMappingURL=hash-to-curve.js.map
File diff suppressed because one or more lines are too long
-81
View File
@@ -1,81 +0,0 @@
export declare function mod(a: bigint, b: bigint): bigint;
/**
* Efficiently exponentiate num to power and do modular division.
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
* @example
* powMod(2n, 6n, 11n) // 64n % 11n == 9n
*/
export declare function pow(num: bigint, power: bigint, modulo: bigint): bigint;
export declare function pow2(x: bigint, power: bigint, modulo: bigint): bigint;
export declare function invert(number: bigint, modulo: bigint): bigint;
export declare function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T;
export declare function FpSqrt(P: bigint): <T>(Fp: IField<T>, n: T) => T;
export declare const isNegativeLE: (num: bigint, modulo: bigint) => boolean;
export interface IField<T> {
ORDER: bigint;
BYTES: number;
BITS: number;
MASK: bigint;
ZERO: T;
ONE: T;
create: (num: T) => T;
isValid: (num: T) => boolean;
is0: (num: T) => boolean;
neg(num: T): T;
inv(num: T): T;
sqrt(num: T): T;
sqr(num: T): T;
eql(lhs: T, rhs: T): boolean;
add(lhs: T, rhs: T): T;
sub(lhs: T, rhs: T): T;
mul(lhs: T, rhs: T | bigint): T;
pow(lhs: T, power: bigint): T;
div(lhs: T, rhs: T | bigint): T;
addN(lhs: T, rhs: T): T;
subN(lhs: T, rhs: T): T;
mulN(lhs: T, rhs: T | bigint): T;
sqrN(num: T): T;
isOdd?(num: T): boolean;
pow(lhs: T, power: bigint): T;
invertBatch: (lst: T[]) => T[];
toBytes(num: T): Uint8Array;
fromBytes(bytes: Uint8Array): T;
cmov(a: T, b: T, c: boolean): T;
}
export declare function validateField<T>(field: IField<T>): IField<T>;
export declare function FpPow<T>(f: IField<T>, num: T, power: bigint): T;
export declare function FpInvertBatch<T>(f: IField<T>, nums: T[]): T[];
export declare function FpDiv<T>(f: IField<T>, lhs: T, rhs: T | bigint): T;
export declare function FpIsSquare<T>(f: IField<T>): (x: T) => boolean;
export declare function nLength(n: bigint, nBitLength?: number): {
nBitLength: number;
nByteLength: number;
};
type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
/**
* Initializes a galois field over prime. Non-primes are not supported for now.
* Do not init in loop: slow. Very fragile: always run a benchmark on change.
* Major performance gains:
* a) non-normalized operations like mulN instead of mul
* b) `Object.freeze`
* c) Same object shape: never add or remove keys
* @param ORDER prime positive bigint
* @param bitLen how many bits the field consumes
* @param isLE (def: false) if encoding / decoding should be in little-endian
* @param redef optional faster redefinitions of sqrt and other methods
*/
export declare function Field(ORDER: bigint, bitLen?: number, isLE?: boolean, redef?: Partial<IField<bigint>>): Readonly<FpField>;
export declare function FpSqrtOdd<T>(Fp: IField<T>, elm: T): T;
export declare function FpSqrtEven<T>(Fp: IField<T>, elm: T): T;
/**
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
* and convert them into private scalar, with the modulo bias being neglible.
* Needs at least 40 bytes of input for 32-byte private key.
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
* @param hash hash output from SHA3 or a similar function
* @returns valid private scalar
*/
export declare function hashToPrivateScalar(hash: string | Uint8Array, groupOrder: bigint, isLE?: boolean): bigint;
export {};
//# sourceMappingURL=modular.d.ts.map
@@ -1 +0,0 @@
{"version":3,"file":"modular.d.ts","sourceRoot":"","sources":["../src/abstract/modular.ts"],"names":[],"mappings":"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"}
-379
View File
@@ -1,379 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hashToPrivateScalar = exports.FpSqrtEven = exports.FpSqrtOdd = exports.Field = exports.nLength = exports.FpIsSquare = exports.FpDiv = exports.FpInvertBatch = exports.FpPow = exports.validateField = exports.isNegativeLE = exports.FpSqrt = exports.tonelliShanks = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0;
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Utilities for modular arithmetics and finite fields
const utils_js_1 = require("./utils.js");
// prettier-ignore
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
// prettier-ignore
const _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
// prettier-ignore
const _9n = BigInt(9), _16n = BigInt(16);
// Calculates a modulo b
function mod(a, b) {
const result = a % b;
return result >= _0n ? result : b + result;
}
exports.mod = mod;
/**
* Efficiently exponentiate num to power and do modular division.
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
* @example
* powMod(2n, 6n, 11n) // 64n % 11n == 9n
*/
// TODO: use field version && remove
function pow(num, power, modulo) {
if (modulo <= _0n || power < _0n)
throw new Error('Expected power/modulo > 0');
if (modulo === _1n)
return _0n;
let res = _1n;
while (power > _0n) {
if (power & _1n)
res = (res * num) % modulo;
num = (num * num) % modulo;
power >>= _1n;
}
return res;
}
exports.pow = pow;
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
function pow2(x, power, modulo) {
let res = x;
while (power-- > _0n) {
res *= res;
res %= modulo;
}
return res;
}
exports.pow2 = pow2;
// Inverses number over modulo
function invert(number, modulo) {
if (number === _0n || modulo <= _0n) {
throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`);
}
// Eucledian GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
// Fermat's little theorem "CT-like" version inv(n) = n^(m-2) mod m is 30x slower.
let a = mod(number, modulo);
let b = modulo;
// prettier-ignore
let x = _0n, y = _1n, u = _1n, v = _0n;
while (a !== _0n) {
// JIT applies optimization if those two lines follow each other
const q = b / a;
const r = b % a;
const m = x - u * q;
const n = y - v * q;
// prettier-ignore
b = a, a = r, x = u, y = v, u = m, v = n;
}
const gcd = b;
if (gcd !== _1n)
throw new Error('invert: does not exist');
return mod(x, modulo);
}
exports.invert = invert;
// Tonelli-Shanks algorithm
// Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12)
// Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks
function tonelliShanks(P) {
// Legendre constant: used to calculate Legendre symbol (a | p),
// which denotes the value of a^((p-1)/2) (mod p).
// (a | p) ≡ 1 if a is a square (mod p)
// (a | p) ≡ -1 if a is not a square (mod p)
// (a | p) ≡ 0 if a ≡ 0 (mod p)
const legendreC = (P - _1n) / _2n;
let Q, S, Z;
// Step 1: By factoring out powers of 2 from p - 1,
// find q and s such that p - 1 = q*(2^s) with q odd
for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++)
;
// Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++)
;
// Fast-path
if (S === 1) {
const p1div4 = (P + _1n) / _4n;
return function tonelliFast(Fp, n) {
const root = Fp.pow(n, p1div4);
if (!Fp.eql(Fp.sqr(root), n))
throw new Error('Cannot find square root');
return root;
};
}
// Slow-path
const Q1div2 = (Q + _1n) / _2n;
return function tonelliSlow(Fp, n) {
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE))
throw new Error('Cannot find square root');
let r = S;
// TODO: will fail at Fp2/etc
let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
let x = Fp.pow(n, Q1div2); // first guess at the square root
let b = Fp.pow(n, Q); // first guess at the fudge factor
while (!Fp.eql(b, Fp.ONE)) {
if (Fp.eql(b, Fp.ZERO))
return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
// Find m such b^(2^m)==1
let m = 1;
for (let t2 = Fp.sqr(b); m < r; m++) {
if (Fp.eql(t2, Fp.ONE))
break;
t2 = Fp.sqr(t2); // t2 *= t2
}
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
g = Fp.sqr(ge); // g = ge * ge
x = Fp.mul(x, ge); // x *= ge
b = Fp.mul(b, g); // b *= g
r = m;
}
return x;
};
}
exports.tonelliShanks = tonelliShanks;
function FpSqrt(P) {
// NOTE: different algorithms can give different roots, it is up to user to decide which one they want.
// For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
// P ≡ 3 (mod 4)
// √n = n^((P+1)/4)
if (P % _4n === _3n) {
// Not all roots possible!
// const ORDER =
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
// const NUM = 72057594037927816n;
const p1div4 = (P + _1n) / _4n;
return function sqrt3mod4(Fp, n) {
const root = Fp.pow(n, p1div4);
// Throw if root**2 != n
if (!Fp.eql(Fp.sqr(root), n))
throw new Error('Cannot find square root');
return root;
};
}
// Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
if (P % _8n === _5n) {
const c1 = (P - _5n) / _8n;
return function sqrt5mod8(Fp, n) {
const n2 = Fp.mul(n, _2n);
const v = Fp.pow(n2, c1);
const nv = Fp.mul(n, v);
const i = Fp.mul(Fp.mul(nv, _2n), v);
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
if (!Fp.eql(Fp.sqr(root), n))
throw new Error('Cannot find square root');
return root;
};
}
// P ≡ 9 (mod 16)
if (P % _16n === _9n) {
// NOTE: tonelli is too slow for bls-Fp2 calculations even on start
// Means we cannot use sqrt for constants at all!
//
// const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
// const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
// const c3 = Fp.sqrt(Fp.negate(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
// const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
// sqrt = (x) => {
// let tv1 = Fp.pow(x, c4); // 1. tv1 = x^c4
// let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
// const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
// let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
// const e1 = Fp.equals(Fp.square(tv2), x); // 5. e1 = (tv2^2) == x
// const e2 = Fp.equals(Fp.square(tv3), x); // 6. e2 = (tv3^2) == x
// tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
// tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
// const e3 = Fp.equals(Fp.square(tv2), x); // 9. e3 = (tv2^2) == x
// return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
// }
}
// Other cases: Tonelli-Shanks algorithm
return tonelliShanks(P);
}
exports.FpSqrt = FpSqrt;
// Little-endian check for first LE bit (last BE bit);
const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
exports.isNegativeLE = isNegativeLE;
// prettier-ignore
const FIELD_FIELDS = [
'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
'eql', 'add', 'sub', 'mul', 'pow', 'div',
'addN', 'subN', 'mulN', 'sqrN'
];
function validateField(field) {
const initial = {
ORDER: 'bigint',
MASK: 'bigint',
BYTES: 'isSafeInteger',
BITS: 'isSafeInteger',
};
const opts = FIELD_FIELDS.reduce((map, val) => {
map[val] = 'function';
return map;
}, initial);
return (0, utils_js_1.validateObject)(field, opts);
}
exports.validateField = validateField;
// Generic field functions
function FpPow(f, num, power) {
// Should have same speed as pow for bigints
// TODO: benchmark!
if (power < _0n)
throw new Error('Expected power > 0');
if (power === _0n)
return f.ONE;
if (power === _1n)
return num;
let p = f.ONE;
let d = num;
while (power > _0n) {
if (power & _1n)
p = f.mul(p, d);
d = f.sqr(d);
power >>= _1n;
}
return p;
}
exports.FpPow = FpPow;
// 0 is non-invertible: non-batched version will throw on 0
function FpInvertBatch(f, nums) {
const tmp = new Array(nums.length);
// Walk from first to last, multiply them by each other MOD p
const lastMultiplied = nums.reduce((acc, num, i) => {
if (f.is0(num))
return acc;
tmp[i] = acc;
return f.mul(acc, num);
}, f.ONE);
// Invert last element
const inverted = f.inv(lastMultiplied);
// Walk from last to first, multiply them by inverted each other MOD p
nums.reduceRight((acc, num, i) => {
if (f.is0(num))
return acc;
tmp[i] = f.mul(acc, tmp[i]);
return f.mul(acc, num);
}, inverted);
return tmp;
}
exports.FpInvertBatch = FpInvertBatch;
function FpDiv(f, lhs, rhs) {
return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
}
exports.FpDiv = FpDiv;
// This function returns True whenever the value x is a square in the field F.
function FpIsSquare(f) {
const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
return (x) => {
const p = f.pow(x, legendreConst);
return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
};
}
exports.FpIsSquare = FpIsSquare;
// CURVE.n lengths
function nLength(n, nBitLength) {
// Bit size, byte size of CURVE.n
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
const nByteLength = Math.ceil(_nBitLength / 8);
return { nBitLength: _nBitLength, nByteLength };
}
exports.nLength = nLength;
/**
* Initializes a galois field over prime. Non-primes are not supported for now.
* Do not init in loop: slow. Very fragile: always run a benchmark on change.
* Major performance gains:
* a) non-normalized operations like mulN instead of mul
* b) `Object.freeze`
* c) Same object shape: never add or remove keys
* @param ORDER prime positive bigint
* @param bitLen how many bits the field consumes
* @param isLE (def: false) if encoding / decoding should be in little-endian
* @param redef optional faster redefinitions of sqrt and other methods
*/
function Field(ORDER, bitLen, isLE = false, redef = {}) {
if (ORDER <= _0n)
throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
if (BYTES > 2048)
throw new Error('Field lengths over 2048 bytes are not supported');
const sqrtP = FpSqrt(ORDER);
const f = Object.freeze({
ORDER,
BITS,
BYTES,
MASK: (0, utils_js_1.bitMask)(BITS),
ZERO: _0n,
ONE: _1n,
create: (num) => mod(num, ORDER),
isValid: (num) => {
if (typeof num !== 'bigint')
throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
},
is0: (num) => num === _0n,
isOdd: (num) => (num & _1n) === _1n,
neg: (num) => mod(-num, ORDER),
eql: (lhs, rhs) => lhs === rhs,
sqr: (num) => mod(num * num, ORDER),
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
pow: (num, power) => FpPow(f, num, power),
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
// Same as above, but doesn't normalize
sqrN: (num) => num * num,
addN: (lhs, rhs) => lhs + rhs,
subN: (lhs, rhs) => lhs - rhs,
mulN: (lhs, rhs) => lhs * rhs,
inv: (num) => invert(num, ORDER),
sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
invertBatch: (lst) => FpInvertBatch(f, lst),
// TODO: do we really need constant cmov?
// We don't have const-time bigints anyway, so probably will be not very useful
cmov: (a, b, c) => (c ? b : a),
toBytes: (num) => (isLE ? (0, utils_js_1.numberToBytesLE)(num, BYTES) : (0, utils_js_1.numberToBytesBE)(num, BYTES)),
fromBytes: (bytes) => {
if (bytes.length !== BYTES)
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
return isLE ? (0, utils_js_1.bytesToNumberLE)(bytes) : (0, utils_js_1.bytesToNumberBE)(bytes);
},
});
return Object.freeze(f);
}
exports.Field = Field;
function FpSqrtOdd(Fp, elm) {
if (!Fp.isOdd)
throw new Error(`Field doesn't have isOdd`);
const root = Fp.sqrt(elm);
return Fp.isOdd(root) ? root : Fp.neg(root);
}
exports.FpSqrtOdd = FpSqrtOdd;
function FpSqrtEven(Fp, elm) {
if (!Fp.isOdd)
throw new Error(`Field doesn't have isOdd`);
const root = Fp.sqrt(elm);
return Fp.isOdd(root) ? Fp.neg(root) : root;
}
exports.FpSqrtEven = FpSqrtEven;
/**
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
* and convert them into private scalar, with the modulo bias being neglible.
* Needs at least 40 bytes of input for 32-byte private key.
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
* @param hash hash output from SHA3 or a similar function
* @returns valid private scalar
*/
function hashToPrivateScalar(hash, groupOrder, isLE = false) {
hash = (0, utils_js_1.ensureBytes)('privateHash', hash);
const hashLen = hash.length;
const minLen = nLength(groupOrder).nByteLength + 8;
if (minLen < 24 || hashLen < minLen || hashLen > 1024)
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
const num = isLE ? (0, utils_js_1.bytesToNumberLE)(hash) : (0, utils_js_1.bytesToNumberBE)(hash);
return mod(num, groupOrder - _1n) + _1n;
}
exports.hashToPrivateScalar = hashToPrivateScalar;
//# sourceMappingURL=modular.js.map
File diff suppressed because one or more lines are too long
-26
View File
@@ -1,26 +0,0 @@
type Hex = string | Uint8Array;
export type CurveType = {
P: bigint;
nByteLength: number;
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array;
domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array;
a: bigint;
montgomeryBits: number;
powPminus2?: (x: bigint) => bigint;
xyToU?: (x: bigint, y: bigint) => bigint;
Gu: bigint;
randomBytes?: (bytesLength?: number) => Uint8Array;
};
export type CurveFn = {
scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
scalarMultBase: (scalar: Hex) => Uint8Array;
getSharedSecret: (privateKeyA: Hex, publicKeyB: Hex) => Uint8Array;
getPublicKey: (privateKey: Hex) => Uint8Array;
utils: {
randomPrivateKey: () => Uint8Array;
};
GuBytes: Uint8Array;
};
export declare function montgomery(curveDef: CurveType): CurveFn;
export {};
//# sourceMappingURL=montgomery.d.ts.map
@@ -1 +0,0 @@
{"version":3,"file":"montgomery.d.ts","sourceRoot":"","sources":["../src/abstract/montgomery.ts"],"names":[],"mappings":"AAMA,KAAK,GAAG,GAAG,MAAM,GAAG,UAAU,CAAC;AAE/B,MAAM,MAAM,SAAS,GAAG;IACtB,CAAC,EAAE,MAAM,CAAC;IACV,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,UAAU,CAAC;IACtD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,KAAK,UAAU,CAAC;IAC5E,CAAC,EAAE,MAAM,CAAC;IACV,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,MAAM,KAAK,UAAU,CAAC;CACpD,CAAC;AACF,MAAM,MAAM,OAAO,GAAG;IACpB,UAAU,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,KAAK,UAAU,CAAC;IAChD,cAAc,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,UAAU,CAAC;IAC5C,eAAe,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,KAAK,UAAU,CAAC;IACnE,YAAY,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,UAAU,CAAC;IAC9C,KAAK,EAAE;QAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;KAAE,CAAC;IAC9C,OAAO,EAAE,UAAU,CAAC;CACrB,CAAC;AAuBF,wBAAgB,UAAU,CAAC,QAAQ,EAAE,SAAS,GAAG,OAAO,CA0IvD"}
-161
View File
@@ -1,161 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.montgomery = void 0;
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
const modular_js_1 = require("./modular.js");
const utils_js_1 = require("./utils.js");
const _0n = BigInt(0);
const _1n = BigInt(1);
function validateOpts(curve) {
(0, utils_js_1.validateObject)(curve, {
a: 'bigint',
}, {
montgomeryBits: 'isSafeInteger',
nByteLength: 'isSafeInteger',
adjustScalarBytes: 'function',
domain: 'function',
powPminus2: 'function',
Gu: 'bigint',
});
// Set defaults
return Object.freeze({ ...curve });
}
// NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
// Uses only one coordinate instead of two
function montgomery(curveDef) {
const CURVE = validateOpts(curveDef);
const { P } = CURVE;
const modP = (n) => (0, modular_js_1.mod)(n, P);
const montgomeryBits = CURVE.montgomeryBits;
const montgomeryBytes = Math.ceil(montgomeryBits / 8);
const fieldLen = CURVE.nByteLength;
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
const powPminus2 = CURVE.powPminus2 || ((x) => (0, modular_js_1.pow)(x, P - BigInt(2), P));
// cswap from RFC7748. But it is not from RFC7748!
/*
cswap(swap, x_2, x_3):
dummy = mask(swap) AND (x_2 XOR x_3)
x_2 = x_2 XOR dummy
x_3 = x_3 XOR dummy
Return (x_2, x_3)
Where mask(swap) is the all-1 or all-0 word of the same length as x_2
and x_3, computed, e.g., as mask(swap) = 0 - swap.
*/
function cswap(swap, x_2, x_3) {
const dummy = modP(swap * (x_2 - x_3));
x_2 = modP(x_2 - dummy);
x_3 = modP(x_3 + dummy);
return [x_2, x_3];
}
// Accepts 0 as well
function assertFieldElement(n) {
if (typeof n === 'bigint' && _0n <= n && n < P)
return n;
throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
}
// x25519 from 4
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
const a24 = (CURVE.a - BigInt(2)) / BigInt(4);
/**
*
* @param pointU u coordinate (x) on Montgomery Curve 25519
* @param scalar by which the point would be multiplied
* @returns new Point on Montgomery curve
*/
function montgomeryLadder(pointU, scalar) {
const u = assertFieldElement(pointU);
// Section 5: Implementations MUST accept non-canonical values and process them as
// if they had been reduced modulo the field prime.
const k = assertFieldElement(scalar);
const x_1 = u;
let x_2 = _1n;
let z_2 = _0n;
let x_3 = u;
let z_3 = _1n;
let swap = _0n;
let sw;
for (let t = BigInt(montgomeryBits - 1); t >= _0n; t--) {
const k_t = (k >> t) & _1n;
swap ^= k_t;
sw = cswap(swap, x_2, x_3);
x_2 = sw[0];
x_3 = sw[1];
sw = cswap(swap, z_2, z_3);
z_2 = sw[0];
z_3 = sw[1];
swap = k_t;
const A = x_2 + z_2;
const AA = modP(A * A);
const B = x_2 - z_2;
const BB = modP(B * B);
const E = AA - BB;
const C = x_3 + z_3;
const D = x_3 - z_3;
const DA = modP(D * A);
const CB = modP(C * B);
const dacb = DA + CB;
const da_cb = DA - CB;
x_3 = modP(dacb * dacb);
z_3 = modP(x_1 * modP(da_cb * da_cb));
x_2 = modP(AA * BB);
z_2 = modP(E * (AA + modP(a24 * E)));
}
// (x_2, x_3) = cswap(swap, x_2, x_3)
sw = cswap(swap, x_2, x_3);
x_2 = sw[0];
x_3 = sw[1];
// (z_2, z_3) = cswap(swap, z_2, z_3)
sw = cswap(swap, z_2, z_3);
z_2 = sw[0];
z_3 = sw[1];
// z_2^(p - 2)
const z2 = powPminus2(z_2);
// Return x_2 * (z_2^(p - 2))
return modP(x_2 * z2);
}
function encodeUCoordinate(u) {
return (0, utils_js_1.numberToBytesLE)(modP(u), montgomeryBytes);
}
function decodeUCoordinate(uEnc) {
// Section 5: When receiving such an array, implementations of X25519
// MUST mask the most significant bit in the final byte.
// This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
const u = (0, utils_js_1.ensureBytes)('u coordinate', uEnc, montgomeryBytes);
// u[fieldLen-1] crashes QuickJS (TypeError: out-of-bound numeric index)
if (fieldLen === montgomeryBytes)
u[fieldLen - 1] &= 127; // 0b0111_1111
return (0, utils_js_1.bytesToNumberLE)(u);
}
function decodeScalar(n) {
const bytes = (0, utils_js_1.ensureBytes)('scalar', n);
if (bytes.length !== montgomeryBytes && bytes.length !== fieldLen)
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
return (0, utils_js_1.bytesToNumberLE)(adjustScalarBytes(bytes));
}
function scalarMult(scalar, u) {
const pointU = decodeUCoordinate(u);
const _scalar = decodeScalar(scalar);
const pu = montgomeryLadder(pointU, _scalar);
// The result was not contributory
// https://cr.yp.to/ecdh.html#validate
if (pu === _0n)
throw new Error('Invalid private or public key received');
return encodeUCoordinate(pu);
}
// Computes public key from private. By doing scalar multiplication of base point.
const GuBytes = encodeUCoordinate(CURVE.Gu);
function scalarMultBase(scalar) {
return scalarMult(scalar, GuBytes);
}
return {
scalarMult,
scalarMultBase,
getSharedSecret: (privateKey, publicKey) => scalarMult(privateKey, publicKey),
getPublicKey: (privateKey) => scalarMultBase(privateKey),
utils: { randomPrivateKey: () => CURVE.randomBytes(CURVE.nByteLength) },
GuBytes: GuBytes,
};
}
exports.montgomery = montgomery;
//# sourceMappingURL=montgomery.js.map
File diff suppressed because one or more lines are too long
-30
View File
@@ -1,30 +0,0 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import { IField } from './modular.js';
export type PoseidonOpts = {
Fp: IField<bigint>;
t: number;
roundsFull: number;
roundsPartial: number;
sboxPower?: number;
reversePartialPowIdx?: boolean;
mds: bigint[][];
roundConstants: bigint[][];
};
export declare function validateOpts(opts: PoseidonOpts): Readonly<{
rounds: number;
sboxFn: (n: bigint) => bigint;
roundConstants: bigint[][];
mds: bigint[][];
Fp: IField<bigint>;
t: number;
roundsFull: number;
roundsPartial: number;
sboxPower?: number | undefined;
reversePartialPowIdx?: boolean | undefined;
}>;
export declare function splitConstants(rc: bigint[], t: number): bigint[][];
export declare function poseidon(opts: PoseidonOpts): {
(values: bigint[]): bigint[];
roundConstants: bigint[][];
};
//# sourceMappingURL=poseidon.d.ts.map
@@ -1 +0,0 @@
{"version":3,"file":"poseidon.d.ts","sourceRoot":"","sources":["../src/abstract/poseidon.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EAAE,MAAM,EAAwB,MAAM,cAAc,CAAC;AAG5D,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC,EAAE,MAAM,CAAC;IACV,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC;IAChB,cAAc,EAAE,MAAM,EAAE,EAAE,CAAC;CAC5B,CAAC;AAEF,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY;;gBAgB5B,MAAM;;;;;;;;;GAgCxB;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,cAarD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY;aAeU,MAAM,EAAE;;EAsB5D"}
-116
View File
@@ -1,116 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.poseidon = exports.splitConstants = exports.validateOpts = void 0;
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
const modular_js_1 = require("./modular.js");
function validateOpts(opts) {
const { Fp } = opts;
(0, modular_js_1.validateField)(Fp);
for (const i of ['t', 'roundsFull', 'roundsPartial']) {
if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
throw new Error(`Poseidon: invalid param ${i}=${opts[i]} (${typeof opts[i]})`);
}
if (opts.reversePartialPowIdx !== undefined && typeof opts.reversePartialPowIdx !== 'boolean')
throw new Error(`Poseidon: invalid param reversePartialPowIdx=${opts.reversePartialPowIdx}`);
// Default is 5, but by some reasons stark uses 3
let sboxPower = opts.sboxPower;
if (sboxPower === undefined)
sboxPower = 5;
if (typeof sboxPower !== 'number' || !Number.isSafeInteger(sboxPower))
throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
const _sboxPower = BigInt(sboxPower);
let sboxFn = (n) => (0, modular_js_1.FpPow)(Fp, n, _sboxPower);
// Unwrapped sbox power for common cases (195->142μs)
if (sboxPower === 3)
sboxFn = (n) => Fp.mul(Fp.sqrN(n), n);
else if (sboxPower === 5)
sboxFn = (n) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
if (opts.roundsFull % 2 !== 0)
throw new Error(`Poseidon roundsFull is not even: ${opts.roundsFull}`);
const rounds = opts.roundsFull + opts.roundsPartial;
if (!Array.isArray(opts.roundConstants) || opts.roundConstants.length !== rounds)
throw new Error('Poseidon: wrong round constants');
const roundConstants = opts.roundConstants.map((rc) => {
if (!Array.isArray(rc) || rc.length !== opts.t)
throw new Error(`Poseidon wrong round constants: ${rc}`);
return rc.map((i) => {
if (typeof i !== 'bigint' || !Fp.isValid(i))
throw new Error(`Poseidon wrong round constant=${i}`);
return Fp.create(i);
});
});
// MDS is TxT matrix
if (!Array.isArray(opts.mds) || opts.mds.length !== opts.t)
throw new Error('Poseidon: wrong MDS matrix');
const mds = opts.mds.map((mdsRow) => {
if (!Array.isArray(mdsRow) || mdsRow.length !== opts.t)
throw new Error(`Poseidon MDS matrix row: ${mdsRow}`);
return mdsRow.map((i) => {
if (typeof i !== 'bigint')
throw new Error(`Poseidon MDS matrix value=${i}`);
return Fp.create(i);
});
});
return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds });
}
exports.validateOpts = validateOpts;
function splitConstants(rc, t) {
if (typeof t !== 'number')
throw new Error('poseidonSplitConstants: wrong t');
if (!Array.isArray(rc) || rc.length % t)
throw new Error('poseidonSplitConstants: wrong rc');
const res = [];
let tmp = [];
for (let i = 0; i < rc.length; i++) {
tmp.push(rc[i]);
if (tmp.length === t) {
res.push(tmp);
tmp = [];
}
}
return res;
}
exports.splitConstants = splitConstants;
function poseidon(opts) {
const { t, Fp, rounds, sboxFn, reversePartialPowIdx } = validateOpts(opts);
const halfRoundsFull = Math.floor(opts.roundsFull / 2);
const partialIdx = reversePartialPowIdx ? t - 1 : 0;
const poseidonRound = (values, isFull, idx) => {
values = values.map((i, j) => Fp.add(i, opts.roundConstants[idx][j]));
if (isFull)
values = values.map((i) => sboxFn(i));
else
values[partialIdx] = sboxFn(values[partialIdx]);
// Matrix multiplication
values = opts.mds.map((i) => i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO));
return values;
};
const poseidonHash = function poseidonHash(values) {
if (!Array.isArray(values) || values.length !== t)
throw new Error(`Poseidon: wrong values (expected array of bigints with length ${t})`);
values = values.map((i) => {
if (typeof i !== 'bigint')
throw new Error(`Poseidon: wrong value=${i} (${typeof i})`);
return Fp.create(i);
});
let round = 0;
// Apply r_f/2 full rounds.
for (let i = 0; i < halfRoundsFull; i++)
values = poseidonRound(values, true, round++);
// Apply r_p partial rounds.
for (let i = 0; i < opts.roundsPartial; i++)
values = poseidonRound(values, false, round++);
// Apply r_f/2 full rounds.
for (let i = 0; i < halfRoundsFull; i++)
values = poseidonRound(values, true, round++);
if (round !== rounds)
throw new Error(`Poseidon: wrong number of rounds: last round=${round}, total=${rounds}`);
return values;
};
// For verification in tests
poseidonHash.roundConstants = opts.roundConstants;
return poseidonHash;
}
exports.poseidon = poseidon;
//# sourceMappingURL=poseidon.js.map
File diff suppressed because one or more lines are too long
-54
View File
@@ -1,54 +0,0 @@
export type Hex = Uint8Array | string;
export type PrivKey = Hex | bigint;
export type CHash = {
(message: Uint8Array | string): Uint8Array;
blockLen: number;
outputLen: number;
create(opts?: {
dkLen?: number;
}): any;
};
export type FHash = (message: Uint8Array | string) => Uint8Array;
export declare function bytesToHex(bytes: Uint8Array): string;
export declare function numberToHexUnpadded(num: number | bigint): string;
export declare function hexToNumber(hex: string): bigint;
export declare function hexToBytes(hex: string): Uint8Array;
export declare function bytesToNumberBE(bytes: Uint8Array): bigint;
export declare function bytesToNumberLE(bytes: Uint8Array): bigint;
export declare const numberToBytesBE: (n: bigint, len: number) => Uint8Array;
export declare const numberToBytesLE: (n: bigint, len: number) => Uint8Array;
export declare const numberToVarBytesBE: (n: bigint) => Uint8Array;
export declare function ensureBytes(title: string, hex: Hex, expectedLength?: number): Uint8Array;
export declare function concatBytes(...arrs: Uint8Array[]): Uint8Array;
export declare function equalBytes(b1: Uint8Array, b2: Uint8Array): boolean;
export declare function utf8ToBytes(str: string): Uint8Array;
export declare function bitLen(n: bigint): number;
export declare const bitGet: (n: bigint, pos: number) => bigint;
export declare const bitSet: (n: bigint, pos: number, value: boolean) => bigint;
export declare const bitMask: (n: number) => bigint;
type Pred<T> = (v: Uint8Array) => T | undefined;
/**
* Minimal HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
* @returns function that will call DRBG until 2nd arg returns something meaningful
* @example
* const drbg = createHmacDRBG<Key>(32, 32, hmac);
* drbg(seed, bytesToKey); // bytesToKey must return Key or undefined
*/
export declare function createHmacDrbg<T>(hashLen: number, qByteLen: number, hmacFn: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array): (seed: Uint8Array, predicate: Pred<T>) => T;
declare const validatorFns: {
readonly bigint: (val: any) => boolean;
readonly function: (val: any) => boolean;
readonly boolean: (val: any) => boolean;
readonly string: (val: any) => boolean;
readonly isSafeInteger: (val: any) => boolean;
readonly array: (val: any) => boolean;
readonly field: (val: any, object: any) => any;
readonly hash: (val: any) => boolean;
};
type Validator = keyof typeof validatorFns;
type ValMap<T extends Record<string, any>> = {
[K in keyof T]?: Validator;
};
export declare function validateObject<T extends Record<string, any>>(object: T, validators: ValMap<T>, optValidators?: ValMap<T>): T;
export {};
//# sourceMappingURL=utils.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/abstract/utils.ts"],"names":[],"mappings":"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"}
-244
View File
@@ -1,244 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateObject = exports.createHmacDrbg = exports.bitMask = exports.bitSet = exports.bitGet = exports.bitLen = exports.utf8ToBytes = exports.equalBytes = exports.concatBytes = exports.ensureBytes = exports.numberToVarBytesBE = exports.numberToBytesLE = exports.numberToBytesBE = exports.bytesToNumberLE = exports.bytesToNumberBE = exports.hexToBytes = exports.hexToNumber = exports.numberToHexUnpadded = exports.bytesToHex = void 0;
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
const _0n = BigInt(0);
const _1n = BigInt(1);
const _2n = BigInt(2);
const u8a = (a) => a instanceof Uint8Array;
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
function bytesToHex(bytes) {
if (!u8a(bytes))
throw new Error('Uint8Array expected');
// pre-caching improves the speed 6x
let hex = '';
for (let i = 0; i < bytes.length; i++) {
hex += hexes[bytes[i]];
}
return hex;
}
exports.bytesToHex = bytesToHex;
function numberToHexUnpadded(num) {
const hex = num.toString(16);
return hex.length & 1 ? `0${hex}` : hex;
}
exports.numberToHexUnpadded = numberToHexUnpadded;
function hexToNumber(hex) {
if (typeof hex !== 'string')
throw new Error('hex string expected, got ' + typeof hex);
// Big Endian
return BigInt(hex === '' ? '0' : `0x${hex}`);
}
exports.hexToNumber = hexToNumber;
// Caching slows it down 2-3x
function hexToBytes(hex) {
if (typeof hex !== 'string')
throw new Error('hex string expected, got ' + typeof hex);
if (hex.length % 2)
throw new Error('hex string is invalid: unpadded ' + hex.length);
const array = new Uint8Array(hex.length / 2);
for (let i = 0; i < array.length; i++) {
const j = i * 2;
const hexByte = hex.slice(j, j + 2);
const byte = Number.parseInt(hexByte, 16);
if (Number.isNaN(byte) || byte < 0)
throw new Error('invalid byte sequence');
array[i] = byte;
}
return array;
}
exports.hexToBytes = hexToBytes;
// Big Endian
function bytesToNumberBE(bytes) {
return hexToNumber(bytesToHex(bytes));
}
exports.bytesToNumberBE = bytesToNumberBE;
function bytesToNumberLE(bytes) {
if (!u8a(bytes))
throw new Error('Uint8Array expected');
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
}
exports.bytesToNumberLE = bytesToNumberLE;
const numberToBytesBE = (n, len) => hexToBytes(n.toString(16).padStart(len * 2, '0'));
exports.numberToBytesBE = numberToBytesBE;
const numberToBytesLE = (n, len) => (0, exports.numberToBytesBE)(n, len).reverse();
exports.numberToBytesLE = numberToBytesLE;
// Returns variable number bytes (minimal bigint encoding?)
const numberToVarBytesBE = (n) => hexToBytes(numberToHexUnpadded(n));
exports.numberToVarBytesBE = numberToVarBytesBE;
function ensureBytes(title, hex, expectedLength) {
let res;
if (typeof hex === 'string') {
try {
res = hexToBytes(hex);
}
catch (e) {
throw new Error(`${title} must be valid hex string, got "${hex}". Cause: ${e}`);
}
}
else if (u8a(hex)) {
// Uint8Array.from() instead of hash.slice() because node.js Buffer
// is instance of Uint8Array, and its slice() creates **mutable** copy
res = Uint8Array.from(hex);
}
else {
throw new Error(`${title} must be hex string or Uint8Array`);
}
const len = res.length;
if (typeof expectedLength === 'number' && len !== expectedLength)
throw new Error(`${title} expected ${expectedLength} bytes, got ${len}`);
return res;
}
exports.ensureBytes = ensureBytes;
// Copies several Uint8Arrays into one.
function concatBytes(...arrs) {
const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0));
let pad = 0; // walk through each item, ensure they have proper type
arrs.forEach((a) => {
if (!u8a(a))
throw new Error('Uint8Array expected');
r.set(a, pad);
pad += a.length;
});
return r;
}
exports.concatBytes = concatBytes;
function equalBytes(b1, b2) {
// We don't care about timing attacks here
if (b1.length !== b2.length)
return false;
for (let i = 0; i < b1.length; i++)
if (b1[i] !== b2[i])
return false;
return true;
}
exports.equalBytes = equalBytes;
function utf8ToBytes(str) {
if (typeof str !== 'string') {
throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
}
return new TextEncoder().encode(str);
}
exports.utf8ToBytes = utf8ToBytes;
// Bit operations
// Amount of bits inside bigint (Same as n.toString(2).length)
function bitLen(n) {
let len;
for (len = 0; n > _0n; n >>= _1n, len += 1)
;
return len;
}
exports.bitLen = bitLen;
// Gets single bit at position. NOTE: first bit position is 0 (same as arrays)
// Same as !!+Array.from(n.toString(2)).reverse()[pos]
const bitGet = (n, pos) => (n >> BigInt(pos)) & _1n;
exports.bitGet = bitGet;
// Sets single bit at position
const bitSet = (n, pos, value) => n | ((value ? _1n : _0n) << BigInt(pos));
exports.bitSet = bitSet;
// Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`))
// Not using ** operator with bigints for old engines.
const bitMask = (n) => (_2n << BigInt(n - 1)) - _1n;
exports.bitMask = bitMask;
// DRBG
const u8n = (data) => new Uint8Array(data); // creates Uint8Array
const u8fr = (arr) => Uint8Array.from(arr); // another shortcut
/**
* Minimal HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
* @returns function that will call DRBG until 2nd arg returns something meaningful
* @example
* const drbg = createHmacDRBG<Key>(32, 32, hmac);
* drbg(seed, bytesToKey); // bytesToKey must return Key or undefined
*/
function createHmacDrbg(hashLen, qByteLen, hmacFn) {
if (typeof hashLen !== 'number' || hashLen < 2)
throw new Error('hashLen must be a number');
if (typeof qByteLen !== 'number' || qByteLen < 2)
throw new Error('qByteLen must be a number');
if (typeof hmacFn !== 'function')
throw new Error('hmacFn must be a function');
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
let v = u8n(hashLen); // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
let k = u8n(hashLen); // Steps B and C of RFC6979 3.2: set hashLen, in our case always same
let i = 0; // Iterations counter, will throw when over 1000
const reset = () => {
v.fill(1);
k.fill(0);
i = 0;
};
const h = (...b) => hmacFn(k, v, ...b); // hmac(k)(v, ...values)
const reseed = (seed = u8n()) => {
// HMAC-DRBG reseed() function. Steps D-G
k = h(u8fr([0x00]), seed); // k = hmac(k || v || 0x00 || seed)
v = h(); // v = hmac(k || v)
if (seed.length === 0)
return;
k = h(u8fr([0x01]), seed); // k = hmac(k || v || 0x01 || seed)
v = h(); // v = hmac(k || v)
};
const gen = () => {
// HMAC-DRBG generate() function
if (i++ >= 1000)
throw new Error('drbg: tried 1000 values');
let len = 0;
const out = [];
while (len < qByteLen) {
v = h();
const sl = v.slice();
out.push(sl);
len += v.length;
}
return concatBytes(...out);
};
const genUntil = (seed, pred) => {
reset();
reseed(seed); // Steps D-G
let res = undefined; // Step H: grind until k is in [1..n-1]
while (!(res = pred(gen())))
reseed();
reset();
return res;
};
return genUntil;
}
exports.createHmacDrbg = createHmacDrbg;
// Validating curves and fields
const validatorFns = {
bigint: (val) => typeof val === 'bigint',
function: (val) => typeof val === 'function',
boolean: (val) => typeof val === 'boolean',
string: (val) => typeof val === 'string',
isSafeInteger: (val) => Number.isSafeInteger(val),
array: (val) => Array.isArray(val),
field: (val, object) => object.Fp.isValid(val),
hash: (val) => typeof val === 'function' && Number.isSafeInteger(val.outputLen),
};
// type Record<K extends string | number | symbol, T> = { [P in K]: T; }
function validateObject(object, validators, optValidators = {}) {
const checkField = (fieldName, type, isOptional) => {
const checkVal = validatorFns[type];
if (typeof checkVal !== 'function')
throw new Error(`Invalid validator "${type}", expected function`);
const val = object[fieldName];
if (isOptional && val === undefined)
return;
if (!checkVal(val, object)) {
throw new Error(`Invalid param ${String(fieldName)}=${val} (${typeof val}), expected ${type}`);
}
};
for (const [fieldName, type] of Object.entries(validators))
checkField(fieldName, type, false);
for (const [fieldName, type] of Object.entries(optValidators))
checkField(fieldName, type, true);
return object;
}
exports.validateObject = validateObject;
// validate type tests
// const o: { a: number; b: number; c: number } = { a: 1, b: 5, c: 6 };
// const z0 = validateObject(o, { a: 'isSafeInteger' }, { c: 'bigint' }); // Ok!
// // Should fail type-check
// const z1 = validateObject(o, { a: 'tmp' }, { c: 'zz' });
// const z2 = validateObject(o, { a: 'isSafeInteger' }, { c: 'zz' });
// const z3 = validateObject(o, { test: 'boolean', z: 'bug' });
// const z4 = validateObject(o, { a: 'boolean', z: 'bug' });
//# sourceMappingURL=utils.js.map
File diff suppressed because one or more lines are too long
-225
View File
@@ -1,225 +0,0 @@
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
import * as mod from './modular.js';
import * as ut from './utils.js';
import { CHash, Hex, PrivKey } from './utils.js';
import { Group, GroupConstructor, BasicCurve, AffinePoint } from './curve.js';
export type { AffinePoint };
type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
type EndomorphismOpts = {
beta: bigint;
splitScalar: (k: bigint) => {
k1neg: boolean;
k1: bigint;
k2neg: boolean;
k2: bigint;
};
};
export type BasicWCurve<T> = BasicCurve<T> & {
a: T;
b: T;
allowedPrivateKeyLengths?: readonly number[];
wrapPrivateKey?: boolean;
endo?: EndomorphismOpts;
isTorsionFree?: (c: ProjConstructor<T>, point: ProjPointType<T>) => boolean;
clearCofactor?: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
};
type Entropy = Hex | true;
export type SignOpts = {
lowS?: boolean;
extraEntropy?: Entropy;
prehash?: boolean;
};
export type VerOpts = {
lowS?: boolean;
prehash?: boolean;
};
/**
* ### Design rationale for types
*
* * Interaction between classes from different curves should fail:
* `k256.Point.BASE.add(p256.Point.BASE)`
* * For this purpose we want to use `instanceof` operator, which is fast and works during runtime
* * Different calls of `curve()` would return different classes -
* `curve(params) !== curve(params)`: if somebody decided to monkey-patch their curve,
* it won't affect others
*
* TypeScript can't infer types for classes created inside a function. Classes is one instance of nominative types in TypeScript and interfaces only check for shape, so it's hard to create unique type for every function call.
*
* We can use generic types via some param, like curve opts, but that would:
* 1. Enable interaction between `curve(params)` and `curve(params)` (curves of same params)
* which is hard to debug.
* 2. Params can be generic and we can't enforce them to be constant value:
* if somebody creates curve from non-constant params,
* it would be allowed to interact with other curves with non-constant params
*
* TODO: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#unique-symbol
*/
export interface ProjPointType<T> extends Group<ProjPointType<T>> {
readonly px: T;
readonly py: T;
readonly pz: T;
get x(): T;
get y(): T;
multiply(scalar: bigint): ProjPointType<T>;
toAffine(iz?: T): AffinePoint<T>;
isTorsionFree(): boolean;
clearCofactor(): ProjPointType<T>;
assertValidity(): void;
hasEvenY(): boolean;
toRawBytes(isCompressed?: boolean): Uint8Array;
toHex(isCompressed?: boolean): string;
multiplyUnsafe(scalar: bigint): ProjPointType<T>;
multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
_setWindowSize(windowSize: number): void;
}
export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
new (x: T, y: T, z: T): ProjPointType<T>;
fromAffine(p: AffinePoint<T>): ProjPointType<T>;
fromHex(hex: Hex): ProjPointType<T>;
fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
normalizeZ(points: ProjPointType<T>[]): ProjPointType<T>[];
}
export type CurvePointsType<T> = BasicWCurve<T> & {
fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
toBytes?: (c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array;
};
export type CurvePointsRes<T> = {
ProjectivePoint: ProjConstructor<T>;
normPrivateKeyToScalar: (key: PrivKey) => bigint;
weierstrassEquation: (x: T) => T;
isWithinCurveOrder: (num: bigint) => boolean;
};
export declare const DER: {
Err: {
new (m?: string): {
name: string;
message: string;
stack?: string | undefined;
};
};
_parseInt(data: Uint8Array): {
d: bigint;
l: Uint8Array;
};
toSig(hex: string | Uint8Array): {
r: bigint;
s: bigint;
};
hexFromSig(sig: {
r: bigint;
s: bigint;
}): string;
};
export declare function weierstrassPoints<T>(opts: CurvePointsType<T>): {
CURVE: Readonly<{
readonly nBitLength: number;
readonly nByteLength: number;
readonly Fp: mod.IField<T>;
readonly n: bigint;
readonly h: bigint;
readonly hEff?: bigint | undefined;
readonly Gx: T;
readonly Gy: T;
readonly allowInfinityPoint?: boolean | undefined;
readonly a: T;
readonly b: T;
readonly allowedPrivateKeyLengths?: readonly number[] | undefined;
readonly wrapPrivateKey?: boolean | undefined;
readonly endo?: EndomorphismOpts | undefined;
readonly isTorsionFree?: ((c: ProjConstructor<T>, point: ProjPointType<T>) => boolean) | undefined;
readonly clearCofactor?: ((c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>) | undefined;
readonly fromBytes?: ((bytes: Uint8Array) => AffinePoint<T>) | undefined;
readonly toBytes?: ((c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array) | undefined;
readonly p: bigint;
}>;
ProjectivePoint: ProjConstructor<T>;
normPrivateKeyToScalar: (key: PrivKey) => bigint;
weierstrassEquation: (x: T) => T;
isWithinCurveOrder: (num: bigint) => boolean;
};
export interface SignatureType {
readonly r: bigint;
readonly s: bigint;
readonly recovery?: number;
assertValidity(): void;
addRecoveryBit(recovery: number): SignatureType;
hasHighS(): boolean;
normalizeS(): SignatureType;
recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
toCompactRawBytes(): Uint8Array;
toCompactHex(): string;
toDERRawBytes(isCompressed?: boolean): Uint8Array;
toDERHex(isCompressed?: boolean): string;
}
export type SignatureConstructor = {
new (r: bigint, s: bigint): SignatureType;
fromCompact(hex: Hex): SignatureType;
fromDER(hex: Hex): SignatureType;
};
type SignatureLike = {
r: bigint;
s: bigint;
};
export type PubKey = Hex | ProjPointType<bigint>;
export type CurveType = BasicWCurve<bigint> & {
hash: CHash;
hmac: HmacFnSync;
randomBytes: (bytesLength?: number) => Uint8Array;
lowS?: boolean;
bits2int?: (bytes: Uint8Array) => bigint;
bits2int_modN?: (bytes: Uint8Array) => bigint;
};
declare function validateOpts(curve: CurveType): Readonly<{
readonly nBitLength: number;
readonly nByteLength: number;
readonly Fp: mod.IField<bigint>;
readonly n: bigint;
readonly h: bigint;
readonly hEff?: bigint | undefined;
readonly Gx: bigint;
readonly Gy: bigint;
readonly allowInfinityPoint?: boolean | undefined;
readonly a: bigint;
readonly b: bigint;
readonly allowedPrivateKeyLengths?: readonly number[] | undefined;
readonly wrapPrivateKey?: boolean | undefined;
readonly endo?: EndomorphismOpts | undefined;
readonly isTorsionFree?: ((c: ProjConstructor<bigint>, point: ProjPointType<bigint>) => boolean) | undefined;
readonly clearCofactor?: ((c: ProjConstructor<bigint>, point: ProjPointType<bigint>) => ProjPointType<bigint>) | undefined;
readonly hash: ut.CHash;
readonly hmac: HmacFnSync;
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array;
lowS: boolean;
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined;
readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined;
readonly p: bigint;
}>;
export type CurveFn = {
CURVE: ReturnType<typeof validateOpts>;
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
ProjectivePoint: ProjConstructor<bigint>;
Signature: SignatureConstructor;
utils: {
normPrivateKeyToScalar: (key: PrivKey) => bigint;
isValidPrivateKey(privateKey: PrivKey): boolean;
randomPrivateKey: () => Uint8Array;
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
};
};
export declare function weierstrass(curveDef: CurveType): CurveFn;
export declare function SWUFpSqrtRatio<T>(Fp: mod.IField<T>, Z: T): (u: T, v: T) => {
isValid: boolean;
value: T;
};
export declare function mapToCurveSimpleSWU<T>(Fp: mod.IField<T>, opts: {
A: T;
B: T;
Z: T;
}): (u: T) => {
x: T;
y: T;
};
//# sourceMappingURL=weierstrass.d.ts.map
File diff suppressed because one or more lines are too long

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