From e8e4c7f9c3ee16c28b0a7e6cee65a958b0110db9 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Mon, 24 Oct 2022 09:26:32 +0200 Subject: [PATCH 001/106] works --- lnbits/extensions/cashu/migrations.py | 3 +- .../cashu/templates/cashu/_api_docs.html | 12 +- .../cashu/templates/cashu/index.html | 8 +- .../cashu/templates/cashu/wallet.html | 12 +- lnbits/extensions/cashu/views_api.py | 119 ++++++++++++------ poetry.lock | 75 ++++------- pyproject.toml | 2 +- 7 files changed, 125 insertions(+), 106 deletions(-) diff --git a/lnbits/extensions/cashu/migrations.py b/lnbits/extensions/cashu/migrations.py index ec5207658..b54c41087 100644 --- a/lnbits/extensions/cashu/migrations.py +++ b/lnbits/extensions/cashu/migrations.py @@ -12,7 +12,8 @@ async def m001_initial(db): fraction BOOL, maxsats INT, coins INT, - keyset_id TEXT NOT NULL + keyset_id TEXT NOT NULL, + issued_sat INT ); """ ) diff --git a/lnbits/extensions/cashu/templates/cashu/_api_docs.html b/lnbits/extensions/cashu/templates/cashu/_api_docs.html index 3476d41aa..27ce55f98 100644 --- a/lnbits/extensions/cashu/templates/cashu/_api_docs.html +++ b/lnbits/extensions/cashu/templates/cashu/_api_docs.html @@ -8,7 +8,7 @@ - GET /cashu/api/v1/cashus + GET /cashu/api/v1/mints
Headers
{"X-Api-Key": <invoice_key>}
Body (application/json)
@@ -18,7 +18,7 @@ [<cashu_object>, ...]
Curl example
curl -X GET {{ request.base_url }}cashu/api/v1/cashus -H "X-Api-Key: + >curl -X GET {{ request.base_url }}cashu/api/v1/mints -H "X-Api-Key: <invoice_key>"
@@ -27,7 +27,7 @@ - POST /cashu/api/v1/cashus + POST /cashu/api/v1/mints
Headers
{"X-Api-Key": <invoice_key>}
Body (application/json)
@@ -43,7 +43,7 @@ >
Curl example
curl -X POST {{ request.base_url }}cashu/api/v1/cashus -d '{"name": + >curl -X POST {{ request.base_url }}cashu/api/v1/mints -d '{"name": <string>, "currency": <string>}' -H "Content-type: application/json" -H "X-Api-Key: <admin_key>" @@ -62,7 +62,7 @@ DELETE - /cashu/api/v1/cashus/<cashu_id>
Headers
{"X-Api-Key": <admin_key>}
@@ -71,7 +71,7 @@
Curl example
curl -X DELETE {{ request.base_url - }}cashu/api/v1/cashus/<cashu_id> -H "X-Api-Key: + }}cashu/api/v1/mints/<cashu_id> -H "X-Api-Key: <admin_key>"
diff --git a/lnbits/extensions/cashu/templates/cashu/index.html b/lnbits/extensions/cashu/templates/cashu/index.html index dcb2b4a74..20ed567ca 100644 --- a/lnbits/extensions/cashu/templates/cashu/index.html +++ b/lnbits/extensions/cashu/templates/cashu/index.html @@ -271,7 +271,7 @@ LNbits.api .request( 'GET', - '/cashu/api/v1/cashus?all_wallets=true', + '/cashu/api/v1/mints?all_wallets=true', this.g.user.wallets[0].inkey ) .then(function (response) { @@ -294,7 +294,7 @@ LNbits.api .request( 'POST', - '/cashu/api/v1/cashus', + '/cashu/api/v1/mints', _.findWhere(this.g.user.wallets, {id: this.formDialog.data.wallet}) .inkey, data @@ -314,13 +314,13 @@ LNbits.utils .confirmDialog( - 'Are you sure you want to delete this Mint? It will suck for users.' + "Are you sure you want to delete this Mint? This mint's users will not be able to redeem their tokens!" ) .onOk(function () { LNbits.api .request( 'DELETE', - '/cashu/api/v1/cashus/' + cashuId, + '/cashu/api/v1/mints/' + cashuId, _.findWhere(self.g.user.wallets, {id: cashu.wallet}).adminkey ) .then(function (response) { diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html index 4f12cd1c9..11ac044b3 100644 --- a/lnbits/extensions/cashu/templates/cashu/wallet.html +++ b/lnbits/extensions/cashu/templates/cashu/wallet.html @@ -913,7 +913,7 @@ page_container %} try { const {data} = await LNbits.api.request( 'GET', - `/cashu/api/v1/cashu/${this.mintId}/mint?amount=${this.invoiceData.amount}` + `/cashu/api/v1/${this.mintId}/mint?amount=${this.invoiceData.amount}` ) console.log('### data', data) @@ -940,7 +940,7 @@ page_container %} try { const {data} = await LNbits.api.request( 'POST', - `/cashu/api/v1/cashu/${this.mintId}/mint?payment_hash=${invoice.hash}`, + `/cashu/api/v1/${this.mintId}/mint?payment_hash=${invoice.hash}`, '', { blinded_messages: [] @@ -991,7 +991,7 @@ page_container %} try { const {data} = await LNbits.api.request( 'POST', - `/cashu/api/v1/cashu/${this.mintId}/mint?payment_hash=${hash}`, + `/cashu/api/v1/${this.mintId}/mint?payment_hash=${hash}`, '', { blinded_messages: blindedMessages @@ -1086,7 +1086,7 @@ page_container %} try { const {data} = await LNbits.api.request( 'POST', - `/cashu/api/v1/cashu/${this.mintId}/split`, + `/cashu/api/v1/${this.mintId}/split`, '', payload ) @@ -1221,7 +1221,7 @@ page_container %} try { const {data} = await LNbits.api.request( 'POST', - `/cashu/api/v1/cashu/${this.mintId}/melt`, + `/cashu/api/v1/${this.mintId}/melt`, '', payload ) @@ -1257,7 +1257,7 @@ page_container %} fetchMintKeys: async function () { const {data} = await LNbits.api.request( 'GET', - `/cashu/api/v1/cashu/${this.mintId}/keys` + `/cashu/api/v1/${this.mintId}/keys` ) this.keys = data localStorage.setItem('cashu.keys', JSON.stringify(data)) diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py index 28857663f..d4654c55a 100644 --- a/lnbits/extensions/cashu/views_api.py +++ b/lnbits/extensions/cashu/views_api.py @@ -47,17 +47,20 @@ from .models import Cashu # --------- extension imports -LIGHTNING = False +LIGHTNING = True ######################################## ############### LNBITS MINTS ########### ######################################## -# todo: use /mints -@cashu_ext.get("/api/v1/cashus", status_code=HTTPStatus.OK) + +@cashu_ext.get("/api/v1/mints", status_code=HTTPStatus.OK) async def api_cashus( all_wallets: bool = Query(False), wallet: WalletTypeInfo = Depends(get_key_type) ): + """ + Get all mints of this wallet. + """ wallet_ids = [wallet.wallet.id] if all_wallets: wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids @@ -65,8 +68,11 @@ async def api_cashus( return [cashu.dict() for cashu in await get_cashus(wallet_ids)] -@cashu_ext.post("/api/v1/cashus", status_code=HTTPStatus.CREATED) +@cashu_ext.post("/api/v1/mints", status_code=HTTPStatus.CREATED) async def api_cashu_create(data: Cashu, wallet: WalletTypeInfo = Depends(get_key_type)): + """ + Create a new mint for this wallet. + """ cashu_id = urlsafe_short_hash() # generate a new keyset in cashu keyset = await ledger.load_keyset(cashu_id) @@ -78,12 +84,35 @@ async def api_cashu_create(data: Cashu, wallet: WalletTypeInfo = Depends(get_key return cashu.dict() +@cashu_ext.delete("/api/v1/mints/{cashu_id}") +async def api_cashu_delete( + cashu_id: str, wallet: WalletTypeInfo = Depends(require_admin_key) +): + """ + Delete an existing cashu mint. + """ + cashu = await get_cashu(cashu_id) + + if not cashu: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Cashu mint does not exist." + ) + + if cashu.wallet != wallet.wallet.id: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not your Cashu mint." + ) + + await delete_cashu(cashu_id) + raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + + ####################################### ########### CASHU ENDPOINTS ########### ####################################### -@cashu_ext.get("/api/v1/cashu/{cashu_id}/keys", status_code=HTTPStatus.OK) +@cashu_ext.get("/api/v1/{cashu_id}/keys", status_code=HTTPStatus.OK) async def keys(cashu_id: str = Query(None)) -> dict[int, str]: """Get the public keys of the mint""" cashu: Union[Cashu, None] = await get_cashu(cashu_id) @@ -96,7 +125,20 @@ async def keys(cashu_id: str = Query(None)) -> dict[int, str]: return ledger.get_keyset(keyset_id=cashu.keyset_id) -@cashu_ext.get("/api/v1/cashu/{cashu_id}/mint") +@cashu_ext.get("/api/v1/{cashu_id}/keysets", status_code=HTTPStatus.OK) +async def keysets(cashu_id: str = Query(None)) -> dict[str, list[str]]: + """Get the public keys of the mint""" + cashu: Union[Cashu, None] = await get_cashu(cashu_id) + + if not cashu: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." + ) + + return {"keysets": [cashu.keyset_id]} + + +@cashu_ext.get("/api/v1/{cashu_id}/mint") async def request_mint(cashu_id: str = Query(None), amount: int = 0) -> GetMintResponse: """ Request minting of new tokens. The mint responds with a Lightning invoice. @@ -134,7 +176,7 @@ async def request_mint(cashu_id: str = Query(None), amount: int = 0) -> GetMintR return resp -@cashu_ext.post("/api/v1/cashu/{cashu_id}/mint") +@cashu_ext.post("/api/v1/{cashu_id}/mint") async def mint_coins( data: MintRequest, cashu_id: str = Query(None), @@ -157,7 +199,7 @@ async def mint_coins( if invoice is None: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, - detail="Mint does not have this invoice.", + detail="Mint does not know this invoice.", ) if invoice.issued == True: raise HTTPException( @@ -173,27 +215,31 @@ async def mint_coins( ) status: PaymentStatus = await check_transaction_status(cashu.wallet, payment_hash) - # todo: revert to: status.paid != True: + if status.paid != True: raise HTTPException( status_code=HTTPStatus.PAYMENT_REQUIRED, detail="Invoice not paid." ) try: - await ledger.crud.update_lightning_invoice( - db=ledger.db, hash=payment_hash, issued=True - ) keyset = ledger.keysets.keysets[cashu.keyset_id] promises = await ledger._generate_promises( B_s=data.blinded_messages, keyset=keyset ) + assert len(promises), HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="No promises returned." + ) + await ledger.crud.update_lightning_invoice( + db=ledger.db, hash=payment_hash, issued=True + ) + return promises except Exception as e: logger.error(e) raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)) -@cashu_ext.post("/api/v1/cashu/{cashu_id}/melt") +@cashu_ext.post("/api/v1/{cashu_id}/melt") async def melt_coins( payload: MeltRequest, cashu_id: str = Query(None) ) -> GetMeltResponse: @@ -211,7 +257,7 @@ async def melt_coins( # TOKENS assert all([p.id == cashu.keyset_id for p in proofs]), HTTPException( status_code=HTTPStatus.BAD_REQUEST, - detail="Proofs include tokens from other mint.", + detail="Proofs include tokens from another mint.", ) assert all([ledger._verify_proof(p) for p in proofs]), HTTPException( @@ -248,19 +294,33 @@ async def melt_coins( return GetMeltResponse(paid=status.paid, preimage=status.preimage) -@cashu_ext.post("/api/v1/check") -async def check_spendable(payload: CheckRequest) -> Dict[int, bool]: +@cashu_ext.post("/api/v1/{cashu_id}/check") +async def check_spendable( + payload: CheckRequest, cashu_id: str = Query(None) +) -> Dict[int, bool]: """Check whether a secret has been spent already or not.""" + cashu: Union[None, Cashu] = await get_cashu(cashu_id) + if cashu is None: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." + ) return await ledger.check_spendable(payload.proofs) -@cashu_ext.post("/api/v1/checkfees") -async def check_fees(payload: CheckFeesRequest) -> CheckFeesResponse: +@cashu_ext.post("/api/v1/{cashu_id}/checkfees") +async def check_fees( + payload: CheckFeesRequest, cashu_id: str = Query(None) +) -> CheckFeesResponse: """ Responds with the fees necessary to pay a Lightning invoice. Used by wallets for figuring out the fees they need to supply. This is can be useful for checking whether an invoice is internal (Cashu-to-Cashu). """ + cashu: Union[None, Cashu] = await get_cashu(cashu_id) + if cashu is None: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." + ) invoice_obj = bolt11.decode(payload.pr) internal_checking_id = await check_internal(invoice_obj.payment_hash) @@ -271,7 +331,7 @@ async def check_fees(payload: CheckFeesRequest) -> CheckFeesResponse: return CheckFeesResponse(fee=fees_msat / 1000) -@cashu_ext.post("/api/v1/cashu/{cashu_id}/split") +@cashu_ext.post("/api/v1/{cashu_id}/split") async def split( payload: SplitRequest, cashu_id: str = Query(None) ) -> PostSplitResponse: @@ -291,7 +351,8 @@ async def split( assert outputs, Exception("no outputs provided.") split_return = None try: - split_return = await ledger.split(proofs, amount, outputs, cashu.keyset_id) + keyset = ledger.keysets.keysets[cashu.keyset_id] + split_return = await ledger.split(proofs, amount, outputs, keyset) except Exception as exc: HTTPException( status_code=HTTPStatus.BAD_REQUEST, @@ -318,24 +379,6 @@ async def split( # return cashu.dict() -# @cashu_ext.delete("/api/v1s/{cashu_id}") -# async def api_cashu_delete( -# cashu_id: str, wallet: WalletTypeInfo = Depends(require_admin_key) -# ): -# cashu = await get_cashu(cashu_id) - -# if not cashu: -# raise HTTPException( -# status_code=HTTPStatus.NOT_FOUND, detail="Cashu does not exist." -# ) - -# if cashu.wallet != wallet.wallet.id: -# raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your Cashu.") - -# await delete_cashu(cashu_id) -# raise HTTPException(status_code=HTTPStatus.NO_CONTENT) - - # ######################################## # #################????################### # ######################################## diff --git a/poetry.lock b/poetry.lock index 9c426798f..b87b102f9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -124,55 +124,33 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "cashu" version = "0.4.2" -description = "Ecash wallet and mint with Bitcoin Lightning support" +description = "Ecash wallet and mint." category = "main" optional = false -python-versions = ">=3.7" +python-versions = "^3.7" +develop = false [package.dependencies] -anyio = {version = "3.6.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -attrs = {version = "22.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -bech32 = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -bitstring = {version = "3.1.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -certifi = {version = "2022.9.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -cffi = {version = "1.15.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -charset-normalizer = {version = "2.0.12", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -click = {version = "8.0.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -colorama = {version = "0.4.5", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and platform_system == \"Windows\" or python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""} -ecdsa = {version = "0.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -environs = {version = "9.5.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -fastapi = {version = "0.83.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -h11 = {version = "0.12.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -idna = {version = "3.4", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -importlib-metadata = {version = "5.0.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""} -iniconfig = {version = "1.1.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -loguru = {version = "0.6.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -marshmallow = {version = "3.18.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -outcome = {version = "1.2.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -packaging = {version = "21.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -pluggy = {version = "1.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -py = {version = "1.11.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -pycparser = {version = "2.21", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -pydantic = {version = "1.10.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -pyparsing = {version = "3.0.9", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -pytest = {version = "7.1.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -pytest-asyncio = {version = "0.19.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -python-bitcoinlib = {version = "0.11.2", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -python-dotenv = {version = "0.21.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -represent = {version = "1.6.0.post0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -requests = {version = "2.27.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -secp256k1 = {version = "0.14.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -six = {version = "1.16.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -sniffio = {version = "1.3.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -sqlalchemy = {version = "1.3.24", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -sqlalchemy-aio = {version = "0.17.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -starlette = {version = "0.19.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -tomli = {version = "2.0.1", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -typing-extensions = {version = "4.4.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -urllib3 = {version = "1.26.12", markers = "python_version >= \"3.7\" and python_version < \"4\""} -uvicorn = {version = "0.18.3", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} -win32-setctime = {version = "1.1.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\" and sys_platform == \"win32\""} -zipp = {version = "3.9.0", markers = "python_version >= \"3.7\" and python_version < \"3.8\""} +bech32 = "^1.2.0" +bitstring = "^3.1.9" +click = "8.0.4" +ecdsa = "^0.18.0" +environs = "^9.5.0" +fastapi = "^0.83.0" +h11 = "0.12.0" +loguru = "^0.6.0" +pydantic = "^1.10.2" +pytest-asyncio = "0.19.0" +python-bitcoinlib = "^0.11.2" +requests = "2.27.1" +secp256k1 = "^0.14.0" +SQLAlchemy = "1.3.24" +sqlalchemy-aio = "^0.17.0" +uvicorn = "^0.18.3" + +[package.source] +type = "directory" +url = "../cashu" [[package]] name = "Cerberus" @@ -1143,7 +1121,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7" -content-hash = "7de5e4d432bff49de536b1c90082a6a0821533b3d0fa9d92c22ccaa758d1a65f" +content-hash = "ded9ab80b3bec75bf904c3f598afbdff5f1577aaedbec25045c3b42c332f8ccc" [metadata.files] aiofiles = [ @@ -1206,10 +1184,7 @@ black = [ {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, ] -cashu = [ - {file = "cashu-0.4.2-py3-none-any.whl", hash = "sha256:6d24f5e921c33dae1b6823f5e34feab0d6d5662b56a67c29095d48241163a887"}, - {file = "cashu-0.4.2.tar.gz", hash = "sha256:97564481501cbe163e6be4d3cdd0d52d2841e15b830a0185c3c329657e4b8c36"}, -] +cashu = [] Cerberus = [ {file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"}, ] diff --git a/pyproject.toml b/pyproject.toml index 24c041d78..f1a523489 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,7 +64,7 @@ protobuf = "^4.21.6" Cerberus = "^1.3.4" async-timeout = "^4.0.2" pyln-client = "0.11.1" -cashu = "0.4.2" +cashu = {path = "../cashu"} [tool.poetry.dev-dependencies] From ecd5dadcd13ed8e92bc505c9ee8064886e40d732 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 26 Oct 2022 02:30:28 +0200 Subject: [PATCH 002/106] doesnt work yet --- .../cashu/templates/cashu/wallet.html | 390 ++++++++++-------- 1 file changed, 218 insertions(+), 172 deletions(-) diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html index 11ac044b3..ee243c1f4 100644 --- a/lnbits/extensions/cashu/templates/cashu/wallet.html +++ b/lnbits/extensions/cashu/templates/cashu/wallet.html @@ -71,7 +71,7 @@ page_container %} - + @@ -109,7 +109,7 @@ page_container %} {% endraw %} - +
- How much would you like to buy? + Create a Lightning invoice
@@ -260,7 +261,7 @@ page_container %} color="grey" >Copy invoice - Request Invoice Show TokensSend Tokens Burn Tokens
- Accept TokensReceive Tokens Close -
+
{% raw %} - Amount: {{ sellData.invoice.sat }} + Amount: {{ payInvoiceData.invoice.sat }} sats
- Description: {{ sellData.invoice.description }}
- Expire date: {{ sellData.invoice.expireDate }}
- Expired: {{ sellData.invoice.expired }}
- Hash: {{ sellData.invoice.hash }} {% endraw %} + Description: {{ payInvoiceData.invoice.description + }}
+ Expire date: {{ payInvoiceData.invoice.expireDate + }}
+ Expired: {{ payInvoiceData.invoice.expired }}
+ Hash: {{ payInvoiceData.invoice.hash }} {% endraw + %}
Check Invoice - Sell Token + Pay invoice Close @@ -458,7 +460,7 @@ page_container %} bolt11: '', hash: '' }, - sellData: { + payInvoiceData: { invoice: '', bolt11: '' }, @@ -475,6 +477,7 @@ page_container %} showPayInvoice: false, showSendTokens: false, showReceiveTokens: false, + promises: [], tokens: [], tab: 'tokens', @@ -614,7 +617,7 @@ page_container %} }, tokenList: function () { - const x = this.tokens + const x = this.proofs .filter(t => t.promises?.length) .map(t => t.blindedMessages) .flat() @@ -636,7 +639,7 @@ page_container %} }, balance: function () { - return this.tokens + return this.proofs .filter(t => t.promises?.length) .map(t => t.blindedMessages) .flat() @@ -891,8 +894,8 @@ page_container %} showPayInvoiceDialog: function () { console.log('### showPayInvoiceDialog') - this.sellData.invoice = '' - this.sellData.bolt11 = '' + this.payInvoiceData.invoice = '' + this.payInvoiceData.bolt11 = '' this.showPayInvoice = true }, @@ -909,31 +912,6 @@ page_container %} this.showReceiveTokens = true }, - requestInvoice: async function () { - try { - const {data} = await LNbits.api.request( - 'GET', - `/cashu/api/v1/${this.mintId}/mint?amount=${this.invoiceData.amount}` - ) - console.log('### data', data) - - this.invoiceData.bolt11 = data.pr - this.invoiceData.hash = data.hash - this.invoicesCashu.push({ - ..._.clone(this.invoiceData), - date: currentDateStr(), - status: 'pending' - }) - this.storeinvoicesCashu() - const amounts = splitAmount(this.invoiceData.amount) - await this.requestTokens(amounts, this.invoiceData.hash) - this.tab = 'orders' - } catch (error) { - console.error(error) - LNbits.utils.notifyApiError(error) - } - }, - checkXXXXXX: async function () { for (const invoice of this.invoicesCashu) { if (invoice.status === 'pending') { @@ -955,24 +933,72 @@ page_container %} } }, - recheckInvoice: async function (hash) { - console.log('### recheckInvoice.hash', hash) - const tokens = this.tokens.find(bt => bt.hash === hash) - console.log('### recheckInvoice.tokens', tokens) - if (!tokens) { - console.error('####### no token for hash', hash) - return - } - const promises = await this.fetchPromisesFromMint( - hash, - tokens.blindedMessages - ) - if (promises && promises.length) { - tokens.promises = promises - tokens.status = 'paid' - this.storeTokens() + //////////////////////// MINT ////////////////////////////////////////// - const invoice = this.invoicesCashu.find(bo => bo.hash === hash) + requestMint: async function () { + // gets an invoice from the mint to get new tokens + try { + const {data} = await LNbits.api.request( + 'GET', + `/cashu/api/v1/${this.mintId}/mint?amount=${this.invoiceData.amount}` + ) + console.log('### data', data) + + this.invoiceData.bolt11 = data.pr + this.invoiceData.hash = data.hash + this.invoicesCashu.push({ + ..._.clone(this.invoiceData), + date: currentDateStr(), + status: 'pending' + }) + this.storeinvoicesCashu() + this.tab = 'invoices' + return data + } catch (error) { + console.error(error) + LNbits.utils.notifyApiError(error) + } + }, + mintApi: async function (amounts, payment_hash) { + console.log('### promises', payment_hash) + try { + let secrets = generateSecrets(amounts) + let {blinded_messages, rs} = constructOutputs(amounts, secrets) + const {promises} = await LNbits.api.request( + 'POST', + `/cashu/api/v1/${this.mintId}/mint?payment_hash=${payment_hash}`, + '', + { + blinded_messages: blinded_messages + } + ) + console.log('### promises data', promises) + return promises + } catch (error) { + console.error(error) + LNbits.utils.notifyApiError(error) + } + }, + mint: async function (amount, payment_hash) { + try { + const split = splitAmount(amount) + const proofs = await mintApi(split, payment_hash) + } catch (error) { + console.error(error) + LNbits.utils.notifyApiError(error) + } + }, + recheckInvoice: async function (payment_hash) { + console.log('### recheckInvoice.hash', payment_hash) + const invoice = this.invoicesCashu.find(i => i.hash === payment_hash) + const amounts = splitAmount(invoice.amount) + const newTokens = await this.buildTokens(amounts, payment_hash) + const promises = await this.mint(invoice.amount, payment_hash) + if (promises && promises.length) { + newTokens.promises = promises + newTokens.status = 'paid' + this.proofs.push(newTokens) + this.storeProofs() invoice.status = 'paid' this.storeinvoicesCashu() } @@ -980,56 +1006,116 @@ page_container %} requestTokens: async function (amounts, paymentHash) { const newTokens = await this.buildTokens(amounts, paymentHash) - this.tokens.push(newTokens) - this.storeTokens() - console.log('### this.tokens', this.tokens) - // await this.fetchPromisesFromMint(paymentHash, newTokens.newTokens) + // this.proofs.push(newTokens) + // this.storeProofs() + // console.log('### this.proofs', this.proofs) + // await this.mint(paymentHash, newTokens.newTokens) }, - fetchPromisesFromMint: async function (hash, blindedMessages) { - console.log('### promises', hash, blindedMessages) - try { - const {data} = await LNbits.api.request( - 'POST', - `/cashu/api/v1/${this.mintId}/mint?payment_hash=${hash}`, - '', - { - blinded_messages: blindedMessages - } - ) - console.log('### promises data', data) - return data - } catch (error) { - console.error(error) - LNbits.utils.notifyApiError(error) + generateSecrets: async function (amounts) { + const secrets = [] + for (let i = 0; i < amounts.length; i++) { + const secret = nobleSecp256k1.utils.randomBytes(32) + secrets.push(encodedSecret) + } + return secrets + }, + constructOutputs: async function (amounts, secrets) { + const blindedMessages = [] + const randomBlindingFactors = [] + for (let i = 0; i < amounts.length; i++) { + const {B_, r} = await step1Bob(secret) + blindedMessages.push(B_) + randomBlindingFactors.push(r) + } + return { + blindedMessages, + randomBlindingFactors } }, - buildAndShowTokens: async function () { + constructProofs: function (promises, secrets, rs) { + const proofs = [] + for (let i = 0; i < promises.length; i++) { + let {amount, C, secret} = promiseToProof( + promises[i].amount, + promises[i]['C_'], + promises[i].secret, + promises[i].randomBlindingFactor + ) + } + return proofs + }, + + promiseToProof: function (amount, C_hex, secret, randomBlindingFactor) { + const C_ = nobleSecp256k1.Point.fromHex(C_hex) + const A = this.keys[amount] + const C = step3Bob( + C_, + randomBlindingFactor, + nobleSecp256k1.Point.fromHex(A) + ) + return { + amount, + C: C.toHex(true), + secret + } + }, + buildTokens: async function (amounts, paymentHash) { + const blindedMessages = [] + const secrets = [] + const randomBlindingFactors = [] + for (let i = 0; i < amounts.length; i++) { + const secret = nobleSecp256k1.utils.randomBytes(32) + // const secret = nobleSecp256k1.utils.hexToBytes('0000000000000000000000000000000000000000000000000000000000000003') + // todo: base64Url + const encodedSecret = uint8ToBase64.encode(secret) + secrets.push(encodedSecret) + const {B_, randomBlindingFactor} = await step1Bob(secret) + randomBlindingFactors.push(randomBlindingFactor) + blindedMessages.push({amount: amounts[i], B_: B_}) + } + + const newTokens = { + hash: paymentHash, + blindedMessages, + randomBlindingFactors, + secrets, + status: 'pending' + } + return newTokens + }, + + //////////////////////////////////////////////////////////////////////////////////// + + sendTokens: async function () { const amounts = splitAmount(this.sendData.amount) const sendTokens = [] - for (const amount of amounts) { - const token = this.findTokenForAmount(amount) - if (token) { - sendTokens.push(token) - } else { - this.$q.notify({ - timeout: 5000, - type: 'warning', - message: `Cannot select amount for denomination ${amount}` - }) - this.sendData.tokens = '' - this.sendData.tokensBase64 = '' - return - } - } + sendTokens.push(this.proofs) + // for (const amount of amounts) { + // const token = this.findTokenForAmount(amount) + // if (token) { + // sendTokens.push(token) + // } else { + // this.$q.notify({ + // timeout: 5000, + // type: 'warning', + // message: `Cannot select amount for denomination ${amount}` + // }) + // this.sendData.tokens = '' + // this.sendData.tokensBase64 = '' + // return + // } + // } + this.sendData.tokens = '' + this.sendData.tokensBase64 = '' console.log('### sendTokens', sendTokens) - this.sendData.tokens = sendTokens.map(t => { + this.sendData.tokens = sendTokens.map((token, tokenIndex) => { return this.promiseToProof( - t.promise.amount, - t.promise['C_'], - t.secret, - t.randomBlindingFactor + token.promises[tokenIndex].amount, + token.promises[tokenIndex]['C_'], + token.promises[tokenIndex].secret, + token.promises[tokenIndex].randomBlindingFactor ) }) console.log('### this.sendData.tokens', this.sendData.tokens) @@ -1038,7 +1124,7 @@ page_container %} burnTokens: function () { for (const sentToken of this.sendData.tokens) { - for (const token of this.tokens) { + for (const token of this.proofs) { if (token.status === 'paid') { const secretIndex = token.secrets.findIndex( s => s === sentToken.secret @@ -1058,12 +1144,12 @@ page_container %} timeout: 5000, message: 'Tokens burned' }) - this.storeTokens() + this.storeProofs() this.showSendTokens = false - console.log('### this.tokens', this.tokens) + console.log('### this.proofs', this.proofs) }, - acceptTokens: async function () { + receiveTokens: async function () { this.showReceiveTokens = false console.log('### receive tokens', this.receiveData.tokensBase64) if (this.receiveData.tokensBase64) { @@ -1097,8 +1183,8 @@ page_container %} // Object.assign(newTokens[i], promises) // } console.log('newTokens 2', newTokens) - this.tokens.push(newTokens) - this.storeTokens() + this.proofs.push(newTokens) + this.storeProofs() } catch (error) { console.error(error) LNbits.utils.notifyApiError(error) @@ -1107,7 +1193,7 @@ page_container %} }, findTokenForAmount: function (amount) { - for (const token of this.tokens) { + for (const token of this.proofs) { const index = token.promises?.findIndex(p => p.amount === amount) if (index >= 0) { return { @@ -1119,37 +1205,10 @@ page_container %} } }, - constructProof: function (token) {}, - - buildTokens: async function (amounts, paymentHash) { - const blindedMessages = [] - const secrets = [] - const randomBlindingFactors = [] - for (let i = 0; i < amounts.length; i++) { - const secret = nobleSecp256k1.utils.randomBytes(32) - // const secret = nobleSecp256k1.utils.hexToBytes('0000000000000000000000000000000000000000000000000000000000000003') - // todo: base64Url - const encodedSecret = uint8ToBase64.encode(secret) - secrets.push(encodedSecret) - const {B_, randomBlindingFactor} = await step1Bob(secret) - randomBlindingFactors.push(randomBlindingFactor) - blindedMessages.push({amount: amounts[i], B_: B_}) - } - - const newTokens = { - hash: paymentHash, - blindedMessages, - randomBlindingFactors, - secrets, - status: 'pending' - } - return newTokens - }, - checkInvoice: function () { console.log('#### checkInvoice') try { - const invoice = decode(this.sellData.bolt11) + const invoice = decode(this.payInvoiceData.bolt11) const cleanInvoice = { msat: invoice.human_readable_part.amount, @@ -1177,10 +1236,13 @@ page_container %} } } - this.sellData.invoice = cleanInvoice + this.payInvoiceData.invoice = cleanInvoice }) - console.log('#### this.sellData.invoice', this.sellData.invoice) + console.log( + '#### this.payInvoiceData.invoice', + this.payInvoiceData.invoice + ) } catch (error) { this.$q.notify({ timeout: 5000, @@ -1191,10 +1253,10 @@ page_container %} } }, - sellTokens: async function () { + melt: async function () { console.log('#### sell tokens') - const amount = this.sellData.invoice.sat - const paidTokens = this.tokens.filter(t => t.promises?.length) + const amount = this.payInvoiceData.invoice.sat + const paidTokens = this.proofs.filter(t => t.promises?.length) console.log('### paidTokens', paidTokens) const proofs = paidTokens.map(token => { return token.promises.map((promise, promiseIndex) => { @@ -1215,7 +1277,7 @@ page_container %} const payload = { proofs: proofs.flat(), amount, - invoice: this.sellData.bolt11 + invoice: this.payInvoiceData.bolt11 } console.log('#### payload', JSON.stringify(payload)) try { @@ -1237,22 +1299,6 @@ page_container %} // C_hex = promise['C_'] // amount = promise.amount - promiseToProof: function (amount, C_hex, secret, randomBlindingFactor) { - const C_ = nobleSecp256k1.Point.fromHex(C_hex) - const A = this.keys[amount] - - const C = step3Bob( - C_, - randomBlindingFactor, - nobleSecp256k1.Point.fromHex(A) - ) - - return { - amount, - secret, - C: C.toHex(true) - } - }, fetchMintKeys: async function () { const {data} = await LNbits.api.request( @@ -1269,10 +1315,10 @@ page_container %} JSON.stringify(this.invoicesCashu) ) }, - storeTokens: function () { + storeProofs: function () { localStorage.setItem( - 'cashu.tokens', - JSON.stringify(this.tokens, bigIntStringify) + 'cashu.proofs', + JSON.stringify(this.proofs, bigIntStringify) ) } }, @@ -1330,9 +1376,9 @@ page_container %} this.invoicesCashu = JSON.parse( localStorage.getItem('cashu.invoicesCashu') || '[]' ) - this.tokens = JSON.parse(localStorage.getItem('cashu.tokens') || '[]') + this.proofs = JSON.parse(localStorage.getItem('cashu.proofs') || '[]') console.log('### invoicesCashu', this.invoicesCashu) - console.table('### tokens', this.tokens) + console.table('### tokens', this.proofs) console.log('#### this.mintId', this.mintId) console.log('#### this.mintName', this.mintName) From 970748216f0108c43dc2285cd9a85fe75106c2d3 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 28 Oct 2022 19:34:04 +0200 Subject: [PATCH 003/106] split doesnt work yet --- lnbits/extensions/cashu/static/js/dhke.js | 12 +- .../cashu/templates/cashu/wallet.html | 417 +++++++++++------- lnbits/extensions/cashu/views_api.py | 1 - 3 files changed, 269 insertions(+), 161 deletions(-) diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js index c9e2d146e..c35a34e9d 100644 --- a/lnbits/extensions/cashu/static/js/dhke.js +++ b/lnbits/extensions/cashu/static/js/dhke.js @@ -19,17 +19,15 @@ async function hashToCurve(secretMessage) { return point } -async function step1Bob(secretMessage) { +async function step1Alice(secretMessage) { const Y = await hashToCurve(secretMessage) - const randomBlindingFactor = bytesToNumber( - nobleSecp256k1.utils.randomPrivateKey() - ) - const P = nobleSecp256k1.Point.fromPrivateKey(randomBlindingFactor) + const r = bytesToNumber(nobleSecp256k1.utils.randomPrivateKey()) + const P = nobleSecp256k1.Point.fromPrivateKey(r) const B_ = Y.add(P) - return {B_: B_.toHex(true), randomBlindingFactor} + return {B_: B_.toHex(true), r} } -function step3Bob(C_, r, A) { +function step3Alice(C_, r, A) { const rInt = BigInt(r) const C = C_.subtract(A.multiply(rInt)) return C diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html index ee243c1f4..4af9c1485 100644 --- a/lnbits/extensions/cashu/templates/cashu/wallet.html +++ b/lnbits/extensions/cashu/templates/cashu/wallet.html @@ -426,6 +426,10 @@ page_container %} } {% endblock %} {% block scripts %} + + + + - - - - {% endblock %} diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py index d4654c55a..4d3e854c6 100644 --- a/lnbits/extensions/cashu/views_api.py +++ b/lnbits/extensions/cashu/views_api.py @@ -347,7 +347,6 @@ async def split( proofs = payload.proofs amount = payload.amount outputs = payload.outputs.blinded_messages - # backwards compatibility with clients < v0.2.2 assert outputs, Exception("no outputs provided.") split_return = None try: From ff724bf744db8fa16bee39b5cff11be9fd8132b2 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 2 Nov 2022 23:43:37 +0100 Subject: [PATCH 004/106] for r, dont use bigint but hex string --- lnbits/extensions/cashu/static/js/dhke.js | 8 +++++--- lnbits/extensions/cashu/templates/cashu/wallet.html | 10 ++++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js index c35a34e9d..935bf6d47 100644 --- a/lnbits/extensions/cashu/static/js/dhke.js +++ b/lnbits/extensions/cashu/static/js/dhke.js @@ -21,14 +21,16 @@ async function hashToCurve(secretMessage) { async function step1Alice(secretMessage) { const Y = await hashToCurve(secretMessage) - const r = bytesToNumber(nobleSecp256k1.utils.randomPrivateKey()) + const rpk = nobleSecp256k1.utils.randomPrivateKey() + const r = bytesToNumber(rpk) const P = nobleSecp256k1.Point.fromPrivateKey(r) const B_ = Y.add(P) - return {B_: B_.toHex(true), r} + return {B_: B_.toHex(true), r: nobleSecp256k1.utils.bytesToHex(rpk)} } function step3Alice(C_, r, A) { - const rInt = BigInt(r) + // const rInt = BigInt(r) + const rInt = bytesToNumber(r) const C = C_.subtract(A.multiply(rInt)) return C } diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html index 4af9c1485..105c1e08e 100644 --- a/lnbits/extensions/cashu/templates/cashu/wallet.html +++ b/lnbits/extensions/cashu/templates/cashu/wallet.html @@ -1060,7 +1060,11 @@ page_container %} promiseToProof: function (id, amount, C_hex, secret, r) { const C_ = nobleSecp256k1.Point.fromHex(C_hex) const A = this.keys[amount] - const C = step3Alice(C_, r, nobleSecp256k1.Point.fromHex(A)) + const C = step3Alice( + C_, + nobleSecp256k1.utils.hexToBytes(r), + nobleSecp256k1.Point.fromHex(A) + ) return { id, amount, @@ -1226,7 +1230,9 @@ page_container %} const rs = [] for (let i = 0; i < amounts.length; i++) { const secret = nobleSecp256k1.utils.randomBytes(32) - // const secret = nobleSecp256k1.utils.hexToBytes('0000000000000000000000000000000000000000000000000000000000000003') + // const secret = nobleSecp256k1.utils.hexToBytes( + // '0000000000000000000000000000000000000000000000000000000000000000' + // ) // todo: base64Url const encodedSecret = uint8ToBase64.encode(secret) secrets.push(encodedSecret) From bddb13269bf26dda4b4c9feb7ecb8542a2774bd9 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Thu, 3 Nov 2022 15:15:09 +0200 Subject: [PATCH 005/106] fix: token validation --- lnbits/extensions/cashu/static/js/dhke.js | 3 +++ lnbits/extensions/cashu/templates/cashu/wallet.html | 2 +- lnbits/extensions/cashu/views_api.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/cashu/static/js/dhke.js b/lnbits/extensions/cashu/static/js/dhke.js index 935bf6d47..9c7aee1b4 100644 --- a/lnbits/extensions/cashu/static/js/dhke.js +++ b/lnbits/extensions/cashu/static/js/dhke.js @@ -20,6 +20,9 @@ async function hashToCurve(secretMessage) { } async function step1Alice(secretMessage) { + // todo: document & validate `secretMessage` format + secretMessage = uint8ToBase64.encode(secretMessage) + secretMessage = new TextEncoder().encode(secretMessage); const Y = await hashToCurve(secretMessage) const rpk = nobleSecp256k1.utils.randomPrivateKey() const r = bytesToNumber(rpk) diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html index 105c1e08e..26bc3d26c 100644 --- a/lnbits/extensions/cashu/templates/cashu/wallet.html +++ b/lnbits/extensions/cashu/templates/cashu/wallet.html @@ -1278,7 +1278,7 @@ page_container %} // } this.sendData.tokens = '' this.sendData.tokensBase64 = '' - console.log('### sendTokens', sendTokens) + // console.log('### sendTokens', sendTokens) // this.sendData.tokens = sendTokens.map((token, tokenIndex) => { // return this.promiseToProof( // token.promises[tokenIndex].amount, diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py index 4d3e854c6..806347cbf 100644 --- a/lnbits/extensions/cashu/views_api.py +++ b/lnbits/extensions/cashu/views_api.py @@ -353,7 +353,7 @@ async def split( keyset = ledger.keysets.keysets[cashu.keyset_id] split_return = await ledger.split(proofs, amount, outputs, keyset) except Exception as exc: - HTTPException( + raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(exc), ) From 359b8bc4d2e14e93ec68a25dd9c1e3e39ac50627 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 4 Nov 2022 15:25:51 +0100 Subject: [PATCH 006/106] works --- .../cashu/templates/cashu/wallet.html | 579 ++++++++++-------- 1 file changed, 335 insertions(+), 244 deletions(-) diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html index 26bc3d26c..3a9c48dbf 100644 --- a/lnbits/extensions/cashu/templates/cashu/wallet.html +++ b/lnbits/extensions/cashu/templates/cashu/wallet.html @@ -10,8 +10,9 @@ page_container %}
Pay invoice @@ -43,28 +45,28 @@ page_container %}
-
+
ReceiveReceive Tokens
-
-
+
+
- Send
@@ -82,24 +84,24 @@ page_container %} :data="tokenList" :columns="tokensTable.columns" :pagination.sync="tokensTable.pagination" - no-data-label="No tokens made yet" + no-data-label="No tokens yet" :filter="tokensTable.filter" > {% raw %} {% endraw %} @@ -186,17 +183,17 @@ page_container %}
{{props.row.amount}}
-
{{props.row.date}}
+
{{props.row.bolt11}}
@@ -269,6 +266,9 @@ page_container %}
{{props.row.date}}
+
{{props.row.token}}
@@ -931,13 +931,6 @@ page_container %} field: 'sum', sortable: true } - // { - // name: 'memo', - // align: 'left', - // label: 'Memo', - // field: 'memo', - // sortable: true - // } ], pagination: { rowsPerPage: 5 @@ -960,13 +953,6 @@ page_container %} field: 'amount', sortable: true }, - // { - // name: 'memo', - // align: 'left', - // label: 'Memo', - // field: 'memo', - // sortable: true - // }, { name: 'date', align: 'left', @@ -974,6 +960,13 @@ page_container %} field: 'date', sortable: true }, + // { + // name: 'memo', + // align: 'left', + // label: 'Memo', + // field: 'memo', + // sortable: true + // }, { name: 'bolt11', align: 'left', @@ -1020,6 +1013,13 @@ page_container %} field: 'date', sortable: true }, + // { + // name: 'memo', + // align: 'left', + // label: 'Memo', + // field: 'memo', + // sortable: true + // }, { name: 'token', align: 'left', From 9479d6f97060efc9e0e5079fc8e308bd67101118 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Mon, 14 Nov 2022 08:01:53 -0600 Subject: [PATCH 101/106] open in same window --- lnbits/extensions/cashu/templates/cashu/mint.html | 1 - 1 file changed, 1 deletion(-) diff --git a/lnbits/extensions/cashu/templates/cashu/mint.html b/lnbits/extensions/cashu/templates/cashu/mint.html index b9300c3d8..5a899bdb1 100644 --- a/lnbits/extensions/cashu/templates/cashu/mint.html +++ b/lnbits/extensions/cashu/templates/cashu/mint.html @@ -14,7 +14,6 @@ class="q-my-xl text-white" style="font-size: 1.5rem" href="../wallet?mint_id={{ mint_id }}" - target="”_blank”" >Open wallet From e44e8c855c5533d3e54caf2e8ad44d4415fe3c75 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Mon, 14 Nov 2022 08:08:35 -0600 Subject: [PATCH 102/106] update mint cover --- .../cashu/templates/cashu/mint.html | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/lnbits/extensions/cashu/templates/cashu/mint.html b/lnbits/extensions/cashu/templates/cashu/mint.html index 5a899bdb1..2077a509c 100644 --- a/lnbits/extensions/cashu/templates/cashu/mint.html +++ b/lnbits/extensions/cashu/templates/cashu/mint.html @@ -17,47 +17,47 @@ >Open wallet -
-
-
-

