Skip to content

Commit

Permalink
Merge branch 'main' into mint_use_embit_for_lnbits
Browse files Browse the repository at this point in the history
  • Loading branch information
callebtc committed Mar 26, 2024
2 parents 02d090f + e040479 commit d24e95b
Show file tree
Hide file tree
Showing 45 changed files with 980 additions and 351 deletions.
27 changes: 19 additions & 8 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ MINT_DERIVATION_PATH="m/0'/0'/0'"

MINT_DATABASE=data/mint

# Lightning
# Supported: FakeWallet, LndRestWallet, CoreLightningRestWallet, BlinkWallet, LNbitsWallet, StrikeWallet
MINT_LIGHTNING_BACKEND=FakeWallet
# Funding source backends
# Supported: FakeWallet, LndRestWallet, CoreLightningRestWallet, BlinkWallet, LNbitsWallet, StrikeUSDWallet
MINT_BACKEND_BOLT11_SAT=FakeWallet
# Only works if a usd derivation path is set
# MINT_BACKEND_BOLT11_SAT=FakeWallet

# for use with LNbitsWallet
MINT_LNBITS_ENDPOINT=https://legend.lnbits.com
Expand All @@ -76,18 +78,27 @@ MINT_CORELIGHTNING_REST_CERT="./clightning-2-rest/certificate.pem"
# Use with BlinkWallet
MINT_BLINK_KEY=blink_abcdefgh

# Use with StrikeWallet
# Use with StrikeUSDWallet (usd only, does not currently support sats/msats)
MINT_STRIKE_KEY=ABC123

# fee to reserve in percent of the amount
LIGHTNING_FEE_PERCENT=1.0
# minimum fee to reserve
LIGHTNING_RESERVE_FEE_MIN=2000

# Management
# max peg-in amount in satoshis
# Limits

# Max peg-in amount in satoshis
# MINT_MAX_PEG_IN=100000
# max peg-out amount in satoshis
# Max peg-out amount in satoshis
# MINT_MAX_PEG_OUT=100000
# use to allow only peg-out to LN
# Use to allow only peg-out to LN
# MINT_PEG_OUT_ONLY=FALSE

# Rate limit requests to mint. Make sure that you can see request IPs in the logs.
# You may need to adjust your reverse proxy if you only see requests originating from 127.0.0.1
# MINT_RATE_LIMIT=TRUE
# Determines the number of all requests allowed per minute per IP
# MINT_GLOBAL_RATE_LIMIT_PER_MINUTE=60
# Determines the number of transactions (mint, melt, swap) allowed per minute per IP
# MINT_TRANSACTION_RATE_LIMIT_PER_MINUTE=20
13 changes: 12 additions & 1 deletion .github/workflows/docker.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
name: Docker Build

on:
push:
release:
types: [published]

Expand All @@ -21,6 +20,15 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Cache Docker layers
uses: actions/cache@v4
id: cache
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Determine Tag
id: get_tag
run: |
Expand All @@ -36,3 +44,6 @@ jobs:
context: .
push: ${{ github.event_name == 'release' }}
tags: ${{ secrets.DOCKER_USERNAME }}/${{ github.event.repository.name }}:${{ steps.get_tag.outputs.tag }}
platforms: linux/amd64,linux/arm64
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
35 changes: 35 additions & 0 deletions .github/workflows/pypi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Pip package

on:
push:
release:
types: [published]

jobs:
build-and-push:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]
poetry-version: ["1.7.1"]
steps:
- name: Checkout
uses: actions/checkout@v4

- uses: ./.github/actions/prepare
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ matrix.poetry-version }}

