From bb6efeda03747bb3fc1e459938bc6c3a4d17b313 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Sun, 9 Oct 2022 22:35:54 +0200 Subject: [PATCH 1/7] coinselect --- cashu/wallet/wallet.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/cashu/wallet/wallet.py b/cashu/wallet/wallet.py index b00c982..b7ec8bb 100644 --- a/cashu/wallet/wallet.py +++ b/cashu/wallet/wallet.py @@ -388,24 +388,31 @@ class Wallet(LedgerAPI): ).decode() return token - async def _get_spendable_proofs(self, proofs: List[Proof]): + async def _select_proofs_to_send(self, proofs: List[Proof], amount_to_send: int): """ Selects proofs that can be used with the current mint. Chooses: 1) Proofs that are not marked as reserved 2) Proofs that have a keyset id that is in self.keysets (active keysets of mint) - !!! optional for backwards compatibility with legacy clients """ + # select proofs that are in the active keysets of the mint proofs = [ p for p in proofs if p.id in self.keysets or not p.id ] # "or not p.id" is for backwards compatibility with proofs without a keyset id + # select proofs that are not reserved proofs = [p for p in proofs if not p.reserved] - return proofs + # select proofs based on amount to send + sorted_proofs = sorted(proofs, key=lambda p: p.amount) + send_proofs = [] + while sum_proofs(send_proofs) < amount_to_send: + send_proofs.append(sorted_proofs[len(send_proofs)]) + return send_proofs async def split_to_send(self, proofs: List[Proof], amount, scnd_secret: str = None): """Like self.split but only considers non-reserved tokens.""" if scnd_secret: logger.debug(f"Spending conditions: {scnd_secret}") - spendable_proofs = await self._get_spendable_proofs(proofs) + spendable_proofs = await self._select_proofs_to_send(proofs, amount) if sum_proofs(spendable_proofs) < amount: raise Exception("balance too low.") return await self.split( From c002ac8e9244849cac9f335cf284348b9e0cbe13 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 11 Jan 2023 03:24:20 +0100 Subject: [PATCH 2/7] add sponsor button --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..3f6fd13 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://legend.lnbits.com/tipjar/794 From 5d1a539b02757cf74ab61120496cc9c4fce54aa3 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 11 Jan 2023 03:36:49 +0100 Subject: [PATCH 3/7] mypy --- cashu/wallet/wallet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cashu/wallet/wallet.py b/cashu/wallet/wallet.py index b265d27..731164f 100644 --- a/cashu/wallet/wallet.py +++ b/cashu/wallet/wallet.py @@ -563,7 +563,7 @@ class Wallet(LedgerAPI): proofs = [p for p in proofs if not p.reserved] # select proofs based on amount to send sorted_proofs = sorted(proofs, key=lambda p: p.amount) - send_proofs = [] + send_proofs: List[Proof] = [] while sum_proofs(send_proofs) < amount_to_send: send_proofs.append(sorted_proofs[len(send_proofs)]) return send_proofs From db034a0e00b4df05fe4841ed4189cc822c7ef870 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 11 Jan 2023 03:41:11 +0100 Subject: [PATCH 4/7] fix tests --- cashu/wallet/wallet.py | 7 +++++-- tests/test_wallet.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cashu/wallet/wallet.py b/cashu/wallet/wallet.py index 731164f..b9a45f1 100644 --- a/cashu/wallet/wallet.py +++ b/cashu/wallet/wallet.py @@ -561,6 +561,10 @@ class Wallet(LedgerAPI): ] # "or not p.id" is for backwards compatibility with proofs without a keyset id # select proofs that are not reserved proofs = [p for p in proofs if not p.reserved] + + if sum_proofs(proofs) < amount_to_send: + raise Exception("balance too low.") + # select proofs based on amount to send sorted_proofs = sorted(proofs, key=lambda p: p.amount) send_proofs: List[Proof] = [] @@ -598,8 +602,7 @@ class Wallet(LedgerAPI): if scnd_secret: logger.debug(f"Spending conditions: {scnd_secret}") spendable_proofs = await self._select_proofs_to_send(proofs, amount) - if sum_proofs(spendable_proofs) < amount: - raise Exception("balance too low.") + keep_proofs, send_proofs = await self.split( [p for p in spendable_proofs if not p.reserved], amount, scnd_secret ) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 493503f..1917792 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -101,7 +101,7 @@ async def test_split_to_send(wallet1: Wallet): keep_proofs, spendable_proofs = await wallet1.split_to_send( wallet1.proofs, 32, set_reserved=True ) - get_spendable = await wallet1._get_spendable_proofs(wallet1.proofs) + get_spendable = await wallet1._select_proofs_to_send(wallet1.proofs, 32) assert keep_proofs == get_spendable assert sum_proofs(spendable_proofs) == 32 From e24ed990a190a5273a00b13ddfba38f58706357c Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 11 Jan 2023 03:43:53 +0100 Subject: [PATCH 5/7] comments --- cashu/wallet/wallet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cashu/wallet/wallet.py b/cashu/wallet/wallet.py index b9a45f1..cc31331 100644 --- a/cashu/wallet/wallet.py +++ b/cashu/wallet/wallet.py @@ -561,11 +561,11 @@ class Wallet(LedgerAPI): ] # "or not p.id" is for backwards compatibility with proofs without a keyset id # select proofs that are not reserved proofs = [p for p in proofs if not p.reserved] - + # check that enough spendable proofs exist if sum_proofs(proofs) < amount_to_send: raise Exception("balance too low.") - # select proofs based on amount to send + # coinselect based on amount to send sorted_proofs = sorted(proofs, key=lambda p: p.amount) send_proofs: List[Proof] = [] while sum_proofs(send_proofs) < amount_to_send: From a7b9fb1b0d4c55c8705c0b948574ed2c915e36ef Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 11 Jan 2023 03:48:00 +0100 Subject: [PATCH 6/7] bump version to 0.7.1. --- README.md | 2 +- cashu/core/settings.py | 2 +- pyproject.toml | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2f40cc3..67e6c57 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ cashu info Returns: ```bash -Version: 0.7.0 +Version: 0.7.1 Debug: False Cashu dir: /home/user/.cashu Wallet: wallet diff --git a/cashu/core/settings.py b/cashu/core/settings.py index e22b118..e3dd787 100644 --- a/cashu/core/settings.py +++ b/cashu/core/settings.py @@ -57,4 +57,4 @@ NOSTR_PRIVATE_KEY = env.str("NOSTR_PRIVATE_KEY", default=None) NOSTR_RELAYS = env.list("NOSTR_RELAYS", default=["wss://nostr-pub.wellorder.net"]) MAX_ORDER = 64 -VERSION = "0.7.0" +VERSION = "0.7.1" diff --git a/pyproject.toml b/pyproject.toml index 8745a1b..861f4a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "cashu" -version = "0.7.0" +version = "0.7.1" description = "Ecash wallet and mint." authors = ["calle "] license = "MIT" diff --git a/setup.py b/setup.py index 20093d0..7ab643e 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ entry_points = {"console_scripts": ["cashu = cashu.wallet.cli:cli"]} setuptools.setup( name="cashu", - version="0.7.0", + version="0.7.1", description="Ecash wallet and mint with Bitcoin Lightning support", long_description=long_description, long_description_content_type="text/markdown", From d3a0a74989019071f54d8bc5e6c5402c100aecab Mon Sep 17 00:00:00 2001 From: Tim Bouma Date: Wed, 11 Jan 2023 19:09:13 -0500 Subject: [PATCH 7/7] formatting plus missing parenthesis --- docs/specs/00.md | 51 ++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/docs/specs/00.md b/docs/specs/00.md index 7957895..ec4a43e 100644 --- a/docs/specs/00.md +++ b/docs/specs/00.md @@ -5,68 +5,76 @@ Receiving user: `Carol` Mint: `Bob` ## Bob (mint) + - `k` private key of mint (one for each amount) - `K` public key of mint - `Q` promise (blinded signature) ## Alice (user) + - `x` random string (secret message), corresponds to point `Y` on curve - `r` private key (blinding factor) - `T` blinded message - `Z` proof (unblinded signature) -# Blind Diffie-Hellmann key exchange (BDHKE) -- Mint `Bob` publishes `K = kG` -- `Alice` picks secret `x` and computes `Y = hash_to_curve(x)` -- `Alice` sends to `Bob`: `T = Y + rG` with `r` being a random nonce -- `Bob` sends back to `Alice` blinded key: `Q = kT` (these two steps are the DH key exchange) -- `Alice` can calculate the unblinded key as `Q - rK = kY + krG - krG = kY = Z` -- Alice can take the pair `(x, Z)` as a token and can send it to `Carol`. -- `Carol` can send `(x, Z)` to `Bob` who then checks that `k*hash_to_curve(x) == Z`, and if so treats it as a valid spend of a token, adding `x` to the list of spent secrets. +## Blind Diffie-Hellmann key exchange (BDHKE) + +- Mint `Bob` publishes `K = kG` +- `Alice` picks secret `x` and computes `Y = hash_to_curve(x)` +- `Alice` sends to `Bob`: `T = Y + rG` with `r` being a random nonce +- `Bob` sends back to `Alice` blinded key: `Q = kT` (these two steps are the DH key exchange) +- `Alice` can calculate the unblinded key as `Q - rK = kY + krG - krG = kY = Z` +- Alice can take the pair `(x, Z)` as a token and can send it to `Carol`. +- `Carol` can send `(x, Z)` to `Bob` who then checks that `k*hash_to_curve(x) == Z`, and if so treats it as a valid spend of a token, adding `x` to the list of spent secrets. ## 0.1 - Models ### `BlindedMessage` + A encrypted ("blinded") secret and an amount sent from `Alice` to `Bob`. ```json { - "amount": int, - "B_": str + "amount": int, + "B_": str } ``` ### `BlindedSignature` + A signature on the `BlindedMessage` sent from `Bob` to `Alice`. ```json { - "amount": int, - "C_": str, - "id": str | None + "amount": int, + "C_": str, + "id": str | None } ``` ### `Proof` -A `Proof` is also called a `Token` and has the following form: + +A `Proof` is also called a `Token` and has the following form: ```json { - "amount": int, - "secret": str, - "C": str, - "id": None | str, - "script": P2SHScript | None, + "amount": int, + "secret": str, + "C": str, + "id": None | str, + "script": P2SHScript | None, } ``` ### `Proofs` + A list of `Proof`'s. In general, this will be used for most operations instead of a single `Proof`. `Proofs` can be serialized (see Methods/Serialization [TODO: Link Serialization]) ## 0.2 - Methods ### Serialization of `Proofs` -To send and receive `Proofs`, wallets serialize them in a `base64_urlsafe` format. + +To send and receive `Proofs`, wallets serialize them in a `base64_urlsafe` format. Example: @@ -84,10 +92,11 @@ Example: "secret": "d_PPc5KpuAB2M60WYAW5-Q", "C": "0270e0a37f7a0b21eab43af751dd3c03f61f04c626c0448f603f1d1f5ae5a7d7e6" } +] ``` becomes ``` W3siaWQiOiAiRFNBbDludnZ5ZnZhIiwgImFtb3VudCI6IDgsICJzZWNyZXQiOiAiRGJSS0l5YTBldGR3STVzRkFOMEFYUSIsICJDIjogIjAyZGY3ZjJmYzI5NjMxYjcxYTFkYjExYzE2M2IwYjFjYjQwNDQ0YWEyYjNkMjUzZDQzYjY4ZDc3YTcyZWQyZDYyNSJ9LCB7ImlkIjogIkRTQWw5bnZ2eWZ2YSIsICJhbW91bnQiOiAxNiwgInNlY3JldCI6ICJkX1BQYzVLcHVBQjJNNjBXWUFXNS1RIiwgIkMiOiAiMDI3MGUwYTM3ZjdhMGIyMWVhYjQzYWY3NTFkZDNjMDNmNjFmMDRjNjI2YzA0NDhmNjAzZjFkMWY1YWU1YTdkN2U2In1d -``` \ No newline at end of file +```