Read the following carefully!

-

- This is a - Cashu - mint which is an Ecash system on Bitcoin. -

-

- Open this page in your browser
- Before you continue, make sure to open this page in your device's - native browser application (Safari for iOS, Chrome for Android) if - you want to add Cashu wallet to your home screen. -

-

- Add to home screen
- You can add Cashu to your home screen as a progressive web app - (PWA). When you open the wallet in your browser, 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, meaning losing access to your wallet will - mean you will lose the 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
- We hold no responsibility for people losing access to funds. Use at - your own risk! -

-
+ + + + +
Read the following carefully!
+

+ This is a + Cashu + mint which is an Ecash system on Bitcoin. +

+

+ Open this page in your browser
+ Before you continue, make sure to open this page in your device's + native browser application (Safari for iOS, Chrome for Android) if you + want to add Cashu wallet to your home screen. +

+

+ Add to home screen
+ You can add Cashu to your home screen as a progressive web app (PWA). + When you open the wallet in your browser, 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, meaning losing access to your wallet will + mean you will lose the 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
+ We hold no responsibility for people losing access to funds. Use at + your own risk! +

From d6cf0311d8cd1402ea1b8576eb30f1e86089dad6 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Mon, 14 Nov 2022 08:09:15 -0600 Subject: [PATCH 103/106] update mint cover --- lnbits/extensions/cashu/templates/cashu/mint.html | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lnbits/extensions/cashu/templates/cashu/mint.html b/lnbits/extensions/cashu/templates/cashu/mint.html index 2077a509c..5afb03432 100644 --- a/lnbits/extensions/cashu/templates/cashu/mint.html +++ b/lnbits/extensions/cashu/templates/cashu/mint.html @@ -4,11 +4,7 @@
- +

