diff --git a/lnbits/extensions/cashu/README.md b/lnbits/extensions/cashu/README.md
deleted file mode 100644
index 8f53b474..00000000
--- a/lnbits/extensions/cashu/README.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Cashu
-
-## Create ecash mint for pegging in/out of ecash
-
-
-
-### Usage
-
-1. Enable extension
-2. Create a Mint
-3. Share wallet
diff --git a/lnbits/extensions/cashu/__init__.py b/lnbits/extensions/cashu/__init__.py
deleted file mode 100644
index ca831ce0..00000000
--- a/lnbits/extensions/cashu/__init__.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import asyncio
-
-from environs import Env
-from fastapi import APIRouter
-from fastapi.staticfiles import StaticFiles
-
-from lnbits.db import Database
-from lnbits.helpers import template_renderer
-from lnbits.tasks import catch_everything_and_restart
-
-db = Database("ext_cashu")
-
-
-cashu_static_files = [
- {
- "path": "/cashu/static",
- "app": StaticFiles(directory="lnbits/extensions/cashu/static"),
- "name": "cashu_static",
- }
-]
-from cashu.mint.ledger import Ledger
-
-env = Env()
-env.read_env()
-
-ledger = Ledger(
- db=db,
- seed=env.str("CASHU_PRIVATE_KEY", default="SuperSecretPrivateKey"),
- derivation_path="0/0/0/1",
-)
-
-cashu_ext: APIRouter = APIRouter(prefix="/cashu", tags=["cashu"])
-
-
-def cashu_renderer():
- return template_renderer(["lnbits/extensions/cashu/templates"])
-
-
-from .tasks import startup_cashu_mint, wait_for_paid_invoices
-from .views import * # noqa: F401,F403
-from .views_api import * # noqa: F401,F403
-
-
-def cashu_start():
- loop = asyncio.get_event_loop()
- loop.create_task(catch_everything_and_restart(startup_cashu_mint))
- loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
diff --git a/lnbits/extensions/cashu/config.json b/lnbits/extensions/cashu/config.json
deleted file mode 100644
index 14ff1743..00000000
--- a/lnbits/extensions/cashu/config.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "Cashu",
- "short_description": "Ecash mint and wallet",
- "tile": "/cashu/static/image/cashu.png",
- "contributors": ["calle", "vlad", "arcbtc"],
- "hidden": false
-}
diff --git a/lnbits/extensions/cashu/crud.py b/lnbits/extensions/cashu/crud.py
deleted file mode 100644
index e27cc98c..00000000
--- a/lnbits/extensions/cashu/crud.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from typing import List, Optional, Union
-
-from . import db
-from .models import Cashu
-
-
-async def create_cashu(
- cashu_id: str, keyset_id: str, wallet_id: str, data: Cashu
-) -> Cashu:
-
- await db.execute(
- """
- INSERT INTO cashu.cashu (id, wallet, name, tickershort, fraction, maxsats, coins, keyset_id)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
- """,
- (
- cashu_id,
- wallet_id,
- data.name,
- data.tickershort,
- data.fraction,
- data.maxsats,
- data.coins,
- keyset_id,
- ),
- )
-
- cashu = await get_cashu(cashu_id)
- assert cashu, "Newly created cashu couldn't be retrieved"
- return cashu
-
-
-async def get_cashu(cashu_id) -> Optional[Cashu]:
- row = await db.fetchone("SELECT * FROM cashu.cashu WHERE id = ?", (cashu_id,))
- return Cashu(**row) if row else None
-
-
-async def get_cashus(wallet_ids: Union[str, List[str]]) -> List[Cashu]:
- if isinstance(wallet_ids, str):
- wallet_ids = [wallet_ids]
-
- q = ",".join(["?"] * len(wallet_ids))
- rows = await db.fetchall(
- f"SELECT * FROM cashu.cashu WHERE wallet IN ({q})", (*wallet_ids,)
- )
-
- return [Cashu(**row) for row in rows]
-
-
-async def delete_cashu(cashu_id) -> None:
- await db.execute("DELETE FROM cashu.cashu WHERE id = ?", (cashu_id,))
diff --git a/lnbits/extensions/cashu/migrations.py b/lnbits/extensions/cashu/migrations.py
deleted file mode 100644
index b54c4108..00000000
--- a/lnbits/extensions/cashu/migrations.py
+++ /dev/null
@@ -1,33 +0,0 @@
-async def m001_initial(db):
- """
- Initial cashu table.
- """
- await db.execute(
- """
- CREATE TABLE cashu.cashu (
- id TEXT PRIMARY KEY,
- wallet TEXT NOT NULL,
- name TEXT NOT NULL,
- tickershort TEXT DEFAULT 'sats',
- fraction BOOL,
- maxsats INT,
- coins INT,
- keyset_id TEXT NOT NULL,
- issued_sat INT
- );
- """
- )
-
- """
- Initial cashus table.
- """
- await db.execute(
- """
- CREATE TABLE cashu.pegs (
- id TEXT PRIMARY KEY,
- wallet TEXT NOT NULL,
- inout BOOL NOT NULL,
- amount INT
- );
- """
- )
diff --git a/lnbits/extensions/cashu/models.py b/lnbits/extensions/cashu/models.py
deleted file mode 100644
index aaff195f..00000000
--- a/lnbits/extensions/cashu/models.py
+++ /dev/null
@@ -1,148 +0,0 @@
-from sqlite3 import Row
-from typing import List
-
-from fastapi import Query
-from pydantic import BaseModel
-
-
-class Cashu(BaseModel):
- id: str = Query(None)
- name: str = Query(None)
- wallet: str = Query(None)
- tickershort: str = Query(None)
- fraction: bool = Query(None)
- maxsats: int = Query(0)
- coins: int = Query(0)
- keyset_id: str = Query(None)
-
- @classmethod
- def from_row(cls, row: Row):
- return cls(**dict(row))
-
-
-class Pegs(BaseModel):
- id: str
- wallet: str
- inout: str
- amount: str
-
- @classmethod
- def from_row(cls, row: Row):
- return cls(**dict(row))
-
-
-class PayLnurlWData(BaseModel):
- lnurl: str
-
-
-class Promises(BaseModel):
- id: str
- amount: int
- B_b: str
- C_b: str
- cashu_id: str
-
-
-class Proof(BaseModel):
- amount: int
- secret: str
- C: str
- reserved: bool = False # whether this proof is reserved for sending
- send_id: str = "" # unique ID of send attempt
- time_created: str = ""
- time_reserved: str = ""
-
- @classmethod
- def from_row(cls, row: Row):
- return cls(
- amount=row[0],
- C=row[1],
- secret=row[2],
- reserved=row[3] or False,
- send_id=row[4] or "",
- time_created=row[5] or "",
- time_reserved=row[6] or "",
- )
-
- @classmethod
- def from_dict(cls, d: dict):
- assert "secret" in d, "no secret in proof"
- assert "amount" in d, "no amount in proof"
- assert "C" in d, "no C in proof"
- return cls(
- amount=d["amount"],
- C=d["C"],
- secret=d["secret"],
- reserved=d.get("reserved") or False,
- send_id=d.get("send_id") or "",
- time_created=d.get("time_created") or "",
- time_reserved=d.get("time_reserved") or "",
- )
-
- def to_dict(self):
- return dict(amount=self.amount, secret=self.secret, C=self.C)
-
- def __getitem__(self, key):
- return self.__getattribute__(key)
-
- def __setitem__(self, key, val):
- self.__setattr__(key, val)
-
-
-class Proofs(BaseModel):
- """TODO: Use this model"""
-
- proofs: List[Proof]
-
-
-class Invoice(BaseModel):
- amount: int
- pr: str
- hash: str
- issued: bool = False
-
- @classmethod
- def from_row(cls, row: Row):
- return cls(
- amount=int(row[0]),
- pr=str(row[1]),
- hash=str(row[2]),
- issued=bool(row[3]),
- )
-
-
-class BlindedMessage(BaseModel):
- amount: int
- B_: str
-
-
-class BlindedSignature(BaseModel):
- amount: int
- C_: str
-
- @classmethod
- def from_dict(cls, d: dict):
- return cls(
- amount=d["amount"],
- C_=d["C_"],
- )
-
-
-class MintPayloads(BaseModel):
- blinded_messages: List[BlindedMessage] = []
-
-
-class SplitPayload(BaseModel):
- proofs: List[Proof]
- amount: int
- output_data: MintPayloads
-
-
-class CheckPayload(BaseModel):
- proofs: List[Proof]
-
-
-class MeltPayload(BaseModel):
- proofs: List[Proof]
- amount: int
- invoice: str
diff --git a/lnbits/extensions/cashu/static/image/cashu.png b/lnbits/extensions/cashu/static/image/cashu.png
deleted file mode 100644
index e90611fc..00000000
Binary files a/lnbits/extensions/cashu/static/image/cashu.png and /dev/null differ
diff --git a/lnbits/extensions/cashu/static/js/base64.js b/lnbits/extensions/cashu/static/js/base64.js
deleted file mode 100644
index b150882f..00000000
--- a/lnbits/extensions/cashu/static/js/base64.js
+++ /dev/null
@@ -1,37 +0,0 @@
-function unescapeBase64Url(str) {
- return (str + '==='.slice((str.length + 3) % 4))
- .replace(/-/g, '+')
- .replace(/_/g, '/')
-}
-
-function escapeBase64Url(str) {
- return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
-}
-
-const uint8ToBase64 = (function (exports) {
- 'use strict'
-
- var fromCharCode = String.fromCharCode
- var encode = function encode(uint8array) {
- var output = []
-
- for (var i = 0, length = uint8array.length; i < length; i++) {
- output.push(fromCharCode(uint8array[i]))
- }
-
- return btoa(output.join(''))
- }
-
- var asCharCode = function asCharCode(c) {
- return c.charCodeAt(0)
- }
-
- var decode = function decode(chars) {
- return Uint8Array.from(atob(chars), asCharCode)
- }
-
- exports.decode = decode
- exports.encode = encode
-
- return exports
-})({})
diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js
deleted file mode 100644
index cebef240..00000000
--- a/lnbits/extensions/cashu/static/js/dhke.js
+++ /dev/null
@@ -1,31 +0,0 @@
-async function hashToCurve(secretMessage) {
- let point
- while (!point) {
- const hash = await nobleSecp256k1.utils.sha256(secretMessage)
- const hashHex = nobleSecp256k1.utils.bytesToHex(hash)
- const pointX = '02' + hashHex
- try {
- point = nobleSecp256k1.Point.fromHex(pointX)
- } catch (error) {
- secretMessage = await nobleSecp256k1.utils.sha256(secretMessage)
- }
- }
- return point
-}
-
-async function step1Alice(secretMessage) {
- secretMessage = uint8ToBase64.encode(secretMessage)
- secretMessage = new TextEncoder().encode(secretMessage)
- const Y = await hashToCurve(secretMessage)
- const r_bytes = nobleSecp256k1.utils.randomPrivateKey()
- const r = bytesToNumber(r_bytes)
- const P = nobleSecp256k1.Point.fromPrivateKey(r)
- const B_ = Y.add(P)
- return {B_: B_.toHex(true), r: nobleSecp256k1.utils.bytesToHex(r_bytes)}
-}
-
-function step3Alice(C_, r, A) {
- const rInt = bytesToNumber(r)
- const C = C_.subtract(A.multiply(rInt))
- return C
-}
diff --git a/lnbits/extensions/cashu/static/js/noble-secp256k1.js b/lnbits/extensions/cashu/static/js/noble-secp256k1.js
deleted file mode 100644
index 6a6bd441..00000000
--- a/lnbits/extensions/cashu/static/js/noble-secp256k1.js
+++ /dev/null
@@ -1,1178 +0,0 @@
-;(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined'
- ? factory(exports)
- : typeof define === 'function' && define.amd
- ? define(['exports'], factory)
- : ((global =
- typeof globalThis !== 'undefined' ? globalThis : global || self),
- factory((global.nobleSecp256k1 = {})))
-})(this, function (exports) {
- 'use strict'
-
- const _nodeResolve_empty = {}
-
- const nodeCrypto = /*#__PURE__*/ Object.freeze({
- __proto__: null,
- default: _nodeResolve_empty
- })
-
- /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
- const _0n = BigInt(0)
- const _1n = BigInt(1)
- const _2n = BigInt(2)
- const _3n = BigInt(3)
- const _8n = BigInt(8)
- const POW_2_256 = _2n ** BigInt(256)
- const CURVE = {
- a: _0n,
- b: BigInt(7),
- P: POW_2_256 - _2n ** BigInt(32) - BigInt(977),
- n: POW_2_256 - BigInt('432420386565659656852420866394968145599'),
- h: _1n,
- Gx: BigInt(
- '55066263022277343669578718895168534326250603453777594175500187360389116729240'
- ),
- Gy: BigInt(
- '32670510020758816978083085130507043184471273380659243275938904335757337482424'
- ),
- beta: BigInt(
- '0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'
- )
- }
- function weistrass(x) {
- const {a, b} = CURVE
- const x2 = mod(x * x)
- const x3 = mod(x2 * x)
- return mod(x3 + a * x + b)
- }
- const USE_ENDOMORPHISM = CURVE.a === _0n
- class JacobianPoint {
- constructor(x, y, z) {
- this.x = x
- this.y = y
- this.z = z
- }
- static fromAffine(p) {
- if (!(p instanceof Point)) {
- throw new TypeError('JacobianPoint#fromAffine: expected Point')
- }
- return new JacobianPoint(p.x, p.y, _1n)
- }
- static toAffineBatch(points) {
- const toInv = invertBatch(points.map(p => p.z))
- return points.map((p, i) => p.toAffine(toInv[i]))
- }
- static normalizeZ(points) {
- return JacobianPoint.toAffineBatch(points).map(JacobianPoint.fromAffine)
- }
- equals(other) {
- if (!(other instanceof JacobianPoint))
- throw new TypeError('JacobianPoint expected')
- const {x: X1, y: Y1, z: Z1} = this
- const {x: X2, y: Y2, z: Z2} = other
- const Z1Z1 = mod(Z1 ** _2n)
- const Z2Z2 = mod(Z2 ** _2n)
- const U1 = mod(X1 * Z2Z2)
- const U2 = mod(X2 * Z1Z1)
- const S1 = mod(mod(Y1 * Z2) * Z2Z2)
- const S2 = mod(mod(Y2 * Z1) * Z1Z1)
- return U1 === U2 && S1 === S2
- }
- negate() {
- return new JacobianPoint(this.x, mod(-this.y), this.z)
- }
- double() {
- const {x: X1, y: Y1, z: Z1} = this
- const A = mod(X1 ** _2n)
- const B = mod(Y1 ** _2n)
- const C = mod(B ** _2n)
- const D = mod(_2n * (mod((X1 + B) ** _2n) - A - C))
- const E = mod(_3n * A)
- const F = mod(E ** _2n)
- const X3 = mod(F - _2n * D)
- const Y3 = mod(E * (D - X3) - _8n * C)
- const Z3 = mod(_2n * Y1 * Z1)
- return new JacobianPoint(X3, Y3, Z3)
- }
- add(other) {
- if (!(other instanceof JacobianPoint))
- throw new TypeError('JacobianPoint expected')
- const {x: X1, y: Y1, z: Z1} = this
- const {x: X2, y: Y2, z: Z2} = other
- if (X2 === _0n || Y2 === _0n) return this
- if (X1 === _0n || Y1 === _0n) return other
- const Z1Z1 = mod(Z1 ** _2n)
- const Z2Z2 = mod(Z2 ** _2n)
- const U1 = mod(X1 * Z2Z2)
- const U2 = mod(X2 * Z1Z1)
- const S1 = mod(mod(Y1 * Z2) * Z2Z2)
- const S2 = mod(mod(Y2 * Z1) * Z1Z1)
- const H = mod(U2 - U1)
- const r = mod(S2 - S1)
- if (H === _0n) {
- if (r === _0n) {
- return this.double()
- } else {
- return JacobianPoint.ZERO
- }
- }
- const HH = mod(H ** _2n)
- const HHH = mod(H * HH)
- const V = mod(U1 * HH)
- const X3 = mod(r ** _2n - HHH - _2n * V)
- const Y3 = mod(r * (V - X3) - S1 * HHH)
- const Z3 = mod(Z1 * Z2 * H)
- return new JacobianPoint(X3, Y3, Z3)
- }
- subtract(other) {
- return this.add(other.negate())
- }
- multiplyUnsafe(scalar) {
- const P0 = JacobianPoint.ZERO
- if (typeof scalar === 'bigint' && scalar === _0n) return P0
- let n = normalizeScalar(scalar)
- if (n === _1n) return this
- if (!USE_ENDOMORPHISM) {
- let p = P0
- let d = this
- while (n > _0n) {
- if (n & _1n) p = p.add(d)
- d = d.double()
- n >>= _1n
- }
- return p
- }
- let {k1neg, k1, k2neg, k2} = splitScalarEndo(n)
- let k1p = P0
- let k2p = P0
- let d = this
- while (k1 > _0n || k2 > _0n) {
- if (k1 & _1n) k1p = k1p.add(d)
- if (k2 & _1n) k2p = k2p.add(d)
- d = d.double()
- k1 >>= _1n
- k2 >>= _1n
- }
- if (k1neg) k1p = k1p.negate()
- if (k2neg) k2p = k2p.negate()
- k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z)
- return k1p.add(k2p)
- }
- precomputeWindow(W) {
- const windows = USE_ENDOMORPHISM ? 128 / W + 1 : 256 / W + 1
- const points = []
- let p = this
- let base = p
- for (let window = 0; window < windows; window++) {
- base = p
- points.push(base)
- for (let i = 1; i < 2 ** (W - 1); i++) {
- base = base.add(p)
- points.push(base)
- }
- p = base.double()
- }
- return points
- }
- wNAF(n, affinePoint) {
- if (!affinePoint && this.equals(JacobianPoint.BASE))
- affinePoint = Point.BASE
- const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1
- if (256 % W) {
- throw new Error(
- 'Point#wNAF: Invalid precomputation window, must be power of 2'
- )
- }
- let precomputes = affinePoint && pointPrecomputes.get(affinePoint)
- if (!precomputes) {
- precomputes = this.precomputeWindow(W)
- if (affinePoint && W !== 1) {
- precomputes = JacobianPoint.normalizeZ(precomputes)
- pointPrecomputes.set(affinePoint, precomputes)
- }
- }
- let p = JacobianPoint.ZERO
- let f = JacobianPoint.ZERO
- const windows = 1 + (USE_ENDOMORPHISM ? 128 / W : 256 / W)
- const windowSize = 2 ** (W - 1)
- const mask = BigInt(2 ** W - 1)
- const maxNumber = 2 ** W
- const shiftBy = BigInt(W)
- for (let window = 0; window < windows; window++) {
- const offset = window * windowSize
- let wbits = Number(n & mask)
- n >>= shiftBy
- if (wbits > windowSize) {
- wbits -= maxNumber
- n += _1n
- }
- if (wbits === 0) {
- let pr = precomputes[offset]
- if (window % 2) pr = pr.negate()
- f = f.add(pr)
- } else {
- let cached = precomputes[offset + Math.abs(wbits) - 1]
- if (wbits < 0) cached = cached.negate()
- p = p.add(cached)
- }
- }
- return {p, f}
- }
- multiply(scalar, affinePoint) {
- let n = normalizeScalar(scalar)
- let point
- let fake
- if (USE_ENDOMORPHISM) {
- const {k1neg, k1, k2neg, k2} = splitScalarEndo(n)
- let {p: k1p, f: f1p} = this.wNAF(k1, affinePoint)
- let {p: k2p, f: f2p} = this.wNAF(k2, affinePoint)
- if (k1neg) k1p = k1p.negate()
- if (k2neg) k2p = k2p.negate()
- k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z)
- point = k1p.add(k2p)
- fake = f1p.add(f2p)
- } else {
- const {p, f} = this.wNAF(n, affinePoint)
- point = p
- fake = f
- }
- return JacobianPoint.normalizeZ([point, fake])[0]
- }
- toAffine(invZ = invert(this.z)) {
- const {x, y, z} = this
- const iz1 = invZ
- const iz2 = mod(iz1 * iz1)
- const iz3 = mod(iz2 * iz1)
- const ax = mod(x * iz2)
- const ay = mod(y * iz3)
- const zz = mod(z * iz1)
- if (zz !== _1n) throw new Error('invZ was invalid')
- return new Point(ax, ay)
- }
- }
- JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, _1n)
- JacobianPoint.ZERO = new JacobianPoint(_0n, _1n, _0n)
- const pointPrecomputes = new WeakMap()
- class Point {
- constructor(x, y) {
- this.x = x
- this.y = y
- }
- _setWindowSize(windowSize) {
- this._WINDOW_SIZE = windowSize
- pointPrecomputes.delete(this)
- }
- static fromCompressedHex(bytes) {
- const isShort = bytes.length === 32
- const x = bytesToNumber(isShort ? bytes : bytes.subarray(1))
- if (!isValidFieldElement(x)) throw new Error('Point is not on curve')
- const y2 = weistrass(x)
- let y = sqrtMod(y2)
- const isYOdd = (y & _1n) === _1n
- if (isShort) {
- if (isYOdd) y = mod(-y)
- } else {
- const isFirstByteOdd = (bytes[0] & 1) === 1
- if (isFirstByteOdd !== isYOdd) y = mod(-y)
- }
- const point = new Point(x, y)
- point.assertValidity()
- return point
- }
- static fromUncompressedHex(bytes) {
- const x = bytesToNumber(bytes.subarray(1, 33))
- const y = bytesToNumber(bytes.subarray(33, 65))
- const point = new Point(x, y)
- point.assertValidity()
- return point
- }
- static fromHex(hex) {
- const bytes = ensureBytes(hex)
- const len = bytes.length
- const header = bytes[0]
- if (len === 32 || (len === 33 && (header === 0x02 || header === 0x03))) {
- return this.fromCompressedHex(bytes)
- }
- if (len === 65 && header === 0x04) return this.fromUncompressedHex(bytes)
- throw new Error(
- `Point.fromHex: received invalid point. Expected 32-33 compressed bytes or 65 uncompressed bytes, not ${len}`
- )
- }
- static fromPrivateKey(privateKey) {
- return Point.BASE.multiply(normalizePrivateKey(privateKey))
- }
- static fromSignature(msgHash, signature, recovery) {
- msgHash = ensureBytes(msgHash)
- const h = truncateHash(msgHash)
- const {r, s} = normalizeSignature(signature)
- if (recovery !== 0 && recovery !== 1) {
- throw new Error('Cannot recover signature: invalid recovery bit')
- }
- const prefix = recovery & 1 ? '03' : '02'
- const R = Point.fromHex(prefix + numTo32bStr(r))
- const {n} = CURVE
- const rinv = invert(r, n)
- const u1 = mod(-h * rinv, n)
- const u2 = mod(s * rinv, n)
- const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2)
- if (!Q) throw new Error('Cannot recover signature: point at infinify')
- Q.assertValidity()
- return Q
- }
- toRawBytes(isCompressed = false) {
- return hexToBytes(this.toHex(isCompressed))
- }
- toHex(isCompressed = false) {
- const x = numTo32bStr(this.x)
- if (isCompressed) {
- const prefix = this.y & _1n ? '03' : '02'
- return `${prefix}${x}`
- } else {
- return `04${x}${numTo32bStr(this.y)}`
- }
- }
- toHexX() {
- return this.toHex(true).slice(2)
- }
- toRawX() {
- return this.toRawBytes(true).slice(1)
- }
- assertValidity() {
- const msg = 'Point is not on elliptic curve'
- const {x, y} = this
- if (!isValidFieldElement(x) || !isValidFieldElement(y))
- throw new Error(msg)
- const left = mod(y * y)
- const right = weistrass(x)
- if (mod(left - right) !== _0n) throw new Error(msg)
- }
- equals(other) {
- return this.x === other.x && this.y === other.y
- }
- negate() {
- return new Point(this.x, mod(-this.y))
- }
- double() {
- return JacobianPoint.fromAffine(this).double().toAffine()
- }
- add(other) {
- return JacobianPoint.fromAffine(this)
- .add(JacobianPoint.fromAffine(other))
- .toAffine()
- }
- subtract(other) {
- return this.add(other.negate())
- }
- multiply(scalar) {
- return JacobianPoint.fromAffine(this).multiply(scalar, this).toAffine()
- }
- multiplyAndAddUnsafe(Q, a, b) {
- const P = JacobianPoint.fromAffine(this)
- const aP =
- a === _0n || a === _1n || this !== Point.BASE
- ? P.multiplyUnsafe(a)
- : P.multiply(a)
- const bQ = JacobianPoint.fromAffine(Q).multiplyUnsafe(b)
- const sum = aP.add(bQ)
- return sum.equals(JacobianPoint.ZERO) ? undefined : sum.toAffine()
- }
- }
- Point.BASE = new Point(CURVE.Gx, CURVE.Gy)
- Point.ZERO = new Point(_0n, _0n)
- function sliceDER(s) {
- return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s
- }
- function parseDERInt(data) {
- if (data.length < 2 || data[0] !== 0x02) {
- throw new Error(`Invalid signature integer tag: ${bytesToHex(data)}`)
- }
- const len = data[1]
- const res = data.subarray(2, len + 2)
- if (!len || res.length !== len) {
- throw new Error(`Invalid signature integer: wrong length`)
- }
- if (res[0] === 0x00 && res[1] <= 0x7f) {
- throw new Error('Invalid signature integer: trailing length')
- }
- return {data: bytesToNumber(res), left: data.subarray(len + 2)}
- }
- function parseDERSignature(data) {
- if (data.length < 2 || data[0] != 0x30) {
- throw new Error(`Invalid signature tag: ${bytesToHex(data)}`)
- }
- if (data[1] !== data.length - 2) {
- throw new Error('Invalid signature: incorrect length')
- }
- const {data: r, left: sBytes} = parseDERInt(data.subarray(2))
- const {data: s, left: rBytesLeft} = parseDERInt(sBytes)
- if (rBytesLeft.length) {
- throw new Error(
- `Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}`
- )
- }
- return {r, s}
- }
- class Signature {
- constructor(r, s) {
- this.r = r
- this.s = s
- this.assertValidity()
- }
- static fromCompact(hex) {
- const arr = isUint8a(hex)
- const name = 'Signature.fromCompact'
- if (typeof hex !== 'string' && !arr)
- throw new TypeError(`${name}: Expected string or Uint8Array`)
- const str = arr ? bytesToHex(hex) : hex
- if (str.length !== 128) throw new Error(`${name}: Expected 64-byte hex`)
- return new Signature(
- hexToNumber(str.slice(0, 64)),
- hexToNumber(str.slice(64, 128))
- )
- }
- static fromDER(hex) {
- const arr = isUint8a(hex)
- if (typeof hex !== 'string' && !arr)
- throw new TypeError(`Signature.fromDER: Expected string or Uint8Array`)
- const {r, s} = parseDERSignature(arr ? hex : hexToBytes(hex))
- return new Signature(r, s)
- }
- static fromHex(hex) {
- return this.fromDER(hex)
- }
- assertValidity() {
- const {r, s} = this
- if (!isWithinCurveOrder(r))
- throw new Error('Invalid Signature: r must be 0 < r < n')
- if (!isWithinCurveOrder(s))
- throw new Error('Invalid Signature: s must be 0 < s < n')
- }
- hasHighS() {
- const HALF = CURVE.n >> _1n
- return this.s > HALF
- }
- normalizeS() {
- return this.hasHighS() ? new Signature(this.r, CURVE.n - this.s) : this
- }
- toDERRawBytes(isCompressed = false) {
- return hexToBytes(this.toDERHex(isCompressed))
- }
- toDERHex(isCompressed = false) {
- const sHex = sliceDER(numberToHexUnpadded(this.s))
- if (isCompressed) return sHex
- const rHex = sliceDER(numberToHexUnpadded(this.r))
- const rLen = numberToHexUnpadded(rHex.length / 2)
- const sLen = numberToHexUnpadded(sHex.length / 2)
- const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4)
- return `30${length}02${rLen}${rHex}02${sLen}${sHex}`
- }
- toRawBytes() {
- return this.toDERRawBytes()
- }
- toHex() {
- return this.toDERHex()
- }
- toCompactRawBytes() {
- return hexToBytes(this.toCompactHex())
- }
- toCompactHex() {
- return numTo32bStr(this.r) + numTo32bStr(this.s)
- }
- }
- function concatBytes(...arrays) {
- if (!arrays.every(isUint8a)) throw new Error('Uint8Array list expected')
- if (arrays.length === 1) return arrays[0]
- const length = arrays.reduce((a, arr) => a + arr.length, 0)
- const result = new Uint8Array(length)
- for (let i = 0, pad = 0; i < arrays.length; i++) {
- const arr = arrays[i]
- result.set(arr, pad)
- pad += arr.length
- }
- return result
- }
- function isUint8a(bytes) {
- return bytes instanceof Uint8Array
- }
- const hexes = Array.from({length: 256}, (v, i) =>
- i.toString(16).padStart(2, '0')
- )
- function bytesToHex(uint8a) {
- if (!(uint8a instanceof Uint8Array)) throw new Error('Expected Uint8Array')
- let hex = ''
- for (let i = 0; i < uint8a.length; i++) {
- hex += hexes[uint8a[i]]
- }
- return hex
- }
- function numTo32bStr(num) {
- if (num > POW_2_256) throw new Error('Expected number < 2^256')
- return num.toString(16).padStart(64, '0')
- }
- function numTo32b(num) {
- return hexToBytes(numTo32bStr(num))
- }
- function numberToHexUnpadded(num) {
- const hex = num.toString(16)
- return hex.length & 1 ? `0${hex}` : hex
- }
- function hexToNumber(hex) {
- if (typeof hex !== 'string') {
- throw new TypeError('hexToNumber: expected string, got ' + typeof hex)
- }
- return BigInt(`0x${hex}`)
- }
- function hexToBytes(hex) {
- if (typeof hex !== 'string') {
- throw new TypeError('hexToBytes: expected string, got ' + typeof hex)
- }
- if (hex.length % 2)
- throw new Error('hexToBytes: received invalid unpadded hex' + 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
- }
- function bytesToNumber(bytes) {
- return hexToNumber(bytesToHex(bytes))
- }
- function ensureBytes(hex) {
- return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex)
- }
- function normalizeScalar(num) {
- if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0)
- return BigInt(num)
- if (typeof num === 'bigint' && isWithinCurveOrder(num)) return num
- throw new TypeError('Expected valid private scalar: 0 < scalar < curve.n')
- }
- function mod(a, b = CURVE.P) {
- const result = a % b
- return result >= _0n ? result : b + result
- }
- function pow2(x, power) {
- const {P} = CURVE
- let res = x
- while (power-- > _0n) {
- res *= res
- res %= P
- }
- return res
- }
- function sqrtMod(x) {
- const {P} = CURVE
- const _6n = BigInt(6)
- const _11n = BigInt(11)
- const _22n = BigInt(22)
- const _23n = BigInt(23)
- const _44n = BigInt(44)
- const _88n = BigInt(88)
- const b2 = (x * x * x) % P
- const b3 = (b2 * b2 * x) % P
- const b6 = (pow2(b3, _3n) * b3) % P
- const b9 = (pow2(b6, _3n) * b3) % P
- const b11 = (pow2(b9, _2n) * b2) % P
- const b22 = (pow2(b11, _11n) * b11) % P
- const b44 = (pow2(b22, _22n) * b22) % P
- const b88 = (pow2(b44, _44n) * b44) % P
- const b176 = (pow2(b88, _88n) * b88) % P
- const b220 = (pow2(b176, _44n) * b44) % P
- const b223 = (pow2(b220, _3n) * b3) % P
- const t1 = (pow2(b223, _23n) * b22) % P
- const t2 = (pow2(t1, _6n) * b2) % P
- return pow2(t2, _2n)
- }
- function invert(number, modulo = CURVE.P) {
- if (number === _0n || modulo <= _0n) {
- throw new Error(
- `invert: expected positive integers, got n=${number} mod=${modulo}`
- )
- }
- let a = mod(number, modulo)
- let b = modulo
- let x = _0n,
- u = _1n
- while (a !== _0n) {
- const q = b / a
- const r = b % a
- const m = x - u * q
- ;(b = a), (a = r), (x = u), (u = m)
- }
- const gcd = b
- if (gcd !== _1n) throw new Error('invert: does not exist')
- return mod(x, modulo)
- }
- function invertBatch(nums, p = CURVE.P) {
- const scratch = new Array(nums.length)
- const lastMultiplied = nums.reduce((acc, num, i) => {
- if (num === _0n) return acc
- scratch[i] = acc
- return mod(acc * num, p)
- }, _1n)
- const inverted = invert(lastMultiplied, p)
- nums.reduceRight((acc, num, i) => {
- if (num === _0n) return acc
- scratch[i] = mod(acc * scratch[i], p)
- return mod(acc * num, p)
- }, inverted)
- return scratch
- }
- const divNearest = (a, b) => (a + b / _2n) / b
- const POW_2_128 = _2n ** BigInt(128)
- function splitScalarEndo(k) {
- const {n} = CURVE
- const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15')
- const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3')
- const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8')
- const b2 = a1
- const c1 = divNearest(b2 * k, n)
- const c2 = divNearest(-b1 * k, n)
- let k1 = mod(k - c1 * a1 - c2 * a2, n)
- let k2 = mod(-c1 * b1 - c2 * b2, n)
- const k1neg = k1 > POW_2_128
- const k2neg = k2 > POW_2_128
- if (k1neg) k1 = n - k1
- if (k2neg) k2 = n - k2
- if (k1 > POW_2_128 || k2 > POW_2_128) {
- throw new Error('splitScalarEndo: Endomorphism failed, k=' + k)
- }
- return {k1neg, k1, k2neg, k2}
- }
- function truncateHash(hash) {
- const {n} = CURVE
- const byteLength = hash.length
- const delta = byteLength * 8 - 256
- let h = bytesToNumber(hash)
- if (delta > 0) h = h >> BigInt(delta)
- if (h >= n) h -= n
- return h
- }
- class HmacDrbg {
- constructor() {
- this.v = new Uint8Array(32).fill(1)
- this.k = new Uint8Array(32).fill(0)
- this.counter = 0
- }
- hmac(...values) {
- return utils.hmacSha256(this.k, ...values)
- }
- hmacSync(...values) {
- if (typeof utils.hmacSha256Sync !== 'function')
- throw new Error('utils.hmacSha256Sync is undefined, you need to set it')
- const res = utils.hmacSha256Sync(this.k, ...values)
- if (res instanceof Promise)
- throw new Error('To use sync sign(), ensure utils.hmacSha256 is sync')
- return res
- }
- incr() {
- if (this.counter >= 1000) {
- throw new Error('Tried 1,000 k values for sign(), all were invalid')
- }
- this.counter += 1
- }
- async reseed(seed = new Uint8Array()) {
- this.k = await this.hmac(this.v, Uint8Array.from([0x00]), seed)
- this.v = await this.hmac(this.v)
- if (seed.length === 0) return
- this.k = await this.hmac(this.v, Uint8Array.from([0x01]), seed)
- this.v = await this.hmac(this.v)
- }
- reseedSync(seed = new Uint8Array()) {
- this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed)
- this.v = this.hmacSync(this.v)
- if (seed.length === 0) return
- this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed)
- this.v = this.hmacSync(this.v)
- }
- async generate() {
- this.incr()
- this.v = await this.hmac(this.v)
- return this.v
- }
- generateSync() {
- this.incr()
- this.v = this.hmacSync(this.v)
- return this.v
- }
- }
- function isWithinCurveOrder(num) {
- return _0n < num && num < CURVE.n
- }
- function isValidFieldElement(num) {
- return _0n < num && num < CURVE.P
- }
- function kmdToSig(kBytes, m, d) {
- const k = bytesToNumber(kBytes)
- if (!isWithinCurveOrder(k)) return
- const {n} = CURVE
- const q = Point.BASE.multiply(k)
- const r = mod(q.x, n)
- if (r === _0n) return
- const s = mod(invert(k, n) * mod(m + d * r, n), n)
- if (s === _0n) return
- const sig = new Signature(r, s)
- const recovery = (q.x === sig.r ? 0 : 2) | Number(q.y & _1n)
- return {sig, recovery}
- }
- function normalizePrivateKey(key) {
- let num
- if (typeof key === 'bigint') {
- num = key
- } else if (
- typeof key === 'number' &&
- Number.isSafeInteger(key) &&
- key > 0
- ) {
- num = BigInt(key)
- } else if (typeof key === 'string') {
- if (key.length !== 64) throw new Error('Expected 32 bytes of private key')
- num = hexToNumber(key)
- } else if (isUint8a(key)) {
- if (key.length !== 32) throw new Error('Expected 32 bytes of private key')
- num = bytesToNumber(key)
- } else {
- throw new TypeError('Expected valid private key')
- }
- if (!isWithinCurveOrder(num))
- throw new Error('Expected private key: 0 < key < n')
- return num
- }
- function normalizePublicKey(publicKey) {
- if (publicKey instanceof Point) {
- publicKey.assertValidity()
- return publicKey
- } else {
- return Point.fromHex(publicKey)
- }
- }
- function normalizeSignature(signature) {
- if (signature instanceof Signature) {
- signature.assertValidity()
- return signature
- }
- try {
- return Signature.fromDER(signature)
- } catch (error) {
- return Signature.fromCompact(signature)
- }
- }
- function getPublicKey(privateKey, isCompressed = false) {
- return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed)
- }
- function recoverPublicKey(
- msgHash,
- signature,
- recovery,
- isCompressed = false
- ) {
- return Point.fromSignature(msgHash, signature, recovery).toRawBytes(
- isCompressed
- )
- }
- function isPub(item) {
- const arr = isUint8a(item)
- const str = typeof item === 'string'
- const len = (arr || str) && item.length
- if (arr) return len === 33 || len === 65
- if (str) return len === 66 || len === 130
- if (item instanceof Point) return true
- return false
- }
- function getSharedSecret(privateA, publicB, isCompressed = false) {
- if (isPub(privateA))
- throw new TypeError('getSharedSecret: first arg must be private key')
- if (!isPub(publicB))
- throw new TypeError('getSharedSecret: second arg must be public key')
- const b = normalizePublicKey(publicB)
- b.assertValidity()
- return b.multiply(normalizePrivateKey(privateA)).toRawBytes(isCompressed)
- }
- function bits2int(bytes) {
- const slice = bytes.length > 32 ? bytes.slice(0, 32) : bytes
- return bytesToNumber(slice)
- }
- function bits2octets(bytes) {
- const z1 = bits2int(bytes)
- const z2 = mod(z1, CURVE.n)
- return int2octets(z2 < _0n ? z1 : z2)
- }
- function int2octets(num) {
- if (typeof num !== 'bigint') throw new Error('Expected bigint')
- const hex = numTo32bStr(num)
- return hexToBytes(hex)
- }
- function initSigArgs(msgHash, privateKey, extraEntropy) {
- if (msgHash == null)
- throw new Error(`sign: expected valid message hash, not "${msgHash}"`)
- const h1 = ensureBytes(msgHash)
- const d = normalizePrivateKey(privateKey)
- const seedArgs = [int2octets(d), bits2octets(h1)]
- if (extraEntropy != null) {
- if (extraEntropy === true) extraEntropy = utils.randomBytes(32)
- const e = ensureBytes(extraEntropy)
- if (e.length !== 32)
- throw new Error('sign: Expected 32 bytes of extra data')
- seedArgs.push(e)
- }
- const seed = concatBytes(...seedArgs)
- const m = bits2int(h1)
- return {seed, m, d}
- }
- function finalizeSig(recSig, opts) {
- let {sig, recovery} = recSig
- const {canonical, der, recovered} = Object.assign(
- {canonical: true, der: true},
- opts
- )
- if (canonical && sig.hasHighS()) {
- sig = sig.normalizeS()
- recovery ^= 1
- }
- const hashed = der ? sig.toDERRawBytes() : sig.toCompactRawBytes()
- return recovered ? [hashed, recovery] : hashed
- }
- async function sign(msgHash, privKey, opts = {}) {
- const {seed, m, d} = initSigArgs(msgHash, privKey, opts.extraEntropy)
- let sig
- const drbg = new HmacDrbg()
- await drbg.reseed(seed)
- while (!(sig = kmdToSig(await drbg.generate(), m, d))) await drbg.reseed()
- return finalizeSig(sig, opts)
- }
- function signSync(msgHash, privKey, opts = {}) {
- const {seed, m, d} = initSigArgs(msgHash, privKey, opts.extraEntropy)
- let sig
- const drbg = new HmacDrbg()
- drbg.reseedSync(seed)
- while (!(sig = kmdToSig(drbg.generateSync(), m, d))) drbg.reseedSync()
- return finalizeSig(sig, opts)
- }
- const vopts = {strict: true}
- function verify(signature, msgHash, publicKey, opts = vopts) {
- let sig
- try {
- sig = normalizeSignature(signature)
- msgHash = ensureBytes(msgHash)
- } catch (error) {
- return false
- }
- const {r, s} = sig
- if (opts.strict && sig.hasHighS()) return false
- const h = truncateHash(msgHash)
- let P
- try {
- P = normalizePublicKey(publicKey)
- } catch (error) {
- return false
- }
- const {n} = CURVE
- const sinv = invert(s, n)
- const u1 = mod(h * sinv, n)
- const u2 = mod(r * sinv, n)
- const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2)
- if (!R) return false
- const v = mod(R.x, n)
- return v === r
- }
- function finalizeSchnorrChallenge(ch) {
- return mod(bytesToNumber(ch), CURVE.n)
- }
- function hasEvenY(point) {
- return (point.y & _1n) === _0n
- }
- class SchnorrSignature {
- constructor(r, s) {
- this.r = r
- this.s = s
- this.assertValidity()
- }
- static fromHex(hex) {
- const bytes = ensureBytes(hex)
- if (bytes.length !== 64)
- throw new TypeError(
- `SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`
- )
- const r = bytesToNumber(bytes.subarray(0, 32))
- const s = bytesToNumber(bytes.subarray(32, 64))
- return new SchnorrSignature(r, s)
- }
- assertValidity() {
- const {r, s} = this
- if (!isValidFieldElement(r) || !isWithinCurveOrder(s))
- throw new Error('Invalid signature')
- }
- toHex() {
- return numTo32bStr(this.r) + numTo32bStr(this.s)
- }
- toRawBytes() {
- return hexToBytes(this.toHex())
- }
- }
- function schnorrGetPublicKey(privateKey) {
- return Point.fromPrivateKey(privateKey).toRawX()
- }
- function initSchnorrSigArgs(message, privateKey, auxRand) {
- if (message == null)
- throw new TypeError(`sign: Expected valid message, not "${message}"`)
- const m = ensureBytes(message)
- const d0 = normalizePrivateKey(privateKey)
- const rand = ensureBytes(auxRand)
- if (rand.length !== 32)
- throw new TypeError('sign: Expected 32 bytes of aux randomness')
- const P = Point.fromPrivateKey(d0)
- const px = P.toRawX()
- const d = hasEvenY(P) ? d0 : CURVE.n - d0
- return {m, P, px, d, rand}
- }
- function initSchnorrNonce(d, t0h) {
- return numTo32b(d ^ bytesToNumber(t0h))
- }
- function finalizeSchnorrNonce(k0h) {
- const k0 = mod(bytesToNumber(k0h), CURVE.n)
- if (k0 === _0n)
- throw new Error('sign: Creation of signature failed. k is zero')
- const R = Point.fromPrivateKey(k0)
- const rx = R.toRawX()
- const k = hasEvenY(R) ? k0 : CURVE.n - k0
- return {R, rx, k}
- }
- function finalizeSchnorrSig(R, k, e, d) {
- return new SchnorrSignature(R.x, mod(k + e * d, CURVE.n)).toRawBytes()
- }
- async function schnorrSign(
- message,
- privateKey,
- auxRand = utils.randomBytes()
- ) {
- const {m, px, d, rand} = initSchnorrSigArgs(message, privateKey, auxRand)
- const t = initSchnorrNonce(d, await utils.taggedHash(TAGS.aux, rand))
- const {R, rx, k} = finalizeSchnorrNonce(
- await utils.taggedHash(TAGS.nonce, t, px, m)
- )
- const e = finalizeSchnorrChallenge(
- await utils.taggedHash(TAGS.challenge, rx, px, m)
- )
- const sig = finalizeSchnorrSig(R, k, e, d)
- const isValid = await schnorrVerify(sig, m, px)
- if (!isValid) throw new Error('sign: Invalid signature produced')
- return sig
- }
- function schnorrSignSync(message, privateKey, auxRand = utils.randomBytes()) {
- const {m, px, d, rand} = initSchnorrSigArgs(message, privateKey, auxRand)
- const t = initSchnorrNonce(d, utils.taggedHashSync(TAGS.aux, rand))
- const {R, rx, k} = finalizeSchnorrNonce(
- utils.taggedHashSync(TAGS.nonce, t, px, m)
- )
- const e = finalizeSchnorrChallenge(
- utils.taggedHashSync(TAGS.challenge, rx, px, m)
- )
- const sig = finalizeSchnorrSig(R, k, e, d)
- const isValid = schnorrVerifySync(sig, m, px)
- if (!isValid) throw new Error('sign: Invalid signature produced')
- return sig
- }
- function initSchnorrVerify(signature, message, publicKey) {
- const raw = signature instanceof SchnorrSignature
- const sig = raw ? signature : SchnorrSignature.fromHex(signature)
- if (raw) sig.assertValidity()
- return {
- ...sig,
- m: ensureBytes(message),
- P: normalizePublicKey(publicKey)
- }
- }
- function finalizeSchnorrVerify(r, P, s, e) {
- const R = Point.BASE.multiplyAndAddUnsafe(
- P,
- normalizePrivateKey(s),
- mod(-e, CURVE.n)
- )
- if (!R || !hasEvenY(R) || R.x !== r) return false
- return true
- }
- async function schnorrVerify(signature, message, publicKey) {
- try {
- const {r, s, m, P} = initSchnorrVerify(signature, message, publicKey)
- const e = finalizeSchnorrChallenge(
- await utils.taggedHash(TAGS.challenge, numTo32b(r), P.toRawX(), m)
- )
- return finalizeSchnorrVerify(r, P, s, e)
- } catch (error) {
- return false
- }
- }
- function schnorrVerifySync(signature, message, publicKey) {
- try {
- const {r, s, m, P} = initSchnorrVerify(signature, message, publicKey)
- const e = finalizeSchnorrChallenge(
- utils.taggedHashSync(TAGS.challenge, numTo32b(r), P.toRawX(), m)
- )
- return finalizeSchnorrVerify(r, P, s, e)
- } catch (error) {
- return false
- }
- }
- const schnorr = {
- Signature: SchnorrSignature,
- getPublicKey: schnorrGetPublicKey,
- sign: schnorrSign,
- verify: schnorrVerify,
- signSync: schnorrSignSync,
- verifySync: schnorrVerifySync
- }
- Point.BASE._setWindowSize(8)
- const crypto = {
- node: nodeCrypto,
- web: typeof self === 'object' && 'crypto' in self ? self.crypto : undefined
- }
- const TAGS = {
- challenge: 'BIP0340/challenge',
- aux: 'BIP0340/aux',
- nonce: 'BIP0340/nonce'
- }
- const TAGGED_HASH_PREFIXES = {}
- const utils = {
- isValidPrivateKey(privateKey) {
- try {
- normalizePrivateKey(privateKey)
- return true
- } catch (error) {
- return false
- }
- },
- privateAdd: (privateKey, tweak) => {
- const p = normalizePrivateKey(privateKey)
- const t = normalizePrivateKey(tweak)
- return numTo32b(mod(p + t, CURVE.n))
- },
- privateNegate: privateKey => {
- const p = normalizePrivateKey(privateKey)
- return numTo32b(CURVE.n - p)
- },
- pointAddScalar: (p, tweak, isCompressed) => {
- const P = Point.fromHex(p)
- const t = normalizePrivateKey(tweak)
- const Q = Point.BASE.multiplyAndAddUnsafe(P, t, _1n)
- if (!Q) throw new Error('Tweaked point at infinity')
- return Q.toRawBytes(isCompressed)
- },
- pointMultiply: (p, tweak, isCompressed) => {
- const P = Point.fromHex(p)
- const t = bytesToNumber(ensureBytes(tweak))
- return P.multiply(t).toRawBytes(isCompressed)
- },
- hashToPrivateKey: hash => {
- hash = ensureBytes(hash)
- if (hash.length < 40 || hash.length > 1024)
- throw new Error('Expected 40-1024 bytes of private key as per FIPS 186')
- const num = mod(bytesToNumber(hash), CURVE.n - _1n) + _1n
- return numTo32b(num)
- },
- randomBytes: (bytesLength = 32) => {
- if (crypto.web) {
- return crypto.web.getRandomValues(new Uint8Array(bytesLength))
- } else if (crypto.node) {
- const {randomBytes} = crypto.node
- return Uint8Array.from(randomBytes(bytesLength))
- } else {
- throw new Error("The environment doesn't have randomBytes function")
- }
- },
- randomPrivateKey: () => {
- return utils.hashToPrivateKey(utils.randomBytes(40))
- },
- bytesToHex,
- hexToBytes,
- concatBytes,
- mod,
- invert,
- sha256: async (...messages) => {
- if (crypto.web) {
- const buffer = await crypto.web.subtle.digest(
- 'SHA-256',
- concatBytes(...messages)
- )
- return new Uint8Array(buffer)
- } else if (crypto.node) {
- const {createHash} = crypto.node
- const hash = createHash('sha256')
- messages.forEach(m => hash.update(m))
- return Uint8Array.from(hash.digest())
- } else {
- throw new Error("The environment doesn't have sha256 function")
- }
- },
- hmacSha256: async (key, ...messages) => {
- if (crypto.web) {
- const ckey = await crypto.web.subtle.importKey(
- 'raw',
- key,
- {name: 'HMAC', hash: {name: 'SHA-256'}},
- false,
- ['sign']
- )
- const message = concatBytes(...messages)
- const buffer = await crypto.web.subtle.sign('HMAC', ckey, message)
- return new Uint8Array(buffer)
- } else if (crypto.node) {
- const {createHmac} = crypto.node
- const hash = createHmac('sha256', key)
- messages.forEach(m => hash.update(m))
- return Uint8Array.from(hash.digest())
- } else {
- throw new Error("The environment doesn't have hmac-sha256 function")
- }
- },
- sha256Sync: undefined,
- hmacSha256Sync: undefined,
- taggedHash: async (tag, ...messages) => {
- let tagP = TAGGED_HASH_PREFIXES[tag]
- if (tagP === undefined) {
- const tagH = await utils.sha256(
- Uint8Array.from(tag, c => c.charCodeAt(0))
- )
- tagP = concatBytes(tagH, tagH)
- TAGGED_HASH_PREFIXES[tag] = tagP
- }
- return utils.sha256(tagP, ...messages)
- },
- taggedHashSync: (tag, ...messages) => {
- if (typeof utils.sha256Sync !== 'function')
- throw new Error('utils.sha256Sync is undefined, you need to set it')
- let tagP = TAGGED_HASH_PREFIXES[tag]
- if (tagP === undefined) {
- const tagH = utils.sha256Sync(
- Uint8Array.from(tag, c => c.charCodeAt(0))
- )
- tagP = concatBytes(tagH, tagH)
- TAGGED_HASH_PREFIXES[tag] = tagP
- }
- return utils.sha256Sync(tagP, ...messages)
- },
- precompute(windowSize = 8, point = Point.BASE) {
- const cached = point === Point.BASE ? point : new Point(point.x, point.y)
- cached._setWindowSize(windowSize)
- cached.multiply(_3n)
- return cached
- }
- }
-
- exports.CURVE = CURVE
- exports.Point = Point
- exports.Signature = Signature
- exports.getPublicKey = getPublicKey
- exports.getSharedSecret = getSharedSecret
- exports.recoverPublicKey = recoverPublicKey
- exports.schnorr = schnorr
- exports.sign = sign
- exports.signSync = signSync
- exports.utils = utils
- exports.verify = verify
-
- Object.defineProperty(exports, '__esModule', {value: true})
-})
diff --git a/lnbits/extensions/cashu/static/js/utils.js b/lnbits/extensions/cashu/static/js/utils.js
deleted file mode 100644
index cf852b58..00000000
--- a/lnbits/extensions/cashu/static/js/utils.js
+++ /dev/null
@@ -1,23 +0,0 @@
-function splitAmount(value) {
- const chunks = []
- for (let i = 0; i < 32; i++) {
- const mask = 1 << i
- if ((value & mask) !== 0) chunks.push(Math.pow(2, i))
- }
- return chunks
-}
-
-function bytesToNumber(bytes) {
- return hexToNumber(nobleSecp256k1.utils.bytesToHex(bytes))
-}
-
-function bigIntStringify(key, value) {
- return typeof value === 'bigint' ? value.toString() : value
-}
-
-function hexToNumber(hex) {
- if (typeof hex !== 'string') {
- throw new TypeError('hexToNumber: expected string, got ' + typeof hex)
- }
- return BigInt(`0x${hex}`)
-}
diff --git a/lnbits/extensions/cashu/tasks.py b/lnbits/extensions/cashu/tasks.py
deleted file mode 100644
index 982d3ac1..00000000
--- a/lnbits/extensions/cashu/tasks.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import asyncio
-
-from cashu.core.migrations import migrate_databases
-from cashu.mint import migrations
-
-from lnbits.core.models import Payment
-from lnbits.tasks import register_invoice_listener
-
-from . import db, ledger
-
-
-async def startup_cashu_mint():
- await migrate_databases(db, migrations)
- await ledger.load_used_proofs()
- await ledger.init_keysets(autosave=False)
-
-
-async def wait_for_paid_invoices():
- invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
-
- while True:
- payment = await invoice_queue.get()
- await on_invoice_paid(payment)
-
-
-async def on_invoice_paid(payment: Payment) -> None:
- if payment.extra.get("tag") != "cashu":
- return
-
- return
diff --git a/lnbits/extensions/cashu/templates/cashu/_api_docs.html b/lnbits/extensions/cashu/templates/cashu/_api_docs.html
deleted file mode 100644
index f7bb19f6..00000000
--- a/lnbits/extensions/cashu/templates/cashu/_api_docs.html
+++ /dev/null
@@ -1,80 +0,0 @@
- Create Cashu ecash mints and wallets.
- Here you can create multiple cashu mints that you can share. Each mint - can service many users but all ecash tokens of a mint are only valid - inside that mint and not across different mints. To exchange funds - between mints, use Lightning payments. -
- Important - -- If you are the operator of this LNbits instance, make sure to set - CASHU_PRIVATE_KEY="randomkey" in your configuration file. Do not - create mints before setting the key and do not change the key once - set. -
-- This is a - Cashu - mint. Cashu is an ecash system for Bitcoin. -
-
- Open this page in your native browser
- Before you continue to the wallet, make sure to open this page in your
- device's native browser application (Safari for iOS, Chrome for
- Android). Do not use Cashu in an embedded browser that opens when you
- click a link in a messenger.
-
- Add wallet to home screen
- You can add Cashu to your home screen as a progressive web app (PWA).
- After opening the wallet in your browser (click the link above), on
- Android (Chrome), click the menu at the upper right. On iOS (Safari),
- click the share button. Now press the Add to Home screen button.
-
- Backup your wallet
- Ecash is a bearer asset. That means losing access to your wallet will
- make you lose your funds. The wallet stores ecash tokens on your
- device's database. If you lose the link or delete your your data
- without backing up, you will lose your tokens. Press the Backup button
- in the wallet to download a copy of your tokens.
-
- This service is in BETA
- Cashu is still experimental and in active development. There are
- likely bugs in this implementation so please use this with caution. We
- hold no responsibility for people losing access to funds. Use at your
- own risk!
-
- Description: {{
- payInvoiceData.invoice.description }}
- Expire date: {{ payInvoiceData.invoice.expireDate
- }}
- Hash: {{ payInvoiceData.invoice.hash }}
-
- {{ payInvoiceData.domain }} is requesting {{ - payInvoiceData.lnurlpay.maxSendable | msatoshiFormat }} {% - endraw %} {{LNBITS_DENOMINATION}} {% raw %} - -
-
- {{ payInvoiceData.lnurlpay.targetUser ||
- payInvoiceData.domain }}
- is requesting
- between
- {{ payInvoiceData.lnurlpay.minSendable | msatoshiFormat }}
- and
- {{ payInvoiceData.lnurlpay.maxSendable | msatoshiFormat }}
- {% endraw %} {{LNBITS_DENOMINATION}} {% raw %}
-
-
- {{ payInvoiceData.lnurlpay.description }} -
-
-
Please take a moment to read the following information.
- -- Open this wallet on your device's native browser - Cashu stores your ecash on your device locally. For the best - experience, use this wallet with your device's native web browser - (for example Safari for iOS, Chrome for Android). -
-- Add to home screen. - Add Cashu to your home screen as a progressive web app (PWA). On - Android Chrome, click the hamburger menu at the upper right. On - iOS Safari, click the share button. Now press the Add to Home - screen button. -
-- This software is in BETA! We hold no - responsibility for people losing access to funds. Use at your own - risk! Ecash is a bearer asset, meaning losing access to this - wallet will mean you will lose the funds. This wallet stores ecash - tokens in its database. If you lose the link or delete your your - data without backing up, you will lose your tokens. Press the - Backup button to download a copy of your tokens. -
-- Bookmark this page and backup your tokens! - Ecash is a bearer asset, meaning losing access to this wallet will - mean you will lose the funds. This wallet stores ecash tokens in its - database. If you lose the link or delete your your data without - backing up, you will lose your tokens. Press the Backup button to - download a copy of your tokens. -
-- Add to home screen. - You can add Cashu to your home screen as a progressive web app - (PWA). On Android Chrome, click the hamburger menu at the upper - right. On iOS Safari, click the share button. Now press the Add to - Home screen button. -
-- This software is in BETA! We hold no responsibility - for people losing access to funds. Use at your own risk! -
-- A Cashu mint does not know about your financial activity but it - controls your funds. Make sure that you trust the operator of this - mint. -
-