- name: Build package
run: |
poetry build
- name: Install package
run: |
pip install --upgrade dist/*.whl
- name: Upload to PyPI on release
if: github.event_name == 'release'
run: |
poetry publish -u __token__ -p ${{ secrets.PYPI_API_TOKEN }}
2 changes: 1 addition & 1 deletion .github/workflows/regtest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:
MINT_PORT: 3337
MINT_TEST_DATABASE: ${{ inputs.mint-database }}
TOR: false
MINT_LIGHTNING_BACKEND: ${{ inputs.backend-wallet-class }}
MINT_BACKEND_BOLT11_SAT: ${{ inputs.backend-wallet-class }}
MINT_LNBITS_ENDPOINT: http://localhost:5001
MINT_LNBITS_KEY: d08a3313322a4514af75d488bcc27eee
MINT_LND_REST_ENDPOINT: https://localhost:8081/
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
poetry-version: ${{ inputs.poetry-version }}
- name: Run tests
env:
MINT_LIGHTNING_BACKEND: FakeWallet
MINT_BACKEND_BOLT11_SAT: FakeWallet
WALLET_NAME: test_wallet
MINT_HOST: localhost
MINT_PORT: 3337
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ test:
test-lndrest:
PYTHONUNBUFFERED=1 \
DEBUG=true \
MINT_LIGHTNING_BACKEND=LndRestWallet \
MINT_BACKEND_BOLT11_SAT=LndRestWallet \
MINT_LND_REST_ENDPOINT=https://localhost:8081/ \
MINT_LND_REST_CERT=../cashu-regtest-enviroment/data/lnd-3/tls.cert \
MINT_LND_REST_MACAROON=../cashu-regtest-enviroment/data/lnd-3/data/chain/bitcoin/regtest/admin.macaroon \
Expand Down
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

*Disclaimer: The author is NOT a cryptographer and this work has not been reviewed. This means that there is very likely a fatal flaw somewhere. Cashu is still experimental and not production-ready.*

Cashu is an Ecash implementation based on David Wagner's variant of Chaumian blinding ([protocol specs](https://github.com/cashubtc/nuts)). Token logic based on [minicash](https://github.com/phyro/minicash) ([description](https://gist.github.com/phyro/935badc682057f418842c72961cf096c)) which implements a [Blind Diffie-Hellman Key Exchange](https://cypherpunks.venona.com/date/1996/03/msg01848.html) scheme written down [here](https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406). The database mechanics in Cashu Nutshell and the Lightning backend uses parts from [LNbits](https://github.com/lnbits/lnbits-legend).
Cashu is an Ecash implementation based on David Wagner's variant of Chaumian blinding ([protocol specs](https://github.com/cashubtc/nuts)). Token logic based on [minicash](https://github.com/phyro/minicash) ([description](https://gist.github.com/phyro/935badc682057f418842c72961cf096c)) which implements a [Blind Diffie-Hellman Key Exchange](https://cypherpunks.venona.com/date/1996/03/msg01848.html) scheme written down [here](https://gist.github.com/RubenSomsen/be7a4760dd4596d06963d67baf140406).

<p align="center">
<a href="#the-cashu-protocol">Cashu protocol</a> ·
Expand Down Expand Up @@ -169,12 +169,19 @@ You can find the API docs at [http://localhost:4448/docs](http://localhost:4448/
# Running a mint
This command runs the mint on your local computer. Skip this step if you want to use the [public test mint](#test-instance) instead.

Before you can run your own mint, make sure to enable a Lightning backend in `MINT_LIGHTNING_BACKEND` and set `MINT_PRIVATE_KEY` in your `.env` file.
## Docker

```
docker run -d -p 3338:3338 --name nutshell -e MINT_BACKEND_BOLT11_SAT=FakeWallet -e MINT_LISTEN_HOST=0.0.0.0 -e MINT_LISTEN_PORT=3338 -e MINT_PRIVATE_KEY=TEST_PRIVATE_KEY cashubtc/nutshell:0.15.2 poetry run mint
```

## From this repository
Before you can run your own mint, make sure to enable a Lightning backend in `MINT_BACKEND_BOLT11_SAT` and set `MINT_PRIVATE_KEY` in your `.env` file.
```bash
poetry run mint
```

For testing, you can use Nutshell without a Lightning backend by setting `MINT_LIGHTNING_BACKEND=FakeWallet` in the `.env` file.
For testing, you can use Nutshell without a Lightning backend by setting `MINT_BACKEND_BOLT11_SAT=FakeWallet` in the `.env` file.


# Running tests
Expand All @@ -185,7 +192,7 @@ poetry install --with dev

Then, make sure to set up your mint's `.env` file to use a fake Lightning backend and disable Tor:
```bash
MINT_LIGHTNING_BACKEND=FakeWallet
MINT_BACKEND_BOLT11_SAT=FakeWallet
TOR=FALSE
```
You can run the tests with
Expand Down
13 changes: 13 additions & 0 deletions cashu/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,13 @@ def from_row(cls, row: Row):
# ------- API: INFO -------


class MintMeltMethodSetting(BaseModel):
method: str
unit: str
min_amount: Optional[int] = None
max_amount: Optional[int] = None


class GetInfoResponse(BaseModel):
name: Optional[str] = None
pubkey: Optional[str] = None
Expand Down Expand Up @@ -528,6 +535,12 @@ class CheckFeesResponse_deprecated(BaseModel):
# ------- API: RESTORE -------


class PostRestoreRequest(BaseModel):
outputs: List[BlindedMessage] = Field(
..., max_items=settings.mint_max_request_length
)


class PostRestoreResponse(BaseModel):
outputs: List[BlindedMessage] = []
signatures: List[BlindedSignature] = []
Expand Down
27 changes: 19 additions & 8 deletions cashu/core/crypto/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def derive_keys(mnemonic: str, derivation_path: str):
"""
Deterministic derivation of keys for 2^n values.
"""
<<<<<<< HEAD
if use_bip32_lib:
root = BIP32.from_seed(mnemonic.encode()) # type: ignore
orders_str = [f"/{i}'" for i in range(settings.max_order)]
Expand All @@ -43,28 +44,38 @@ def derive_keys(mnemonic: str, derivation_path: str):
)
for i in range(settings.max_order)
}
=======
bip32 = BIP32.from_seed(mnemonic.encode())
orders_str = [f"/{i}'" for i in range(settings.max_order)]
return {
2**i: PrivateKey(
bip32.get_privkey_from_path(derivation_path + orders_str[i]),
raw=True,
)
for i in range(settings.max_order)
}
>>>>>>> main


def derive_keys_sha256(master_key: str, derivation_path: str = ""):
def derive_keys_sha256(seed: str, derivation_path: str = ""):
"""
Deterministic derivation of keys for 2^n values.
TODO: Implement BIP32.
"""
return {
2
** i: PrivateKey(
hashlib.sha256(
(master_key + derivation_path + str(i)).encode("utf-8")
).digest()[:32],
2**i: PrivateKey(
hashlib.sha256((seed + derivation_path + str(i)).encode("utf-8")).digest()[
:32
],
raw=True,
)
for i in range(settings.max_order)
}


def derive_pubkey(master_key: str):
def derive_pubkey(seed: str):
return PrivateKey(
hashlib.sha256((master_key).encode("utf-8")).digest()[:32],
hashlib.sha256((seed).encode("utf-8")).digest()[:32],
raw=True,
).pubkey

Expand Down
7 changes: 3 additions & 4 deletions cashu/core/legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@


def derive_keys_backwards_compatible_insecure_pre_0_12(
master_key: str, derivation_path: str = ""
seed: str, derivation_path: str = ""
):
"""
WARNING: Broken key derivation for backwards compatibility with 0.11.
"""
return {
2
** i: PrivateKey(
hashlib.sha256((master_key + derivation_path + str(i)).encode("utf-8"))
2**i: PrivateKey(
hashlib.sha256((seed + derivation_path + str(i)).encode("utf-8"))
.hexdigest()
.encode("utf-8")[:32],
raw=True,
Expand Down
73 changes: 52 additions & 21 deletions cashu/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,52 @@ class MintSettings(CashuSettings):
mint_derivation_path_list: List[str] = Field(default=[])
mint_listen_host: str = Field(default="127.0.0.1")
mint_listen_port: int = Field(default=3338)
mint_lightning_backend: str = Field(default="LNbitsWallet")

mint_database: str = Field(default="data/mint")
mint_test_database: str = Field(default="test_data/test_mint")
mint_duplicate_keysets: bool = Field(
default=True,
title="Duplicate keysets",
description=(
"Whether to duplicate keysets for backwards compatibility before v1 API"
" (Nutshell 0.15.0)."
),
)


class MintBackends(MintSettings):
mint_lightning_backend: str = Field(default="") # deprecated
mint_backend_bolt11_sat: str = Field(default="")
mint_backend_bolt11_usd: str = Field(default="")

mint_lnbits_endpoint: str = Field(default=None)
mint_lnbits_key: str = Field(default=None)
mint_strike_key: str = Field(default=None)
mint_blink_key: str = Field(default=None)


class MintLimits(MintSettings):
mint_rate_limit: bool = Field(
default=False, title="Rate limit", description="IP-based rate limiter."
)
mint_global_rate_limit_per_minute: int = Field(
default=60,
gt=0,
title="Global rate limit per minute",
description="Number of requests an IP can make per minute to all endpoints.",
)
mint_transaction_rate_limit_per_minute: int = Field(
default=20,
gt=0,
title="Transaction rate limit per minute",
description="Number of requests an IP can make per minute to transaction endpoints.",
)
mint_max_request_length: int = Field(
default=1000,
title="Maximum request length",
description="Maximum length of REST API request arrays.",
)

mint_peg_out_only: bool = Field(
default=False,
title="Peg-out only",
Expand All @@ -73,27 +116,9 @@ class MintSettings(CashuSettings):
title="Maximum peg-out",
description="Maximum amount for a melt operation.",
)
mint_max_request_length: int = Field(
default=1000,
title="Maximum request length",
description="Maximum length of REST API request arrays.",
)
mint_max_balance: int = Field(
default=None, title="Maximum mint balance", description="Maximum mint balance."
)
mint_duplicate_keysets: bool = Field(
default=True,
title="Duplicate keysets",
description=(
"Whether to duplicate keysets for backwards compatibility before v1 API"
" (Nutshell 0.15.0)."
),
)

mint_lnbits_endpoint: str = Field(default=None)
mint_lnbits_key: str = Field(default=None)
mint_strike_key: str = Field(default=None)
mint_blink_key: str = Field(default=None)


class FakeWalletSettings(MintSettings):
Expand Down Expand Up @@ -134,13 +159,13 @@ class WalletSettings(CashuSettings):
"wss://relay.damus.io",
"wss://nostr.mom",
"wss://relay.snort.social",
"wss://nostr.fmt.wiz.biz",
"wss://nostr.mutinywallet.com",
"wss://relay.minibits.cash",
"wss://nos.lol",
"wss://relay.nostr.band",
"wss://relay.bitcoiner.social",
"wss://140.f7z.io",
"wss://relayable.org",
"wss://relay.primal.net",
]
)

Expand All @@ -167,6 +192,8 @@ class Settings(
LndRestFundingSource,
CoreLightningRestFundingSource,
FakeWalletSettings,
MintLimits,
MintBackends,
MintSettings,
MintInformation,
WalletSettings,
Expand Down Expand Up @@ -204,5 +231,9 @@ def startup_settings_tasks():
if settings.socks_host and settings.socks_port:
settings.socks_proxy = f"socks5://{settings.socks_host}:{settings.socks_port}"

# backwards compatibility: set mint_backend_bolt11_sat from mint_lightning_backend
if settings.mint_lightning_backend:
settings.mint_backend_bolt11_sat = settings.mint_lightning_backend


startup_settings_tasks()
Loading

0 comments on commit d24e95b

Please sign in to comment.