{{ mint_name }}

Date: Mon, 14 Nov 2022 08:09:56 -0600 Subject: [PATCH 104/106] update mint cover --- lnbits/extensions/cashu/templates/cashu/mint.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/cashu/templates/cashu/mint.html b/lnbits/extensions/cashu/templates/cashu/mint.html index 5afb03432..2077a509c 100644 --- a/lnbits/extensions/cashu/templates/cashu/mint.html +++ b/lnbits/extensions/cashu/templates/cashu/mint.html @@ -4,7 +4,11 @@
- +

{{ mint_name }}

Date: Mon, 14 Nov 2022 08:14:59 -0600 Subject: [PATCH 105/106] update mint cover --- .../cashu/templates/cashu/mint.html | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/lnbits/extensions/cashu/templates/cashu/mint.html b/lnbits/extensions/cashu/templates/cashu/mint.html index 2077a509c..50d201b3b 100644 --- a/lnbits/extensions/cashu/templates/cashu/mint.html +++ b/lnbits/extensions/cashu/templates/cashu/mint.html @@ -24,31 +24,28 @@
Read the following carefully!

This is a - Cashu - mint which is an Ecash system on Bitcoin. + mint. Cashu is an ecash system for Bitcoin.

- Open this page in your browser
- Before you continue, make sure to open this page in your device's - native browser application (Safari for iOS, Chrome for Android) if you - want to add Cashu wallet to your home screen. + 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) if you want to add Cashu wallet to your home screen.

- Add to home screen
+ Add wallet to home screen
You can add Cashu to your home screen as a progressive web app (PWA). - When you open the wallet in your browser, 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. + 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, meaning losing access to your wallet will - mean you will lose the funds. The wallet stores ecash tokens on your + 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. From 8e9b2cfc38bbdc30a710451b157849f710204396 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Mon, 14 Nov 2022 08:16:46 -0600 Subject: [PATCH 106/106] update mint cover --- lnbits/extensions/cashu/templates/cashu/mint.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/cashu/templates/cashu/mint.html b/lnbits/extensions/cashu/templates/cashu/mint.html index 50d201b3b..ee6ab606c 100644 --- a/lnbits/extensions/cashu/templates/cashu/mint.html +++ b/lnbits/extensions/cashu/templates/cashu/mint.html @@ -33,7 +33,8 @@ 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) if you want to add Cashu wallet to your home screen. + Android). Do not use Cashu in an embedded browser that opens when you + click a link in a messenger.

Add wallet to home screen